diff --git a/docs/rest-apis/gateway/llm-provider-template-management.md b/docs/rest-apis/gateway/llm-provider-template-management.md index 11c6b7c0cf..50d6d453a8 100644 --- a/docs/rest-apis/gateway/llm-provider-template-management.md +++ b/docs/rest-apis/gateway/llm-provider-template-management.md @@ -254,11 +254,24 @@ Status Code **200** |»»»» promptTokens|[ExtractionIdentifier](schemas.md#schemaextractionidentifier)|false|none|none| |»»»»» location|string|true|none|Where to find the token information| |»»»»» identifier|string|true|none|JSONPath expression or header name to identify the token value| +|»»»»» fallbackIdentifiers|[string]|false|none|Additional locations to try when the primary identifier yields no
value, in order. Used where one provider returns several response
shapes on the same path.| +|»»»»» valueMap|object|false|none|Maps values this provider reports onto the vocabulary the gateway
bills on. A reported value that is not a key is used unchanged.
Used where providers name the same billing tier differently.| +|»»»»»» **additionalProperties**|string|false|none|none| |»»»» completionTokens|[ExtractionIdentifier](schemas.md#schemaextractionidentifier)|false|none|none| |»»»» totalTokens|[ExtractionIdentifier](schemas.md#schemaextractionidentifier)|false|none|none| |»»»» remainingTokens|[ExtractionIdentifier](schemas.md#schemaextractionidentifier)|false|none|none| |»»»» requestModel|[ExtractionIdentifier](schemas.md#schemaextractionidentifier)|false|none|none| |»»»» responseModel|[ExtractionIdentifier](schemas.md#schemaextractionidentifier)|false|none|none| +|»»»» cachedTokens|[ExtractionIdentifier](schemas.md#schemaextractionidentifier)|false|none|none| +|»»»» cacheWriteTokens|[ExtractionIdentifier](schemas.md#schemaextractionidentifier)|false|none|none| +|»»»» cacheWrite1hTokens|[ExtractionIdentifier](schemas.md#schemaextractionidentifier)|false|none|none| +|»»»» reasoningTokens|[ExtractionIdentifier](schemas.md#schemaextractionidentifier)|false|none|none| +|»»»» audioInputTokens|[ExtractionIdentifier](schemas.md#schemaextractionidentifier)|false|none|none| +|»»»» audioOutputTokens|[ExtractionIdentifier](schemas.md#schemaextractionidentifier)|false|none|none| +|»»»» serviceTier|[ExtractionIdentifier](schemas.md#schemaextractionidentifier)|false|none|none| +|»»»» providerFields|object|false|none|Named locations a provider-specific calculator needs but the closed
vocabulary does not cover. The value at each location is handed to
the calculator as-is; only its position is declared here.| +|»»»»» **additionalProperties**|[ExtractionIdentifier](schemas.md#schemaextractionidentifier)|false|none|none| +|»»»» cacheAccounting|string|false|none|Whether the cached token count reported by the provider is already
part of the input token total, or additional to it. One of
'inclusive' or 'additive'. Defaults to inclusive when omitted.| |»»»» resourceMappings|[LLMProviderTemplateResourceMappings](schemas.md#schemallmprovidertemplateresourcemappings)|false|none|none| |»»»»» resources|[[LLMProviderTemplateResourceMapping](schemas.md#schemallmprovidertemplateresourcemapping)]|false|none|none| |»»»»»» resource|string|true|none|Resource path pattern for this mapping| @@ -268,6 +281,14 @@ Status Code **200** |»»»»»» remainingTokens|[ExtractionIdentifier](schemas.md#schemaextractionidentifier)|false|none|none| |»»»»»» requestModel|[ExtractionIdentifier](schemas.md#schemaextractionidentifier)|false|none|none| |»»»»»» responseModel|[ExtractionIdentifier](schemas.md#schemaextractionidentifier)|false|none|none| +|»»»»»» cachedTokens|[ExtractionIdentifier](schemas.md#schemaextractionidentifier)|false|none|none| +|»»»»»» cacheWriteTokens|[ExtractionIdentifier](schemas.md#schemaextractionidentifier)|false|none|none| +|»»»»»» cacheWrite1hTokens|[ExtractionIdentifier](schemas.md#schemaextractionidentifier)|false|none|none| +|»»»»»» reasoningTokens|[ExtractionIdentifier](schemas.md#schemaextractionidentifier)|false|none|none| +|»»»»»» audioInputTokens|[ExtractionIdentifier](schemas.md#schemaextractionidentifier)|false|none|none| +|»»»»»» audioOutputTokens|[ExtractionIdentifier](schemas.md#schemaextractionidentifier)|false|none|none| +|»»»»»» serviceTier|[ExtractionIdentifier](schemas.md#schemaextractionidentifier)|false|none|none| +|»»»»»» cacheAccounting|string|false|none|Whether the cached token count reported by the provider is already
part of the input token total, or additional to it. One of
'inclusive' or 'additive'. Overrides the template-level setting for
this resource; when omitted, the template-level value is inherited.| *and* diff --git a/docs/rest-apis/gateway/schemas.md b/docs/rest-apis/gateway/schemas.md index 9c4cec8704..20c602ea54 100644 --- a/docs/rest-apis/gateway/schemas.md +++ b/docs/rest-apis/gateway/schemas.md @@ -1785,56 +1785,348 @@ and "version": "v1.0", "promptTokens": { "location": "payload", - "identifier": "$.usage.inputTokens" + "identifier": "$.usage.inputTokens", + "fallbackIdentifiers": [ + "$.usage.input_tokens", + "$.usage.prompt_tokens" + ], + "valueMap": { + "ON_DEMAND_PRIORITY": "priority", + "ON_DEMAND_FLEX": "flex" + } }, "completionTokens": { "location": "payload", - "identifier": "$.usage.inputTokens" + "identifier": "$.usage.inputTokens", + "fallbackIdentifiers": [ + "$.usage.input_tokens", + "$.usage.prompt_tokens" + ], + "valueMap": { + "ON_DEMAND_PRIORITY": "priority", + "ON_DEMAND_FLEX": "flex" + } }, "totalTokens": { "location": "payload", - "identifier": "$.usage.inputTokens" + "identifier": "$.usage.inputTokens", + "fallbackIdentifiers": [ + "$.usage.input_tokens", + "$.usage.prompt_tokens" + ], + "valueMap": { + "ON_DEMAND_PRIORITY": "priority", + "ON_DEMAND_FLEX": "flex" + } }, "remainingTokens": { "location": "payload", - "identifier": "$.usage.inputTokens" + "identifier": "$.usage.inputTokens", + "fallbackIdentifiers": [ + "$.usage.input_tokens", + "$.usage.prompt_tokens" + ], + "valueMap": { + "ON_DEMAND_PRIORITY": "priority", + "ON_DEMAND_FLEX": "flex" + } }, "requestModel": { "location": "payload", - "identifier": "$.usage.inputTokens" + "identifier": "$.usage.inputTokens", + "fallbackIdentifiers": [ + "$.usage.input_tokens", + "$.usage.prompt_tokens" + ], + "valueMap": { + "ON_DEMAND_PRIORITY": "priority", + "ON_DEMAND_FLEX": "flex" + } }, "responseModel": { "location": "payload", - "identifier": "$.usage.inputTokens" + "identifier": "$.usage.inputTokens", + "fallbackIdentifiers": [ + "$.usage.input_tokens", + "$.usage.prompt_tokens" + ], + "valueMap": { + "ON_DEMAND_PRIORITY": "priority", + "ON_DEMAND_FLEX": "flex" + } + }, + "cachedTokens": { + "location": "payload", + "identifier": "$.usage.inputTokens", + "fallbackIdentifiers": [ + "$.usage.input_tokens", + "$.usage.prompt_tokens" + ], + "valueMap": { + "ON_DEMAND_PRIORITY": "priority", + "ON_DEMAND_FLEX": "flex" + } + }, + "cacheWriteTokens": { + "location": "payload", + "identifier": "$.usage.inputTokens", + "fallbackIdentifiers": [ + "$.usage.input_tokens", + "$.usage.prompt_tokens" + ], + "valueMap": { + "ON_DEMAND_PRIORITY": "priority", + "ON_DEMAND_FLEX": "flex" + } + }, + "cacheWrite1hTokens": { + "location": "payload", + "identifier": "$.usage.inputTokens", + "fallbackIdentifiers": [ + "$.usage.input_tokens", + "$.usage.prompt_tokens" + ], + "valueMap": { + "ON_DEMAND_PRIORITY": "priority", + "ON_DEMAND_FLEX": "flex" + } + }, + "reasoningTokens": { + "location": "payload", + "identifier": "$.usage.inputTokens", + "fallbackIdentifiers": [ + "$.usage.input_tokens", + "$.usage.prompt_tokens" + ], + "valueMap": { + "ON_DEMAND_PRIORITY": "priority", + "ON_DEMAND_FLEX": "flex" + } + }, + "audioInputTokens": { + "location": "payload", + "identifier": "$.usage.inputTokens", + "fallbackIdentifiers": [ + "$.usage.input_tokens", + "$.usage.prompt_tokens" + ], + "valueMap": { + "ON_DEMAND_PRIORITY": "priority", + "ON_DEMAND_FLEX": "flex" + } }, + "audioOutputTokens": { + "location": "payload", + "identifier": "$.usage.inputTokens", + "fallbackIdentifiers": [ + "$.usage.input_tokens", + "$.usage.prompt_tokens" + ], + "valueMap": { + "ON_DEMAND_PRIORITY": "priority", + "ON_DEMAND_FLEX": "flex" + } + }, + "serviceTier": { + "location": "payload", + "identifier": "$.usage.inputTokens", + "fallbackIdentifiers": [ + "$.usage.input_tokens", + "$.usage.prompt_tokens" + ], + "valueMap": { + "ON_DEMAND_PRIORITY": "priority", + "ON_DEMAND_FLEX": "flex" + } + }, + "providerFields": { + "property1": { + "location": "payload", + "identifier": "$.usage.inputTokens", + "fallbackIdentifiers": [ + "$.usage.input_tokens", + "$.usage.prompt_tokens" + ], + "valueMap": { + "ON_DEMAND_PRIORITY": "priority", + "ON_DEMAND_FLEX": "flex" + } + }, + "property2": { + "location": "payload", + "identifier": "$.usage.inputTokens", + "fallbackIdentifiers": [ + "$.usage.input_tokens", + "$.usage.prompt_tokens" + ], + "valueMap": { + "ON_DEMAND_PRIORITY": "priority", + "ON_DEMAND_FLEX": "flex" + } + } + }, + "cacheAccounting": "inclusive", "resourceMappings": { "resources": [ { "resource": "/responses", "promptTokens": { "location": "payload", - "identifier": "$.usage.inputTokens" + "identifier": "$.usage.inputTokens", + "fallbackIdentifiers": [ + "$.usage.input_tokens", + "$.usage.prompt_tokens" + ], + "valueMap": { + "ON_DEMAND_PRIORITY": "priority", + "ON_DEMAND_FLEX": "flex" + } }, "completionTokens": { "location": "payload", - "identifier": "$.usage.inputTokens" + "identifier": "$.usage.inputTokens", + "fallbackIdentifiers": [ + "$.usage.input_tokens", + "$.usage.prompt_tokens" + ], + "valueMap": { + "ON_DEMAND_PRIORITY": "priority", + "ON_DEMAND_FLEX": "flex" + } }, "totalTokens": { "location": "payload", - "identifier": "$.usage.inputTokens" + "identifier": "$.usage.inputTokens", + "fallbackIdentifiers": [ + "$.usage.input_tokens", + "$.usage.prompt_tokens" + ], + "valueMap": { + "ON_DEMAND_PRIORITY": "priority", + "ON_DEMAND_FLEX": "flex" + } }, "remainingTokens": { "location": "payload", - "identifier": "$.usage.inputTokens" + "identifier": "$.usage.inputTokens", + "fallbackIdentifiers": [ + "$.usage.input_tokens", + "$.usage.prompt_tokens" + ], + "valueMap": { + "ON_DEMAND_PRIORITY": "priority", + "ON_DEMAND_FLEX": "flex" + } }, "requestModel": { "location": "payload", - "identifier": "$.usage.inputTokens" + "identifier": "$.usage.inputTokens", + "fallbackIdentifiers": [ + "$.usage.input_tokens", + "$.usage.prompt_tokens" + ], + "valueMap": { + "ON_DEMAND_PRIORITY": "priority", + "ON_DEMAND_FLEX": "flex" + } }, "responseModel": { "location": "payload", - "identifier": "$.usage.inputTokens" - } + "identifier": "$.usage.inputTokens", + "fallbackIdentifiers": [ + "$.usage.input_tokens", + "$.usage.prompt_tokens" + ], + "valueMap": { + "ON_DEMAND_PRIORITY": "priority", + "ON_DEMAND_FLEX": "flex" + } + }, + "cachedTokens": { + "location": "payload", + "identifier": "$.usage.inputTokens", + "fallbackIdentifiers": [ + "$.usage.input_tokens", + "$.usage.prompt_tokens" + ], + "valueMap": { + "ON_DEMAND_PRIORITY": "priority", + "ON_DEMAND_FLEX": "flex" + } + }, + "cacheWriteTokens": { + "location": "payload", + "identifier": "$.usage.inputTokens", + "fallbackIdentifiers": [ + "$.usage.input_tokens", + "$.usage.prompt_tokens" + ], + "valueMap": { + "ON_DEMAND_PRIORITY": "priority", + "ON_DEMAND_FLEX": "flex" + } + }, + "cacheWrite1hTokens": { + "location": "payload", + "identifier": "$.usage.inputTokens", + "fallbackIdentifiers": [ + "$.usage.input_tokens", + "$.usage.prompt_tokens" + ], + "valueMap": { + "ON_DEMAND_PRIORITY": "priority", + "ON_DEMAND_FLEX": "flex" + } + }, + "reasoningTokens": { + "location": "payload", + "identifier": "$.usage.inputTokens", + "fallbackIdentifiers": [ + "$.usage.input_tokens", + "$.usage.prompt_tokens" + ], + "valueMap": { + "ON_DEMAND_PRIORITY": "priority", + "ON_DEMAND_FLEX": "flex" + } + }, + "audioInputTokens": { + "location": "payload", + "identifier": "$.usage.inputTokens", + "fallbackIdentifiers": [ + "$.usage.input_tokens", + "$.usage.prompt_tokens" + ], + "valueMap": { + "ON_DEMAND_PRIORITY": "priority", + "ON_DEMAND_FLEX": "flex" + } + }, + "audioOutputTokens": { + "location": "payload", + "identifier": "$.usage.inputTokens", + "fallbackIdentifiers": [ + "$.usage.input_tokens", + "$.usage.prompt_tokens" + ], + "valueMap": { + "ON_DEMAND_PRIORITY": "priority", + "ON_DEMAND_FLEX": "flex" + } + }, + "serviceTier": { + "location": "payload", + "identifier": "$.usage.inputTokens", + "fallbackIdentifiers": [ + "$.usage.input_tokens", + "$.usage.prompt_tokens" + ], + "valueMap": { + "ON_DEMAND_PRIORITY": "priority", + "ON_DEMAND_FLEX": "flex" + } + }, + "cacheAccounting": "inclusive" } ] } @@ -1856,6 +2148,16 @@ and |remainingTokens|[ExtractionIdentifier](#schemaextractionidentifier)|false|none|none| |requestModel|[ExtractionIdentifier](#schemaextractionidentifier)|false|none|none| |responseModel|[ExtractionIdentifier](#schemaextractionidentifier)|false|none|none| +|cachedTokens|[ExtractionIdentifier](#schemaextractionidentifier)|false|none|none| +|cacheWriteTokens|[ExtractionIdentifier](#schemaextractionidentifier)|false|none|none| +|cacheWrite1hTokens|[ExtractionIdentifier](#schemaextractionidentifier)|false|none|none| +|reasoningTokens|[ExtractionIdentifier](#schemaextractionidentifier)|false|none|none| +|audioInputTokens|[ExtractionIdentifier](#schemaextractionidentifier)|false|none|none| +|audioOutputTokens|[ExtractionIdentifier](#schemaextractionidentifier)|false|none|none| +|serviceTier|[ExtractionIdentifier](#schemaextractionidentifier)|false|none|none| +|providerFields|object|false|none|Named locations a provider-specific calculator needs but the closed
vocabulary does not cover. The value at each location is handed to
the calculator as-is; only its position is declared here.| +|» **additionalProperties**|[ExtractionIdentifier](#schemaextractionidentifier)|false|none|none| +|cacheAccounting|string|false|none|Whether the cached token count reported by the provider is already
part of the input token total, or additional to it. One of
'inclusive' or 'additive'. Defaults to inclusive when omitted.| |resourceMappings|[LLMProviderTemplateResourceMappings](#schemallmprovidertemplateresourcemappings)|false|none|none| ## LLMProviderTemplateResourceMappings @@ -1872,28 +2174,161 @@ and "resource": "/responses", "promptTokens": { "location": "payload", - "identifier": "$.usage.inputTokens" + "identifier": "$.usage.inputTokens", + "fallbackIdentifiers": [ + "$.usage.input_tokens", + "$.usage.prompt_tokens" + ], + "valueMap": { + "ON_DEMAND_PRIORITY": "priority", + "ON_DEMAND_FLEX": "flex" + } }, "completionTokens": { "location": "payload", - "identifier": "$.usage.inputTokens" + "identifier": "$.usage.inputTokens", + "fallbackIdentifiers": [ + "$.usage.input_tokens", + "$.usage.prompt_tokens" + ], + "valueMap": { + "ON_DEMAND_PRIORITY": "priority", + "ON_DEMAND_FLEX": "flex" + } }, "totalTokens": { "location": "payload", - "identifier": "$.usage.inputTokens" + "identifier": "$.usage.inputTokens", + "fallbackIdentifiers": [ + "$.usage.input_tokens", + "$.usage.prompt_tokens" + ], + "valueMap": { + "ON_DEMAND_PRIORITY": "priority", + "ON_DEMAND_FLEX": "flex" + } }, "remainingTokens": { "location": "payload", - "identifier": "$.usage.inputTokens" + "identifier": "$.usage.inputTokens", + "fallbackIdentifiers": [ + "$.usage.input_tokens", + "$.usage.prompt_tokens" + ], + "valueMap": { + "ON_DEMAND_PRIORITY": "priority", + "ON_DEMAND_FLEX": "flex" + } }, "requestModel": { "location": "payload", - "identifier": "$.usage.inputTokens" + "identifier": "$.usage.inputTokens", + "fallbackIdentifiers": [ + "$.usage.input_tokens", + "$.usage.prompt_tokens" + ], + "valueMap": { + "ON_DEMAND_PRIORITY": "priority", + "ON_DEMAND_FLEX": "flex" + } }, "responseModel": { "location": "payload", - "identifier": "$.usage.inputTokens" - } + "identifier": "$.usage.inputTokens", + "fallbackIdentifiers": [ + "$.usage.input_tokens", + "$.usage.prompt_tokens" + ], + "valueMap": { + "ON_DEMAND_PRIORITY": "priority", + "ON_DEMAND_FLEX": "flex" + } + }, + "cachedTokens": { + "location": "payload", + "identifier": "$.usage.inputTokens", + "fallbackIdentifiers": [ + "$.usage.input_tokens", + "$.usage.prompt_tokens" + ], + "valueMap": { + "ON_DEMAND_PRIORITY": "priority", + "ON_DEMAND_FLEX": "flex" + } + }, + "cacheWriteTokens": { + "location": "payload", + "identifier": "$.usage.inputTokens", + "fallbackIdentifiers": [ + "$.usage.input_tokens", + "$.usage.prompt_tokens" + ], + "valueMap": { + "ON_DEMAND_PRIORITY": "priority", + "ON_DEMAND_FLEX": "flex" + } + }, + "cacheWrite1hTokens": { + "location": "payload", + "identifier": "$.usage.inputTokens", + "fallbackIdentifiers": [ + "$.usage.input_tokens", + "$.usage.prompt_tokens" + ], + "valueMap": { + "ON_DEMAND_PRIORITY": "priority", + "ON_DEMAND_FLEX": "flex" + } + }, + "reasoningTokens": { + "location": "payload", + "identifier": "$.usage.inputTokens", + "fallbackIdentifiers": [ + "$.usage.input_tokens", + "$.usage.prompt_tokens" + ], + "valueMap": { + "ON_DEMAND_PRIORITY": "priority", + "ON_DEMAND_FLEX": "flex" + } + }, + "audioInputTokens": { + "location": "payload", + "identifier": "$.usage.inputTokens", + "fallbackIdentifiers": [ + "$.usage.input_tokens", + "$.usage.prompt_tokens" + ], + "valueMap": { + "ON_DEMAND_PRIORITY": "priority", + "ON_DEMAND_FLEX": "flex" + } + }, + "audioOutputTokens": { + "location": "payload", + "identifier": "$.usage.inputTokens", + "fallbackIdentifiers": [ + "$.usage.input_tokens", + "$.usage.prompt_tokens" + ], + "valueMap": { + "ON_DEMAND_PRIORITY": "priority", + "ON_DEMAND_FLEX": "flex" + } + }, + "serviceTier": { + "location": "payload", + "identifier": "$.usage.inputTokens", + "fallbackIdentifiers": [ + "$.usage.input_tokens", + "$.usage.prompt_tokens" + ], + "valueMap": { + "ON_DEMAND_PRIORITY": "priority", + "ON_DEMAND_FLEX": "flex" + } + }, + "cacheAccounting": "inclusive" } ] } @@ -1918,28 +2353,161 @@ and "resource": "/responses", "promptTokens": { "location": "payload", - "identifier": "$.usage.inputTokens" + "identifier": "$.usage.inputTokens", + "fallbackIdentifiers": [ + "$.usage.input_tokens", + "$.usage.prompt_tokens" + ], + "valueMap": { + "ON_DEMAND_PRIORITY": "priority", + "ON_DEMAND_FLEX": "flex" + } }, "completionTokens": { "location": "payload", - "identifier": "$.usage.inputTokens" + "identifier": "$.usage.inputTokens", + "fallbackIdentifiers": [ + "$.usage.input_tokens", + "$.usage.prompt_tokens" + ], + "valueMap": { + "ON_DEMAND_PRIORITY": "priority", + "ON_DEMAND_FLEX": "flex" + } }, "totalTokens": { "location": "payload", - "identifier": "$.usage.inputTokens" + "identifier": "$.usage.inputTokens", + "fallbackIdentifiers": [ + "$.usage.input_tokens", + "$.usage.prompt_tokens" + ], + "valueMap": { + "ON_DEMAND_PRIORITY": "priority", + "ON_DEMAND_FLEX": "flex" + } }, "remainingTokens": { "location": "payload", - "identifier": "$.usage.inputTokens" + "identifier": "$.usage.inputTokens", + "fallbackIdentifiers": [ + "$.usage.input_tokens", + "$.usage.prompt_tokens" + ], + "valueMap": { + "ON_DEMAND_PRIORITY": "priority", + "ON_DEMAND_FLEX": "flex" + } }, "requestModel": { "location": "payload", - "identifier": "$.usage.inputTokens" + "identifier": "$.usage.inputTokens", + "fallbackIdentifiers": [ + "$.usage.input_tokens", + "$.usage.prompt_tokens" + ], + "valueMap": { + "ON_DEMAND_PRIORITY": "priority", + "ON_DEMAND_FLEX": "flex" + } }, "responseModel": { "location": "payload", - "identifier": "$.usage.inputTokens" - } + "identifier": "$.usage.inputTokens", + "fallbackIdentifiers": [ + "$.usage.input_tokens", + "$.usage.prompt_tokens" + ], + "valueMap": { + "ON_DEMAND_PRIORITY": "priority", + "ON_DEMAND_FLEX": "flex" + } + }, + "cachedTokens": { + "location": "payload", + "identifier": "$.usage.inputTokens", + "fallbackIdentifiers": [ + "$.usage.input_tokens", + "$.usage.prompt_tokens" + ], + "valueMap": { + "ON_DEMAND_PRIORITY": "priority", + "ON_DEMAND_FLEX": "flex" + } + }, + "cacheWriteTokens": { + "location": "payload", + "identifier": "$.usage.inputTokens", + "fallbackIdentifiers": [ + "$.usage.input_tokens", + "$.usage.prompt_tokens" + ], + "valueMap": { + "ON_DEMAND_PRIORITY": "priority", + "ON_DEMAND_FLEX": "flex" + } + }, + "cacheWrite1hTokens": { + "location": "payload", + "identifier": "$.usage.inputTokens", + "fallbackIdentifiers": [ + "$.usage.input_tokens", + "$.usage.prompt_tokens" + ], + "valueMap": { + "ON_DEMAND_PRIORITY": "priority", + "ON_DEMAND_FLEX": "flex" + } + }, + "reasoningTokens": { + "location": "payload", + "identifier": "$.usage.inputTokens", + "fallbackIdentifiers": [ + "$.usage.input_tokens", + "$.usage.prompt_tokens" + ], + "valueMap": { + "ON_DEMAND_PRIORITY": "priority", + "ON_DEMAND_FLEX": "flex" + } + }, + "audioInputTokens": { + "location": "payload", + "identifier": "$.usage.inputTokens", + "fallbackIdentifiers": [ + "$.usage.input_tokens", + "$.usage.prompt_tokens" + ], + "valueMap": { + "ON_DEMAND_PRIORITY": "priority", + "ON_DEMAND_FLEX": "flex" + } + }, + "audioOutputTokens": { + "location": "payload", + "identifier": "$.usage.inputTokens", + "fallbackIdentifiers": [ + "$.usage.input_tokens", + "$.usage.prompt_tokens" + ], + "valueMap": { + "ON_DEMAND_PRIORITY": "priority", + "ON_DEMAND_FLEX": "flex" + } + }, + "serviceTier": { + "location": "payload", + "identifier": "$.usage.inputTokens", + "fallbackIdentifiers": [ + "$.usage.input_tokens", + "$.usage.prompt_tokens" + ], + "valueMap": { + "ON_DEMAND_PRIORITY": "priority", + "ON_DEMAND_FLEX": "flex" + } + }, + "cacheAccounting": "inclusive" } ``` @@ -1955,6 +2523,14 @@ and |remainingTokens|[ExtractionIdentifier](#schemaextractionidentifier)|false|none|none| |requestModel|[ExtractionIdentifier](#schemaextractionidentifier)|false|none|none| |responseModel|[ExtractionIdentifier](#schemaextractionidentifier)|false|none|none| +|cachedTokens|[ExtractionIdentifier](#schemaextractionidentifier)|false|none|none| +|cacheWriteTokens|[ExtractionIdentifier](#schemaextractionidentifier)|false|none|none| +|cacheWrite1hTokens|[ExtractionIdentifier](#schemaextractionidentifier)|false|none|none| +|reasoningTokens|[ExtractionIdentifier](#schemaextractionidentifier)|false|none|none| +|audioInputTokens|[ExtractionIdentifier](#schemaextractionidentifier)|false|none|none| +|audioOutputTokens|[ExtractionIdentifier](#schemaextractionidentifier)|false|none|none| +|serviceTier|[ExtractionIdentifier](#schemaextractionidentifier)|false|none|none| +|cacheAccounting|string|false|none|Whether the cached token count reported by the provider is already
part of the input token total, or additional to it. One of
'inclusive' or 'additive'. Overrides the template-level setting for
this resource; when omitted, the template-level value is inherited.| ## ExtractionIdentifier @@ -1966,7 +2542,15 @@ and ```json { "location": "payload", - "identifier": "$.usage.inputTokens" + "identifier": "$.usage.inputTokens", + "fallbackIdentifiers": [ + "$.usage.input_tokens", + "$.usage.prompt_tokens" + ], + "valueMap": { + "ON_DEMAND_PRIORITY": "priority", + "ON_DEMAND_FLEX": "flex" + } } ``` @@ -1977,6 +2561,9 @@ and |---|---|---|---|---| |location|string|true|none|Where to find the token information| |identifier|string|true|none|JSONPath expression or header name to identify the token value| +|fallbackIdentifiers|[string]|false|none|Additional locations to try when the primary identifier yields no
value, in order. Used where one provider returns several response
shapes on the same path.| +|valueMap|object|false|none|Maps values this provider reports onto the vocabulary the gateway
bills on. A reported value that is not a key is used unchanged.
Used where providers name the same billing tier differently.| +|» **additionalProperties**|string|false|none|none| ##### Enumerated Values diff --git a/gateway/gateway-controller/api/management-openapi.yaml b/gateway/gateway-controller/api/management-openapi.yaml index 454eba734d..6ea472d7e5 100644 --- a/gateway/gateway-controller/api/management-openapi.yaml +++ b/gateway/gateway-controller/api/management-openapi.yaml @@ -4113,6 +4113,35 @@ components: $ref: '#/components/schemas/ExtractionIdentifier' responseModel: $ref: '#/components/schemas/ExtractionIdentifier' + cachedTokens: + $ref: '#/components/schemas/ExtractionIdentifier' + cacheWriteTokens: + $ref: '#/components/schemas/ExtractionIdentifier' + cacheWrite1hTokens: + $ref: '#/components/schemas/ExtractionIdentifier' + reasoningTokens: + $ref: '#/components/schemas/ExtractionIdentifier' + audioInputTokens: + $ref: '#/components/schemas/ExtractionIdentifier' + audioOutputTokens: + $ref: '#/components/schemas/ExtractionIdentifier' + serviceTier: + $ref: '#/components/schemas/ExtractionIdentifier' + providerFields: + type: object + additionalProperties: + $ref: '#/components/schemas/ExtractionIdentifier' + description: | + Named locations a provider-specific calculator needs but the closed + vocabulary does not cover. The value at each location is handed to + the calculator as-is; only its position is declared here. + cacheAccounting: + type: string + description: | + Whether the cached token count reported by the provider is already + part of the input token total, or additional to it. One of + 'inclusive' or 'additive'. Defaults to inclusive when omitted. + example: inclusive resourceMappings: $ref: '#/components/schemas/LLMProviderTemplateResourceMappings' @@ -4146,6 +4175,28 @@ components: $ref: '#/components/schemas/ExtractionIdentifier' responseModel: $ref: '#/components/schemas/ExtractionIdentifier' + cachedTokens: + $ref: '#/components/schemas/ExtractionIdentifier' + cacheWriteTokens: + $ref: '#/components/schemas/ExtractionIdentifier' + cacheWrite1hTokens: + $ref: '#/components/schemas/ExtractionIdentifier' + reasoningTokens: + $ref: '#/components/schemas/ExtractionIdentifier' + audioInputTokens: + $ref: '#/components/schemas/ExtractionIdentifier' + audioOutputTokens: + $ref: '#/components/schemas/ExtractionIdentifier' + serviceTier: + $ref: '#/components/schemas/ExtractionIdentifier' + cacheAccounting: + type: string + description: | + Whether the cached token count reported by the provider is already + part of the input token total, or additional to it. One of + 'inclusive' or 'additive'. Overrides the template-level setting for + this resource; when omitted, the template-level value is inherited. + example: inclusive ExtractionIdentifier: type: object @@ -4166,6 +4217,28 @@ components: type: string description: JSONPath expression or header name to identify the token value example: $.usage.inputTokens + fallbackIdentifiers: + type: array + items: + type: string + description: | + Additional locations to try when the primary identifier yields no + value, in order. Used where one provider returns several response + shapes on the same path. + example: + - $.usage.input_tokens + - $.usage.prompt_tokens + valueMap: + type: object + additionalProperties: + type: string + description: | + Maps values this provider reports onto the vocabulary the gateway + bills on. A reported value that is not a key is used unchanged. + Used where providers name the same billing tier differently. + example: + ON_DEMAND_PRIORITY: priority + ON_DEMAND_FLEX: flex LLMProviderConfigurationRequest: type: object diff --git a/gateway/gateway-controller/default-llm-provider-templates/anthropic-template.yaml b/gateway/gateway-controller/default-llm-provider-templates/anthropic-template.yaml index 0b4f38524f..ced7c4f814 100644 --- a/gateway/gateway-controller/default-llm-provider-templates/anthropic-template.yaml +++ b/gateway/gateway-controller/default-llm-provider-templates/anthropic-template.yaml @@ -28,9 +28,13 @@ spec: promptTokens: location: payload identifier: $.usage.input_tokens + fallbackIdentifiers: + - $.message.usage.input_tokens completionTokens: location: payload identifier: $.usage.output_tokens + fallbackIdentifiers: + - $.message.usage.output_tokens remainingTokens: location: header identifier: anthropic-ratelimit-tokens-remaining @@ -40,3 +44,54 @@ spec: responseModel: location: payload identifier: $.model + fallbackIdentifiers: + - $.message.model + cachedTokens: + location: payload + identifier: $.usage.cache_read_input_tokens + fallbackIdentifiers: + - $.message.usage.cache_read_input_tokens + cacheWriteTokens: + location: payload + identifier: $.usage.cache_creation.ephemeral_5m_input_tokens + fallbackIdentifiers: + - $.message.usage.cache_creation.ephemeral_5m_input_tokens + cacheWrite1hTokens: + location: payload + identifier: $.usage.cache_creation.ephemeral_1h_input_tokens + fallbackIdentifiers: + - $.message.usage.cache_creation.ephemeral_1h_input_tokens + reasoningTokens: + location: payload + identifier: $.usage.output_tokens_details.thinking_tokens + fallbackIdentifiers: + - $.message.usage.output_tokens_details.thinking_tokens + serviceTier: + location: payload + identifier: $.usage.service_tier + fallbackIdentifiers: + - $.message.usage.service_tier + valueMap: + priority: priority + batch: batch + providerFields: + # The streaming envelope nests usage under "message", so both locations are + # declared and the calculator never sees the difference. + inferenceGeo: + location: payload + identifier: $.usage.inference_geo + fallbackIdentifiers: + - $.message.usage.inference_geo + webSearchRequests: + location: payload + identifier: $.usage.server_tool_use.web_search_requests + fallbackIdentifiers: + - $.message.usage.server_tool_use.web_search_requests + # Request-side parameters Anthropic does not echo in the response. + speed: + location: payload + identifier: $.speed + searchContextSize: + location: payload + identifier: $.web_search_options.search_context_size + cacheAccounting: additive diff --git a/gateway/gateway-controller/default-llm-provider-templates/awsbedrock-template.yaml b/gateway/gateway-controller/default-llm-provider-templates/awsbedrock-template.yaml index 196359ab59..438ad572da 100644 --- a/gateway/gateway-controller/default-llm-provider-templates/awsbedrock-template.yaml +++ b/gateway/gateway-controller/default-llm-provider-templates/awsbedrock-template.yaml @@ -28,15 +28,49 @@ spec: promptTokens: location: payload identifier: $.usage.inputTokens + fallbackIdentifiers: + - $.usage.input_tokens + - $.usage.prompt_tokens + - $.inputTextTokenCount completionTokens: location: payload identifier: $.usage.outputTokens + fallbackIdentifiers: + - $.usage.output_tokens + - $.usage.completion_tokens + - $.results[0].tokenCount totalTokens: location: payload identifier: $.usage.totalTokens requestModel: location: pathParam - identifier: model/([A-Za-z0-9.:-]+)/ + identifier: model/([A-Za-z0-9.:%-]+)/ responseModel: location: pathParam - identifier: model/([A-Za-z0-9.:-]+)/ + identifier: model/([A-Za-z0-9.:%-]+)/ + cachedTokens: + location: payload + identifier: $.usage.cacheReadInputTokens + fallbackIdentifiers: + - $.usage.cache_read_input_tokens + - $.usage.prompt_tokens_details.cached_tokens + cacheWriteTokens: + location: payload + identifier: $.usage.cacheWriteInputTokens + fallbackIdentifiers: + - $.usage.cache_creation_input_tokens + - $.usage.prompt_tokens_details.cache_write_tokens + serviceTier: + location: payload + identifier: $.serviceTier.type + valueMap: + priority: priority + flex: flex + providerFields: + # The per-entry cache array. Bedrock reports one entry per TTL, so the + # 5-minute and 1-hour split is read from here rather than from a single + # field. Only its position is declared; the calculator walks the entries. + cacheDetails: + location: payload + identifier: $.usage.cacheDetails + cacheAccounting: additive diff --git a/gateway/gateway-controller/default-llm-provider-templates/gemini-template.yaml b/gateway/gateway-controller/default-llm-provider-templates/gemini-template.yaml index 79a5a8be94..047c68ccf1 100644 --- a/gateway/gateway-controller/default-llm-provider-templates/gemini-template.yaml +++ b/gateway/gateway-controller/default-llm-provider-templates/gemini-template.yaml @@ -39,7 +39,47 @@ spec: identifier: x-ratelimit-remaining-tokens requestModel: location: pathParam - identifier: (?<=models/)[a-zA-Z0-9.\-]+ + identifier: models/([a-zA-Z0-9.\-]+) responseModel: location: payload identifier: $.modelVersion + serviceTier: + location: payload + identifier: $.usageMetadata.serviceTier # Developer API + fallbackIdentifiers: + - $.usageMetadata.trafficType # Vertex API + valueMap: + priority: priority + flex: flex + ON_DEMAND_PRIORITY: priority + ON_DEMAND_FLEX: flex + cachedTokens: + location: payload + identifier: $.usageMetadata.cachedContentTokenCount + reasoningTokens: + location: payload + identifier: $.usageMetadata.thoughtsTokenCount + providerFields: + # Per-modality token lists. Only their position is declared; the calculator + # picks the modality it needs out of the entries. + promptTokensDetails: + location: payload + identifier: $.usageMetadata.promptTokensDetails + cacheTokensDetails: + location: payload + identifier: $.usageMetadata.cacheTokensDetails + candidatesTokensDetails: + location: payload + identifier: $.usageMetadata.candidatesTokensDetails + responseTokensDetails: + location: payload + identifier: $.usageMetadata.responseTokensDetails + toolUsePromptTokenCount: + location: payload + identifier: $.usageMetadata.toolUsePromptTokenCount + # Grounding searches are billed per query and no field reports the number, + # so the calculator counts the entries. + candidates: + location: payload + identifier: $.candidates + cacheAccounting: inclusive diff --git a/gateway/gateway-controller/default-llm-provider-templates/mistral-template.yaml b/gateway/gateway-controller/default-llm-provider-templates/mistral-template.yaml index d2bee935e4..512cad99ba 100644 --- a/gateway/gateway-controller/default-llm-provider-templates/mistral-template.yaml +++ b/gateway/gateway-controller/default-llm-provider-templates/mistral-template.yaml @@ -43,3 +43,9 @@ spec: responseModel: location: payload identifier: $.model + providerFields: + # Voxtral bills audio input by duration; prompt_tokens covers only text. + promptAudioSeconds: + location: payload + identifier: $.usage.prompt_audio_seconds + cacheAccounting: inclusive diff --git a/gateway/gateway-controller/default-llm-provider-templates/openai-template.yaml b/gateway/gateway-controller/default-llm-provider-templates/openai-template.yaml index b22d6ae62f..5e7185175f 100644 --- a/gateway/gateway-controller/default-llm-provider-templates/openai-template.yaml +++ b/gateway/gateway-controller/default-llm-provider-templates/openai-template.yaml @@ -43,6 +43,35 @@ spec: responseModel: location: payload identifier: $.model + cachedTokens: + location: payload + identifier: $.usage.prompt_tokens_details.cached_tokens + reasoningTokens: + location: payload + identifier: $.usage.completion_tokens_details.reasoning_tokens + audioInputTokens: + location: payload + identifier: $.usage.prompt_tokens_details.audio_tokens + audioOutputTokens: + location: payload + identifier: $.usage.completion_tokens_details.audio_tokens + serviceTier: + location: payload + identifier: $.service_tier + valueMap: + priority: priority + flex: flex + fast: priority + providerFields: + # A url_citation annotation inside a choice marks a web search call. + choices: + location: payload + identifier: $.choices + # The search depth is request-side only. + searchContextSize: + location: payload + identifier: $.web_search_options.search_context_size + cacheAccounting: inclusive resourceMappings: resources: - resource: /responses @@ -52,3 +81,9 @@ spec: completionTokens: location: payload identifier: $.usage.output_tokens + cachedTokens: + location: payload + identifier: $.usage.input_tokens_details.cached_tokens + reasoningTokens: + location: payload + identifier: $.usage.output_tokens_details.reasoning_tokens diff --git a/gateway/gateway-controller/pkg/api/management/generated.go b/gateway/gateway-controller/pkg/api/management/generated.go index 5ab19469d7..c2c071374e 100644 --- a/gateway/gateway-controller/pkg/api/management/generated.go +++ b/gateway/gateway-controller/pkg/api/management/generated.go @@ -679,11 +679,21 @@ type ErrorResponse struct { // ExtractionIdentifier defines model for ExtractionIdentifier. type ExtractionIdentifier struct { + // FallbackIdentifiers Additional locations to try when the primary identifier yields no + // value, in order. Used where one provider returns several response + // shapes on the same path. + FallbackIdentifiers *[]string `json:"fallbackIdentifiers,omitempty" yaml:"fallbackIdentifiers,omitempty"` + // Identifier JSONPath expression or header name to identify the token value Identifier string `json:"identifier" yaml:"identifier"` // Location Where to find the token information Location ExtractionIdentifierLocation `json:"location" yaml:"location"` + + // ValueMap Maps values this provider reports onto the vocabulary the gateway + // bills on. A reported value that is not a key is used unchanged. + // Used where providers name the same billing tier differently. + ValueMap *map[string]string `json:"valueMap,omitempty" yaml:"valueMap,omitempty"` } // ExtractionIdentifierLocation Where to find the token information @@ -855,7 +865,17 @@ type LLMProviderTemplateKind string // LLMProviderTemplateData defines model for LLMProviderTemplateData. type LLMProviderTemplateData struct { - CompletionTokens *ExtractionIdentifier `json:"completionTokens,omitempty" yaml:"completionTokens,omitempty"` + AudioInputTokens *ExtractionIdentifier `json:"audioInputTokens,omitempty" yaml:"audioInputTokens,omitempty"` + AudioOutputTokens *ExtractionIdentifier `json:"audioOutputTokens,omitempty" yaml:"audioOutputTokens,omitempty"` + + // CacheAccounting Whether the cached token count reported by the provider is already + // part of the input token total, or additional to it. One of + // 'inclusive' or 'additive'. Defaults to inclusive when omitted. + CacheAccounting *string `json:"cacheAccounting,omitempty" yaml:"cacheAccounting,omitempty"` + CacheWrite1hTokens *ExtractionIdentifier `json:"cacheWrite1hTokens,omitempty" yaml:"cacheWrite1hTokens,omitempty"` + CacheWriteTokens *ExtractionIdentifier `json:"cacheWriteTokens,omitempty" yaml:"cacheWriteTokens,omitempty"` + CachedTokens *ExtractionIdentifier `json:"cachedTokens,omitempty" yaml:"cachedTokens,omitempty"` + CompletionTokens *ExtractionIdentifier `json:"completionTokens,omitempty" yaml:"completionTokens,omitempty"` // DisplayName Human-readable LLM Template name DisplayName string `json:"displayName" yaml:"displayName"` @@ -868,12 +888,19 @@ type LLMProviderTemplateData struct { // ManagedBy Origin of the template. Built-in templates use 'wso2'; custom // templates default to 'customer' and may be set to any value. - ManagedBy *string `json:"managedBy,omitempty" yaml:"managedBy,omitempty"` - PromptTokens *ExtractionIdentifier `json:"promptTokens,omitempty" yaml:"promptTokens,omitempty"` + ManagedBy *string `json:"managedBy,omitempty" yaml:"managedBy,omitempty"` + PromptTokens *ExtractionIdentifier `json:"promptTokens,omitempty" yaml:"promptTokens,omitempty"` + + // ProviderFields Named locations a provider-specific calculator needs but the closed + // vocabulary does not cover. The value at each location is handed to + // the calculator as-is; only its position is declared here. + ProviderFields *map[string]ExtractionIdentifier `json:"providerFields,omitempty" yaml:"providerFields,omitempty"` + ReasoningTokens *ExtractionIdentifier `json:"reasoningTokens,omitempty" yaml:"reasoningTokens,omitempty"` RemainingTokens *ExtractionIdentifier `json:"remainingTokens,omitempty" yaml:"remainingTokens,omitempty"` RequestModel *ExtractionIdentifier `json:"requestModel,omitempty" yaml:"requestModel,omitempty"` ResourceMappings *LLMProviderTemplateResourceMappings `json:"resourceMappings,omitempty" yaml:"resourceMappings,omitempty"` ResponseModel *ExtractionIdentifier `json:"responseModel,omitempty" yaml:"responseModel,omitempty"` + ServiceTier *ExtractionIdentifier `json:"serviceTier,omitempty" yaml:"serviceTier,omitempty"` TotalTokens *ExtractionIdentifier `json:"totalTokens,omitempty" yaml:"totalTokens,omitempty"` // Version Template content version (e.g. v1.0). Multiple versions of the same @@ -900,14 +927,27 @@ type LLMProviderTemplateRequestKind string // LLMProviderTemplateResourceMapping defines model for LLMProviderTemplateResourceMapping. type LLMProviderTemplateResourceMapping struct { - CompletionTokens *ExtractionIdentifier `json:"completionTokens,omitempty" yaml:"completionTokens,omitempty"` - PromptTokens *ExtractionIdentifier `json:"promptTokens,omitempty" yaml:"promptTokens,omitempty"` - RemainingTokens *ExtractionIdentifier `json:"remainingTokens,omitempty" yaml:"remainingTokens,omitempty"` - RequestModel *ExtractionIdentifier `json:"requestModel,omitempty" yaml:"requestModel,omitempty"` + AudioInputTokens *ExtractionIdentifier `json:"audioInputTokens,omitempty" yaml:"audioInputTokens,omitempty"` + AudioOutputTokens *ExtractionIdentifier `json:"audioOutputTokens,omitempty" yaml:"audioOutputTokens,omitempty"` + + // CacheAccounting Whether the cached token count reported by the provider is already + // part of the input token total, or additional to it. One of + // 'inclusive' or 'additive'. Overrides the template-level setting for + // this resource; when omitted, the template-level value is inherited. + CacheAccounting *string `json:"cacheAccounting,omitempty" yaml:"cacheAccounting,omitempty"` + CacheWrite1hTokens *ExtractionIdentifier `json:"cacheWrite1hTokens,omitempty" yaml:"cacheWrite1hTokens,omitempty"` + CacheWriteTokens *ExtractionIdentifier `json:"cacheWriteTokens,omitempty" yaml:"cacheWriteTokens,omitempty"` + CachedTokens *ExtractionIdentifier `json:"cachedTokens,omitempty" yaml:"cachedTokens,omitempty"` + CompletionTokens *ExtractionIdentifier `json:"completionTokens,omitempty" yaml:"completionTokens,omitempty"` + PromptTokens *ExtractionIdentifier `json:"promptTokens,omitempty" yaml:"promptTokens,omitempty"` + ReasoningTokens *ExtractionIdentifier `json:"reasoningTokens,omitempty" yaml:"reasoningTokens,omitempty"` + RemainingTokens *ExtractionIdentifier `json:"remainingTokens,omitempty" yaml:"remainingTokens,omitempty"` + RequestModel *ExtractionIdentifier `json:"requestModel,omitempty" yaml:"requestModel,omitempty"` // Resource Resource path pattern for this mapping Resource string `json:"resource" yaml:"resource"` ResponseModel *ExtractionIdentifier `json:"responseModel,omitempty" yaml:"responseModel,omitempty"` + ServiceTier *ExtractionIdentifier `json:"serviceTier,omitempty" yaml:"serviceTier,omitempty"` TotalTokens *ExtractionIdentifier `json:"totalTokens,omitempty" yaml:"totalTokens,omitempty"` } @@ -4561,261 +4601,270 @@ func HandlerWithOptions(si ServerInterface, options StdHTTPServerOptions) http.H // Base64 encoded, gzipped, json marshaled Swagger object var swaggerSpec = []string{ - "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", - "/wcAAP//nIc3NSrXAQA=", + "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", + "/22YsUw64QEA", } // GetSwagger returns the content of the embedded swagger specification file diff --git a/gateway/gateway-controller/pkg/config/llm_parser_test.go b/gateway/gateway-controller/pkg/config/llm_parser_test.go index 4de0978f3e..e6eb5b6531 100644 --- a/gateway/gateway-controller/pkg/config/llm_parser_test.go +++ b/gateway/gateway-controller/pkg/config/llm_parser_test.go @@ -1031,3 +1031,138 @@ spec: assert.NotNil(t, template.Spec.PromptTokens) assert.NotNil(t, template.Spec.CompletionTokens) } + +// A template carrying the usage detail fields must survive YAML parsing with +// every field intact, including inside a resource mapping. +func TestParseLLMProviderTemplate_UsageDetailFieldsSurviveYAML(t *testing.T) { + yamlSpec := []byte(` +apiVersion: gateway.api-platform.wso2.com/v1 +kind: LlmProviderTemplate +metadata: + name: roundtrip-check +spec: + displayName: Roundtrip Check + cacheAccounting: additive + promptTokens: + location: payload + identifier: $.usage.input_tokens + cachedTokens: + location: payload + identifier: $.usage.cache_read_input_tokens + cacheWriteTokens: + location: payload + identifier: $.usage.cache_creation.ephemeral_5m_input_tokens + cacheWrite1hTokens: + location: payload + identifier: $.usage.cache_creation.ephemeral_1h_input_tokens + reasoningTokens: + location: payload + identifier: $.usage.completion_tokens_details.reasoning_tokens + audioInputTokens: + location: payload + identifier: $.usage.prompt_tokens_details.audio_tokens + audioOutputTokens: + location: payload + identifier: $.usage.completion_tokens_details.audio_tokens + serviceTier: + location: payload + identifier: $.usage.service_tier + valueMap: + ON_DEMAND_PRIORITY: priority + ON_DEMAND_FLEX: flex + providerFields: + cacheDetails: + location: payload + identifier: $.usage.cacheDetails + cacheTokensDetails: + location: payload + identifier: $.usageMetadata.cacheTokensDetails + resourceMappings: + resources: + - resource: /responses + cacheAccounting: inclusive + cachedTokens: + location: payload + identifier: $.usage.input_tokens_details.cached_tokens +`) + + parser := NewParser() + + var tmpl api.LLMProviderTemplate + if err := parser.Parse(yamlSpec, "application/yaml", &tmpl); err != nil { + t.Fatalf("parse failed: %v", err) + } + + spec := tmpl.Spec + + // The parsed template must also pass validation, so a field that parses but + // fails deploy-time checks is caught here too. + if errs := NewLLMValidator().validateTemplateSpec(&spec); len(errs) != 0 { + t.Fatalf("expected valid template, got %v", errs) + } + + if spec.CacheAccounting == nil || *spec.CacheAccounting != "additive" { + t.Errorf("cacheAccounting not preserved: %v", spec.CacheAccounting) + } + + checks := map[string]*api.ExtractionIdentifier{ + "$.usage.cache_read_input_tokens": spec.CachedTokens, + "$.usage.cache_creation.ephemeral_5m_input_tokens": spec.CacheWriteTokens, + "$.usage.cache_creation.ephemeral_1h_input_tokens": spec.CacheWrite1hTokens, + "$.usage.completion_tokens_details.reasoning_tokens": spec.ReasoningTokens, + "$.usage.prompt_tokens_details.audio_tokens": spec.AudioInputTokens, + "$.usage.completion_tokens_details.audio_tokens": spec.AudioOutputTokens, + "$.usage.service_tier": spec.ServiceTier, + } + for want, got := range checks { + if got == nil { + t.Errorf("field for %q was dropped", want) + continue + } + if got.Identifier != want { + t.Errorf("identifier mismatch: got %q want %q", got.Identifier, want) + } + } + + // valueMap and providerFields are map-typed, so a missing or mistagged field + // on the generated model drops them silently rather than failing the parse. + if spec.ServiceTier == nil || spec.ServiceTier.ValueMap == nil { + t.Fatal("serviceTier.valueMap was dropped by the typed model") + } + valueMap := *spec.ServiceTier.ValueMap + if got := valueMap["ON_DEMAND_PRIORITY"]; got != "priority" { + t.Errorf("ON_DEMAND_PRIORITY = %q, want priority", got) + } + if got := valueMap["ON_DEMAND_FLEX"]; got != "flex" { + t.Errorf("ON_DEMAND_FLEX = %q, want flex", got) + } + + if spec.ProviderFields == nil { + t.Fatal("providerFields was dropped by the typed model") + } + providerFields := *spec.ProviderFields + if len(providerFields) != 2 { + t.Fatalf("got %d providerFields entries, want 2: %#v", len(providerFields), providerFields) + } + if got := providerFields["cacheDetails"]; got.Identifier != "$.usage.cacheDetails" { + t.Errorf("cacheDetails identifier = %q", got.Identifier) + } + if got := providerFields["cacheTokensDetails"]; string(got.Location) != "payload" { + t.Errorf("cacheTokensDetails location = %q", got.Location) + } + + if spec.ResourceMappings == nil || spec.ResourceMappings.Resources == nil { + t.Fatal("resourceMappings was dropped") + } + resources := *spec.ResourceMappings.Resources + if len(resources) != 1 { + t.Fatalf("expected 1 resource mapping, got %d", len(resources)) + } + m := resources[0] + if m.CachedTokens == nil || m.CachedTokens.Identifier != "$.usage.input_tokens_details.cached_tokens" { + t.Errorf("mapping cachedTokens not preserved: %v", m.CachedTokens) + } + if m.CacheAccounting == nil || *m.CacheAccounting != "inclusive" { + t.Errorf("mapping cacheAccounting not preserved: %v", m.CacheAccounting) + } +} diff --git a/gateway/gateway-controller/pkg/config/llm_validator.go b/gateway/gateway-controller/pkg/config/llm_validator.go index 293828b2be..86ccf284e7 100644 --- a/gateway/gateway-controller/pkg/config/llm_validator.go +++ b/gateway/gateway-controller/pkg/config/llm_validator.go @@ -20,8 +20,10 @@ package config import ( "fmt" + "maps" "net/url" "regexp" + "slices" "strings" "unicode" @@ -173,6 +175,53 @@ func (v *LLMValidator) validateTemplateSpec(spec *api.LLMProviderTemplateData) [ spec.RemainingTokens)...) } + if spec.CachedTokens != nil { + errors = append(errors, v.validateExtractionIdentifier("spec.cachedTokens", spec.CachedTokens)...) + } + + if spec.CacheWriteTokens != nil { + errors = append(errors, v.validateExtractionIdentifier("spec.cacheWriteTokens", spec.CacheWriteTokens)...) + } + + if spec.CacheWrite1hTokens != nil { + errors = append(errors, v.validateExtractionIdentifier("spec.cacheWrite1hTokens", spec.CacheWrite1hTokens)...) + } + + if spec.ReasoningTokens != nil { + errors = append(errors, v.validateExtractionIdentifier("spec.reasoningTokens", spec.ReasoningTokens)...) + } + + if spec.AudioInputTokens != nil { + errors = append(errors, v.validateExtractionIdentifier("spec.audioInputTokens", spec.AudioInputTokens)...) + } + + if spec.AudioOutputTokens != nil { + errors = append(errors, v.validateExtractionIdentifier("spec.audioOutputTokens", spec.AudioOutputTokens)...) + } + + if spec.ServiceTier != nil { + errors = append(errors, v.validateExtractionIdentifier("spec.serviceTier", spec.ServiceTier)...) + } + + if spec.ProviderFields != nil { + // Sorted so two identical requests report errors in the same order; + // Go randomises map iteration. + for _, name := range slices.Sorted(maps.Keys(*spec.ProviderFields)) { + if name == "" { + errors = append(errors, ValidationError{ + Field: "spec.providerFields", + Message: "providerFields key cannot be empty", + }) + continue + } + field := (*spec.ProviderFields)[name] + errors = append(errors, v.validateExtractionIdentifier( + fmt.Sprintf("spec.providerFields.%s", name), &field)...) + } + } + + errors = append(errors, v.validateCacheAccounting("spec.cacheAccounting", spec.CacheAccounting)...) + if spec.ResourceMappings != nil { errors = append(errors, v.validateTemplateResourceMappings("spec.resourceMappings", spec.ResourceMappings)...) } @@ -244,6 +293,44 @@ func (v *LLMValidator) validateTemplateResourceMapping(fieldPrefix string, errors = append(errors, v.validateExtractionIdentifier(fieldPrefix+".responseModel", mapping.ResponseModel)...) } + if mapping.CachedTokens != nil { + errors = append(errors, v.validateExtractionIdentifier( + fmt.Sprintf("%s.cachedTokens", fieldPrefix), mapping.CachedTokens)...) + } + + if mapping.CacheWriteTokens != nil { + errors = append(errors, v.validateExtractionIdentifier( + fmt.Sprintf("%s.cacheWriteTokens", fieldPrefix), mapping.CacheWriteTokens)...) + } + + if mapping.CacheWrite1hTokens != nil { + errors = append(errors, v.validateExtractionIdentifier( + fmt.Sprintf("%s.cacheWrite1hTokens", fieldPrefix), mapping.CacheWrite1hTokens)...) + } + + if mapping.ReasoningTokens != nil { + errors = append(errors, v.validateExtractionIdentifier( + fmt.Sprintf("%s.reasoningTokens", fieldPrefix), mapping.ReasoningTokens)...) + } + + if mapping.AudioInputTokens != nil { + errors = append(errors, v.validateExtractionIdentifier( + fmt.Sprintf("%s.audioInputTokens", fieldPrefix), mapping.AudioInputTokens)...) + } + + if mapping.AudioOutputTokens != nil { + errors = append(errors, v.validateExtractionIdentifier( + fmt.Sprintf("%s.audioOutputTokens", fieldPrefix), mapping.AudioOutputTokens)...) + } + + if mapping.ServiceTier != nil { + errors = append(errors, v.validateExtractionIdentifier( + fmt.Sprintf("%s.serviceTier", fieldPrefix), mapping.ServiceTier)...) + } + + errors = append(errors, v.validateCacheAccounting( + fmt.Sprintf("%s.cacheAccounting", fieldPrefix), mapping.CacheAccounting)...) + return errors } @@ -269,9 +356,51 @@ func (v *LLMValidator) validateExtractionIdentifier( }) } + if identifier.FallbackIdentifiers != nil { + for i, fallback := range *identifier.FallbackIdentifiers { + if fallback == "" { + errors = append(errors, ValidationError{ + Field: fmt.Sprintf("%s.fallbackIdentifiers[%d]", fieldPrefix, i), + Message: "fallback identifier cannot be empty", + }) + } + } + } + + // A key names a value the provider reports, so an empty key matches nothing. + // An empty target is allowed: it states that the reported value carries no + // rates of its own and should be billed as standard. + if identifier.ValueMap != nil { + for key := range *identifier.ValueMap { + if key == "" { + errors = append(errors, ValidationError{ + Field: fmt.Sprintf("%s.valueMap", fieldPrefix), + Message: "valueMap key cannot be empty", + }) + } + } + } + return errors } +// validateCacheAccounting checks the cache accounting mode. An absent value is +// valid and means inclusive. +func (v *LLMValidator) validateCacheAccounting(fieldPrefix string, value *string) []ValidationError { + if value == nil { + return nil + } + + if *value != "inclusive" && *value != "additive" { + return []ValidationError{{ + Field: fieldPrefix, + Message: "cacheAccounting must be 'inclusive' or 'additive'", + }} + } + + return nil +} + // validateLLMProvider validates an LLM provider configuration func (v *LLMValidator) validateLLMProvider(provider *api.LLMProviderConfiguration) []ValidationError { var errors []ValidationError diff --git a/gateway/gateway-controller/pkg/config/llm_validator_additional_test.go b/gateway/gateway-controller/pkg/config/llm_validator_additional_test.go index bd343404b5..32cf88c28a 100644 --- a/gateway/gateway-controller/pkg/config/llm_validator_additional_test.go +++ b/gateway/gateway-controller/pkg/config/llm_validator_additional_test.go @@ -1,6 +1,7 @@ package config import ( + "fmt" "testing" api "github.com/wso2/api-platform/gateway/gateway-controller/pkg/api/management" @@ -595,3 +596,225 @@ func TestLLMValidator_ValidateTemplateResourceMapping_WithSpaceInResource(t *tes t.Errorf("Expected error for resource with spaces, got: %v", errors) } } + +func TestLLMValidator_ValidateTemplateSpec_NewUsageFields_Valid(t *testing.T) { + v := NewLLMValidator() + accounting := "additive" + spec := &api.LLMProviderTemplateData{ + DisplayName: "anthropic", + CachedTokens: &api.ExtractionIdentifier{ + Location: api.Payload, + Identifier: "$.usage.cache_read_input_tokens", + }, + CacheWriteTokens: &api.ExtractionIdentifier{ + Location: api.Payload, + Identifier: "$.usage.cache_creation.ephemeral_5m_input_tokens", + }, + ServiceTier: &api.ExtractionIdentifier{ + Location: api.Payload, + Identifier: "$.usage.service_tier", + }, + CacheAccounting: &accounting, + } + + errs := v.validateTemplateSpec(spec) + if len(errs) != 0 { + t.Fatalf("expected no validation errors, got %v", errs) + } +} + +func TestLLMValidator_ValidateTemplateSpec_NewUsageFields_MissingIdentifier(t *testing.T) { + v := NewLLMValidator() + spec := &api.LLMProviderTemplateData{ + DisplayName: "openai", + CachedTokens: &api.ExtractionIdentifier{ + Location: api.Payload, + Identifier: "", + }, + } + + errs := v.validateTemplateSpec(spec) + if len(errs) != 1 { + t.Fatalf("expected 1 validation error, got %d: %v", len(errs), errs) + } + if errs[0].Field != "spec.cachedTokens.identifier" { + t.Errorf("expected field spec.cachedTokens.identifier, got %s", errs[0].Field) + } +} + +func TestLLMValidator_ValidateTemplateSpec_CacheAccounting_Invalid(t *testing.T) { + v := NewLLMValidator() + accounting := "sometimes" + spec := &api.LLMProviderTemplateData{ + DisplayName: "openai", + CacheAccounting: &accounting, + } + + errs := v.validateTemplateSpec(spec) + if len(errs) != 1 { + t.Fatalf("expected 1 validation error, got %d: %v", len(errs), errs) + } + if errs[0].Field != "spec.cacheAccounting" { + t.Errorf("expected field spec.cacheAccounting, got %s", errs[0].Field) + } +} + +func TestLLMValidator_ValidateTemplateSpec_CacheAccounting_OmittedIsValid(t *testing.T) { + v := NewLLMValidator() + spec := &api.LLMProviderTemplateData{DisplayName: "openai"} + + errs := v.validateTemplateSpec(spec) + if len(errs) != 0 { + t.Fatalf("expected no validation errors, got %v", errs) + } +} + +func TestLLMValidator_ValidateTemplateResourceMapping_NewUsageFields(t *testing.T) { + v := NewLLMValidator() + accounting := "inclusive" + mapping := &api.LLMProviderTemplateResourceMapping{ + Resource: "/responses", + CachedTokens: &api.ExtractionIdentifier{ + Location: api.Payload, + Identifier: "$.usage.input_tokens_details.cached_tokens", + }, + CacheAccounting: &accounting, + } + + errs := v.validateTemplateResourceMapping("spec.resourceMappings.resources[0]", mapping) + if len(errs) != 0 { + t.Fatalf("expected no validation errors, got %v", errs) + } +} + +// TestLLMValidator_ValidateTemplateResourceMapping_FieldPaths pins the exact +// error field path built for each mapping-level identifier field, so a wrong +// field-name prefix in validateTemplateResourceMapping cannot pass silently. +func TestLLMValidator_ValidateTemplateResourceMapping_FieldPaths(t *testing.T) { + const prefix = "spec.resourceMappings.resources[0]" + + badIdentifier := &api.ExtractionIdentifier{ + Location: api.Payload, + Identifier: "", + } + + tests := []struct { + name string + fieldName string + build func() *api.LLMProviderTemplateResourceMapping + }{ + { + name: "cachedTokens", + fieldName: "cachedTokens", + build: func() *api.LLMProviderTemplateResourceMapping { + return &api.LLMProviderTemplateResourceMapping{Resource: "/responses", CachedTokens: badIdentifier} + }, + }, + { + name: "cacheWriteTokens", + fieldName: "cacheWriteTokens", + build: func() *api.LLMProviderTemplateResourceMapping { + return &api.LLMProviderTemplateResourceMapping{Resource: "/responses", CacheWriteTokens: badIdentifier} + }, + }, + { + name: "cacheWrite1hTokens", + fieldName: "cacheWrite1hTokens", + build: func() *api.LLMProviderTemplateResourceMapping { + return &api.LLMProviderTemplateResourceMapping{Resource: "/responses", CacheWrite1hTokens: badIdentifier} + }, + }, + { + name: "reasoningTokens", + fieldName: "reasoningTokens", + build: func() *api.LLMProviderTemplateResourceMapping { + return &api.LLMProviderTemplateResourceMapping{Resource: "/responses", ReasoningTokens: badIdentifier} + }, + }, + { + name: "audioInputTokens", + fieldName: "audioInputTokens", + build: func() *api.LLMProviderTemplateResourceMapping { + return &api.LLMProviderTemplateResourceMapping{Resource: "/responses", AudioInputTokens: badIdentifier} + }, + }, + { + name: "audioOutputTokens", + fieldName: "audioOutputTokens", + build: func() *api.LLMProviderTemplateResourceMapping { + return &api.LLMProviderTemplateResourceMapping{Resource: "/responses", AudioOutputTokens: badIdentifier} + }, + }, + { + name: "serviceTier", + fieldName: "serviceTier", + build: func() *api.LLMProviderTemplateResourceMapping { + return &api.LLMProviderTemplateResourceMapping{Resource: "/responses", ServiceTier: badIdentifier} + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + v := NewLLMValidator() + mapping := tt.build() + + errs := v.validateTemplateResourceMapping(prefix, mapping) + if len(errs) != 1 { + t.Fatalf("expected exactly 1 validation error, got %d: %v", len(errs), errs) + } + + wantField := fmt.Sprintf("%s.%s.identifier", prefix, tt.fieldName) + if errs[0].Field != wantField { + t.Errorf("expected field %q, got %q", wantField, errs[0].Field) + } + }) + } +} + +// TestLLMValidator_ValidateTemplateResourceMapping_CacheAccounting_Invalid pins +// the exact error field path for an invalid mapping-level cacheAccounting value. +func TestLLMValidator_ValidateTemplateResourceMapping_CacheAccounting_Invalid(t *testing.T) { + const prefix = "spec.resourceMappings.resources[0]" + v := NewLLMValidator() + accounting := "sometimes" + mapping := &api.LLMProviderTemplateResourceMapping{ + Resource: "/responses", + CacheAccounting: &accounting, + } + + errs := v.validateTemplateResourceMapping(prefix, mapping) + if len(errs) != 1 { + t.Fatalf("expected exactly 1 validation error, got %d: %v", len(errs), errs) + } + + wantField := prefix + ".cacheAccounting" + if errs[0].Field != wantField { + t.Errorf("expected field %q, got %q", wantField, errs[0].Field) + } +} + +// TestLLMValidator_ValidateExtractionIdentifier_EmptyFallback pins the exact +// error field path for an empty entry inside fallbackIdentifiers. +func TestLLMValidator_ValidateExtractionIdentifier_EmptyFallback(t *testing.T) { + v := NewLLMValidator() + fallbacks := []string{"$.usage.prompt_tokens", ""} + spec := &api.LLMProviderTemplateData{ + DisplayName: "bedrock", + PromptTokens: &api.ExtractionIdentifier{ + Location: api.Payload, + Identifier: "$.usage.inputTokens", + FallbackIdentifiers: &fallbacks, + }, + } + + errs := v.validateTemplateSpec(spec) + if len(errs) != 1 { + t.Fatalf("expected exactly 1 validation error, got %d: %v", len(errs), errs) + } + + wantField := "spec.promptTokens.fallbackIdentifiers[1]" + if errs[0].Field != wantField { + t.Errorf("expected field %q, got %q", wantField, errs[0].Field) + } +} diff --git a/gateway/gateway-controller/pkg/config/llm_validator_test.go b/gateway/gateway-controller/pkg/config/llm_validator_test.go index d0464a52b6..31fb49d954 100644 --- a/gateway/gateway-controller/pkg/config/llm_validator_test.go +++ b/gateway/gateway-controller/pkg/config/llm_validator_test.go @@ -2121,3 +2121,199 @@ func TestValidateLLMProvider_UpstreamRef(t *testing.T) { assert.Empty(t, errs) }) } + +// TestValidateLLMProviderTemplate_ValueMap covers the value-map translation +// table. Keys name values the provider reports, so an empty key can never +// match anything; an empty target is meaningful and states that the reported +// value carries no distinct rates. +func TestValidateLLMProviderTemplate_ValueMap(t *testing.T) { + tests := []struct { + name string + valueMap map[string]string + expectError bool + }{ + { + name: "empty key is rejected", + valueMap: map[string]string{"": "priority"}, + expectError: true, + }, + { + name: "empty target is accepted", + valueMap: map[string]string{"scale": ""}, + expectError: false, + }, + { + name: "populated map is accepted", + valueMap: map[string]string{"ON_DEMAND_PRIORITY": "priority", "ON_DEMAND_FLEX": "flex"}, + expectError: false, + }, + } + + validator := NewLLMValidator() + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + valueMap := tt.valueMap + template := api.LLMProviderTemplate{ + ApiVersion: "gateway.api-platform.wso2.com/v1", + Kind: api.LLMProviderTemplateKindLlmProviderTemplate, + Metadata: api.Metadata{Name: "openai"}, + Spec: api.LLMProviderTemplateData{ + DisplayName: "test", + PromptTokens: &api.ExtractionIdentifier{ + Location: api.Payload, + Identifier: "$.usage.prompt_tokens", + ValueMap: &valueMap, + }, + }, + } + + errors := validator.Validate(&template) + + if tt.expectError { + require.NotEmpty(t, errors, "Should have validation errors") + found := false + for _, err := range errors { + if strings.Contains(err.Field, "valueMap") { + found = true + break + } + } + assert.True(t, found, "Should find a valueMap error, got %v", errors) + } else { + assert.Empty(t, errors, "Should not have validation errors") + } + }) + } +} + +func TestValidateLLMProviderTemplate_ProviderFields(t *testing.T) { + emptyFallback := []string{""} + emptyValueMapKey := map[string]string{"": "priority"} + + tests := []struct { + name string + providerFields map[string]api.ExtractionIdentifier + expectedField string + }{ + { + name: "valid entry is accepted", + providerFields: map[string]api.ExtractionIdentifier{ + "choices": {Location: api.Payload, Identifier: "$.choices"}, + }, + }, + { + name: "unsupported location is rejected", + providerFields: map[string]api.ExtractionIdentifier{ + "choices": {Location: "payloadx", Identifier: "$.choices"}, + }, + expectedField: "spec.providerFields.choices.location", + }, + { + name: "missing identifier is rejected", + providerFields: map[string]api.ExtractionIdentifier{ + "choices": {Location: api.Payload, Identifier: ""}, + }, + expectedField: "spec.providerFields.choices.identifier", + }, + { + name: "empty fallback identifier is rejected", + providerFields: map[string]api.ExtractionIdentifier{ + "choices": { + Location: api.Payload, + Identifier: "$.choices", + FallbackIdentifiers: &emptyFallback, + }, + }, + expectedField: "spec.providerFields.choices.fallbackIdentifiers[0]", + }, + { + name: "empty valueMap key is rejected", + providerFields: map[string]api.ExtractionIdentifier{ + "choices": { + Location: api.Payload, + Identifier: "$.choices", + ValueMap: &emptyValueMapKey, + }, + }, + expectedField: "spec.providerFields.choices.valueMap", + }, + { + name: "empty map key is rejected", + providerFields: map[string]api.ExtractionIdentifier{ + "": {Location: api.Payload, Identifier: "$.choices"}, + }, + expectedField: "spec.providerFields", + }, + } + + validator := NewLLMValidator() + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + providerFields := tt.providerFields + template := api.LLMProviderTemplate{ + ApiVersion: "gateway.api-platform.wso2.com/v1", + Kind: api.LLMProviderTemplateKindLlmProviderTemplate, + Metadata: api.Metadata{Name: "openai"}, + Spec: api.LLMProviderTemplateData{ + DisplayName: "test", + PromptTokens: &api.ExtractionIdentifier{ + Location: api.Payload, + Identifier: "$.usage.prompt_tokens", + }, + ProviderFields: &providerFields, + }, + } + + errors := validator.Validate(&template) + + if tt.expectedField == "" { + assert.Empty(t, errors, "Should not have validation errors") + return + } + + require.NotEmpty(t, errors, "Should have validation errors") + found := false + for _, err := range errors { + if err.Field == tt.expectedField { + found = true + break + } + } + assert.True(t, found, "Should report %q, got %v", tt.expectedField, errors) + }) + } +} + +// A providerFields entry must be reported under its own key, so an operator +// with several entries can tell which one is wrong. +func TestValidateLLMProviderTemplate_ProviderFieldsErrorsAreKeyed(t *testing.T) { + providerFields := map[string]api.ExtractionIdentifier{ + "choices": {Location: "bogus", Identifier: "$.choices"}, + "searchContextSize": {Location: api.Payload, Identifier: ""}, + } + + template := api.LLMProviderTemplate{ + ApiVersion: "gateway.api-platform.wso2.com/v1", + Kind: api.LLMProviderTemplateKindLlmProviderTemplate, + Metadata: api.Metadata{Name: "openai"}, + Spec: api.LLMProviderTemplateData{ + DisplayName: "test", + ProviderFields: &providerFields, + }, + } + + fields := make([]string, 0, 2) + for _, err := range NewLLMValidator().Validate(&template) { + if strings.HasPrefix(err.Field, "spec.providerFields") { + fields = append(fields, err.Field) + } + } + + // Sorted key order, so the list is identical on every run. + assert.Equal(t, []string{ + "spec.providerFields.choices.location", + "spec.providerFields.searchContextSize.identifier", + }, fields) +} diff --git a/gateway/gateway-runtime/policy-engine/internal/executor/chain.go b/gateway/gateway-runtime/policy-engine/internal/executor/chain.go index 2380a295c1..dd5db116f3 100644 --- a/gateway/gateway-runtime/policy-engine/internal/executor/chain.go +++ b/gateway/gateway-runtime/policy-engine/internal/executor/chain.go @@ -796,6 +796,7 @@ func (c *ChainExecutor) ExecuteStreamingRequestPolicies( currentChunk = &policy.StreamBody{ Chunk: fwd.Body, EndOfStream: currentChunk.EndOfStream, + Index: currentChunk.Index, } } @@ -939,11 +940,11 @@ func (c *ChainExecutor) ExecuteStreamingResponsePolicies( switch a := action.(type) { case policy.ForwardResponseChunk: if a.Body != nil { - currentChunk = &policy.StreamBody{Chunk: a.Body, EndOfStream: currentChunk.EndOfStream} + currentChunk = &policy.StreamBody{Chunk: a.Body, EndOfStream: currentChunk.EndOfStream, Index: currentChunk.Index} } case policy.TerminateResponseChunk: if a.Body != nil { - currentChunk = &policy.StreamBody{Chunk: a.Body, EndOfStream: true} + currentChunk = &policy.StreamBody{Chunk: a.Body, EndOfStream: true, Index: currentChunk.Index} } } diff --git a/gateway/gateway-runtime/policy-engine/internal/executor/chain_test.go b/gateway/gateway-runtime/policy-engine/internal/executor/chain_test.go index a6408a78c9..4dcd2aeff5 100644 --- a/gateway/gateway-runtime/policy-engine/internal/executor/chain_test.go +++ b/gateway/gateway-runtime/policy-engine/internal/executor/chain_test.go @@ -960,3 +960,76 @@ func TestExecuteStreamingResponsePolicies_ModeFirstGating(t *testing.T) { }) } } + +type transformingResponsePolicy struct{} + +func (p *transformingResponsePolicy) Mode() policy.ProcessingMode { + return policy.ProcessingMode{ResponseBodyMode: policy.BodyModeStream} +} + +func (p *transformingResponsePolicy) OnResponseBody(_ context.Context, _ *policy.ResponseContext, _ map[string]interface{}) policy.ResponseAction { + return policy.DownstreamResponseModifications{} +} + +func (p *transformingResponsePolicy) NeedsMoreResponseData(_ []byte) bool { return false } + +func (p *transformingResponsePolicy) OnResponseBodyChunk(_ context.Context, _ *policy.ResponseStreamContext, chunk *policy.StreamBody, _ map[string]interface{}) policy.StreamingResponseAction { + return policy.ForwardResponseChunk{Body: []byte("rewritten")} +} + +type indexRecordingResponsePolicy struct { + seen []uint64 +} + +func (p *indexRecordingResponsePolicy) Mode() policy.ProcessingMode { + return policy.ProcessingMode{ResponseBodyMode: policy.BodyModeStream} +} + +func (p *indexRecordingResponsePolicy) OnResponseBody(_ context.Context, _ *policy.ResponseContext, _ map[string]interface{}) policy.ResponseAction { + return policy.DownstreamResponseModifications{} +} + +func (p *indexRecordingResponsePolicy) NeedsMoreResponseData(_ []byte) bool { return false } + +func (p *indexRecordingResponsePolicy) OnResponseBodyChunk(_ context.Context, _ *policy.ResponseStreamContext, chunk *policy.StreamBody, _ map[string]interface{}) policy.StreamingResponseAction { + p.seen = append(p.seen, chunk.Index) + return policy.ForwardResponseChunk{} +} + +func TestExecuteStreamingResponsePolicies_IndexSurvivesBodyTransformation(t *testing.T) { + tracer := noop.NewTracerProvider().Tracer("test") + executor := NewChainExecutor(nil, nil, tracer) + + recorder := &indexRecordingResponsePolicy{} + respCtx := &policy.ResponseStreamContext{ + SharedContext: testutils.NewTestSharedContext(), + RequestHeaders: policy.NewHeaders(map[string][]string{"content-type": {"application/json"}}), + RequestPath: "/test", + RequestMethod: "POST", + ResponseHeaders: policy.NewHeaders(map[string][]string{"content-type": {"application/json"}}), + ResponseStatus: 200, + } + + policies := []policy.Policy{recorder, &transformingResponsePolicy{}} + specs := []policy.PolicySpec{ + newPolicySpec("record", "v1.0.0", true, nil), + newPolicySpec("transform", "v1.0.0", true, nil), + } + + for _, index := range []uint64{1, 2, 3} { + _, err := executor.ExecuteStreamingResponsePolicies( + context.Background(), + policies, + respCtx, + &policy.StreamBody{Chunk: []byte("chunk"), Index: index, EndOfStream: index == 3}, + specs, + "api", + "route", + false, + ) + require.NoError(t, err) + } + + assert.Equal(t, []uint64{1, 2, 3}, recorder.seen, + "the downstream policy must see the kernel's indexes, not zeros") +} diff --git a/gateway/gateway-runtime/policy-engine/internal/kernel/execution_context.go b/gateway/gateway-runtime/policy-engine/internal/kernel/execution_context.go index 4a9d01249f..771582f9de 100644 --- a/gateway/gateway-runtime/policy-engine/internal/kernel/execution_context.go +++ b/gateway/gateway-runtime/policy-engine/internal/kernel/execution_context.go @@ -126,6 +126,10 @@ type PolicyExecutionContext struct { isStreamingRequest bool requestStreamAccumulator []byte requestStreamContext *policy.RequestStreamContext + // requestChunkIndex numbers the chunks handed to the policy chain. Policies + // that stitch chunks together use it to tell a new chunk from a redelivery, + // so it must advance on every delivery. + requestChunkIndex uint64 // requestStreamDecomp performs per-chunk decompression for compressed streaming // request bodies. Nil when the request is not Content-Encoded. requestStreamDecomp *streamDecompressor @@ -135,6 +139,9 @@ type PolicyExecutionContext struct { isStreamingResponse bool streamAccumulator []byte responseStreamContext *policy.ResponseStreamContext + // responseChunkIndex numbers the chunks handed to the policy chain. See + // requestChunkIndex. + responseChunkIndex uint64 // responseStreamDecomp performs per-chunk decompression for compressed streaming // response bodies. Nil when the response is not Content-Encoded. responseStreamDecomp *streamDecompressor @@ -775,9 +782,11 @@ func (ec *PolicyExecutionContext) processStreamingRequestBody( ctx context.Context, body *extprocv3.HttpBody, ) (*extprocv3.ProcessingResponse, error) { + ec.requestChunkIndex++ chunk := &policy.StreamBody{ Chunk: body.Body, EndOfStream: body.EndOfStream, + Index: ec.requestChunkIndex, } // Compressed request: decompress this chunk, pass directly to policies, @@ -895,9 +904,11 @@ func (ec *PolicyExecutionContext) processStreamingRequestBody( }, nil } + ec.requestChunkIndex++ flushChunk := &policy.StreamBody{ Chunk: ec.requestStreamAccumulator, EndOfStream: chunk.EndOfStream, + Index: ec.requestChunkIndex, } slog.Debug("[streaming] flushing accumulated request data to policies", "route", ec.routeKey, @@ -1110,9 +1121,11 @@ func (ec *PolicyExecutionContext) processStreamingResponseBody( }, nil } + ec.responseChunkIndex++ chunk := &policy.StreamBody{ Chunk: body.Body, EndOfStream: body.EndOfStream, + Index: ec.responseChunkIndex, } // Compressed response: decompress this chunk, pass directly to policies, @@ -1228,9 +1241,11 @@ func (ec *PolicyExecutionContext) processStreamingResponseBody( }, nil } + ec.responseChunkIndex++ flushChunk := &policy.StreamBody{ Chunk: ec.streamAccumulator, EndOfStream: chunk.EndOfStream, + Index: ec.responseChunkIndex, } slog.Debug("[streaming] flushing accumulated response data to policies", "route", ec.routeKey, diff --git a/gateway/gateway-runtime/policy-engine/internal/kernel/execution_context_test.go b/gateway/gateway-runtime/policy-engine/internal/kernel/execution_context_test.go index e4241c3f22..8d9f8add19 100644 --- a/gateway/gateway-runtime/policy-engine/internal/kernel/execution_context_test.go +++ b/gateway/gateway-runtime/policy-engine/internal/kernel/execution_context_test.go @@ -1176,3 +1176,74 @@ func TestProcessStreamingRequestBody_MalformedEncodingFailsClosed(t *testing.T) assert.NotErrorIs(t, err, ErrDecompressedTooLarge) assert.Nil(t, execCtx.requestStreamDecomp) } + +// indexRecordingPolicy records the Index of every chunk the kernel delivers. +type indexRecordingPolicy struct { + seen []uint64 +} + +func (p *indexRecordingPolicy) Mode() policy.ProcessingMode { + return policy.ProcessingMode{ResponseBodyMode: policy.BodyModeStream} +} + +func (p *indexRecordingPolicy) OnResponseBody(_ context.Context, _ *policy.ResponseContext, _ map[string]interface{}) policy.ResponseAction { + return policy.DownstreamResponseModifications{} +} + +// NeedsMoreResponseData returns false so the kernel flushes every chunk, which +// is what a policy doing its own accumulation asks for. +func (p *indexRecordingPolicy) NeedsMoreResponseData(_ []byte) bool { return false } + +func (p *indexRecordingPolicy) OnResponseBodyChunk(_ context.Context, _ *policy.ResponseStreamContext, chunk *policy.StreamBody, _ map[string]interface{}) policy.StreamingResponseAction { + p.seen = append(p.seen, chunk.Index) + return policy.ForwardResponseChunk{} +} + +// A policy that stitches chunks together tells a new chunk from a redelivery by +// its Index, so the kernel must advance it on every delivery. When it does not, +// such a policy keeps only the first chunk and silently works off a partial +// body — which is how streaming LLM responses ended up priced at zero. +func TestStreamingResponse_ChunkIndexAdvancesPerDelivery(t *testing.T) { + rec := &indexRecordingPolicy{} + + kernel := NewKernel() + server := NewExternalProcessorServer(kernel, newTestExecutor(), config.TracingConfig{}, "", testMaxDecompressedBytes, testMaxDecompressedBytes) + chain := ®istry.PolicyChain{ + RequiresResponseBody: true, + SupportsResponseStreaming: true, + Policies: []policy.Policy{rec}, + PolicySpecs: []policy.PolicySpec{{Enabled: true}}, + } + execCtx := newPolicyExecutionContext(server, "test-route", chain) + + execCtx.buildRequestContexts(&extprocv3.HttpHeaders{Headers: &corev3.HeaderMap{}}, RouteMetadata{}) + execCtx.buildResponseContexts(&extprocv3.HttpHeaders{ + Headers: &corev3.HeaderMap{ + Headers: []*corev3.HeaderValue{ + {Key: ":status", RawValue: []byte("200")}, + {Key: "content-type", RawValue: []byte("text/event-stream")}, + }, + }, + }) + + chunks := []string{ + "data: {\"model\":\"m\"}\n\n", + "data: {\"choices\":[]}\n\n", + "data: {\"usage\":{\"prompt_tokens\":6}}\n\n", + "data: [DONE]\n\n", + } + for i, c := range chunks { + _, err := execCtx.processStreamingResponseBody(context.Background(), &extprocv3.HttpBody{ + Body: []byte(c), + EndOfStream: i == len(chunks)-1, + }) + require.NoError(t, err) + } + + require.Len(t, rec.seen, len(chunks), "every chunk should reach the policy") + for i := 1; i < len(rec.seen); i++ { + assert.Greater(t, rec.seen[i], rec.seen[i-1], + "chunk %d index %d must exceed chunk %d index %d; equal indices make accumulating policies discard the chunk", + i, rec.seen[i], i-1, rec.seen[i-1]) + } +} diff --git a/kubernetes/gateway-operator/api/v1/llmprovidertemplate_types.go b/kubernetes/gateway-operator/api/v1/llmprovidertemplate_types.go index e819233ce0..3536200bcc 100644 --- a/kubernetes/gateway-operator/api/v1/llmprovidertemplate_types.go +++ b/kubernetes/gateway-operator/api/v1/llmprovidertemplate_types.go @@ -31,6 +31,16 @@ type ExtractionIdentifier struct { // +kubebuilder:validation:Required // +kubebuilder:validation:Enum=header;pathParam;payload;queryParam Location string `json:"location"` + + // FallbackIdentifiers are additional locations to try, in order, when the + // primary identifier yields no value. + // +optional + FallbackIdentifiers []string `json:"fallbackIdentifiers,omitempty"` + + // ValueMap translates values the provider reports into the vocabulary the + // gateway works in. A reported value that is not a key is used unchanged. + // +optional + ValueMap map[string]string `json:"valueMap,omitempty"` } // LLMProviderTemplateResourceMapping maps a resource path pattern to per-field @@ -52,6 +62,27 @@ type LLMProviderTemplateResourceMapping struct { ResponseModel *ExtractionIdentifier `json:"responseModel,omitempty"` // +optional TotalTokens *ExtractionIdentifier `json:"totalTokens,omitempty"` + + // +optional + AudioInputTokens *ExtractionIdentifier `json:"audioInputTokens,omitempty"` + // +optional + AudioOutputTokens *ExtractionIdentifier `json:"audioOutputTokens,omitempty"` + // +optional + CachedTokens *ExtractionIdentifier `json:"cachedTokens,omitempty"` + // +optional + CacheWrite1hTokens *ExtractionIdentifier `json:"cacheWrite1hTokens,omitempty"` + // +optional + CacheWriteTokens *ExtractionIdentifier `json:"cacheWriteTokens,omitempty"` + // +optional + ReasoningTokens *ExtractionIdentifier `json:"reasoningTokens,omitempty"` + // +optional + ServiceTier *ExtractionIdentifier `json:"serviceTier,omitempty"` + + // CacheAccounting overrides the template-level cache accounting mode for + // this resource. When omitted, the template-level value is inherited. + // +optional + // +kubebuilder:validation:Enum=inclusive;additive + CacheAccounting string `json:"cacheAccounting,omitempty"` } // LLMProviderTemplateResourceMappings lists per-resource extraction mappings. @@ -82,6 +113,33 @@ type LLMProviderTemplateData struct { ResponseModel *ExtractionIdentifier `json:"responseModel,omitempty"` // +optional TotalTokens *ExtractionIdentifier `json:"totalTokens,omitempty"` + + // +optional + AudioInputTokens *ExtractionIdentifier `json:"audioInputTokens,omitempty"` + // +optional + AudioOutputTokens *ExtractionIdentifier `json:"audioOutputTokens,omitempty"` + // +optional + CachedTokens *ExtractionIdentifier `json:"cachedTokens,omitempty"` + // +optional + CacheWrite1hTokens *ExtractionIdentifier `json:"cacheWrite1hTokens,omitempty"` + // +optional + CacheWriteTokens *ExtractionIdentifier `json:"cacheWriteTokens,omitempty"` + // +optional + ReasoningTokens *ExtractionIdentifier `json:"reasoningTokens,omitempty"` + // +optional + ServiceTier *ExtractionIdentifier `json:"serviceTier,omitempty"` + + // ProviderFields names locations a provider-specific calculator needs that + // the closed vocabulary above does not cover. Only the position is declared; + // the value found there is passed through unchanged. + // +optional + ProviderFields map[string]ExtractionIdentifier `json:"providerFields,omitempty"` + + // CacheAccounting states whether the provider's cached token count is part + // of the input total or additional to it. Defaults to inclusive. + // +optional + // +kubebuilder:validation:Enum=inclusive;additive + CacheAccounting string `json:"cacheAccounting,omitempty"` } //+kubebuilder:object:root=true diff --git a/kubernetes/gateway-operator/api/v1/zz_generated.deepcopy.go b/kubernetes/gateway-operator/api/v1/zz_generated.deepcopy.go index f4ca5f8f1a..ecd1902a12 100644 --- a/kubernetes/gateway-operator/api/v1/zz_generated.deepcopy.go +++ b/kubernetes/gateway-operator/api/v1/zz_generated.deepcopy.go @@ -511,6 +511,18 @@ func (in *CertificateSpec) DeepCopy() *CertificateSpec { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ExtractionIdentifier) DeepCopyInto(out *ExtractionIdentifier) { *out = *in + if in.FallbackIdentifiers != nil { + in, out := &in.FallbackIdentifiers, &out.FallbackIdentifiers + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.ValueMap != nil { + in, out := &in.ValueMap, &out.ValueMap + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExtractionIdentifier. @@ -839,22 +851,22 @@ func (in *LLMProviderTemplateData) DeepCopyInto(out *LLMProviderTemplateData) { if in.CompletionTokens != nil { in, out := &in.CompletionTokens, &out.CompletionTokens *out = new(ExtractionIdentifier) - **out = **in + (*in).DeepCopyInto(*out) } if in.PromptTokens != nil { in, out := &in.PromptTokens, &out.PromptTokens *out = new(ExtractionIdentifier) - **out = **in + (*in).DeepCopyInto(*out) } if in.RemainingTokens != nil { in, out := &in.RemainingTokens, &out.RemainingTokens *out = new(ExtractionIdentifier) - **out = **in + (*in).DeepCopyInto(*out) } if in.RequestModel != nil { in, out := &in.RequestModel, &out.RequestModel *out = new(ExtractionIdentifier) - **out = **in + (*in).DeepCopyInto(*out) } if in.ResourceMappings != nil { in, out := &in.ResourceMappings, &out.ResourceMappings @@ -864,12 +876,54 @@ func (in *LLMProviderTemplateData) DeepCopyInto(out *LLMProviderTemplateData) { if in.ResponseModel != nil { in, out := &in.ResponseModel, &out.ResponseModel *out = new(ExtractionIdentifier) - **out = **in + (*in).DeepCopyInto(*out) } if in.TotalTokens != nil { in, out := &in.TotalTokens, &out.TotalTokens *out = new(ExtractionIdentifier) - **out = **in + (*in).DeepCopyInto(*out) + } + if in.AudioInputTokens != nil { + in, out := &in.AudioInputTokens, &out.AudioInputTokens + *out = new(ExtractionIdentifier) + (*in).DeepCopyInto(*out) + } + if in.AudioOutputTokens != nil { + in, out := &in.AudioOutputTokens, &out.AudioOutputTokens + *out = new(ExtractionIdentifier) + (*in).DeepCopyInto(*out) + } + if in.CachedTokens != nil { + in, out := &in.CachedTokens, &out.CachedTokens + *out = new(ExtractionIdentifier) + (*in).DeepCopyInto(*out) + } + if in.CacheWrite1hTokens != nil { + in, out := &in.CacheWrite1hTokens, &out.CacheWrite1hTokens + *out = new(ExtractionIdentifier) + (*in).DeepCopyInto(*out) + } + if in.CacheWriteTokens != nil { + in, out := &in.CacheWriteTokens, &out.CacheWriteTokens + *out = new(ExtractionIdentifier) + (*in).DeepCopyInto(*out) + } + if in.ReasoningTokens != nil { + in, out := &in.ReasoningTokens, &out.ReasoningTokens + *out = new(ExtractionIdentifier) + (*in).DeepCopyInto(*out) + } + if in.ServiceTier != nil { + in, out := &in.ServiceTier, &out.ServiceTier + *out = new(ExtractionIdentifier) + (*in).DeepCopyInto(*out) + } + if in.ProviderFields != nil { + in, out := &in.ProviderFields, &out.ProviderFields + *out = make(map[string]ExtractionIdentifier, len(*in)) + for key, val := range *in { + (*out)[key] = *val.DeepCopy() + } } } @@ -889,32 +943,67 @@ func (in *LLMProviderTemplateResourceMapping) DeepCopyInto(out *LLMProviderTempl if in.CompletionTokens != nil { in, out := &in.CompletionTokens, &out.CompletionTokens *out = new(ExtractionIdentifier) - **out = **in + (*in).DeepCopyInto(*out) } if in.PromptTokens != nil { in, out := &in.PromptTokens, &out.PromptTokens *out = new(ExtractionIdentifier) - **out = **in + (*in).DeepCopyInto(*out) } if in.RemainingTokens != nil { in, out := &in.RemainingTokens, &out.RemainingTokens *out = new(ExtractionIdentifier) - **out = **in + (*in).DeepCopyInto(*out) } if in.RequestModel != nil { in, out := &in.RequestModel, &out.RequestModel *out = new(ExtractionIdentifier) - **out = **in + (*in).DeepCopyInto(*out) } if in.ResponseModel != nil { in, out := &in.ResponseModel, &out.ResponseModel *out = new(ExtractionIdentifier) - **out = **in + (*in).DeepCopyInto(*out) } if in.TotalTokens != nil { in, out := &in.TotalTokens, &out.TotalTokens *out = new(ExtractionIdentifier) - **out = **in + (*in).DeepCopyInto(*out) + } + if in.AudioInputTokens != nil { + in, out := &in.AudioInputTokens, &out.AudioInputTokens + *out = new(ExtractionIdentifier) + (*in).DeepCopyInto(*out) + } + if in.AudioOutputTokens != nil { + in, out := &in.AudioOutputTokens, &out.AudioOutputTokens + *out = new(ExtractionIdentifier) + (*in).DeepCopyInto(*out) + } + if in.CachedTokens != nil { + in, out := &in.CachedTokens, &out.CachedTokens + *out = new(ExtractionIdentifier) + (*in).DeepCopyInto(*out) + } + if in.CacheWrite1hTokens != nil { + in, out := &in.CacheWrite1hTokens, &out.CacheWrite1hTokens + *out = new(ExtractionIdentifier) + (*in).DeepCopyInto(*out) + } + if in.CacheWriteTokens != nil { + in, out := &in.CacheWriteTokens, &out.CacheWriteTokens + *out = new(ExtractionIdentifier) + (*in).DeepCopyInto(*out) + } + if in.ReasoningTokens != nil { + in, out := &in.ReasoningTokens, &out.ReasoningTokens + *out = new(ExtractionIdentifier) + (*in).DeepCopyInto(*out) + } + if in.ServiceTier != nil { + in, out := &in.ServiceTier, &out.ServiceTier + *out = new(ExtractionIdentifier) + (*in).DeepCopyInto(*out) } } diff --git a/kubernetes/gateway-operator/api/v1alpha1/llmprovidertemplate_types.go b/kubernetes/gateway-operator/api/v1alpha1/llmprovidertemplate_types.go index b1ad1ee9bf..932d7f817d 100644 --- a/kubernetes/gateway-operator/api/v1alpha1/llmprovidertemplate_types.go +++ b/kubernetes/gateway-operator/api/v1alpha1/llmprovidertemplate_types.go @@ -31,6 +31,16 @@ type ExtractionIdentifier struct { // +kubebuilder:validation:Required // +kubebuilder:validation:Enum=header;pathParam;payload;queryParam Location string `json:"location"` + + // FallbackIdentifiers are additional locations to try, in order, when the + // primary identifier yields no value. + // +optional + FallbackIdentifiers []string `json:"fallbackIdentifiers,omitempty"` + + // ValueMap translates values the provider reports into the vocabulary the + // gateway works in. A reported value that is not a key is used unchanged. + // +optional + ValueMap map[string]string `json:"valueMap,omitempty"` } // LLMProviderTemplateResourceMapping maps a resource path pattern to per-field @@ -52,6 +62,27 @@ type LLMProviderTemplateResourceMapping struct { ResponseModel *ExtractionIdentifier `json:"responseModel,omitempty"` // +optional TotalTokens *ExtractionIdentifier `json:"totalTokens,omitempty"` + + // +optional + AudioInputTokens *ExtractionIdentifier `json:"audioInputTokens,omitempty"` + // +optional + AudioOutputTokens *ExtractionIdentifier `json:"audioOutputTokens,omitempty"` + // +optional + CachedTokens *ExtractionIdentifier `json:"cachedTokens,omitempty"` + // +optional + CacheWrite1hTokens *ExtractionIdentifier `json:"cacheWrite1hTokens,omitempty"` + // +optional + CacheWriteTokens *ExtractionIdentifier `json:"cacheWriteTokens,omitempty"` + // +optional + ReasoningTokens *ExtractionIdentifier `json:"reasoningTokens,omitempty"` + // +optional + ServiceTier *ExtractionIdentifier `json:"serviceTier,omitempty"` + + // CacheAccounting overrides the template-level cache accounting mode for + // this resource. When omitted, the template-level value is inherited. + // +optional + // +kubebuilder:validation:Enum=inclusive;additive + CacheAccounting string `json:"cacheAccounting,omitempty"` } // LLMProviderTemplateResourceMappings lists per-resource extraction mappings. @@ -82,6 +113,33 @@ type LLMProviderTemplateData struct { ResponseModel *ExtractionIdentifier `json:"responseModel,omitempty"` // +optional TotalTokens *ExtractionIdentifier `json:"totalTokens,omitempty"` + + // +optional + AudioInputTokens *ExtractionIdentifier `json:"audioInputTokens,omitempty"` + // +optional + AudioOutputTokens *ExtractionIdentifier `json:"audioOutputTokens,omitempty"` + // +optional + CachedTokens *ExtractionIdentifier `json:"cachedTokens,omitempty"` + // +optional + CacheWrite1hTokens *ExtractionIdentifier `json:"cacheWrite1hTokens,omitempty"` + // +optional + CacheWriteTokens *ExtractionIdentifier `json:"cacheWriteTokens,omitempty"` + // +optional + ReasoningTokens *ExtractionIdentifier `json:"reasoningTokens,omitempty"` + // +optional + ServiceTier *ExtractionIdentifier `json:"serviceTier,omitempty"` + + // ProviderFields names locations a provider-specific calculator needs that + // the closed vocabulary above does not cover. Only the position is declared; + // the value found there is passed through unchanged. + // +optional + ProviderFields map[string]ExtractionIdentifier `json:"providerFields,omitempty"` + + // CacheAccounting states whether the provider's cached token count is part + // of the input total or additional to it. Defaults to inclusive. + // +optional + // +kubebuilder:validation:Enum=inclusive;additive + CacheAccounting string `json:"cacheAccounting,omitempty"` } //+kubebuilder:object:root=true diff --git a/kubernetes/gateway-operator/api/v1alpha1/zz_generated.deepcopy.go b/kubernetes/gateway-operator/api/v1alpha1/zz_generated.deepcopy.go index 41b7e7c266..aa0a944792 100644 --- a/kubernetes/gateway-operator/api/v1alpha1/zz_generated.deepcopy.go +++ b/kubernetes/gateway-operator/api/v1alpha1/zz_generated.deepcopy.go @@ -511,6 +511,18 @@ func (in *CertificateSpec) DeepCopy() *CertificateSpec { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ExtractionIdentifier) DeepCopyInto(out *ExtractionIdentifier) { *out = *in + if in.FallbackIdentifiers != nil { + in, out := &in.FallbackIdentifiers, &out.FallbackIdentifiers + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.ValueMap != nil { + in, out := &in.ValueMap, &out.ValueMap + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExtractionIdentifier. @@ -839,22 +851,22 @@ func (in *LLMProviderTemplateData) DeepCopyInto(out *LLMProviderTemplateData) { if in.CompletionTokens != nil { in, out := &in.CompletionTokens, &out.CompletionTokens *out = new(ExtractionIdentifier) - **out = **in + (*in).DeepCopyInto(*out) } if in.PromptTokens != nil { in, out := &in.PromptTokens, &out.PromptTokens *out = new(ExtractionIdentifier) - **out = **in + (*in).DeepCopyInto(*out) } if in.RemainingTokens != nil { in, out := &in.RemainingTokens, &out.RemainingTokens *out = new(ExtractionIdentifier) - **out = **in + (*in).DeepCopyInto(*out) } if in.RequestModel != nil { in, out := &in.RequestModel, &out.RequestModel *out = new(ExtractionIdentifier) - **out = **in + (*in).DeepCopyInto(*out) } if in.ResourceMappings != nil { in, out := &in.ResourceMappings, &out.ResourceMappings @@ -864,12 +876,54 @@ func (in *LLMProviderTemplateData) DeepCopyInto(out *LLMProviderTemplateData) { if in.ResponseModel != nil { in, out := &in.ResponseModel, &out.ResponseModel *out = new(ExtractionIdentifier) - **out = **in + (*in).DeepCopyInto(*out) } if in.TotalTokens != nil { in, out := &in.TotalTokens, &out.TotalTokens *out = new(ExtractionIdentifier) - **out = **in + (*in).DeepCopyInto(*out) + } + if in.AudioInputTokens != nil { + in, out := &in.AudioInputTokens, &out.AudioInputTokens + *out = new(ExtractionIdentifier) + (*in).DeepCopyInto(*out) + } + if in.AudioOutputTokens != nil { + in, out := &in.AudioOutputTokens, &out.AudioOutputTokens + *out = new(ExtractionIdentifier) + (*in).DeepCopyInto(*out) + } + if in.CachedTokens != nil { + in, out := &in.CachedTokens, &out.CachedTokens + *out = new(ExtractionIdentifier) + (*in).DeepCopyInto(*out) + } + if in.CacheWrite1hTokens != nil { + in, out := &in.CacheWrite1hTokens, &out.CacheWrite1hTokens + *out = new(ExtractionIdentifier) + (*in).DeepCopyInto(*out) + } + if in.CacheWriteTokens != nil { + in, out := &in.CacheWriteTokens, &out.CacheWriteTokens + *out = new(ExtractionIdentifier) + (*in).DeepCopyInto(*out) + } + if in.ReasoningTokens != nil { + in, out := &in.ReasoningTokens, &out.ReasoningTokens + *out = new(ExtractionIdentifier) + (*in).DeepCopyInto(*out) + } + if in.ServiceTier != nil { + in, out := &in.ServiceTier, &out.ServiceTier + *out = new(ExtractionIdentifier) + (*in).DeepCopyInto(*out) + } + if in.ProviderFields != nil { + in, out := &in.ProviderFields, &out.ProviderFields + *out = make(map[string]ExtractionIdentifier, len(*in)) + for key, val := range *in { + (*out)[key] = *val.DeepCopy() + } } } @@ -889,32 +943,67 @@ func (in *LLMProviderTemplateResourceMapping) DeepCopyInto(out *LLMProviderTempl if in.CompletionTokens != nil { in, out := &in.CompletionTokens, &out.CompletionTokens *out = new(ExtractionIdentifier) - **out = **in + (*in).DeepCopyInto(*out) } if in.PromptTokens != nil { in, out := &in.PromptTokens, &out.PromptTokens *out = new(ExtractionIdentifier) - **out = **in + (*in).DeepCopyInto(*out) } if in.RemainingTokens != nil { in, out := &in.RemainingTokens, &out.RemainingTokens *out = new(ExtractionIdentifier) - **out = **in + (*in).DeepCopyInto(*out) } if in.RequestModel != nil { in, out := &in.RequestModel, &out.RequestModel *out = new(ExtractionIdentifier) - **out = **in + (*in).DeepCopyInto(*out) } if in.ResponseModel != nil { in, out := &in.ResponseModel, &out.ResponseModel *out = new(ExtractionIdentifier) - **out = **in + (*in).DeepCopyInto(*out) } if in.TotalTokens != nil { in, out := &in.TotalTokens, &out.TotalTokens *out = new(ExtractionIdentifier) - **out = **in + (*in).DeepCopyInto(*out) + } + if in.AudioInputTokens != nil { + in, out := &in.AudioInputTokens, &out.AudioInputTokens + *out = new(ExtractionIdentifier) + (*in).DeepCopyInto(*out) + } + if in.AudioOutputTokens != nil { + in, out := &in.AudioOutputTokens, &out.AudioOutputTokens + *out = new(ExtractionIdentifier) + (*in).DeepCopyInto(*out) + } + if in.CachedTokens != nil { + in, out := &in.CachedTokens, &out.CachedTokens + *out = new(ExtractionIdentifier) + (*in).DeepCopyInto(*out) + } + if in.CacheWrite1hTokens != nil { + in, out := &in.CacheWrite1hTokens, &out.CacheWrite1hTokens + *out = new(ExtractionIdentifier) + (*in).DeepCopyInto(*out) + } + if in.CacheWriteTokens != nil { + in, out := &in.CacheWriteTokens, &out.CacheWriteTokens + *out = new(ExtractionIdentifier) + (*in).DeepCopyInto(*out) + } + if in.ReasoningTokens != nil { + in, out := &in.ReasoningTokens, &out.ReasoningTokens + *out = new(ExtractionIdentifier) + (*in).DeepCopyInto(*out) + } + if in.ServiceTier != nil { + in, out := &in.ServiceTier, &out.ServiceTier + *out = new(ExtractionIdentifier) + (*in).DeepCopyInto(*out) } } diff --git a/kubernetes/gateway-operator/config/crd/bases/gateway.api-platform.wso2.com_llmprovidertemplates.yaml b/kubernetes/gateway-operator/config/crd/bases/gateway.api-platform.wso2.com_llmprovidertemplates.yaml index 299fa01e0c..0cd8887c62 100644 --- a/kubernetes/gateway-operator/config/crd/bases/gateway.api-platform.wso2.com_llmprovidertemplates.yaml +++ b/kubernetes/gateway-operator/config/crd/bases/gateway.api-platform.wso2.com_llmprovidertemplates.yaml @@ -45,11 +45,196 @@ spec: payload. The non-resource extractor fields apply when no resourceMappings entry matches the request path. properties: + audioInputTokens: + description: |- + ExtractionIdentifier locates a value within an HTTP request or response. + Mirrors the gateway-controller management API ExtractionIdentifier model. + properties: + fallbackIdentifiers: + description: |- + FallbackIdentifiers are additional locations to try, in order, when the + primary identifier yields no value. + items: + type: string + type: array + identifier: + description: Identifier is a JSONPath expression or header name. + type: string + location: + description: Location is where to look for the value. + enum: + - header + - pathParam + - payload + - queryParam + type: string + valueMap: + additionalProperties: + type: string + description: |- + ValueMap translates values the provider reports into the vocabulary the + gateway works in. A reported value that is not a key is used unchanged. + type: object + required: + - identifier + - location + type: object + audioOutputTokens: + description: |- + ExtractionIdentifier locates a value within an HTTP request or response. + Mirrors the gateway-controller management API ExtractionIdentifier model. + properties: + fallbackIdentifiers: + description: |- + FallbackIdentifiers are additional locations to try, in order, when the + primary identifier yields no value. + items: + type: string + type: array + identifier: + description: Identifier is a JSONPath expression or header name. + type: string + location: + description: Location is where to look for the value. + enum: + - header + - pathParam + - payload + - queryParam + type: string + valueMap: + additionalProperties: + type: string + description: |- + ValueMap translates values the provider reports into the vocabulary the + gateway works in. A reported value that is not a key is used unchanged. + type: object + required: + - identifier + - location + type: object + cacheAccounting: + description: |- + CacheAccounting states whether the provider's cached token count is part + of the input total or additional to it. Defaults to inclusive. + enum: + - inclusive + - additive + type: string + cacheWrite1hTokens: + description: |- + ExtractionIdentifier locates a value within an HTTP request or response. + Mirrors the gateway-controller management API ExtractionIdentifier model. + properties: + fallbackIdentifiers: + description: |- + FallbackIdentifiers are additional locations to try, in order, when the + primary identifier yields no value. + items: + type: string + type: array + identifier: + description: Identifier is a JSONPath expression or header name. + type: string + location: + description: Location is where to look for the value. + enum: + - header + - pathParam + - payload + - queryParam + type: string + valueMap: + additionalProperties: + type: string + description: |- + ValueMap translates values the provider reports into the vocabulary the + gateway works in. A reported value that is not a key is used unchanged. + type: object + required: + - identifier + - location + type: object + cacheWriteTokens: + description: |- + ExtractionIdentifier locates a value within an HTTP request or response. + Mirrors the gateway-controller management API ExtractionIdentifier model. + properties: + fallbackIdentifiers: + description: |- + FallbackIdentifiers are additional locations to try, in order, when the + primary identifier yields no value. + items: + type: string + type: array + identifier: + description: Identifier is a JSONPath expression or header name. + type: string + location: + description: Location is where to look for the value. + enum: + - header + - pathParam + - payload + - queryParam + type: string + valueMap: + additionalProperties: + type: string + description: |- + ValueMap translates values the provider reports into the vocabulary the + gateway works in. A reported value that is not a key is used unchanged. + type: object + required: + - identifier + - location + type: object + cachedTokens: + description: |- + ExtractionIdentifier locates a value within an HTTP request or response. + Mirrors the gateway-controller management API ExtractionIdentifier model. + properties: + fallbackIdentifiers: + description: |- + FallbackIdentifiers are additional locations to try, in order, when the + primary identifier yields no value. + items: + type: string + type: array + identifier: + description: Identifier is a JSONPath expression or header name. + type: string + location: + description: Location is where to look for the value. + enum: + - header + - pathParam + - payload + - queryParam + type: string + valueMap: + additionalProperties: + type: string + description: |- + ValueMap translates values the provider reports into the vocabulary the + gateway works in. A reported value that is not a key is used unchanged. + type: object + required: + - identifier + - location + type: object completionTokens: description: |- ExtractionIdentifier locates a value within an HTTP request or response. Mirrors the gateway-controller management API ExtractionIdentifier model. properties: + fallbackIdentifiers: + description: |- + FallbackIdentifiers are additional locations to try, in order, when the + primary identifier yields no value. + items: + type: string + type: array identifier: description: Identifier is a JSONPath expression or header name. type: string @@ -61,6 +246,13 @@ spec: - payload - queryParam type: string + valueMap: + additionalProperties: + type: string + description: |- + ValueMap translates values the provider reports into the vocabulary the + gateway works in. A reported value that is not a key is used unchanged. + type: object required: - identifier - location @@ -73,6 +265,13 @@ spec: ExtractionIdentifier locates a value within an HTTP request or response. Mirrors the gateway-controller management API ExtractionIdentifier model. properties: + fallbackIdentifiers: + description: |- + FallbackIdentifiers are additional locations to try, in order, when the + primary identifier yields no value. + items: + type: string + type: array identifier: description: Identifier is a JSONPath expression or header name. type: string @@ -84,6 +283,87 @@ spec: - payload - queryParam type: string + valueMap: + additionalProperties: + type: string + description: |- + ValueMap translates values the provider reports into the vocabulary the + gateway works in. A reported value that is not a key is used unchanged. + type: object + required: + - identifier + - location + type: object + providerFields: + additionalProperties: + description: |- + ExtractionIdentifier locates a value within an HTTP request or response. + Mirrors the gateway-controller management API ExtractionIdentifier model. + properties: + fallbackIdentifiers: + description: |- + FallbackIdentifiers are additional locations to try, in order, when the + primary identifier yields no value. + items: + type: string + type: array + identifier: + description: Identifier is a JSONPath expression or header name. + type: string + location: + description: Location is where to look for the value. + enum: + - header + - pathParam + - payload + - queryParam + type: string + valueMap: + additionalProperties: + type: string + description: |- + ValueMap translates values the provider reports into the vocabulary the + gateway works in. A reported value that is not a key is used unchanged. + type: object + required: + - identifier + - location + type: object + description: |- + ProviderFields names locations a provider-specific calculator needs that + the closed vocabulary above does not cover. Only the position is declared; + the value found there is passed through unchanged. + type: object + reasoningTokens: + description: |- + ExtractionIdentifier locates a value within an HTTP request or response. + Mirrors the gateway-controller management API ExtractionIdentifier model. + properties: + fallbackIdentifiers: + description: |- + FallbackIdentifiers are additional locations to try, in order, when the + primary identifier yields no value. + items: + type: string + type: array + identifier: + description: Identifier is a JSONPath expression or header name. + type: string + location: + description: Location is where to look for the value. + enum: + - header + - pathParam + - payload + - queryParam + type: string + valueMap: + additionalProperties: + type: string + description: |- + ValueMap translates values the provider reports into the vocabulary the + gateway works in. A reported value that is not a key is used unchanged. + type: object required: - identifier - location @@ -93,6 +373,13 @@ spec: ExtractionIdentifier locates a value within an HTTP request or response. Mirrors the gateway-controller management API ExtractionIdentifier model. properties: + fallbackIdentifiers: + description: |- + FallbackIdentifiers are additional locations to try, in order, when the + primary identifier yields no value. + items: + type: string + type: array identifier: description: Identifier is a JSONPath expression or header name. type: string @@ -104,6 +391,13 @@ spec: - payload - queryParam type: string + valueMap: + additionalProperties: + type: string + description: |- + ValueMap translates values the provider reports into the vocabulary the + gateway works in. A reported value that is not a key is used unchanged. + type: object required: - identifier - location @@ -113,6 +407,13 @@ spec: ExtractionIdentifier locates a value within an HTTP request or response. Mirrors the gateway-controller management API ExtractionIdentifier model. properties: + fallbackIdentifiers: + description: |- + FallbackIdentifiers are additional locations to try, in order, when the + primary identifier yields no value. + items: + type: string + type: array identifier: description: Identifier is a JSONPath expression or header name. type: string @@ -124,6 +425,13 @@ spec: - payload - queryParam type: string + valueMap: + additionalProperties: + type: string + description: |- + ValueMap translates values the provider reports into the vocabulary the + gateway works in. A reported value that is not a key is used unchanged. + type: object required: - identifier - location @@ -138,11 +446,201 @@ spec: LLMProviderTemplateResourceMapping maps a resource path pattern to per-field extraction identifiers for a given LLM API resource. properties: + audioInputTokens: + description: |- + ExtractionIdentifier locates a value within an HTTP request or response. + Mirrors the gateway-controller management API ExtractionIdentifier model. + properties: + fallbackIdentifiers: + description: |- + FallbackIdentifiers are additional locations to try, in order, when the + primary identifier yields no value. + items: + type: string + type: array + identifier: + description: Identifier is a JSONPath expression or + header name. + type: string + location: + description: Location is where to look for the value. + enum: + - header + - pathParam + - payload + - queryParam + type: string + valueMap: + additionalProperties: + type: string + description: |- + ValueMap translates values the provider reports into the vocabulary the + gateway works in. A reported value that is not a key is used unchanged. + type: object + required: + - identifier + - location + type: object + audioOutputTokens: + description: |- + ExtractionIdentifier locates a value within an HTTP request or response. + Mirrors the gateway-controller management API ExtractionIdentifier model. + properties: + fallbackIdentifiers: + description: |- + FallbackIdentifiers are additional locations to try, in order, when the + primary identifier yields no value. + items: + type: string + type: array + identifier: + description: Identifier is a JSONPath expression or + header name. + type: string + location: + description: Location is where to look for the value. + enum: + - header + - pathParam + - payload + - queryParam + type: string + valueMap: + additionalProperties: + type: string + description: |- + ValueMap translates values the provider reports into the vocabulary the + gateway works in. A reported value that is not a key is used unchanged. + type: object + required: + - identifier + - location + type: object + cacheAccounting: + description: |- + CacheAccounting overrides the template-level cache accounting mode for + this resource. When omitted, the template-level value is inherited. + enum: + - inclusive + - additive + type: string + cacheWrite1hTokens: + description: |- + ExtractionIdentifier locates a value within an HTTP request or response. + Mirrors the gateway-controller management API ExtractionIdentifier model. + properties: + fallbackIdentifiers: + description: |- + FallbackIdentifiers are additional locations to try, in order, when the + primary identifier yields no value. + items: + type: string + type: array + identifier: + description: Identifier is a JSONPath expression or + header name. + type: string + location: + description: Location is where to look for the value. + enum: + - header + - pathParam + - payload + - queryParam + type: string + valueMap: + additionalProperties: + type: string + description: |- + ValueMap translates values the provider reports into the vocabulary the + gateway works in. A reported value that is not a key is used unchanged. + type: object + required: + - identifier + - location + type: object + cacheWriteTokens: + description: |- + ExtractionIdentifier locates a value within an HTTP request or response. + Mirrors the gateway-controller management API ExtractionIdentifier model. + properties: + fallbackIdentifiers: + description: |- + FallbackIdentifiers are additional locations to try, in order, when the + primary identifier yields no value. + items: + type: string + type: array + identifier: + description: Identifier is a JSONPath expression or + header name. + type: string + location: + description: Location is where to look for the value. + enum: + - header + - pathParam + - payload + - queryParam + type: string + valueMap: + additionalProperties: + type: string + description: |- + ValueMap translates values the provider reports into the vocabulary the + gateway works in. A reported value that is not a key is used unchanged. + type: object + required: + - identifier + - location + type: object + cachedTokens: + description: |- + ExtractionIdentifier locates a value within an HTTP request or response. + Mirrors the gateway-controller management API ExtractionIdentifier model. + properties: + fallbackIdentifiers: + description: |- + FallbackIdentifiers are additional locations to try, in order, when the + primary identifier yields no value. + items: + type: string + type: array + identifier: + description: Identifier is a JSONPath expression or + header name. + type: string + location: + description: Location is where to look for the value. + enum: + - header + - pathParam + - payload + - queryParam + type: string + valueMap: + additionalProperties: + type: string + description: |- + ValueMap translates values the provider reports into the vocabulary the + gateway works in. A reported value that is not a key is used unchanged. + type: object + required: + - identifier + - location + type: object completionTokens: description: |- ExtractionIdentifier locates a value within an HTTP request or response. Mirrors the gateway-controller management API ExtractionIdentifier model. properties: + fallbackIdentifiers: + description: |- + FallbackIdentifiers are additional locations to try, in order, when the + primary identifier yields no value. + items: + type: string + type: array identifier: description: Identifier is a JSONPath expression or header name. @@ -155,6 +653,13 @@ spec: - payload - queryParam type: string + valueMap: + additionalProperties: + type: string + description: |- + ValueMap translates values the provider reports into the vocabulary the + gateway works in. A reported value that is not a key is used unchanged. + type: object required: - identifier - location @@ -164,6 +669,13 @@ spec: ExtractionIdentifier locates a value within an HTTP request or response. Mirrors the gateway-controller management API ExtractionIdentifier model. properties: + fallbackIdentifiers: + description: |- + FallbackIdentifiers are additional locations to try, in order, when the + primary identifier yields no value. + items: + type: string + type: array identifier: description: Identifier is a JSONPath expression or header name. @@ -176,6 +688,48 @@ spec: - payload - queryParam type: string + valueMap: + additionalProperties: + type: string + description: |- + ValueMap translates values the provider reports into the vocabulary the + gateway works in. A reported value that is not a key is used unchanged. + type: object + required: + - identifier + - location + type: object + reasoningTokens: + description: |- + ExtractionIdentifier locates a value within an HTTP request or response. + Mirrors the gateway-controller management API ExtractionIdentifier model. + properties: + fallbackIdentifiers: + description: |- + FallbackIdentifiers are additional locations to try, in order, when the + primary identifier yields no value. + items: + type: string + type: array + identifier: + description: Identifier is a JSONPath expression or + header name. + type: string + location: + description: Location is where to look for the value. + enum: + - header + - pathParam + - payload + - queryParam + type: string + valueMap: + additionalProperties: + type: string + description: |- + ValueMap translates values the provider reports into the vocabulary the + gateway works in. A reported value that is not a key is used unchanged. + type: object required: - identifier - location @@ -185,6 +739,13 @@ spec: ExtractionIdentifier locates a value within an HTTP request or response. Mirrors the gateway-controller management API ExtractionIdentifier model. properties: + fallbackIdentifiers: + description: |- + FallbackIdentifiers are additional locations to try, in order, when the + primary identifier yields no value. + items: + type: string + type: array identifier: description: Identifier is a JSONPath expression or header name. @@ -197,6 +758,13 @@ spec: - payload - queryParam type: string + valueMap: + additionalProperties: + type: string + description: |- + ValueMap translates values the provider reports into the vocabulary the + gateway works in. A reported value that is not a key is used unchanged. + type: object required: - identifier - location @@ -206,6 +774,13 @@ spec: ExtractionIdentifier locates a value within an HTTP request or response. Mirrors the gateway-controller management API ExtractionIdentifier model. properties: + fallbackIdentifiers: + description: |- + FallbackIdentifiers are additional locations to try, in order, when the + primary identifier yields no value. + items: + type: string + type: array identifier: description: Identifier is a JSONPath expression or header name. @@ -218,6 +793,13 @@ spec: - payload - queryParam type: string + valueMap: + additionalProperties: + type: string + description: |- + ValueMap translates values the provider reports into the vocabulary the + gateway works in. A reported value that is not a key is used unchanged. + type: object required: - identifier - location @@ -231,6 +813,13 @@ spec: ExtractionIdentifier locates a value within an HTTP request or response. Mirrors the gateway-controller management API ExtractionIdentifier model. properties: + fallbackIdentifiers: + description: |- + FallbackIdentifiers are additional locations to try, in order, when the + primary identifier yields no value. + items: + type: string + type: array identifier: description: Identifier is a JSONPath expression or header name. @@ -243,6 +832,48 @@ spec: - payload - queryParam type: string + valueMap: + additionalProperties: + type: string + description: |- + ValueMap translates values the provider reports into the vocabulary the + gateway works in. A reported value that is not a key is used unchanged. + type: object + required: + - identifier + - location + type: object + serviceTier: + description: |- + ExtractionIdentifier locates a value within an HTTP request or response. + Mirrors the gateway-controller management API ExtractionIdentifier model. + properties: + fallbackIdentifiers: + description: |- + FallbackIdentifiers are additional locations to try, in order, when the + primary identifier yields no value. + items: + type: string + type: array + identifier: + description: Identifier is a JSONPath expression or + header name. + type: string + location: + description: Location is where to look for the value. + enum: + - header + - pathParam + - payload + - queryParam + type: string + valueMap: + additionalProperties: + type: string + description: |- + ValueMap translates values the provider reports into the vocabulary the + gateway works in. A reported value that is not a key is used unchanged. + type: object required: - identifier - location @@ -252,6 +883,13 @@ spec: ExtractionIdentifier locates a value within an HTTP request or response. Mirrors the gateway-controller management API ExtractionIdentifier model. properties: + fallbackIdentifiers: + description: |- + FallbackIdentifiers are additional locations to try, in order, when the + primary identifier yields no value. + items: + type: string + type: array identifier: description: Identifier is a JSONPath expression or header name. @@ -264,6 +902,13 @@ spec: - payload - queryParam type: string + valueMap: + additionalProperties: + type: string + description: |- + ValueMap translates values the provider reports into the vocabulary the + gateway works in. A reported value that is not a key is used unchanged. + type: object required: - identifier - location @@ -278,6 +923,47 @@ spec: ExtractionIdentifier locates a value within an HTTP request or response. Mirrors the gateway-controller management API ExtractionIdentifier model. properties: + fallbackIdentifiers: + description: |- + FallbackIdentifiers are additional locations to try, in order, when the + primary identifier yields no value. + items: + type: string + type: array + identifier: + description: Identifier is a JSONPath expression or header name. + type: string + location: + description: Location is where to look for the value. + enum: + - header + - pathParam + - payload + - queryParam + type: string + valueMap: + additionalProperties: + type: string + description: |- + ValueMap translates values the provider reports into the vocabulary the + gateway works in. A reported value that is not a key is used unchanged. + type: object + required: + - identifier + - location + type: object + serviceTier: + description: |- + ExtractionIdentifier locates a value within an HTTP request or response. + Mirrors the gateway-controller management API ExtractionIdentifier model. + properties: + fallbackIdentifiers: + description: |- + FallbackIdentifiers are additional locations to try, in order, when the + primary identifier yields no value. + items: + type: string + type: array identifier: description: Identifier is a JSONPath expression or header name. type: string @@ -289,6 +975,13 @@ spec: - payload - queryParam type: string + valueMap: + additionalProperties: + type: string + description: |- + ValueMap translates values the provider reports into the vocabulary the + gateway works in. A reported value that is not a key is used unchanged. + type: object required: - identifier - location @@ -298,6 +991,13 @@ spec: ExtractionIdentifier locates a value within an HTTP request or response. Mirrors the gateway-controller management API ExtractionIdentifier model. properties: + fallbackIdentifiers: + description: |- + FallbackIdentifiers are additional locations to try, in order, when the + primary identifier yields no value. + items: + type: string + type: array identifier: description: Identifier is a JSONPath expression or header name. type: string @@ -309,6 +1009,13 @@ spec: - payload - queryParam type: string + valueMap: + additionalProperties: + type: string + description: |- + ValueMap translates values the provider reports into the vocabulary the + gateway works in. A reported value that is not a key is used unchanged. + type: object required: - identifier - location @@ -431,11 +1138,196 @@ spec: payload. The non-resource extractor fields apply when no resourceMappings entry matches the request path. properties: + audioInputTokens: + description: |- + ExtractionIdentifier locates a value within an HTTP request or response. + Mirrors the gateway-controller management API ExtractionIdentifier model. + properties: + fallbackIdentifiers: + description: |- + FallbackIdentifiers are additional locations to try, in order, when the + primary identifier yields no value. + items: + type: string + type: array + identifier: + description: Identifier is a JSONPath expression or header name. + type: string + location: + description: Location is where to look for the value. + enum: + - header + - pathParam + - payload + - queryParam + type: string + valueMap: + additionalProperties: + type: string + description: |- + ValueMap translates values the provider reports into the vocabulary the + gateway works in. A reported value that is not a key is used unchanged. + type: object + required: + - identifier + - location + type: object + audioOutputTokens: + description: |- + ExtractionIdentifier locates a value within an HTTP request or response. + Mirrors the gateway-controller management API ExtractionIdentifier model. + properties: + fallbackIdentifiers: + description: |- + FallbackIdentifiers are additional locations to try, in order, when the + primary identifier yields no value. + items: + type: string + type: array + identifier: + description: Identifier is a JSONPath expression or header name. + type: string + location: + description: Location is where to look for the value. + enum: + - header + - pathParam + - payload + - queryParam + type: string + valueMap: + additionalProperties: + type: string + description: |- + ValueMap translates values the provider reports into the vocabulary the + gateway works in. A reported value that is not a key is used unchanged. + type: object + required: + - identifier + - location + type: object + cacheAccounting: + description: |- + CacheAccounting states whether the provider's cached token count is part + of the input total or additional to it. Defaults to inclusive. + enum: + - inclusive + - additive + type: string + cacheWrite1hTokens: + description: |- + ExtractionIdentifier locates a value within an HTTP request or response. + Mirrors the gateway-controller management API ExtractionIdentifier model. + properties: + fallbackIdentifiers: + description: |- + FallbackIdentifiers are additional locations to try, in order, when the + primary identifier yields no value. + items: + type: string + type: array + identifier: + description: Identifier is a JSONPath expression or header name. + type: string + location: + description: Location is where to look for the value. + enum: + - header + - pathParam + - payload + - queryParam + type: string + valueMap: + additionalProperties: + type: string + description: |- + ValueMap translates values the provider reports into the vocabulary the + gateway works in. A reported value that is not a key is used unchanged. + type: object + required: + - identifier + - location + type: object + cacheWriteTokens: + description: |- + ExtractionIdentifier locates a value within an HTTP request or response. + Mirrors the gateway-controller management API ExtractionIdentifier model. + properties: + fallbackIdentifiers: + description: |- + FallbackIdentifiers are additional locations to try, in order, when the + primary identifier yields no value. + items: + type: string + type: array + identifier: + description: Identifier is a JSONPath expression or header name. + type: string + location: + description: Location is where to look for the value. + enum: + - header + - pathParam + - payload + - queryParam + type: string + valueMap: + additionalProperties: + type: string + description: |- + ValueMap translates values the provider reports into the vocabulary the + gateway works in. A reported value that is not a key is used unchanged. + type: object + required: + - identifier + - location + type: object + cachedTokens: + description: |- + ExtractionIdentifier locates a value within an HTTP request or response. + Mirrors the gateway-controller management API ExtractionIdentifier model. + properties: + fallbackIdentifiers: + description: |- + FallbackIdentifiers are additional locations to try, in order, when the + primary identifier yields no value. + items: + type: string + type: array + identifier: + description: Identifier is a JSONPath expression or header name. + type: string + location: + description: Location is where to look for the value. + enum: + - header + - pathParam + - payload + - queryParam + type: string + valueMap: + additionalProperties: + type: string + description: |- + ValueMap translates values the provider reports into the vocabulary the + gateway works in. A reported value that is not a key is used unchanged. + type: object + required: + - identifier + - location + type: object completionTokens: description: |- ExtractionIdentifier locates a value within an HTTP request or response. Mirrors the gateway-controller management API ExtractionIdentifier model. properties: + fallbackIdentifiers: + description: |- + FallbackIdentifiers are additional locations to try, in order, when the + primary identifier yields no value. + items: + type: string + type: array identifier: description: Identifier is a JSONPath expression or header name. type: string @@ -447,6 +1339,13 @@ spec: - payload - queryParam type: string + valueMap: + additionalProperties: + type: string + description: |- + ValueMap translates values the provider reports into the vocabulary the + gateway works in. A reported value that is not a key is used unchanged. + type: object required: - identifier - location @@ -459,6 +1358,87 @@ spec: ExtractionIdentifier locates a value within an HTTP request or response. Mirrors the gateway-controller management API ExtractionIdentifier model. properties: + fallbackIdentifiers: + description: |- + FallbackIdentifiers are additional locations to try, in order, when the + primary identifier yields no value. + items: + type: string + type: array + identifier: + description: Identifier is a JSONPath expression or header name. + type: string + location: + description: Location is where to look for the value. + enum: + - header + - pathParam + - payload + - queryParam + type: string + valueMap: + additionalProperties: + type: string + description: |- + ValueMap translates values the provider reports into the vocabulary the + gateway works in. A reported value that is not a key is used unchanged. + type: object + required: + - identifier + - location + type: object + providerFields: + additionalProperties: + description: |- + ExtractionIdentifier locates a value within an HTTP request or response. + Mirrors the gateway-controller management API ExtractionIdentifier model. + properties: + fallbackIdentifiers: + description: |- + FallbackIdentifiers are additional locations to try, in order, when the + primary identifier yields no value. + items: + type: string + type: array + identifier: + description: Identifier is a JSONPath expression or header name. + type: string + location: + description: Location is where to look for the value. + enum: + - header + - pathParam + - payload + - queryParam + type: string + valueMap: + additionalProperties: + type: string + description: |- + ValueMap translates values the provider reports into the vocabulary the + gateway works in. A reported value that is not a key is used unchanged. + type: object + required: + - identifier + - location + type: object + description: |- + ProviderFields names locations a provider-specific calculator needs that + the closed vocabulary above does not cover. Only the position is declared; + the value found there is passed through unchanged. + type: object + reasoningTokens: + description: |- + ExtractionIdentifier locates a value within an HTTP request or response. + Mirrors the gateway-controller management API ExtractionIdentifier model. + properties: + fallbackIdentifiers: + description: |- + FallbackIdentifiers are additional locations to try, in order, when the + primary identifier yields no value. + items: + type: string + type: array identifier: description: Identifier is a JSONPath expression or header name. type: string @@ -470,6 +1450,13 @@ spec: - payload - queryParam type: string + valueMap: + additionalProperties: + type: string + description: |- + ValueMap translates values the provider reports into the vocabulary the + gateway works in. A reported value that is not a key is used unchanged. + type: object required: - identifier - location @@ -479,6 +1466,13 @@ spec: ExtractionIdentifier locates a value within an HTTP request or response. Mirrors the gateway-controller management API ExtractionIdentifier model. properties: + fallbackIdentifiers: + description: |- + FallbackIdentifiers are additional locations to try, in order, when the + primary identifier yields no value. + items: + type: string + type: array identifier: description: Identifier is a JSONPath expression or header name. type: string @@ -490,6 +1484,13 @@ spec: - payload - queryParam type: string + valueMap: + additionalProperties: + type: string + description: |- + ValueMap translates values the provider reports into the vocabulary the + gateway works in. A reported value that is not a key is used unchanged. + type: object required: - identifier - location @@ -499,6 +1500,13 @@ spec: ExtractionIdentifier locates a value within an HTTP request or response. Mirrors the gateway-controller management API ExtractionIdentifier model. properties: + fallbackIdentifiers: + description: |- + FallbackIdentifiers are additional locations to try, in order, when the + primary identifier yields no value. + items: + type: string + type: array identifier: description: Identifier is a JSONPath expression or header name. type: string @@ -510,6 +1518,13 @@ spec: - payload - queryParam type: string + valueMap: + additionalProperties: + type: string + description: |- + ValueMap translates values the provider reports into the vocabulary the + gateway works in. A reported value that is not a key is used unchanged. + type: object required: - identifier - location @@ -524,11 +1539,201 @@ spec: LLMProviderTemplateResourceMapping maps a resource path pattern to per-field extraction identifiers for a given LLM API resource. properties: + audioInputTokens: + description: |- + ExtractionIdentifier locates a value within an HTTP request or response. + Mirrors the gateway-controller management API ExtractionIdentifier model. + properties: + fallbackIdentifiers: + description: |- + FallbackIdentifiers are additional locations to try, in order, when the + primary identifier yields no value. + items: + type: string + type: array + identifier: + description: Identifier is a JSONPath expression or + header name. + type: string + location: + description: Location is where to look for the value. + enum: + - header + - pathParam + - payload + - queryParam + type: string + valueMap: + additionalProperties: + type: string + description: |- + ValueMap translates values the provider reports into the vocabulary the + gateway works in. A reported value that is not a key is used unchanged. + type: object + required: + - identifier + - location + type: object + audioOutputTokens: + description: |- + ExtractionIdentifier locates a value within an HTTP request or response. + Mirrors the gateway-controller management API ExtractionIdentifier model. + properties: + fallbackIdentifiers: + description: |- + FallbackIdentifiers are additional locations to try, in order, when the + primary identifier yields no value. + items: + type: string + type: array + identifier: + description: Identifier is a JSONPath expression or + header name. + type: string + location: + description: Location is where to look for the value. + enum: + - header + - pathParam + - payload + - queryParam + type: string + valueMap: + additionalProperties: + type: string + description: |- + ValueMap translates values the provider reports into the vocabulary the + gateway works in. A reported value that is not a key is used unchanged. + type: object + required: + - identifier + - location + type: object + cacheAccounting: + description: |- + CacheAccounting overrides the template-level cache accounting mode for + this resource. When omitted, the template-level value is inherited. + enum: + - inclusive + - additive + type: string + cacheWrite1hTokens: + description: |- + ExtractionIdentifier locates a value within an HTTP request or response. + Mirrors the gateway-controller management API ExtractionIdentifier model. + properties: + fallbackIdentifiers: + description: |- + FallbackIdentifiers are additional locations to try, in order, when the + primary identifier yields no value. + items: + type: string + type: array + identifier: + description: Identifier is a JSONPath expression or + header name. + type: string + location: + description: Location is where to look for the value. + enum: + - header + - pathParam + - payload + - queryParam + type: string + valueMap: + additionalProperties: + type: string + description: |- + ValueMap translates values the provider reports into the vocabulary the + gateway works in. A reported value that is not a key is used unchanged. + type: object + required: + - identifier + - location + type: object + cacheWriteTokens: + description: |- + ExtractionIdentifier locates a value within an HTTP request or response. + Mirrors the gateway-controller management API ExtractionIdentifier model. + properties: + fallbackIdentifiers: + description: |- + FallbackIdentifiers are additional locations to try, in order, when the + primary identifier yields no value. + items: + type: string + type: array + identifier: + description: Identifier is a JSONPath expression or + header name. + type: string + location: + description: Location is where to look for the value. + enum: + - header + - pathParam + - payload + - queryParam + type: string + valueMap: + additionalProperties: + type: string + description: |- + ValueMap translates values the provider reports into the vocabulary the + gateway works in. A reported value that is not a key is used unchanged. + type: object + required: + - identifier + - location + type: object + cachedTokens: + description: |- + ExtractionIdentifier locates a value within an HTTP request or response. + Mirrors the gateway-controller management API ExtractionIdentifier model. + properties: + fallbackIdentifiers: + description: |- + FallbackIdentifiers are additional locations to try, in order, when the + primary identifier yields no value. + items: + type: string + type: array + identifier: + description: Identifier is a JSONPath expression or + header name. + type: string + location: + description: Location is where to look for the value. + enum: + - header + - pathParam + - payload + - queryParam + type: string + valueMap: + additionalProperties: + type: string + description: |- + ValueMap translates values the provider reports into the vocabulary the + gateway works in. A reported value that is not a key is used unchanged. + type: object + required: + - identifier + - location + type: object completionTokens: description: |- ExtractionIdentifier locates a value within an HTTP request or response. Mirrors the gateway-controller management API ExtractionIdentifier model. properties: + fallbackIdentifiers: + description: |- + FallbackIdentifiers are additional locations to try, in order, when the + primary identifier yields no value. + items: + type: string + type: array identifier: description: Identifier is a JSONPath expression or header name. @@ -541,6 +1746,13 @@ spec: - payload - queryParam type: string + valueMap: + additionalProperties: + type: string + description: |- + ValueMap translates values the provider reports into the vocabulary the + gateway works in. A reported value that is not a key is used unchanged. + type: object required: - identifier - location @@ -550,6 +1762,48 @@ spec: ExtractionIdentifier locates a value within an HTTP request or response. Mirrors the gateway-controller management API ExtractionIdentifier model. properties: + fallbackIdentifiers: + description: |- + FallbackIdentifiers are additional locations to try, in order, when the + primary identifier yields no value. + items: + type: string + type: array + identifier: + description: Identifier is a JSONPath expression or + header name. + type: string + location: + description: Location is where to look for the value. + enum: + - header + - pathParam + - payload + - queryParam + type: string + valueMap: + additionalProperties: + type: string + description: |- + ValueMap translates values the provider reports into the vocabulary the + gateway works in. A reported value that is not a key is used unchanged. + type: object + required: + - identifier + - location + type: object + reasoningTokens: + description: |- + ExtractionIdentifier locates a value within an HTTP request or response. + Mirrors the gateway-controller management API ExtractionIdentifier model. + properties: + fallbackIdentifiers: + description: |- + FallbackIdentifiers are additional locations to try, in order, when the + primary identifier yields no value. + items: + type: string + type: array identifier: description: Identifier is a JSONPath expression or header name. @@ -562,6 +1816,13 @@ spec: - payload - queryParam type: string + valueMap: + additionalProperties: + type: string + description: |- + ValueMap translates values the provider reports into the vocabulary the + gateway works in. A reported value that is not a key is used unchanged. + type: object required: - identifier - location @@ -571,6 +1832,13 @@ spec: ExtractionIdentifier locates a value within an HTTP request or response. Mirrors the gateway-controller management API ExtractionIdentifier model. properties: + fallbackIdentifiers: + description: |- + FallbackIdentifiers are additional locations to try, in order, when the + primary identifier yields no value. + items: + type: string + type: array identifier: description: Identifier is a JSONPath expression or header name. @@ -583,6 +1851,13 @@ spec: - payload - queryParam type: string + valueMap: + additionalProperties: + type: string + description: |- + ValueMap translates values the provider reports into the vocabulary the + gateway works in. A reported value that is not a key is used unchanged. + type: object required: - identifier - location @@ -592,6 +1867,13 @@ spec: ExtractionIdentifier locates a value within an HTTP request or response. Mirrors the gateway-controller management API ExtractionIdentifier model. properties: + fallbackIdentifiers: + description: |- + FallbackIdentifiers are additional locations to try, in order, when the + primary identifier yields no value. + items: + type: string + type: array identifier: description: Identifier is a JSONPath expression or header name. @@ -604,6 +1886,13 @@ spec: - payload - queryParam type: string + valueMap: + additionalProperties: + type: string + description: |- + ValueMap translates values the provider reports into the vocabulary the + gateway works in. A reported value that is not a key is used unchanged. + type: object required: - identifier - location @@ -617,6 +1906,48 @@ spec: ExtractionIdentifier locates a value within an HTTP request or response. Mirrors the gateway-controller management API ExtractionIdentifier model. properties: + fallbackIdentifiers: + description: |- + FallbackIdentifiers are additional locations to try, in order, when the + primary identifier yields no value. + items: + type: string + type: array + identifier: + description: Identifier is a JSONPath expression or + header name. + type: string + location: + description: Location is where to look for the value. + enum: + - header + - pathParam + - payload + - queryParam + type: string + valueMap: + additionalProperties: + type: string + description: |- + ValueMap translates values the provider reports into the vocabulary the + gateway works in. A reported value that is not a key is used unchanged. + type: object + required: + - identifier + - location + type: object + serviceTier: + description: |- + ExtractionIdentifier locates a value within an HTTP request or response. + Mirrors the gateway-controller management API ExtractionIdentifier model. + properties: + fallbackIdentifiers: + description: |- + FallbackIdentifiers are additional locations to try, in order, when the + primary identifier yields no value. + items: + type: string + type: array identifier: description: Identifier is a JSONPath expression or header name. @@ -629,6 +1960,13 @@ spec: - payload - queryParam type: string + valueMap: + additionalProperties: + type: string + description: |- + ValueMap translates values the provider reports into the vocabulary the + gateway works in. A reported value that is not a key is used unchanged. + type: object required: - identifier - location @@ -638,6 +1976,13 @@ spec: ExtractionIdentifier locates a value within an HTTP request or response. Mirrors the gateway-controller management API ExtractionIdentifier model. properties: + fallbackIdentifiers: + description: |- + FallbackIdentifiers are additional locations to try, in order, when the + primary identifier yields no value. + items: + type: string + type: array identifier: description: Identifier is a JSONPath expression or header name. @@ -650,6 +1995,13 @@ spec: - payload - queryParam type: string + valueMap: + additionalProperties: + type: string + description: |- + ValueMap translates values the provider reports into the vocabulary the + gateway works in. A reported value that is not a key is used unchanged. + type: object required: - identifier - location @@ -664,6 +2016,47 @@ spec: ExtractionIdentifier locates a value within an HTTP request or response. Mirrors the gateway-controller management API ExtractionIdentifier model. properties: + fallbackIdentifiers: + description: |- + FallbackIdentifiers are additional locations to try, in order, when the + primary identifier yields no value. + items: + type: string + type: array + identifier: + description: Identifier is a JSONPath expression or header name. + type: string + location: + description: Location is where to look for the value. + enum: + - header + - pathParam + - payload + - queryParam + type: string + valueMap: + additionalProperties: + type: string + description: |- + ValueMap translates values the provider reports into the vocabulary the + gateway works in. A reported value that is not a key is used unchanged. + type: object + required: + - identifier + - location + type: object + serviceTier: + description: |- + ExtractionIdentifier locates a value within an HTTP request or response. + Mirrors the gateway-controller management API ExtractionIdentifier model. + properties: + fallbackIdentifiers: + description: |- + FallbackIdentifiers are additional locations to try, in order, when the + primary identifier yields no value. + items: + type: string + type: array identifier: description: Identifier is a JSONPath expression or header name. type: string @@ -675,6 +2068,13 @@ spec: - payload - queryParam type: string + valueMap: + additionalProperties: + type: string + description: |- + ValueMap translates values the provider reports into the vocabulary the + gateway works in. A reported value that is not a key is used unchanged. + type: object required: - identifier - location @@ -684,6 +2084,13 @@ spec: ExtractionIdentifier locates a value within an HTTP request or response. Mirrors the gateway-controller management API ExtractionIdentifier model. properties: + fallbackIdentifiers: + description: |- + FallbackIdentifiers are additional locations to try, in order, when the + primary identifier yields no value. + items: + type: string + type: array identifier: description: Identifier is a JSONPath expression or header name. type: string @@ -695,6 +2102,13 @@ spec: - payload - queryParam type: string + valueMap: + additionalProperties: + type: string + description: |- + ValueMap translates values the provider reports into the vocabulary the + gateway works in. A reported value that is not a key is used unchanged. + type: object required: - identifier - location diff --git a/kubernetes/helm/operator-helm-chart/crds/gateway.api-platform.wso2.com_llmprovidertemplates.yaml b/kubernetes/helm/operator-helm-chart/crds/gateway.api-platform.wso2.com_llmprovidertemplates.yaml index 299fa01e0c..0cd8887c62 100644 --- a/kubernetes/helm/operator-helm-chart/crds/gateway.api-platform.wso2.com_llmprovidertemplates.yaml +++ b/kubernetes/helm/operator-helm-chart/crds/gateway.api-platform.wso2.com_llmprovidertemplates.yaml @@ -45,11 +45,196 @@ spec: payload. The non-resource extractor fields apply when no resourceMappings entry matches the request path. properties: + audioInputTokens: + description: |- + ExtractionIdentifier locates a value within an HTTP request or response. + Mirrors the gateway-controller management API ExtractionIdentifier model. + properties: + fallbackIdentifiers: + description: |- + FallbackIdentifiers are additional locations to try, in order, when the + primary identifier yields no value. + items: + type: string + type: array + identifier: + description: Identifier is a JSONPath expression or header name. + type: string + location: + description: Location is where to look for the value. + enum: + - header + - pathParam + - payload + - queryParam + type: string + valueMap: + additionalProperties: + type: string + description: |- + ValueMap translates values the provider reports into the vocabulary the + gateway works in. A reported value that is not a key is used unchanged. + type: object + required: + - identifier + - location + type: object + audioOutputTokens: + description: |- + ExtractionIdentifier locates a value within an HTTP request or response. + Mirrors the gateway-controller management API ExtractionIdentifier model. + properties: + fallbackIdentifiers: + description: |- + FallbackIdentifiers are additional locations to try, in order, when the + primary identifier yields no value. + items: + type: string + type: array + identifier: + description: Identifier is a JSONPath expression or header name. + type: string + location: + description: Location is where to look for the value. + enum: + - header + - pathParam + - payload + - queryParam + type: string + valueMap: + additionalProperties: + type: string + description: |- + ValueMap translates values the provider reports into the vocabulary the + gateway works in. A reported value that is not a key is used unchanged. + type: object + required: + - identifier + - location + type: object + cacheAccounting: + description: |- + CacheAccounting states whether the provider's cached token count is part + of the input total or additional to it. Defaults to inclusive. + enum: + - inclusive + - additive + type: string + cacheWrite1hTokens: + description: |- + ExtractionIdentifier locates a value within an HTTP request or response. + Mirrors the gateway-controller management API ExtractionIdentifier model. + properties: + fallbackIdentifiers: + description: |- + FallbackIdentifiers are additional locations to try, in order, when the + primary identifier yields no value. + items: + type: string + type: array + identifier: + description: Identifier is a JSONPath expression or header name. + type: string + location: + description: Location is where to look for the value. + enum: + - header + - pathParam + - payload + - queryParam + type: string + valueMap: + additionalProperties: + type: string + description: |- + ValueMap translates values the provider reports into the vocabulary the + gateway works in. A reported value that is not a key is used unchanged. + type: object + required: + - identifier + - location + type: object + cacheWriteTokens: + description: |- + ExtractionIdentifier locates a value within an HTTP request or response. + Mirrors the gateway-controller management API ExtractionIdentifier model. + properties: + fallbackIdentifiers: + description: |- + FallbackIdentifiers are additional locations to try, in order, when the + primary identifier yields no value. + items: + type: string + type: array + identifier: + description: Identifier is a JSONPath expression or header name. + type: string + location: + description: Location is where to look for the value. + enum: + - header + - pathParam + - payload + - queryParam + type: string + valueMap: + additionalProperties: + type: string + description: |- + ValueMap translates values the provider reports into the vocabulary the + gateway works in. A reported value that is not a key is used unchanged. + type: object + required: + - identifier + - location + type: object + cachedTokens: + description: |- + ExtractionIdentifier locates a value within an HTTP request or response. + Mirrors the gateway-controller management API ExtractionIdentifier model. + properties: + fallbackIdentifiers: + description: |- + FallbackIdentifiers are additional locations to try, in order, when the + primary identifier yields no value. + items: + type: string + type: array + identifier: + description: Identifier is a JSONPath expression or header name. + type: string + location: + description: Location is where to look for the value. + enum: + - header + - pathParam + - payload + - queryParam + type: string + valueMap: + additionalProperties: + type: string + description: |- + ValueMap translates values the provider reports into the vocabulary the + gateway works in. A reported value that is not a key is used unchanged. + type: object + required: + - identifier + - location + type: object completionTokens: description: |- ExtractionIdentifier locates a value within an HTTP request or response. Mirrors the gateway-controller management API ExtractionIdentifier model. properties: + fallbackIdentifiers: + description: |- + FallbackIdentifiers are additional locations to try, in order, when the + primary identifier yields no value. + items: + type: string + type: array identifier: description: Identifier is a JSONPath expression or header name. type: string @@ -61,6 +246,13 @@ spec: - payload - queryParam type: string + valueMap: + additionalProperties: + type: string + description: |- + ValueMap translates values the provider reports into the vocabulary the + gateway works in. A reported value that is not a key is used unchanged. + type: object required: - identifier - location @@ -73,6 +265,13 @@ spec: ExtractionIdentifier locates a value within an HTTP request or response. Mirrors the gateway-controller management API ExtractionIdentifier model. properties: + fallbackIdentifiers: + description: |- + FallbackIdentifiers are additional locations to try, in order, when the + primary identifier yields no value. + items: + type: string + type: array identifier: description: Identifier is a JSONPath expression or header name. type: string @@ -84,6 +283,87 @@ spec: - payload - queryParam type: string + valueMap: + additionalProperties: + type: string + description: |- + ValueMap translates values the provider reports into the vocabulary the + gateway works in. A reported value that is not a key is used unchanged. + type: object + required: + - identifier + - location + type: object + providerFields: + additionalProperties: + description: |- + ExtractionIdentifier locates a value within an HTTP request or response. + Mirrors the gateway-controller management API ExtractionIdentifier model. + properties: + fallbackIdentifiers: + description: |- + FallbackIdentifiers are additional locations to try, in order, when the + primary identifier yields no value. + items: + type: string + type: array + identifier: + description: Identifier is a JSONPath expression or header name. + type: string + location: + description: Location is where to look for the value. + enum: + - header + - pathParam + - payload + - queryParam + type: string + valueMap: + additionalProperties: + type: string + description: |- + ValueMap translates values the provider reports into the vocabulary the + gateway works in. A reported value that is not a key is used unchanged. + type: object + required: + - identifier + - location + type: object + description: |- + ProviderFields names locations a provider-specific calculator needs that + the closed vocabulary above does not cover. Only the position is declared; + the value found there is passed through unchanged. + type: object + reasoningTokens: + description: |- + ExtractionIdentifier locates a value within an HTTP request or response. + Mirrors the gateway-controller management API ExtractionIdentifier model. + properties: + fallbackIdentifiers: + description: |- + FallbackIdentifiers are additional locations to try, in order, when the + primary identifier yields no value. + items: + type: string + type: array + identifier: + description: Identifier is a JSONPath expression or header name. + type: string + location: + description: Location is where to look for the value. + enum: + - header + - pathParam + - payload + - queryParam + type: string + valueMap: + additionalProperties: + type: string + description: |- + ValueMap translates values the provider reports into the vocabulary the + gateway works in. A reported value that is not a key is used unchanged. + type: object required: - identifier - location @@ -93,6 +373,13 @@ spec: ExtractionIdentifier locates a value within an HTTP request or response. Mirrors the gateway-controller management API ExtractionIdentifier model. properties: + fallbackIdentifiers: + description: |- + FallbackIdentifiers are additional locations to try, in order, when the + primary identifier yields no value. + items: + type: string + type: array identifier: description: Identifier is a JSONPath expression or header name. type: string @@ -104,6 +391,13 @@ spec: - payload - queryParam type: string + valueMap: + additionalProperties: + type: string + description: |- + ValueMap translates values the provider reports into the vocabulary the + gateway works in. A reported value that is not a key is used unchanged. + type: object required: - identifier - location @@ -113,6 +407,13 @@ spec: ExtractionIdentifier locates a value within an HTTP request or response. Mirrors the gateway-controller management API ExtractionIdentifier model. properties: + fallbackIdentifiers: + description: |- + FallbackIdentifiers are additional locations to try, in order, when the + primary identifier yields no value. + items: + type: string + type: array identifier: description: Identifier is a JSONPath expression or header name. type: string @@ -124,6 +425,13 @@ spec: - payload - queryParam type: string + valueMap: + additionalProperties: + type: string + description: |- + ValueMap translates values the provider reports into the vocabulary the + gateway works in. A reported value that is not a key is used unchanged. + type: object required: - identifier - location @@ -138,11 +446,201 @@ spec: LLMProviderTemplateResourceMapping maps a resource path pattern to per-field extraction identifiers for a given LLM API resource. properties: + audioInputTokens: + description: |- + ExtractionIdentifier locates a value within an HTTP request or response. + Mirrors the gateway-controller management API ExtractionIdentifier model. + properties: + fallbackIdentifiers: + description: |- + FallbackIdentifiers are additional locations to try, in order, when the + primary identifier yields no value. + items: + type: string + type: array + identifier: + description: Identifier is a JSONPath expression or + header name. + type: string + location: + description: Location is where to look for the value. + enum: + - header + - pathParam + - payload + - queryParam + type: string + valueMap: + additionalProperties: + type: string + description: |- + ValueMap translates values the provider reports into the vocabulary the + gateway works in. A reported value that is not a key is used unchanged. + type: object + required: + - identifier + - location + type: object + audioOutputTokens: + description: |- + ExtractionIdentifier locates a value within an HTTP request or response. + Mirrors the gateway-controller management API ExtractionIdentifier model. + properties: + fallbackIdentifiers: + description: |- + FallbackIdentifiers are additional locations to try, in order, when the + primary identifier yields no value. + items: + type: string + type: array + identifier: + description: Identifier is a JSONPath expression or + header name. + type: string + location: + description: Location is where to look for the value. + enum: + - header + - pathParam + - payload + - queryParam + type: string + valueMap: + additionalProperties: + type: string + description: |- + ValueMap translates values the provider reports into the vocabulary the + gateway works in. A reported value that is not a key is used unchanged. + type: object + required: + - identifier + - location + type: object + cacheAccounting: + description: |- + CacheAccounting overrides the template-level cache accounting mode for + this resource. When omitted, the template-level value is inherited. + enum: + - inclusive + - additive + type: string + cacheWrite1hTokens: + description: |- + ExtractionIdentifier locates a value within an HTTP request or response. + Mirrors the gateway-controller management API ExtractionIdentifier model. + properties: + fallbackIdentifiers: + description: |- + FallbackIdentifiers are additional locations to try, in order, when the + primary identifier yields no value. + items: + type: string + type: array + identifier: + description: Identifier is a JSONPath expression or + header name. + type: string + location: + description: Location is where to look for the value. + enum: + - header + - pathParam + - payload + - queryParam + type: string + valueMap: + additionalProperties: + type: string + description: |- + ValueMap translates values the provider reports into the vocabulary the + gateway works in. A reported value that is not a key is used unchanged. + type: object + required: + - identifier + - location + type: object + cacheWriteTokens: + description: |- + ExtractionIdentifier locates a value within an HTTP request or response. + Mirrors the gateway-controller management API ExtractionIdentifier model. + properties: + fallbackIdentifiers: + description: |- + FallbackIdentifiers are additional locations to try, in order, when the + primary identifier yields no value. + items: + type: string + type: array + identifier: + description: Identifier is a JSONPath expression or + header name. + type: string + location: + description: Location is where to look for the value. + enum: + - header + - pathParam + - payload + - queryParam + type: string + valueMap: + additionalProperties: + type: string + description: |- + ValueMap translates values the provider reports into the vocabulary the + gateway works in. A reported value that is not a key is used unchanged. + type: object + required: + - identifier + - location + type: object + cachedTokens: + description: |- + ExtractionIdentifier locates a value within an HTTP request or response. + Mirrors the gateway-controller management API ExtractionIdentifier model. + properties: + fallbackIdentifiers: + description: |- + FallbackIdentifiers are additional locations to try, in order, when the + primary identifier yields no value. + items: + type: string + type: array + identifier: + description: Identifier is a JSONPath expression or + header name. + type: string + location: + description: Location is where to look for the value. + enum: + - header + - pathParam + - payload + - queryParam + type: string + valueMap: + additionalProperties: + type: string + description: |- + ValueMap translates values the provider reports into the vocabulary the + gateway works in. A reported value that is not a key is used unchanged. + type: object + required: + - identifier + - location + type: object completionTokens: description: |- ExtractionIdentifier locates a value within an HTTP request or response. Mirrors the gateway-controller management API ExtractionIdentifier model. properties: + fallbackIdentifiers: + description: |- + FallbackIdentifiers are additional locations to try, in order, when the + primary identifier yields no value. + items: + type: string + type: array identifier: description: Identifier is a JSONPath expression or header name. @@ -155,6 +653,13 @@ spec: - payload - queryParam type: string + valueMap: + additionalProperties: + type: string + description: |- + ValueMap translates values the provider reports into the vocabulary the + gateway works in. A reported value that is not a key is used unchanged. + type: object required: - identifier - location @@ -164,6 +669,13 @@ spec: ExtractionIdentifier locates a value within an HTTP request or response. Mirrors the gateway-controller management API ExtractionIdentifier model. properties: + fallbackIdentifiers: + description: |- + FallbackIdentifiers are additional locations to try, in order, when the + primary identifier yields no value. + items: + type: string + type: array identifier: description: Identifier is a JSONPath expression or header name. @@ -176,6 +688,48 @@ spec: - payload - queryParam type: string + valueMap: + additionalProperties: + type: string + description: |- + ValueMap translates values the provider reports into the vocabulary the + gateway works in. A reported value that is not a key is used unchanged. + type: object + required: + - identifier + - location + type: object + reasoningTokens: + description: |- + ExtractionIdentifier locates a value within an HTTP request or response. + Mirrors the gateway-controller management API ExtractionIdentifier model. + properties: + fallbackIdentifiers: + description: |- + FallbackIdentifiers are additional locations to try, in order, when the + primary identifier yields no value. + items: + type: string + type: array + identifier: + description: Identifier is a JSONPath expression or + header name. + type: string + location: + description: Location is where to look for the value. + enum: + - header + - pathParam + - payload + - queryParam + type: string + valueMap: + additionalProperties: + type: string + description: |- + ValueMap translates values the provider reports into the vocabulary the + gateway works in. A reported value that is not a key is used unchanged. + type: object required: - identifier - location @@ -185,6 +739,13 @@ spec: ExtractionIdentifier locates a value within an HTTP request or response. Mirrors the gateway-controller management API ExtractionIdentifier model. properties: + fallbackIdentifiers: + description: |- + FallbackIdentifiers are additional locations to try, in order, when the + primary identifier yields no value. + items: + type: string + type: array identifier: description: Identifier is a JSONPath expression or header name. @@ -197,6 +758,13 @@ spec: - payload - queryParam type: string + valueMap: + additionalProperties: + type: string + description: |- + ValueMap translates values the provider reports into the vocabulary the + gateway works in. A reported value that is not a key is used unchanged. + type: object required: - identifier - location @@ -206,6 +774,13 @@ spec: ExtractionIdentifier locates a value within an HTTP request or response. Mirrors the gateway-controller management API ExtractionIdentifier model. properties: + fallbackIdentifiers: + description: |- + FallbackIdentifiers are additional locations to try, in order, when the + primary identifier yields no value. + items: + type: string + type: array identifier: description: Identifier is a JSONPath expression or header name. @@ -218,6 +793,13 @@ spec: - payload - queryParam type: string + valueMap: + additionalProperties: + type: string + description: |- + ValueMap translates values the provider reports into the vocabulary the + gateway works in. A reported value that is not a key is used unchanged. + type: object required: - identifier - location @@ -231,6 +813,13 @@ spec: ExtractionIdentifier locates a value within an HTTP request or response. Mirrors the gateway-controller management API ExtractionIdentifier model. properties: + fallbackIdentifiers: + description: |- + FallbackIdentifiers are additional locations to try, in order, when the + primary identifier yields no value. + items: + type: string + type: array identifier: description: Identifier is a JSONPath expression or header name. @@ -243,6 +832,48 @@ spec: - payload - queryParam type: string + valueMap: + additionalProperties: + type: string + description: |- + ValueMap translates values the provider reports into the vocabulary the + gateway works in. A reported value that is not a key is used unchanged. + type: object + required: + - identifier + - location + type: object + serviceTier: + description: |- + ExtractionIdentifier locates a value within an HTTP request or response. + Mirrors the gateway-controller management API ExtractionIdentifier model. + properties: + fallbackIdentifiers: + description: |- + FallbackIdentifiers are additional locations to try, in order, when the + primary identifier yields no value. + items: + type: string + type: array + identifier: + description: Identifier is a JSONPath expression or + header name. + type: string + location: + description: Location is where to look for the value. + enum: + - header + - pathParam + - payload + - queryParam + type: string + valueMap: + additionalProperties: + type: string + description: |- + ValueMap translates values the provider reports into the vocabulary the + gateway works in. A reported value that is not a key is used unchanged. + type: object required: - identifier - location @@ -252,6 +883,13 @@ spec: ExtractionIdentifier locates a value within an HTTP request or response. Mirrors the gateway-controller management API ExtractionIdentifier model. properties: + fallbackIdentifiers: + description: |- + FallbackIdentifiers are additional locations to try, in order, when the + primary identifier yields no value. + items: + type: string + type: array identifier: description: Identifier is a JSONPath expression or header name. @@ -264,6 +902,13 @@ spec: - payload - queryParam type: string + valueMap: + additionalProperties: + type: string + description: |- + ValueMap translates values the provider reports into the vocabulary the + gateway works in. A reported value that is not a key is used unchanged. + type: object required: - identifier - location @@ -278,6 +923,47 @@ spec: ExtractionIdentifier locates a value within an HTTP request or response. Mirrors the gateway-controller management API ExtractionIdentifier model. properties: + fallbackIdentifiers: + description: |- + FallbackIdentifiers are additional locations to try, in order, when the + primary identifier yields no value. + items: + type: string + type: array + identifier: + description: Identifier is a JSONPath expression or header name. + type: string + location: + description: Location is where to look for the value. + enum: + - header + - pathParam + - payload + - queryParam + type: string + valueMap: + additionalProperties: + type: string + description: |- + ValueMap translates values the provider reports into the vocabulary the + gateway works in. A reported value that is not a key is used unchanged. + type: object + required: + - identifier + - location + type: object + serviceTier: + description: |- + ExtractionIdentifier locates a value within an HTTP request or response. + Mirrors the gateway-controller management API ExtractionIdentifier model. + properties: + fallbackIdentifiers: + description: |- + FallbackIdentifiers are additional locations to try, in order, when the + primary identifier yields no value. + items: + type: string + type: array identifier: description: Identifier is a JSONPath expression or header name. type: string @@ -289,6 +975,13 @@ spec: - payload - queryParam type: string + valueMap: + additionalProperties: + type: string + description: |- + ValueMap translates values the provider reports into the vocabulary the + gateway works in. A reported value that is not a key is used unchanged. + type: object required: - identifier - location @@ -298,6 +991,13 @@ spec: ExtractionIdentifier locates a value within an HTTP request or response. Mirrors the gateway-controller management API ExtractionIdentifier model. properties: + fallbackIdentifiers: + description: |- + FallbackIdentifiers are additional locations to try, in order, when the + primary identifier yields no value. + items: + type: string + type: array identifier: description: Identifier is a JSONPath expression or header name. type: string @@ -309,6 +1009,13 @@ spec: - payload - queryParam type: string + valueMap: + additionalProperties: + type: string + description: |- + ValueMap translates values the provider reports into the vocabulary the + gateway works in. A reported value that is not a key is used unchanged. + type: object required: - identifier - location @@ -431,11 +1138,196 @@ spec: payload. The non-resource extractor fields apply when no resourceMappings entry matches the request path. properties: + audioInputTokens: + description: |- + ExtractionIdentifier locates a value within an HTTP request or response. + Mirrors the gateway-controller management API ExtractionIdentifier model. + properties: + fallbackIdentifiers: + description: |- + FallbackIdentifiers are additional locations to try, in order, when the + primary identifier yields no value. + items: + type: string + type: array + identifier: + description: Identifier is a JSONPath expression or header name. + type: string + location: + description: Location is where to look for the value. + enum: + - header + - pathParam + - payload + - queryParam + type: string + valueMap: + additionalProperties: + type: string + description: |- + ValueMap translates values the provider reports into the vocabulary the + gateway works in. A reported value that is not a key is used unchanged. + type: object + required: + - identifier + - location + type: object + audioOutputTokens: + description: |- + ExtractionIdentifier locates a value within an HTTP request or response. + Mirrors the gateway-controller management API ExtractionIdentifier model. + properties: + fallbackIdentifiers: + description: |- + FallbackIdentifiers are additional locations to try, in order, when the + primary identifier yields no value. + items: + type: string + type: array + identifier: + description: Identifier is a JSONPath expression or header name. + type: string + location: + description: Location is where to look for the value. + enum: + - header + - pathParam + - payload + - queryParam + type: string + valueMap: + additionalProperties: + type: string + description: |- + ValueMap translates values the provider reports into the vocabulary the + gateway works in. A reported value that is not a key is used unchanged. + type: object + required: + - identifier + - location + type: object + cacheAccounting: + description: |- + CacheAccounting states whether the provider's cached token count is part + of the input total or additional to it. Defaults to inclusive. + enum: + - inclusive + - additive + type: string + cacheWrite1hTokens: + description: |- + ExtractionIdentifier locates a value within an HTTP request or response. + Mirrors the gateway-controller management API ExtractionIdentifier model. + properties: + fallbackIdentifiers: + description: |- + FallbackIdentifiers are additional locations to try, in order, when the + primary identifier yields no value. + items: + type: string + type: array + identifier: + description: Identifier is a JSONPath expression or header name. + type: string + location: + description: Location is where to look for the value. + enum: + - header + - pathParam + - payload + - queryParam + type: string + valueMap: + additionalProperties: + type: string + description: |- + ValueMap translates values the provider reports into the vocabulary the + gateway works in. A reported value that is not a key is used unchanged. + type: object + required: + - identifier + - location + type: object + cacheWriteTokens: + description: |- + ExtractionIdentifier locates a value within an HTTP request or response. + Mirrors the gateway-controller management API ExtractionIdentifier model. + properties: + fallbackIdentifiers: + description: |- + FallbackIdentifiers are additional locations to try, in order, when the + primary identifier yields no value. + items: + type: string + type: array + identifier: + description: Identifier is a JSONPath expression or header name. + type: string + location: + description: Location is where to look for the value. + enum: + - header + - pathParam + - payload + - queryParam + type: string + valueMap: + additionalProperties: + type: string + description: |- + ValueMap translates values the provider reports into the vocabulary the + gateway works in. A reported value that is not a key is used unchanged. + type: object + required: + - identifier + - location + type: object + cachedTokens: + description: |- + ExtractionIdentifier locates a value within an HTTP request or response. + Mirrors the gateway-controller management API ExtractionIdentifier model. + properties: + fallbackIdentifiers: + description: |- + FallbackIdentifiers are additional locations to try, in order, when the + primary identifier yields no value. + items: + type: string + type: array + identifier: + description: Identifier is a JSONPath expression or header name. + type: string + location: + description: Location is where to look for the value. + enum: + - header + - pathParam + - payload + - queryParam + type: string + valueMap: + additionalProperties: + type: string + description: |- + ValueMap translates values the provider reports into the vocabulary the + gateway works in. A reported value that is not a key is used unchanged. + type: object + required: + - identifier + - location + type: object completionTokens: description: |- ExtractionIdentifier locates a value within an HTTP request or response. Mirrors the gateway-controller management API ExtractionIdentifier model. properties: + fallbackIdentifiers: + description: |- + FallbackIdentifiers are additional locations to try, in order, when the + primary identifier yields no value. + items: + type: string + type: array identifier: description: Identifier is a JSONPath expression or header name. type: string @@ -447,6 +1339,13 @@ spec: - payload - queryParam type: string + valueMap: + additionalProperties: + type: string + description: |- + ValueMap translates values the provider reports into the vocabulary the + gateway works in. A reported value that is not a key is used unchanged. + type: object required: - identifier - location @@ -459,6 +1358,87 @@ spec: ExtractionIdentifier locates a value within an HTTP request or response. Mirrors the gateway-controller management API ExtractionIdentifier model. properties: + fallbackIdentifiers: + description: |- + FallbackIdentifiers are additional locations to try, in order, when the + primary identifier yields no value. + items: + type: string + type: array + identifier: + description: Identifier is a JSONPath expression or header name. + type: string + location: + description: Location is where to look for the value. + enum: + - header + - pathParam + - payload + - queryParam + type: string + valueMap: + additionalProperties: + type: string + description: |- + ValueMap translates values the provider reports into the vocabulary the + gateway works in. A reported value that is not a key is used unchanged. + type: object + required: + - identifier + - location + type: object + providerFields: + additionalProperties: + description: |- + ExtractionIdentifier locates a value within an HTTP request or response. + Mirrors the gateway-controller management API ExtractionIdentifier model. + properties: + fallbackIdentifiers: + description: |- + FallbackIdentifiers are additional locations to try, in order, when the + primary identifier yields no value. + items: + type: string + type: array + identifier: + description: Identifier is a JSONPath expression or header name. + type: string + location: + description: Location is where to look for the value. + enum: + - header + - pathParam + - payload + - queryParam + type: string + valueMap: + additionalProperties: + type: string + description: |- + ValueMap translates values the provider reports into the vocabulary the + gateway works in. A reported value that is not a key is used unchanged. + type: object + required: + - identifier + - location + type: object + description: |- + ProviderFields names locations a provider-specific calculator needs that + the closed vocabulary above does not cover. Only the position is declared; + the value found there is passed through unchanged. + type: object + reasoningTokens: + description: |- + ExtractionIdentifier locates a value within an HTTP request or response. + Mirrors the gateway-controller management API ExtractionIdentifier model. + properties: + fallbackIdentifiers: + description: |- + FallbackIdentifiers are additional locations to try, in order, when the + primary identifier yields no value. + items: + type: string + type: array identifier: description: Identifier is a JSONPath expression or header name. type: string @@ -470,6 +1450,13 @@ spec: - payload - queryParam type: string + valueMap: + additionalProperties: + type: string + description: |- + ValueMap translates values the provider reports into the vocabulary the + gateway works in. A reported value that is not a key is used unchanged. + type: object required: - identifier - location @@ -479,6 +1466,13 @@ spec: ExtractionIdentifier locates a value within an HTTP request or response. Mirrors the gateway-controller management API ExtractionIdentifier model. properties: + fallbackIdentifiers: + description: |- + FallbackIdentifiers are additional locations to try, in order, when the + primary identifier yields no value. + items: + type: string + type: array identifier: description: Identifier is a JSONPath expression or header name. type: string @@ -490,6 +1484,13 @@ spec: - payload - queryParam type: string + valueMap: + additionalProperties: + type: string + description: |- + ValueMap translates values the provider reports into the vocabulary the + gateway works in. A reported value that is not a key is used unchanged. + type: object required: - identifier - location @@ -499,6 +1500,13 @@ spec: ExtractionIdentifier locates a value within an HTTP request or response. Mirrors the gateway-controller management API ExtractionIdentifier model. properties: + fallbackIdentifiers: + description: |- + FallbackIdentifiers are additional locations to try, in order, when the + primary identifier yields no value. + items: + type: string + type: array identifier: description: Identifier is a JSONPath expression or header name. type: string @@ -510,6 +1518,13 @@ spec: - payload - queryParam type: string + valueMap: + additionalProperties: + type: string + description: |- + ValueMap translates values the provider reports into the vocabulary the + gateway works in. A reported value that is not a key is used unchanged. + type: object required: - identifier - location @@ -524,11 +1539,201 @@ spec: LLMProviderTemplateResourceMapping maps a resource path pattern to per-field extraction identifiers for a given LLM API resource. properties: + audioInputTokens: + description: |- + ExtractionIdentifier locates a value within an HTTP request or response. + Mirrors the gateway-controller management API ExtractionIdentifier model. + properties: + fallbackIdentifiers: + description: |- + FallbackIdentifiers are additional locations to try, in order, when the + primary identifier yields no value. + items: + type: string + type: array + identifier: + description: Identifier is a JSONPath expression or + header name. + type: string + location: + description: Location is where to look for the value. + enum: + - header + - pathParam + - payload + - queryParam + type: string + valueMap: + additionalProperties: + type: string + description: |- + ValueMap translates values the provider reports into the vocabulary the + gateway works in. A reported value that is not a key is used unchanged. + type: object + required: + - identifier + - location + type: object + audioOutputTokens: + description: |- + ExtractionIdentifier locates a value within an HTTP request or response. + Mirrors the gateway-controller management API ExtractionIdentifier model. + properties: + fallbackIdentifiers: + description: |- + FallbackIdentifiers are additional locations to try, in order, when the + primary identifier yields no value. + items: + type: string + type: array + identifier: + description: Identifier is a JSONPath expression or + header name. + type: string + location: + description: Location is where to look for the value. + enum: + - header + - pathParam + - payload + - queryParam + type: string + valueMap: + additionalProperties: + type: string + description: |- + ValueMap translates values the provider reports into the vocabulary the + gateway works in. A reported value that is not a key is used unchanged. + type: object + required: + - identifier + - location + type: object + cacheAccounting: + description: |- + CacheAccounting overrides the template-level cache accounting mode for + this resource. When omitted, the template-level value is inherited. + enum: + - inclusive + - additive + type: string + cacheWrite1hTokens: + description: |- + ExtractionIdentifier locates a value within an HTTP request or response. + Mirrors the gateway-controller management API ExtractionIdentifier model. + properties: + fallbackIdentifiers: + description: |- + FallbackIdentifiers are additional locations to try, in order, when the + primary identifier yields no value. + items: + type: string + type: array + identifier: + description: Identifier is a JSONPath expression or + header name. + type: string + location: + description: Location is where to look for the value. + enum: + - header + - pathParam + - payload + - queryParam + type: string + valueMap: + additionalProperties: + type: string + description: |- + ValueMap translates values the provider reports into the vocabulary the + gateway works in. A reported value that is not a key is used unchanged. + type: object + required: + - identifier + - location + type: object + cacheWriteTokens: + description: |- + ExtractionIdentifier locates a value within an HTTP request or response. + Mirrors the gateway-controller management API ExtractionIdentifier model. + properties: + fallbackIdentifiers: + description: |- + FallbackIdentifiers are additional locations to try, in order, when the + primary identifier yields no value. + items: + type: string + type: array + identifier: + description: Identifier is a JSONPath expression or + header name. + type: string + location: + description: Location is where to look for the value. + enum: + - header + - pathParam + - payload + - queryParam + type: string + valueMap: + additionalProperties: + type: string + description: |- + ValueMap translates values the provider reports into the vocabulary the + gateway works in. A reported value that is not a key is used unchanged. + type: object + required: + - identifier + - location + type: object + cachedTokens: + description: |- + ExtractionIdentifier locates a value within an HTTP request or response. + Mirrors the gateway-controller management API ExtractionIdentifier model. + properties: + fallbackIdentifiers: + description: |- + FallbackIdentifiers are additional locations to try, in order, when the + primary identifier yields no value. + items: + type: string + type: array + identifier: + description: Identifier is a JSONPath expression or + header name. + type: string + location: + description: Location is where to look for the value. + enum: + - header + - pathParam + - payload + - queryParam + type: string + valueMap: + additionalProperties: + type: string + description: |- + ValueMap translates values the provider reports into the vocabulary the + gateway works in. A reported value that is not a key is used unchanged. + type: object + required: + - identifier + - location + type: object completionTokens: description: |- ExtractionIdentifier locates a value within an HTTP request or response. Mirrors the gateway-controller management API ExtractionIdentifier model. properties: + fallbackIdentifiers: + description: |- + FallbackIdentifiers are additional locations to try, in order, when the + primary identifier yields no value. + items: + type: string + type: array identifier: description: Identifier is a JSONPath expression or header name. @@ -541,6 +1746,13 @@ spec: - payload - queryParam type: string + valueMap: + additionalProperties: + type: string + description: |- + ValueMap translates values the provider reports into the vocabulary the + gateway works in. A reported value that is not a key is used unchanged. + type: object required: - identifier - location @@ -550,6 +1762,48 @@ spec: ExtractionIdentifier locates a value within an HTTP request or response. Mirrors the gateway-controller management API ExtractionIdentifier model. properties: + fallbackIdentifiers: + description: |- + FallbackIdentifiers are additional locations to try, in order, when the + primary identifier yields no value. + items: + type: string + type: array + identifier: + description: Identifier is a JSONPath expression or + header name. + type: string + location: + description: Location is where to look for the value. + enum: + - header + - pathParam + - payload + - queryParam + type: string + valueMap: + additionalProperties: + type: string + description: |- + ValueMap translates values the provider reports into the vocabulary the + gateway works in. A reported value that is not a key is used unchanged. + type: object + required: + - identifier + - location + type: object + reasoningTokens: + description: |- + ExtractionIdentifier locates a value within an HTTP request or response. + Mirrors the gateway-controller management API ExtractionIdentifier model. + properties: + fallbackIdentifiers: + description: |- + FallbackIdentifiers are additional locations to try, in order, when the + primary identifier yields no value. + items: + type: string + type: array identifier: description: Identifier is a JSONPath expression or header name. @@ -562,6 +1816,13 @@ spec: - payload - queryParam type: string + valueMap: + additionalProperties: + type: string + description: |- + ValueMap translates values the provider reports into the vocabulary the + gateway works in. A reported value that is not a key is used unchanged. + type: object required: - identifier - location @@ -571,6 +1832,13 @@ spec: ExtractionIdentifier locates a value within an HTTP request or response. Mirrors the gateway-controller management API ExtractionIdentifier model. properties: + fallbackIdentifiers: + description: |- + FallbackIdentifiers are additional locations to try, in order, when the + primary identifier yields no value. + items: + type: string + type: array identifier: description: Identifier is a JSONPath expression or header name. @@ -583,6 +1851,13 @@ spec: - payload - queryParam type: string + valueMap: + additionalProperties: + type: string + description: |- + ValueMap translates values the provider reports into the vocabulary the + gateway works in. A reported value that is not a key is used unchanged. + type: object required: - identifier - location @@ -592,6 +1867,13 @@ spec: ExtractionIdentifier locates a value within an HTTP request or response. Mirrors the gateway-controller management API ExtractionIdentifier model. properties: + fallbackIdentifiers: + description: |- + FallbackIdentifiers are additional locations to try, in order, when the + primary identifier yields no value. + items: + type: string + type: array identifier: description: Identifier is a JSONPath expression or header name. @@ -604,6 +1886,13 @@ spec: - payload - queryParam type: string + valueMap: + additionalProperties: + type: string + description: |- + ValueMap translates values the provider reports into the vocabulary the + gateway works in. A reported value that is not a key is used unchanged. + type: object required: - identifier - location @@ -617,6 +1906,48 @@ spec: ExtractionIdentifier locates a value within an HTTP request or response. Mirrors the gateway-controller management API ExtractionIdentifier model. properties: + fallbackIdentifiers: + description: |- + FallbackIdentifiers are additional locations to try, in order, when the + primary identifier yields no value. + items: + type: string + type: array + identifier: + description: Identifier is a JSONPath expression or + header name. + type: string + location: + description: Location is where to look for the value. + enum: + - header + - pathParam + - payload + - queryParam + type: string + valueMap: + additionalProperties: + type: string + description: |- + ValueMap translates values the provider reports into the vocabulary the + gateway works in. A reported value that is not a key is used unchanged. + type: object + required: + - identifier + - location + type: object + serviceTier: + description: |- + ExtractionIdentifier locates a value within an HTTP request or response. + Mirrors the gateway-controller management API ExtractionIdentifier model. + properties: + fallbackIdentifiers: + description: |- + FallbackIdentifiers are additional locations to try, in order, when the + primary identifier yields no value. + items: + type: string + type: array identifier: description: Identifier is a JSONPath expression or header name. @@ -629,6 +1960,13 @@ spec: - payload - queryParam type: string + valueMap: + additionalProperties: + type: string + description: |- + ValueMap translates values the provider reports into the vocabulary the + gateway works in. A reported value that is not a key is used unchanged. + type: object required: - identifier - location @@ -638,6 +1976,13 @@ spec: ExtractionIdentifier locates a value within an HTTP request or response. Mirrors the gateway-controller management API ExtractionIdentifier model. properties: + fallbackIdentifiers: + description: |- + FallbackIdentifiers are additional locations to try, in order, when the + primary identifier yields no value. + items: + type: string + type: array identifier: description: Identifier is a JSONPath expression or header name. @@ -650,6 +1995,13 @@ spec: - payload - queryParam type: string + valueMap: + additionalProperties: + type: string + description: |- + ValueMap translates values the provider reports into the vocabulary the + gateway works in. A reported value that is not a key is used unchanged. + type: object required: - identifier - location @@ -664,6 +2016,47 @@ spec: ExtractionIdentifier locates a value within an HTTP request or response. Mirrors the gateway-controller management API ExtractionIdentifier model. properties: + fallbackIdentifiers: + description: |- + FallbackIdentifiers are additional locations to try, in order, when the + primary identifier yields no value. + items: + type: string + type: array + identifier: + description: Identifier is a JSONPath expression or header name. + type: string + location: + description: Location is where to look for the value. + enum: + - header + - pathParam + - payload + - queryParam + type: string + valueMap: + additionalProperties: + type: string + description: |- + ValueMap translates values the provider reports into the vocabulary the + gateway works in. A reported value that is not a key is used unchanged. + type: object + required: + - identifier + - location + type: object + serviceTier: + description: |- + ExtractionIdentifier locates a value within an HTTP request or response. + Mirrors the gateway-controller management API ExtractionIdentifier model. + properties: + fallbackIdentifiers: + description: |- + FallbackIdentifiers are additional locations to try, in order, when the + primary identifier yields no value. + items: + type: string + type: array identifier: description: Identifier is a JSONPath expression or header name. type: string @@ -675,6 +2068,13 @@ spec: - payload - queryParam type: string + valueMap: + additionalProperties: + type: string + description: |- + ValueMap translates values the provider reports into the vocabulary the + gateway works in. A reported value that is not a key is used unchanged. + type: object required: - identifier - location @@ -684,6 +2084,13 @@ spec: ExtractionIdentifier locates a value within an HTTP request or response. Mirrors the gateway-controller management API ExtractionIdentifier model. properties: + fallbackIdentifiers: + description: |- + FallbackIdentifiers are additional locations to try, in order, when the + primary identifier yields no value. + items: + type: string + type: array identifier: description: Identifier is a JSONPath expression or header name. type: string @@ -695,6 +2102,13 @@ spec: - payload - queryParam type: string + valueMap: + additionalProperties: + type: string + description: |- + ValueMap translates values the provider reports into the vocabulary the + gateway works in. A reported value that is not a key is used unchanged. + type: object required: - identifier - location