diff --git a/.speakeasy/gen.lock b/.speakeasy/gen.lock index 7ec3e1d..1236642 100644 --- a/.speakeasy/gen.lock +++ b/.speakeasy/gen.lock @@ -1,12 +1,12 @@ lockVersion: 2.0.0 id: cfd52247-6a25-4c6d-bbce-fe6fce0cd69d management: - docChecksum: 4cc4a9d9115d0eca9a5eb4786505579d + docChecksum: d2e69fbbac1477d094fdfc212c13670c docVersion: 1.0.0 - speakeasyVersion: 1.658.2 - generationVersion: 2.755.9 - releaseVersion: 0.0.19 - configChecksum: 834d5c1cea368e35ab6535ffb2bf9e7a + speakeasyVersion: 1.668.0 + generationVersion: 2.770.0 + releaseVersion: 0.1.0 + configChecksum: fa0d13fb5fd38982a589d64e64b0d87d repoURL: https://github.com/OpenRouterTeam/python-sdk.git installationURL: https://github.com/OpenRouterTeam/python-sdk.git published: true @@ -15,7 +15,7 @@ features: acceptHeaders: 3.0.0 additionalDependencies: 1.0.0 constsAndDefaults: 1.0.5 - core: 5.23.9 + core: 5.23.13 defaultEnabledRetries: 0.2.0 deprecations: 3.0.2 devContainers: 3.0.0 @@ -27,6 +27,7 @@ features: globalSecurityCallbacks: 1.0.0 globalSecurityFlattening: 1.0.0 globalServerURLs: 3.2.0 + globals: 3.0.0 groups: 3.0.1 methodArguments: 1.0.2 methodSecurity: 3.0.1 @@ -34,8 +35,8 @@ features: nullables: 1.0.1 openEnums: 1.0.1 responseFormat: 1.0.1 - retries: 3.0.2 - sdkHooks: 1.1.0 + retries: 3.0.3 + sdkHooks: 1.2.0 serverEvents: 1.0.11 serverEventsSentinels: 0.1.0 serverIDs: 3.0.0 @@ -65,8 +66,8 @@ generatedFiles: - docs/components/chatgenerationtokenusage.md - docs/components/chatmessagecontentitem.md - docs/components/chatmessagecontentitemaudio.md - - docs/components/chatmessagecontentitemaudioformat.md - docs/components/chatmessagecontentitemaudioinputaudio.md + - docs/components/chatmessagecontentitemcachecontrol.md - docs/components/chatmessagecontentitemimage.md - docs/components/chatmessagecontentitemimagedetail.md - docs/components/chatmessagecontentitemtext.md @@ -105,6 +106,7 @@ generatedFiles: - docs/components/costdetails.md - docs/components/createchargerequest.md - docs/components/datacollection.md + - docs/components/debug.md - docs/components/defaultparameters.md - docs/components/edgenetworktimeoutresponseerrordata.md - docs/components/effort.md @@ -117,6 +119,7 @@ generatedFiles: - docs/components/forbiddenresponseerrordata.md - docs/components/idfileparser.md - docs/components/idmoderation.md + - docs/components/idresponsehealing.md - docs/components/idweb.md - docs/components/ignore.md - docs/components/imagegenerationstatus.md @@ -300,6 +303,7 @@ generatedFiles: - docs/components/plugin.md - docs/components/pluginfileparser.md - docs/components/pluginmoderation.md + - docs/components/pluginresponsehealing.md - docs/components/pluginweb.md - docs/components/pricing.md - docs/components/prompt.md @@ -391,6 +395,7 @@ generatedFiles: - docs/components/toplogprob.md - docs/components/topproviderinfo.md - docs/components/truncation.md + - docs/components/ttl.md - docs/components/type.md - docs/components/typeresponsecompleted.md - docs/components/typeresponsecontentpartadded.md @@ -436,6 +441,7 @@ generatedFiles: - docs/errors/toomanyrequestsresponseerror.md - docs/errors/unauthorizedresponseerror.md - docs/errors/unprocessableentityresponseerror.md + - docs/models/internal/globals.md - docs/models/utils/retryconfig.md - docs/operations/apitype.md - docs/operations/calldata.md @@ -453,7 +459,6 @@ generatedFiles: - docs/operations/createembeddingsprovider.md - docs/operations/createembeddingsrequest.md - docs/operations/createembeddingsresponse.md - - docs/operations/createembeddingsresponsebody.md - docs/operations/createkeysdata.md - docs/operations/createkeyslimitreset.md - docs/operations/createkeysrequest.md @@ -467,6 +472,7 @@ generatedFiles: - docs/operations/exchangeauthcodeforapikeycodechallengemethod.md - docs/operations/exchangeauthcodeforapikeyrequest.md - docs/operations/exchangeauthcodeforapikeyresponse.md + - docs/operations/getcreditsdata.md - docs/operations/getcreditsresponse.md - docs/operations/getcurrentkeydata.md - docs/operations/getcurrentkeyresponse.md @@ -549,11 +555,13 @@ generatedFiles: - src/openrouter/components/assistantmessage.py - src/openrouter/components/badgatewayresponseerrordata.py - src/openrouter/components/badrequestresponseerrordata.py + - src/openrouter/components/chatcompletionfinishreason.py - src/openrouter/components/chaterror.py - src/openrouter/components/chatgenerationparams.py - src/openrouter/components/chatgenerationtokenusage.py - src/openrouter/components/chatmessagecontentitem.py - src/openrouter/components/chatmessagecontentitemaudio.py + - src/openrouter/components/chatmessagecontentitemcachecontrol.py - src/openrouter/components/chatmessagecontentitemimage.py - src/openrouter/components/chatmessagecontentitemtext.py - src/openrouter/components/chatmessagecontentitemvideo.py @@ -562,6 +570,7 @@ generatedFiles: - src/openrouter/components/chatmessagetoolcall.py - src/openrouter/components/chatresponse.py - src/openrouter/components/chatresponsechoice.py + - src/openrouter/components/chatstreamingchoice.py - src/openrouter/components/chatstreamingmessagechunk.py - src/openrouter/components/chatstreamingmessagetoolcall.py - src/openrouter/components/chatstreamingresponsechunk.py @@ -711,6 +720,9 @@ generatedFiles: - src/openrouter/errors/unprocessableentityresponse_error.py - src/openrouter/generations.py - src/openrouter/httpclient.py + - src/openrouter/models/__init__.py + - src/openrouter/models/internal/__init__.py + - src/openrouter/models/internal/globals.py - src/openrouter/models_.py - src/openrouter/oauth.py - src/openrouter/operations/__init__.py @@ -764,7 +776,7 @@ examples: createResponses: speakeasy-default-create-responses: requestBody: - application/json: {"input": [{"type": "message", "role": "user", "content": "Hello, how are you?"}], "tools": [{"type": "function", "name": "get_current_weather", "description": "Get the current weather in a given location", "parameters": {"type": "object", "properties": {"location": {"type": "string"}}}}], "model": "anthropic/claude-4.5-sonnet-20250929", "temperature": 0.7, "top_p": 0.9, "stream": false} + application/json: {"input": [{"type": "message", "role": "user", "content": "Hello, how are you?"}], "tools": [{"type": "function", "name": "get_current_weather", "description": "Get the current weather in a given location", "parameters": {"type": "object", "properties": {"location": {"type": "string"}}}}], "model": "anthropic/claude-4.5-sonnet-20250929", "temperature": 0.7, "top_p": 0.9, "store": false, "service_tier": "auto", "stream": false} responses: "200": application/json: {"id": "resp-abc123", "object": "response", "created_at": 1704067200, "model": "gpt-4", "status": "completed", "output": [{"id": "msg-abc123", "role": "assistant", "type": "message", "status": "completed", "content": [{"type": "output_text", "text": "Hello! How can I help you today?", "annotations": []}]}], "error": null, "incomplete_details": null, "usage": {"input_tokens": 10, "input_tokens_details": {"cached_tokens": 0}, "output_tokens": 25, "output_tokens_details": {"reasoning_tokens": 0}, "total_tokens": 35}, "max_output_tokens": null, "temperature": null, "top_p": null, "instructions": null, "metadata": null, "tools": [], "tool_choice": "auto", "parallel_tool_calls": true} @@ -814,7 +826,7 @@ examples: speakeasy-default-get-credits: responses: "200": - application/json: {} + application/json: {"data": {"total_credits": 100.5, "total_usage": 25.75}} "401": application/json: {"error": {"code": 401, "message": "Missing Authentication header"}} "403": @@ -1104,3 +1116,4 @@ examples: "500": application/json: {"error": {"code": "", "message": ""}} examplesVersion: 1.0.2 +releaseNotes: "## Python SDK Changes:\n* `open_router.beta.responses.send()`: `request` **Changed** **Breaking** :warning:\n* `open_router.embeddings.generate()`: `response.status[200].content[text/event-stream` **Removed** **Breaking** :warning:\n* `open_router.chat.send()`: \n * `request` **Changed** **Breaking** :warning:\n * `response.choices.[].message.content.[array].[]` **Changed** **Breaking** :warning:\n* `open_router.credits.get_credits()`: `response.data` **Added**\n* `open_router.parameters.get_parameters()`: `request.provider` **Changed**\n* `open_router.completions.generate()`: `response` **Changed**\n" diff --git a/.speakeasy/gen.yaml b/.speakeasy/gen.yaml index c68359b..89c3fb4 100644 --- a/.speakeasy/gen.yaml +++ b/.speakeasy/gen.yaml @@ -30,7 +30,7 @@ generation: generateNewTests: true skipResponseBodyAssertions: false python: - version: 0.0.19 + version: 0.1.0 additionalDependencies: dev: {} main: {} @@ -62,12 +62,14 @@ python: operations: operations shared: components webhooks: "" + inferUnionDiscriminators: true inputModelSuffix: input legacyPyright: false license: Apache-2.0 maxMethodParams: 999 methodArguments: infer-optional-args moduleName: "" + multipartArrayFormat: legacy outputModelSuffix: output packageManager: uv packageName: openrouter diff --git a/.speakeasy/out.openapi.yaml b/.speakeasy/out.openapi.yaml index 2987de9..a927524 100644 --- a/.speakeasy/out.openapi.yaml +++ b/.speakeasy/out.openapi.yaml @@ -3464,12 +3464,14 @@ components: - AionLabs - Alibaba - Amazon Bedrock + - Amazon Nova - Anthropic - - Arcee + - Arcee AI - AtlasCloud - Avian - Azure - BaseTen + - BytePlus - Black Forest Labs - Cerebras - Chutes @@ -3484,6 +3486,7 @@ components: - Fireworks - Friendli - GMICloud + - GoPomelo - Google - Google AI Studio - Groq @@ -3514,6 +3517,7 @@ components: - SambaNova - SiliconFlow - Stealth + - StreamLake - Switchpoint - Targon - Together @@ -3648,18 +3652,13 @@ components: nullable: true store: type: boolean - nullable: true + const: false + default: false service_tier: - allOf: - - $ref: '#/components/schemas/OpenAIResponsesServiceTier' - - enum: - - auto - - default - - flex - - scale - - priority - example: auto - x-speakeasy-unknown-values: allow + type: string + enum: + - auto + default: auto truncation: allOf: - $ref: '#/components/schemas/OpenAIResponsesTruncation' @@ -3803,6 +3802,14 @@ components: x-speakeasy-unknown-values: allow required: - id + - type: object + properties: + id: + type: string + enum: + - response-healing + required: + - id description: Plugins you want to enable for this request, including their settings. user: type: string @@ -4597,6 +4604,10 @@ components: status: default uptime_last_30m: 99.5 supports_implicit_caching: true + __schema0: + anyOf: + - $ref: '#/components/schemas/ChatCompletionFinishReason' + - type: 'null' ModelName: type: string ChatMessageContentItemText: @@ -4607,6 +4618,8 @@ components: const: text text: type: string + cache_control: + $ref: '#/components/schemas/ChatMessageContentItemCacheControl' required: - type - text @@ -4646,15 +4659,6 @@ components: type: string format: type: string - enum: - - wav - - mp3 - - flac - - m4a - - ogg - - pcm16 - - pcm24 - x-speakeasy-unknown-values: allow required: - data - format @@ -4832,16 +4836,14 @@ components: - prompt_tokens - total_tokens ChatCompletionFinishReason: - anyOf: - - type: string - enum: - - tool_calls - - stop - - length - - content_filter - - error - x-speakeasy-unknown-values: allow - - type: 'null' + type: string + enum: + - tool_calls + - stop + - length + - content_filter + - error + x-speakeasy-unknown-values: allow JSONSchemaConfig: type: object properties: @@ -4883,6 +4885,20 @@ components: required: - type - grammar + ChatMessageContentItemCacheControl: + type: object + properties: + type: + type: string + const: ephemeral + ttl: + type: string + enum: + - 5m + - 1h + x-speakeasy-unknown-values: allow + required: + - type SystemMessage: type: object properties: @@ -5194,13 +5210,18 @@ components: - type: 'null' user: type: string + debug: + type: object + properties: + echo_upstream_body: + type: boolean required: - messages ChatResponseChoice: type: object properties: finish_reason: - $ref: '#/components/schemas/ChatCompletionFinishReason' + $ref: '#/components/schemas/__schema0' index: type: number message: @@ -5261,7 +5282,7 @@ components: delta: $ref: '#/components/schemas/ChatStreamingMessageChunk' finish_reason: - $ref: '#/components/schemas/ChatCompletionFinishReason' + $ref: '#/components/schemas/__schema0' index: type: number logprobs: @@ -5594,6 +5615,12 @@ components: - type: 'null' finish_reason: $ref: '#/components/schemas/CompletionFinishReason' + native_finish_reason: + type: string + reasoning: + anyOf: + - type: string + - type: 'null' required: - text - index @@ -5611,6 +5638,8 @@ components: type: number model: type: string + provider: + type: string system_fingerprint: type: string choices: @@ -5626,7 +5655,7 @@ components: - model - choices additionalProperties: false - parameters: {} + parameters: {AppIdentifier: {name: HTTP-Referer, in: header, schema: {type: string}, description: "The app identifier should be your app's URL and is used as the primary identifier for rankings.\nThis is used to track API usage per application.\n"}, AppDisplayName: {name: X-Title, in: header, schema: {type: string}, description: "The app display name allows you to customize how your app appears in OpenRouter's dashboard.\n"}} securitySchemes: apiKey: type: http @@ -5829,6 +5858,26 @@ paths: application/json: schema: type: object + properties: + data: + type: object + properties: + total_credits: + type: number + description: Total credits purchased + example: 100.5 + total_usage: + type: number + description: Total credits used + example: 25.75 + required: + - total_credits + - total_usage + example: + total_credits: 100.5 + total_usage: 25.75 + required: + - data description: Total credits purchased and used example: data: @@ -6193,11 +6242,6 @@ paths: - object - data - model - text/event-stream: - schema: - type: string - description: Not used for embeddings - embeddings do not support streaming - x-speakeasy-sse-sentinel: '[DONE]' '400': description: Bad Request - Invalid request parameters or malformed input content: @@ -6730,12 +6774,14 @@ paths: - AionLabs - Alibaba - Amazon Bedrock + - Amazon Nova - Anthropic - - Arcee + - Arcee AI - AtlasCloud - Avian - Azure - BaseTen + - BytePlus - Black Forest Labs - Cerebras - Chutes @@ -6750,6 +6796,7 @@ paths: - Fireworks - Friendli - GMICloud + - GoPomelo - Google - Google AI Studio - Groq @@ -6780,6 +6827,7 @@ paths: - SambaNova - SiliconFlow - Stealth + - StreamLake - Switchpoint - Targon - Together @@ -8385,3 +8433,7 @@ x-retry-strategy: initialDelay: 500 maxDelay: 60000 maxAttempts: 3 +x-speakeasy-globals: + parameters: + - $ref: "#/components/parameters/AppIdentifier" + - $ref: "#/components/parameters/AppDisplayName" diff --git a/.speakeasy/workflow.lock b/.speakeasy/workflow.lock index e8a1112..6c291d0 100644 --- a/.speakeasy/workflow.lock +++ b/.speakeasy/workflow.lock @@ -1,17 +1,18 @@ -speakeasyVersion: 1.658.2 +speakeasyVersion: 1.668.0 sources: -OAS: sourceNamespace: open-router-chat-completions-api - sourceRevisionDigest: sha256:fc0c90dc8ebb69ef4a571e4e63dcd7f8f33c3254c17ad2583b628fbbf3d1ac05 - sourceBlobDigest: sha256:635d63fd18db468c1dcc23700382af788de76594ba3b8292fae9b14c0e5c22c4 + sourceRevisionDigest: sha256:697f956356ab0195b9a9b9a5106e4ac979caa551709b3614c723df972c661829 + sourceBlobDigest: sha256:a286a09ceced7f78a685731aef81d1498b89a6a46f3794219dc93fa2d822853b tags: - latest + - speakeasy-sdk-regen-1763684529 targets: open-router: source: -OAS sourceNamespace: open-router-chat-completions-api - sourceRevisionDigest: sha256:fc0c90dc8ebb69ef4a571e4e63dcd7f8f33c3254c17ad2583b628fbbf3d1ac05 - sourceBlobDigest: sha256:635d63fd18db468c1dcc23700382af788de76594ba3b8292fae9b14c0e5c22c4 + sourceRevisionDigest: sha256:697f956356ab0195b9a9b9a5106e4ac979caa551709b3614c723df972c661829 + sourceBlobDigest: sha256:a286a09ceced7f78a685731aef81d1498b89a6a46f3794219dc93fa2d822853b workflow: workflowVersion: 1.0.0 speakeasyVersion: latest diff --git a/RELEASES.md b/RELEASES.md index 0d0d01f..dae6848 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -8,4 +8,14 @@ Based on: ### Generated - [python v0.0.16] . ### Releases -- [PyPI v0.0.16] https://pypi.org/project/openrouter/0.0.16 - . \ No newline at end of file +- [PyPI v0.0.16] https://pypi.org/project/openrouter/0.0.16 - . + +## 2025-12-04 00:22:56 +### Changes +Based on: +- OpenAPI Doc +- Speakeasy CLI 1.668.0 (2.770.0) https://github.com/speakeasy-api/speakeasy +### Generated +- [python v0.1.0] . +### Releases +- [PyPI v0.1.0] https://pypi.org/project/openrouter/0.1.0 - . \ No newline at end of file diff --git a/docs/components/chatgenerationparams.md b/docs/components/chatgenerationparams.md index 238044f..41db71b 100644 --- a/docs/components/chatgenerationparams.md +++ b/docs/components/chatgenerationparams.md @@ -26,4 +26,5 @@ | `tool_choice` | *Optional[Any]* | :heavy_minus_sign: | N/A | | `tools` | List[[components.ToolDefinitionJSON](../components/tooldefinitionjson.md)] | :heavy_minus_sign: | N/A | | `top_p` | *OptionalNullable[float]* | :heavy_minus_sign: | N/A | -| `user` | *Optional[str]* | :heavy_minus_sign: | N/A | \ No newline at end of file +| `user` | *Optional[str]* | :heavy_minus_sign: | N/A | +| `debug` | [Optional[components.Debug]](../components/debug.md) | :heavy_minus_sign: | N/A | \ No newline at end of file diff --git a/docs/components/chatmessagecontentitemaudioformat.md b/docs/components/chatmessagecontentitemaudioformat.md deleted file mode 100644 index 97119c0..0000000 --- a/docs/components/chatmessagecontentitemaudioformat.md +++ /dev/null @@ -1,14 +0,0 @@ -# ChatMessageContentItemAudioFormat - - -## Values - -| Name | Value | -| ------- | ------- | -| `WAV` | wav | -| `MP3` | mp3 | -| `FLAC` | flac | -| `M4A` | m4a | -| `OGG` | ogg | -| `PCM16` | pcm16 | -| `PCM24` | pcm24 | \ No newline at end of file diff --git a/docs/components/chatmessagecontentitemaudioinputaudio.md b/docs/components/chatmessagecontentitemaudioinputaudio.md index 3665698..f7a62b5 100644 --- a/docs/components/chatmessagecontentitemaudioinputaudio.md +++ b/docs/components/chatmessagecontentitemaudioinputaudio.md @@ -3,7 +3,7 @@ ## Fields -| Field | Type | Required | Description | -| -------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------- | -| `data` | *str* | :heavy_check_mark: | N/A | -| `format_` | [components.ChatMessageContentItemAudioFormat](../components/chatmessagecontentitemaudioformat.md) | :heavy_check_mark: | N/A | \ No newline at end of file +| Field | Type | Required | Description | +| ------------------ | ------------------ | ------------------ | ------------------ | +| `data` | *str* | :heavy_check_mark: | N/A | +| `format_` | *str* | :heavy_check_mark: | N/A | \ No newline at end of file diff --git a/docs/components/chatmessagecontentitemcachecontrol.md b/docs/components/chatmessagecontentitemcachecontrol.md new file mode 100644 index 0000000..2ad30d0 --- /dev/null +++ b/docs/components/chatmessagecontentitemcachecontrol.md @@ -0,0 +1,9 @@ +# ChatMessageContentItemCacheControl + + +## Fields + +| Field | Type | Required | Description | +| ------------------------------------------------ | ------------------------------------------------ | ------------------------------------------------ | ------------------------------------------------ | +| `type` | *Literal["ephemeral"]* | :heavy_check_mark: | N/A | +| `ttl` | [Optional[components.TTL]](../components/ttl.md) | :heavy_minus_sign: | N/A | \ No newline at end of file diff --git a/docs/components/chatmessagecontentitemtext.md b/docs/components/chatmessagecontentitemtext.md index 4fa8b7b..4f8a874 100644 --- a/docs/components/chatmessagecontentitemtext.md +++ b/docs/components/chatmessagecontentitemtext.md @@ -3,7 +3,8 @@ ## Fields -| Field | Type | Required | Description | -| ------------------ | ------------------ | ------------------ | ------------------ | -| `type` | *Literal["text"]* | :heavy_check_mark: | N/A | -| `text` | *str* | :heavy_check_mark: | N/A | \ No newline at end of file +| Field | Type | Required | Description | +| -------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- | +| `type` | *Literal["text"]* | :heavy_check_mark: | N/A | +| `text` | *str* | :heavy_check_mark: | N/A | +| `cache_control` | [Optional[components.ChatMessageContentItemCacheControl]](../components/chatmessagecontentitemcachecontrol.md) | :heavy_minus_sign: | N/A | \ No newline at end of file diff --git a/docs/components/completionchoice.md b/docs/components/completionchoice.md index 758eea9..cb489be 100644 --- a/docs/components/completionchoice.md +++ b/docs/components/completionchoice.md @@ -8,4 +8,6 @@ | `text` | *str* | :heavy_check_mark: | N/A | | `index` | *float* | :heavy_check_mark: | N/A | | `logprobs` | [Nullable[components.CompletionLogprobs]](../components/completionlogprobs.md) | :heavy_check_mark: | N/A | -| `finish_reason` | [Nullable[components.CompletionFinishReason]](../components/completionfinishreason.md) | :heavy_check_mark: | N/A | \ No newline at end of file +| `finish_reason` | [Nullable[components.CompletionFinishReason]](../components/completionfinishreason.md) | :heavy_check_mark: | N/A | +| `native_finish_reason` | *Optional[str]* | :heavy_minus_sign: | N/A | +| `reasoning` | *OptionalNullable[str]* | :heavy_minus_sign: | N/A | \ No newline at end of file diff --git a/docs/components/completionresponse.md b/docs/components/completionresponse.md index 404f19d..3e9a9bd 100644 --- a/docs/components/completionresponse.md +++ b/docs/components/completionresponse.md @@ -9,6 +9,7 @@ | `object` | *Literal["text_completion"]* | :heavy_check_mark: | N/A | | `created` | *float* | :heavy_check_mark: | N/A | | `model` | *str* | :heavy_check_mark: | N/A | +| `provider` | *Optional[str]* | :heavy_minus_sign: | N/A | | `system_fingerprint` | *Optional[str]* | :heavy_minus_sign: | N/A | | `choices` | List[[components.CompletionChoice](../components/completionchoice.md)] | :heavy_check_mark: | N/A | | `usage` | [Optional[components.CompletionUsage]](../components/completionusage.md) | :heavy_minus_sign: | N/A | \ No newline at end of file diff --git a/docs/components/debug.md b/docs/components/debug.md new file mode 100644 index 0000000..459bb4b --- /dev/null +++ b/docs/components/debug.md @@ -0,0 +1,8 @@ +# Debug + + +## Fields + +| Field | Type | Required | Description | +| -------------------- | -------------------- | -------------------- | -------------------- | +| `echo_upstream_body` | *Optional[bool]* | :heavy_minus_sign: | N/A | \ No newline at end of file diff --git a/docs/components/idresponsehealing.md b/docs/components/idresponsehealing.md new file mode 100644 index 0000000..4a12790 --- /dev/null +++ b/docs/components/idresponsehealing.md @@ -0,0 +1,8 @@ +# IDResponseHealing + + +## Values + +| Name | Value | +| ------------------ | ------------------ | +| `RESPONSE_HEALING` | response-healing | \ No newline at end of file diff --git a/docs/components/openresponsesrequest.md b/docs/components/openresponsesrequest.md index fcc6420..56283da 100644 --- a/docs/components/openresponsesrequest.md +++ b/docs/components/openresponsesrequest.md @@ -27,8 +27,8 @@ Request schema for Responses endpoint | `include` | List[[components.OpenAIResponsesIncludable](../components/openairesponsesincludable.md)] | :heavy_minus_sign: | N/A | | | `background` | *OptionalNullable[bool]* | :heavy_minus_sign: | N/A | | | `safety_identifier` | *OptionalNullable[str]* | :heavy_minus_sign: | N/A | | -| `store` | *OptionalNullable[bool]* | :heavy_minus_sign: | N/A | | -| `service_tier` | [OptionalNullable[components.ServiceTier]](../components/servicetier.md) | :heavy_minus_sign: | N/A | auto | +| `store` | *Optional[Literal[False]]* | :heavy_minus_sign: | N/A | | +| `service_tier` | [Optional[components.ServiceTier]](../components/servicetier.md) | :heavy_minus_sign: | N/A | | | `truncation` | [OptionalNullable[components.Truncation]](../components/truncation.md) | :heavy_minus_sign: | N/A | auto | | `stream` | *Optional[bool]* | :heavy_minus_sign: | N/A | | | `provider` | [OptionalNullable[components.Provider]](../components/provider.md) | :heavy_minus_sign: | When multiple model providers are available, optionally indicate your routing preference. | | diff --git a/docs/components/plugin.md b/docs/components/plugin.md index 7f6e744..bc21cd4 100644 --- a/docs/components/plugin.md +++ b/docs/components/plugin.md @@ -21,3 +21,9 @@ value: components.PluginWeb = /* values here */ value: components.PluginFileParser = /* values here */ ``` +### `components.PluginResponseHealing` + +```python +value: components.PluginResponseHealing = /* values here */ +``` + diff --git a/docs/components/pluginresponsehealing.md b/docs/components/pluginresponsehealing.md new file mode 100644 index 0000000..ecee55d --- /dev/null +++ b/docs/components/pluginresponsehealing.md @@ -0,0 +1,8 @@ +# PluginResponseHealing + + +## Fields + +| Field | Type | Required | Description | +| ------------------------------------------------------------------ | ------------------------------------------------------------------ | ------------------------------------------------------------------ | ------------------------------------------------------------------ | +| `id` | [components.IDResponseHealing](../components/idresponsehealing.md) | :heavy_check_mark: | N/A | \ No newline at end of file diff --git a/docs/components/providername.md b/docs/components/providername.md index 9c86cd3..47b7be5 100644 --- a/docs/components/providername.md +++ b/docs/components/providername.md @@ -9,12 +9,14 @@ | `AION_LABS` | AionLabs | | `ALIBABA` | Alibaba | | `AMAZON_BEDROCK` | Amazon Bedrock | +| `AMAZON_NOVA` | Amazon Nova | | `ANTHROPIC` | Anthropic | -| `ARCEE` | Arcee | +| `ARCEE_AI` | Arcee AI | | `ATLAS_CLOUD` | AtlasCloud | | `AVIAN` | Avian | | `AZURE` | Azure | | `BASE_TEN` | BaseTen | +| `BYTE_PLUS` | BytePlus | | `BLACK_FOREST_LABS` | Black Forest Labs | | `CEREBRAS` | Cerebras | | `CHUTES` | Chutes | @@ -29,6 +31,7 @@ | `FIREWORKS` | Fireworks | | `FRIENDLI` | Friendli | | `GMI_CLOUD` | GMICloud | +| `GO_POMELO` | GoPomelo | | `GOOGLE` | Google | | `GOOGLE_AI_STUDIO` | Google AI Studio | | `GROQ` | Groq | @@ -59,6 +62,7 @@ | `SAMBA_NOVA` | SambaNova | | `SILICON_FLOW` | SiliconFlow | | `STEALTH` | Stealth | +| `STREAM_LAKE` | StreamLake | | `SWITCHPOINT` | Switchpoint | | `TARGON` | Targon | | `TOGETHER` | Together | diff --git a/docs/components/servicetier.md b/docs/components/servicetier.md index 3a7a020..bfc6187 100644 --- a/docs/components/servicetier.md +++ b/docs/components/servicetier.md @@ -3,10 +3,6 @@ ## Values -| Name | Value | -| ---------- | ---------- | -| `AUTO` | auto | -| `DEFAULT` | default | -| `FLEX` | flex | -| `PRIORITY` | priority | -| `SCALE` | scale | \ No newline at end of file +| Name | Value | +| ------ | ------ | +| `AUTO` | auto | \ No newline at end of file diff --git a/docs/components/ttl.md b/docs/components/ttl.md new file mode 100644 index 0000000..0cbc7f3 --- /dev/null +++ b/docs/components/ttl.md @@ -0,0 +1,9 @@ +# TTL + + +## Values + +| Name | Value | +| ------- | ------- | +| `FIVEM` | 5m | +| `ONEH` | 1h | \ No newline at end of file diff --git a/docs/models/internal/globals.md b/docs/models/internal/globals.md new file mode 100644 index 0000000..5399b31 --- /dev/null +++ b/docs/models/internal/globals.md @@ -0,0 +1,9 @@ +# Globals + + +## Fields + +| Field | Type | Required | Description | +| ------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | +| `http_referer` | *Optional[str]* | :heavy_minus_sign: | The app identifier should be your app's URL and is used as the primary identifier for rankings.
This is used to track API usage per application.
| +| `x_title` | *Optional[str]* | :heavy_minus_sign: | The app display name allows you to customize how your app appears in OpenRouter's dashboard.
| \ No newline at end of file diff --git a/docs/operations/createembeddingsresponse.md b/docs/operations/createembeddingsresponse.md index 4c379be..2dc9adf 100644 --- a/docs/operations/createembeddingsresponse.md +++ b/docs/operations/createembeddingsresponse.md @@ -1,17 +1,14 @@ # CreateEmbeddingsResponse +Embedding response -## Supported Types -### `operations.CreateEmbeddingsResponseBody` - -```python -value: operations.CreateEmbeddingsResponseBody = /* values here */ -``` - -### `str` - -```python -value: str = /* values here */ -``` +## Fields +| Field | Type | Required | Description | +| ------------------------------------------------------------------------------ | ------------------------------------------------------------------------------ | ------------------------------------------------------------------------------ | ------------------------------------------------------------------------------ | +| `id` | *Optional[str]* | :heavy_minus_sign: | N/A | +| `object` | [operations.Object](../operations/object.md) | :heavy_check_mark: | N/A | +| `data` | List[[operations.CreateEmbeddingsData](../operations/createembeddingsdata.md)] | :heavy_check_mark: | N/A | +| `model` | *str* | :heavy_check_mark: | N/A | +| `usage` | [Optional[operations.Usage]](../operations/usage.md) | :heavy_minus_sign: | N/A | \ No newline at end of file diff --git a/docs/operations/createembeddingsresponsebody.md b/docs/operations/createembeddingsresponsebody.md deleted file mode 100644 index 2252cfd..0000000 --- a/docs/operations/createembeddingsresponsebody.md +++ /dev/null @@ -1,14 +0,0 @@ -# CreateEmbeddingsResponseBody - -Embedding response - - -## Fields - -| Field | Type | Required | Description | -| ------------------------------------------------------------------------------ | ------------------------------------------------------------------------------ | ------------------------------------------------------------------------------ | ------------------------------------------------------------------------------ | -| `id` | *Optional[str]* | :heavy_minus_sign: | N/A | -| `object` | [operations.Object](../operations/object.md) | :heavy_check_mark: | N/A | -| `data` | List[[operations.CreateEmbeddingsData](../operations/createembeddingsdata.md)] | :heavy_check_mark: | N/A | -| `model` | *str* | :heavy_check_mark: | N/A | -| `usage` | [Optional[operations.Usage]](../operations/usage.md) | :heavy_minus_sign: | N/A | \ No newline at end of file diff --git a/docs/operations/getcreditsdata.md b/docs/operations/getcreditsdata.md new file mode 100644 index 0000000..9ba4582 --- /dev/null +++ b/docs/operations/getcreditsdata.md @@ -0,0 +1,9 @@ +# GetCreditsData + + +## Fields + +| Field | Type | Required | Description | Example | +| ----------------------- | ----------------------- | ----------------------- | ----------------------- | ----------------------- | +| `total_credits` | *float* | :heavy_check_mark: | Total credits purchased | 100.5 | +| `total_usage` | *float* | :heavy_check_mark: | Total credits used | 25.75 | \ No newline at end of file diff --git a/docs/operations/getcreditsresponse.md b/docs/operations/getcreditsresponse.md index c52bc01..a58a336 100644 --- a/docs/operations/getcreditsresponse.md +++ b/docs/operations/getcreditsresponse.md @@ -5,5 +5,6 @@ Total credits purchased and used ## Fields -| Field | Type | Required | Description | -| ----------- | ----------- | ----------- | ----------- | \ No newline at end of file +| Field | Type | Required | Description | Example | +| ------------------------------------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ | +| `data` | [operations.GetCreditsData](../operations/getcreditsdata.md) | :heavy_check_mark: | N/A | {
"total_credits": 100.5,
"total_usage": 25.75
} | \ No newline at end of file diff --git a/docs/operations/getparametersprovider.md b/docs/operations/getparametersprovider.md index 21a0244..f8728f0 100644 --- a/docs/operations/getparametersprovider.md +++ b/docs/operations/getparametersprovider.md @@ -9,12 +9,14 @@ | `AION_LABS` | AionLabs | | `ALIBABA` | Alibaba | | `AMAZON_BEDROCK` | Amazon Bedrock | +| `AMAZON_NOVA` | Amazon Nova | | `ANTHROPIC` | Anthropic | -| `ARCEE` | Arcee | +| `ARCEE_AI` | Arcee AI | | `ATLAS_CLOUD` | AtlasCloud | | `AVIAN` | Avian | | `AZURE` | Azure | | `BASE_TEN` | BaseTen | +| `BYTE_PLUS` | BytePlus | | `BLACK_FOREST_LABS` | Black Forest Labs | | `CEREBRAS` | Cerebras | | `CHUTES` | Chutes | @@ -29,6 +31,7 @@ | `FIREWORKS` | Fireworks | | `FRIENDLI` | Friendli | | `GMI_CLOUD` | GMICloud | +| `GO_POMELO` | GoPomelo | | `GOOGLE` | Google | | `GOOGLE_AI_STUDIO` | Google AI Studio | | `GROQ` | Groq | @@ -59,6 +62,7 @@ | `SAMBA_NOVA` | SambaNova | | `SILICON_FLOW` | SiliconFlow | | `STEALTH` | Stealth | +| `STREAM_LAKE` | StreamLake | | `SWITCHPOINT` | Switchpoint | | `TARGON` | Targon | | `TOGETHER` | Together | diff --git a/docs/sdks/chat/README.md b/docs/sdks/chat/README.md index 94ffd97..78c3a29 100644 --- a/docs/sdks/chat/README.md +++ b/docs/sdks/chat/README.md @@ -58,6 +58,7 @@ with OpenRouter( | `tools` | List[[components.ToolDefinitionJSON](../../components/tooldefinitionjson.md)] | :heavy_minus_sign: | N/A | | `top_p` | *OptionalNullable[float]* | :heavy_minus_sign: | N/A | | `user` | *Optional[str]* | :heavy_minus_sign: | N/A | +| `debug` | [Optional[components.Debug]](../../components/debug.md) | :heavy_minus_sign: | N/A | | `retries` | [Optional[utils.RetryConfig]](../../models/utils/retryconfig.md) | :heavy_minus_sign: | Configuration to override the default retry behavior of the client. | ### Response diff --git a/docs/sdks/responses/README.md b/docs/sdks/responses/README.md index 386fd37..001617b 100644 --- a/docs/sdks/responses/README.md +++ b/docs/sdks/responses/README.md @@ -112,8 +112,7 @@ with OpenRouter( | `include` | List[[components.OpenAIResponsesIncludable](../../components/openairesponsesincludable.md)] | :heavy_minus_sign: | N/A | | | `background` | *OptionalNullable[bool]* | :heavy_minus_sign: | N/A | | | `safety_identifier` | *OptionalNullable[str]* | :heavy_minus_sign: | N/A | | -| `store` | *OptionalNullable[bool]* | :heavy_minus_sign: | N/A | | -| `service_tier` | [OptionalNullable[components.ServiceTier]](../../components/servicetier.md) | :heavy_minus_sign: | N/A | auto | +| `service_tier` | [Optional[components.ServiceTier]](../../components/servicetier.md) | :heavy_minus_sign: | N/A | | | `truncation` | [OptionalNullable[components.Truncation]](../../components/truncation.md) | :heavy_minus_sign: | N/A | auto | | `stream` | *Optional[bool]* | :heavy_minus_sign: | N/A | | | `provider` | [OptionalNullable[components.Provider]](../../components/provider.md) | :heavy_minus_sign: | When multiple model providers are available, optionally indicate your routing preference. | | diff --git a/pyproject.toml b/pyproject.toml index e10c064..f34abc4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openrouter" -version = "0.0.19" +version = "0.1.0" description = "Official Python Client SDK for OpenRouter." authors = [{ name = "OpenRouter" },] readme = "README-PYPI.md" diff --git a/src/openrouter/_version.py b/src/openrouter/_version.py index a25d226..0d60f67 100644 --- a/src/openrouter/_version.py +++ b/src/openrouter/_version.py @@ -3,10 +3,10 @@ import importlib.metadata __title__: str = "openrouter" -__version__: str = "0.0.19" +__version__: str = "0.1.0" __openapi_doc_version__: str = "1.0.0" -__gen_version__: str = "2.755.9" -__user_agent__: str = "speakeasy-sdk/python 0.0.19 2.755.9 1.0.0 openrouter" +__gen_version__: str = "2.770.0" +__user_agent__: str = "speakeasy-sdk/python 0.1.0 2.770.0 1.0.0 openrouter" try: if __package__ is not None: diff --git a/src/openrouter/analytics.py b/src/openrouter/analytics.py index 181e1da..a60430c 100644 --- a/src/openrouter/analytics.py +++ b/src/openrouter/analytics.py @@ -58,6 +58,7 @@ def get_user_activity( accept_header_value="application/json", http_headers=http_headers, security=self.sdk_configuration.security, + allow_empty_value=None, timeout_ms=timeout_ms, ) @@ -166,6 +167,7 @@ async def get_user_activity_async( accept_header_value="application/json", http_headers=http_headers, security=self.sdk_configuration.security, + allow_empty_value=None, timeout_ms=timeout_ms, ) diff --git a/src/openrouter/api_keys.py b/src/openrouter/api_keys.py index 0906aa8..f4f0d23 100644 --- a/src/openrouter/api_keys.py +++ b/src/openrouter/api_keys.py @@ -60,6 +60,7 @@ def list( accept_header_value="application/json", http_headers=http_headers, security=self.sdk_configuration.security, + allow_empty_value=None, timeout_ms=timeout_ms, ) @@ -164,6 +165,7 @@ async def list_async( accept_header_value="application/json", http_headers=http_headers, security=self.sdk_configuration.security, + allow_empty_value=None, timeout_ms=timeout_ms, ) @@ -280,6 +282,7 @@ def create( get_serialized_body=lambda: utils.serialize_request_body( request, False, False, "json", operations.CreateKeysRequest ), + allow_empty_value=None, timeout_ms=timeout_ms, ) @@ -401,6 +404,7 @@ async def create_async( get_serialized_body=lambda: utils.serialize_request_body( request, False, False, "json", operations.CreateKeysRequest ), + allow_empty_value=None, timeout_ms=timeout_ms, ) @@ -527,6 +531,7 @@ def update( get_serialized_body=lambda: utils.serialize_request_body( request.body, False, False, "json", operations.UpdateKeysRequestBody ), + allow_empty_value=None, timeout_ms=timeout_ms, ) @@ -658,6 +663,7 @@ async def update_async( get_serialized_body=lambda: utils.serialize_request_body( request.body, False, False, "json", operations.UpdateKeysRequestBody ), + allow_empty_value=None, timeout_ms=timeout_ms, ) @@ -769,6 +775,7 @@ def delete( accept_header_value="application/json", http_headers=http_headers, security=self.sdk_configuration.security, + allow_empty_value=None, timeout_ms=timeout_ms, ) @@ -875,6 +882,7 @@ async def delete_async( accept_header_value="application/json", http_headers=http_headers, security=self.sdk_configuration.security, + allow_empty_value=None, timeout_ms=timeout_ms, ) @@ -981,6 +989,7 @@ def get( accept_header_value="application/json", http_headers=http_headers, security=self.sdk_configuration.security, + allow_empty_value=None, timeout_ms=timeout_ms, ) @@ -1087,6 +1096,7 @@ async def get_async( accept_header_value="application/json", http_headers=http_headers, security=self.sdk_configuration.security, + allow_empty_value=None, timeout_ms=timeout_ms, ) @@ -1188,6 +1198,7 @@ def get_current_key_metadata( accept_header_value="application/json", http_headers=http_headers, security=self.sdk_configuration.security, + allow_empty_value=None, timeout_ms=timeout_ms, ) @@ -1279,6 +1290,7 @@ async def get_current_key_metadata_async( accept_header_value="application/json", http_headers=http_headers, security=self.sdk_configuration.security, + allow_empty_value=None, timeout_ms=timeout_ms, ) diff --git a/src/openrouter/basesdk.py b/src/openrouter/basesdk.py index 17059ed..fcffdcc 100644 --- a/src/openrouter/basesdk.py +++ b/src/openrouter/basesdk.py @@ -60,6 +60,7 @@ def _build_request_async( ] = None, url_override: Optional[str] = None, http_headers: Optional[Mapping[str, str]] = None, + allow_empty_value: Optional[List[str]] = None, ) -> httpx.Request: client = self.sdk_configuration.async_client return self._build_request_with_client( @@ -80,6 +81,7 @@ def _build_request_async( get_serialized_body, url_override, http_headers, + allow_empty_value, ) def _build_request( @@ -102,6 +104,7 @@ def _build_request( ] = None, url_override: Optional[str] = None, http_headers: Optional[Mapping[str, str]] = None, + allow_empty_value: Optional[List[str]] = None, ) -> httpx.Request: client = self.sdk_configuration.client return self._build_request_with_client( @@ -122,6 +125,7 @@ def _build_request( get_serialized_body, url_override, http_headers, + allow_empty_value, ) def _build_request_with_client( @@ -145,6 +149,7 @@ def _build_request_with_client( ] = None, url_override: Optional[str] = None, http_headers: Optional[Mapping[str, str]] = None, + allow_empty_value: Optional[List[str]] = None, ) -> httpx.Request: query_params = {} @@ -160,6 +165,7 @@ def _build_request_with_client( query_params = utils.get_query_params( request if request_has_query_params else None, _globals if request_has_query_params else None, + allow_empty_value, ) else: # Pick up the query parameter from the override so they can be diff --git a/src/openrouter/chat.py b/src/openrouter/chat.py index 5a80cf8..bc00143 100644 --- a/src/openrouter/chat.py +++ b/src/openrouter/chat.py @@ -61,6 +61,7 @@ def send( ] = None, top_p: OptionalNullable[float] = UNSET, user: Optional[str] = None, + debug: Optional[Union[components.Debug, components.DebugTypedDict]] = None, retries: OptionalNullable[utils.RetryConfig] = UNSET, server_url: Optional[str] = None, timeout_ms: Optional[int] = None, @@ -92,6 +93,7 @@ def send( :param tools: :param top_p: :param user: + :param debug: :param retries: Override the default retry configuration for this method :param server_url: Override the default server URL for this method :param timeout_ms: Override the default request timeout configuration for this method in milliseconds @@ -144,6 +146,7 @@ def send( ] = None, top_p: OptionalNullable[float] = UNSET, user: Optional[str] = None, + debug: Optional[Union[components.Debug, components.DebugTypedDict]] = None, retries: OptionalNullable[utils.RetryConfig] = UNSET, server_url: Optional[str] = None, timeout_ms: Optional[int] = None, @@ -175,6 +178,7 @@ def send( :param tools: :param top_p: :param user: + :param debug: :param retries: Override the default retry configuration for this method :param server_url: Override the default server URL for this method :param timeout_ms: Override the default request timeout configuration for this method in milliseconds @@ -226,6 +230,7 @@ def send( ] = None, top_p: OptionalNullable[float] = UNSET, user: Optional[str] = None, + debug: Optional[Union[components.Debug, components.DebugTypedDict]] = None, retries: OptionalNullable[utils.RetryConfig] = UNSET, server_url: Optional[str] = None, timeout_ms: Optional[int] = None, @@ -257,6 +262,7 @@ def send( :param tools: :param top_p: :param user: + :param debug: :param retries: Override the default retry configuration for this method :param server_url: Override the default server URL for this method :param timeout_ms: Override the default request timeout configuration for this method in milliseconds @@ -305,6 +311,7 @@ def send( ), top_p=top_p, user=user, + debug=utils.get_pydantic_model(debug, Optional[components.Debug]), ) req = self._build_request( @@ -323,6 +330,7 @@ def send( get_serialized_body=lambda: utils.serialize_request_body( request, False, False, "json", components.ChatGenerationParams ), + allow_empty_value=None, timeout_ms=timeout_ms, ) @@ -438,6 +446,7 @@ async def send_async( ] = None, top_p: OptionalNullable[float] = UNSET, user: Optional[str] = None, + debug: Optional[Union[components.Debug, components.DebugTypedDict]] = None, retries: OptionalNullable[utils.RetryConfig] = UNSET, server_url: Optional[str] = None, timeout_ms: Optional[int] = None, @@ -469,6 +478,7 @@ async def send_async( :param tools: :param top_p: :param user: + :param debug: :param retries: Override the default retry configuration for this method :param server_url: Override the default server URL for this method :param timeout_ms: Override the default request timeout configuration for this method in milliseconds @@ -521,6 +531,7 @@ async def send_async( ] = None, top_p: OptionalNullable[float] = UNSET, user: Optional[str] = None, + debug: Optional[Union[components.Debug, components.DebugTypedDict]] = None, retries: OptionalNullable[utils.RetryConfig] = UNSET, server_url: Optional[str] = None, timeout_ms: Optional[int] = None, @@ -552,6 +563,7 @@ async def send_async( :param tools: :param top_p: :param user: + :param debug: :param retries: Override the default retry configuration for this method :param server_url: Override the default server URL for this method :param timeout_ms: Override the default request timeout configuration for this method in milliseconds @@ -603,6 +615,7 @@ async def send_async( ] = None, top_p: OptionalNullable[float] = UNSET, user: Optional[str] = None, + debug: Optional[Union[components.Debug, components.DebugTypedDict]] = None, retries: OptionalNullable[utils.RetryConfig] = UNSET, server_url: Optional[str] = None, timeout_ms: Optional[int] = None, @@ -634,6 +647,7 @@ async def send_async( :param tools: :param top_p: :param user: + :param debug: :param retries: Override the default retry configuration for this method :param server_url: Override the default server URL for this method :param timeout_ms: Override the default request timeout configuration for this method in milliseconds @@ -682,6 +696,7 @@ async def send_async( ), top_p=top_p, user=user, + debug=utils.get_pydantic_model(debug, Optional[components.Debug]), ) req = self._build_request_async( @@ -700,6 +715,7 @@ async def send_async( get_serialized_body=lambda: utils.serialize_request_body( request, False, False, "json", components.ChatGenerationParams ), + allow_empty_value=None, timeout_ms=timeout_ms, ) diff --git a/src/openrouter/completions.py b/src/openrouter/completions.py index 4ffeb7b..26bbf6a 100644 --- a/src/openrouter/completions.py +++ b/src/openrouter/completions.py @@ -136,6 +136,7 @@ def generate( get_serialized_body=lambda: utils.serialize_request_body( request, False, False, "json", components.CompletionCreateParams ), + allow_empty_value=None, timeout_ms=timeout_ms, ) @@ -310,6 +311,7 @@ async def generate_async( get_serialized_body=lambda: utils.serialize_request_body( request, False, False, "json", components.CompletionCreateParams ), + allow_empty_value=None, timeout_ms=timeout_ms, ) diff --git a/src/openrouter/components/__init__.py b/src/openrouter/components/__init__.py index 8a34729..d7818e6 100644 --- a/src/openrouter/components/__init__.py +++ b/src/openrouter/components/__init__.py @@ -21,6 +21,7 @@ BadRequestResponseErrorData, BadRequestResponseErrorDataTypedDict, ) + from .chatcompletionfinishreason import ChatCompletionFinishReason from .chaterror import ChatErrorError, ChatErrorErrorTypedDict, Code, CodeTypedDict from .chatgenerationparams import ( ChatGenerationParams, @@ -35,6 +36,8 @@ ChatGenerationParamsStop, ChatGenerationParamsStopTypedDict, ChatGenerationParamsTypedDict, + Debug, + DebugTypedDict, Effort, Reasoning, ReasoningTypedDict, @@ -53,11 +56,15 @@ ) from .chatmessagecontentitemaudio import ( ChatMessageContentItemAudio, - ChatMessageContentItemAudioFormat, ChatMessageContentItemAudioInputAudio, ChatMessageContentItemAudioInputAudioTypedDict, ChatMessageContentItemAudioTypedDict, ) + from .chatmessagecontentitemcachecontrol import ( + ChatMessageContentItemCacheControl, + ChatMessageContentItemCacheControlTypedDict, + TTL, + ) from .chatmessagecontentitemimage import ( ChatMessageContentItemImage, ChatMessageContentItemImageDetail, @@ -98,13 +105,8 @@ ChatMessageToolCallTypedDict, ) from .chatresponse import ChatResponse, ChatResponseTypedDict - from .chatresponsechoice import ( - ChatCompletionFinishReason, - ChatResponseChoice, - ChatResponseChoiceTypedDict, - ChatStreamingChoice, - ChatStreamingChoiceTypedDict, - ) + from .chatresponsechoice import ChatResponseChoice, ChatResponseChoiceTypedDict + from .chatstreamingchoice import ChatStreamingChoice, ChatStreamingChoiceTypedDict from .chatstreamingmessagechunk import ( ChatStreamingMessageChunk, ChatStreamingMessageChunkRole, @@ -422,6 +424,7 @@ Engine, IDFileParser, IDModeration, + IDResponseHealing, IDWeb, Ignore, IgnoreTypedDict, @@ -446,6 +449,8 @@ PluginFileParserTypedDict, PluginModeration, PluginModerationTypedDict, + PluginResponseHealing, + PluginResponseHealingTypedDict, PluginTypedDict, PluginWeb, PluginWebTypedDict, @@ -820,10 +825,11 @@ "ChatGenerationTokenUsageTypedDict", "ChatMessageContentItem", "ChatMessageContentItemAudio", - "ChatMessageContentItemAudioFormat", "ChatMessageContentItemAudioInputAudio", "ChatMessageContentItemAudioInputAudioTypedDict", "ChatMessageContentItemAudioTypedDict", + "ChatMessageContentItemCacheControl", + "ChatMessageContentItemCacheControlTypedDict", "ChatMessageContentItemImage", "ChatMessageContentItemImageDetail", "ChatMessageContentItemImageTypedDict", @@ -896,6 +902,8 @@ "CreateChargeRequest", "CreateChargeRequestTypedDict", "DataCollection", + "Debug", + "DebugTypedDict", "DefaultParameters", "DefaultParametersTypedDict", "EdgeNetworkTimeoutResponseErrorData", @@ -913,6 +921,7 @@ "ForbiddenResponseErrorDataTypedDict", "IDFileParser", "IDModeration", + "IDResponseHealing", "IDWeb", "Ignore", "IgnoreTypedDict", @@ -1206,6 +1215,8 @@ "PluginFileParserTypedDict", "PluginModeration", "PluginModerationTypedDict", + "PluginResponseHealing", + "PluginResponseHealingTypedDict", "PluginTypedDict", "PluginWeb", "PluginWebTypedDict", @@ -1331,6 +1342,7 @@ "SystemMessageContent", "SystemMessageContentTypedDict", "SystemMessageTypedDict", + "TTL", "Tokenizer", "TooManyRequestsResponseErrorData", "TooManyRequestsResponseErrorDataTypedDict", @@ -1401,6 +1413,7 @@ "BadGatewayResponseErrorDataTypedDict": ".badgatewayresponseerrordata", "BadRequestResponseErrorData": ".badrequestresponseerrordata", "BadRequestResponseErrorDataTypedDict": ".badrequestresponseerrordata", + "ChatCompletionFinishReason": ".chatcompletionfinishreason", "ChatErrorError": ".chaterror", "ChatErrorErrorTypedDict": ".chaterror", "Code": ".chaterror", @@ -1417,6 +1430,8 @@ "ChatGenerationParamsStop": ".chatgenerationparams", "ChatGenerationParamsStopTypedDict": ".chatgenerationparams", "ChatGenerationParamsTypedDict": ".chatgenerationparams", + "Debug": ".chatgenerationparams", + "DebugTypedDict": ".chatgenerationparams", "Effort": ".chatgenerationparams", "Reasoning": ".chatgenerationparams", "ReasoningTypedDict": ".chatgenerationparams", @@ -1429,10 +1444,12 @@ "ChatMessageContentItem": ".chatmessagecontentitem", "ChatMessageContentItemTypedDict": ".chatmessagecontentitem", "ChatMessageContentItemAudio": ".chatmessagecontentitemaudio", - "ChatMessageContentItemAudioFormat": ".chatmessagecontentitemaudio", "ChatMessageContentItemAudioInputAudio": ".chatmessagecontentitemaudio", "ChatMessageContentItemAudioInputAudioTypedDict": ".chatmessagecontentitemaudio", "ChatMessageContentItemAudioTypedDict": ".chatmessagecontentitemaudio", + "ChatMessageContentItemCacheControl": ".chatmessagecontentitemcachecontrol", + "ChatMessageContentItemCacheControlTypedDict": ".chatmessagecontentitemcachecontrol", + "TTL": ".chatmessagecontentitemcachecontrol", "ChatMessageContentItemImage": ".chatmessagecontentitemimage", "ChatMessageContentItemImageDetail": ".chatmessagecontentitemimage", "ChatMessageContentItemImageTypedDict": ".chatmessagecontentitemimage", @@ -1462,11 +1479,10 @@ "ChatMessageToolCallTypedDict": ".chatmessagetoolcall", "ChatResponse": ".chatresponse", "ChatResponseTypedDict": ".chatresponse", - "ChatCompletionFinishReason": ".chatresponsechoice", "ChatResponseChoice": ".chatresponsechoice", "ChatResponseChoiceTypedDict": ".chatresponsechoice", - "ChatStreamingChoice": ".chatresponsechoice", - "ChatStreamingChoiceTypedDict": ".chatresponsechoice", + "ChatStreamingChoice": ".chatstreamingchoice", + "ChatStreamingChoiceTypedDict": ".chatstreamingchoice", "ChatStreamingMessageChunk": ".chatstreamingmessagechunk", "ChatStreamingMessageChunkRole": ".chatstreamingmessagechunk", "ChatStreamingMessageChunkTypedDict": ".chatstreamingmessagechunk", @@ -1713,6 +1729,7 @@ "Engine": ".openresponsesrequest", "IDFileParser": ".openresponsesrequest", "IDModeration": ".openresponsesrequest", + "IDResponseHealing": ".openresponsesrequest", "IDWeb": ".openresponsesrequest", "Ignore": ".openresponsesrequest", "IgnoreTypedDict": ".openresponsesrequest", @@ -1737,6 +1754,8 @@ "PluginFileParserTypedDict": ".openresponsesrequest", "PluginModeration": ".openresponsesrequest", "PluginModerationTypedDict": ".openresponsesrequest", + "PluginResponseHealing": ".openresponsesrequest", + "PluginResponseHealingTypedDict": ".openresponsesrequest", "PluginTypedDict": ".openresponsesrequest", "PluginWeb": ".openresponsesrequest", "PluginWebTypedDict": ".openresponsesrequest", diff --git a/src/openrouter/components/chatcompletionfinishreason.py b/src/openrouter/components/chatcompletionfinishreason.py new file mode 100644 index 0000000..686462c --- /dev/null +++ b/src/openrouter/components/chatcompletionfinishreason.py @@ -0,0 +1,17 @@ +"""Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT.""" + +from __future__ import annotations +from openrouter.types import UnrecognizedStr +from typing import Literal, Union + + +ChatCompletionFinishReason = Union[ + Literal[ + "tool_calls", + "stop", + "length", + "content_filter", + "error", + ], + UnrecognizedStr, +] diff --git a/src/openrouter/components/chatgenerationparams.py b/src/openrouter/components/chatgenerationparams.py index 32fd9ca..07b4c33 100644 --- a/src/openrouter/components/chatgenerationparams.py +++ b/src/openrouter/components/chatgenerationparams.py @@ -21,9 +21,9 @@ UNSET_SENTINEL, UnrecognizedStr, ) -from openrouter.utils import validate_const, validate_open_enum +from openrouter.utils import get_discriminator, validate_const, validate_open_enum import pydantic -from pydantic import model_serializer +from pydantic import Discriminator, Tag, model_serializer from pydantic.functional_validators import AfterValidator, PlainValidator from typing import Any, Dict, List, Literal, Optional, Union from typing_extensions import Annotated, NotRequired, TypeAliasType, TypedDict @@ -134,16 +134,16 @@ class ChatGenerationParamsResponseFormatText(BaseModel): ) -ChatGenerationParamsResponseFormatUnion = TypeAliasType( - "ChatGenerationParamsResponseFormatUnion", +ChatGenerationParamsResponseFormatUnion = Annotated[ Union[ - ChatGenerationParamsResponseFormatText, - ChatGenerationParamsResponseFormatJSONObject, - ChatGenerationParamsResponseFormatPython, - ResponseFormatJSONSchema, - ResponseFormatTextGrammar, + Annotated[ChatGenerationParamsResponseFormatText, Tag("text")], + Annotated[ChatGenerationParamsResponseFormatJSONObject, Tag("json_object")], + Annotated[ResponseFormatJSONSchema, Tag("json_schema")], + Annotated[ResponseFormatTextGrammar, Tag("grammar")], + Annotated[ChatGenerationParamsResponseFormatPython, Tag("python")], ], -) + Discriminator(lambda m: get_discriminator(m, "type", "type")), +] ChatGenerationParamsStopTypedDict = TypeAliasType( @@ -156,6 +156,14 @@ class ChatGenerationParamsResponseFormatText(BaseModel): ) +class DebugTypedDict(TypedDict): + echo_upstream_body: NotRequired[bool] + + +class Debug(BaseModel): + echo_upstream_body: Optional[bool] = None + + class ChatGenerationParamsTypedDict(TypedDict): messages: List[MessageTypedDict] model: NotRequired[str] @@ -179,6 +187,7 @@ class ChatGenerationParamsTypedDict(TypedDict): tools: NotRequired[List[ToolDefinitionJSONTypedDict]] top_p: NotRequired[Nullable[float]] user: NotRequired[str] + debug: NotRequired[DebugTypedDict] class ChatGenerationParams(BaseModel): @@ -226,6 +235,8 @@ class ChatGenerationParams(BaseModel): user: Optional[str] = None + debug: Optional[Debug] = None + @model_serializer(mode="wrap") def serialize_model(self, handler): optional_fields = [ @@ -250,6 +261,7 @@ def serialize_model(self, handler): "tools", "top_p", "user", + "debug", ] nullable_fields = [ "frequency_penalty", diff --git a/src/openrouter/components/chatmessagecontentitem.py b/src/openrouter/components/chatmessagecontentitem.py index 87547ca..eba0a3b 100644 --- a/src/openrouter/components/chatmessagecontentitem.py +++ b/src/openrouter/components/chatmessagecontentitem.py @@ -26,9 +26,9 @@ ChatMessageContentItemTypedDict = TypeAliasType( "ChatMessageContentItemTypedDict", Union[ - ChatMessageContentItemTextTypedDict, ChatMessageContentItemImageTypedDict, ChatMessageContentItemAudioTypedDict, + ChatMessageContentItemTextTypedDict, ChatMessageContentItemVideoTypedDict, ], ) diff --git a/src/openrouter/components/chatmessagecontentitemaudio.py b/src/openrouter/components/chatmessagecontentitemaudio.py index 21a8803..b251803 100644 --- a/src/openrouter/components/chatmessagecontentitemaudio.py +++ b/src/openrouter/components/chatmessagecontentitemaudio.py @@ -1,42 +1,23 @@ """Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT.""" from __future__ import annotations -from openrouter.types import BaseModel, UnrecognizedStr -from openrouter.utils import validate_const, validate_open_enum +from openrouter.types import BaseModel +from openrouter.utils import validate_const import pydantic -from pydantic.functional_validators import AfterValidator, PlainValidator -from typing import Literal, Union +from pydantic.functional_validators import AfterValidator +from typing import Literal from typing_extensions import Annotated, TypedDict -ChatMessageContentItemAudioFormat = Union[ - Literal[ - "wav", - "mp3", - "flac", - "m4a", - "ogg", - "pcm16", - "pcm24", - ], - UnrecognizedStr, -] - - class ChatMessageContentItemAudioInputAudioTypedDict(TypedDict): data: str - format_: ChatMessageContentItemAudioFormat + format_: str class ChatMessageContentItemAudioInputAudio(BaseModel): data: str - format_: Annotated[ - Annotated[ - ChatMessageContentItemAudioFormat, PlainValidator(validate_open_enum(False)) - ], - pydantic.Field(alias="format"), - ] + format_: Annotated[str, pydantic.Field(alias="format")] class ChatMessageContentItemAudioTypedDict(TypedDict): diff --git a/src/openrouter/components/chatmessagecontentitemcachecontrol.py b/src/openrouter/components/chatmessagecontentitemcachecontrol.py new file mode 100644 index 0000000..b96625f --- /dev/null +++ b/src/openrouter/components/chatmessagecontentitemcachecontrol.py @@ -0,0 +1,32 @@ +"""Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT.""" + +from __future__ import annotations +from openrouter.types import BaseModel, UnrecognizedStr +from openrouter.utils import validate_const, validate_open_enum +import pydantic +from pydantic.functional_validators import AfterValidator, PlainValidator +from typing import Literal, Optional, Union +from typing_extensions import Annotated, NotRequired, TypedDict + + +TTL = Union[ + Literal[ + "5m", + "1h", + ], + UnrecognizedStr, +] + + +class ChatMessageContentItemCacheControlTypedDict(TypedDict): + type: Literal["ephemeral"] + ttl: NotRequired[TTL] + + +class ChatMessageContentItemCacheControl(BaseModel): + TYPE: Annotated[ + Annotated[Literal["ephemeral"], AfterValidator(validate_const("ephemeral"))], + pydantic.Field(alias="type"), + ] = "ephemeral" + + ttl: Annotated[Optional[TTL], PlainValidator(validate_open_enum(False))] = None diff --git a/src/openrouter/components/chatmessagecontentitemtext.py b/src/openrouter/components/chatmessagecontentitemtext.py index c791fa9..4dc1662 100644 --- a/src/openrouter/components/chatmessagecontentitemtext.py +++ b/src/openrouter/components/chatmessagecontentitemtext.py @@ -1,17 +1,22 @@ """Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT.""" from __future__ import annotations +from .chatmessagecontentitemcachecontrol import ( + ChatMessageContentItemCacheControl, + ChatMessageContentItemCacheControlTypedDict, +) from openrouter.types import BaseModel from openrouter.utils import validate_const import pydantic from pydantic.functional_validators import AfterValidator -from typing import Literal -from typing_extensions import Annotated, TypedDict +from typing import Literal, Optional +from typing_extensions import Annotated, NotRequired, TypedDict class ChatMessageContentItemTextTypedDict(TypedDict): text: str type: Literal["text"] + cache_control: NotRequired[ChatMessageContentItemCacheControlTypedDict] class ChatMessageContentItemText(BaseModel): @@ -21,3 +26,5 @@ class ChatMessageContentItemText(BaseModel): Annotated[Literal["text"], AfterValidator(validate_const("text"))], pydantic.Field(alias="type"), ] = "text" + + cache_control: Optional[ChatMessageContentItemCacheControl] = None diff --git a/src/openrouter/components/chatmessagecontentitemvideo.py b/src/openrouter/components/chatmessagecontentitemvideo.py index fccda91..06be297 100644 --- a/src/openrouter/components/chatmessagecontentitemvideo.py +++ b/src/openrouter/components/chatmessagecontentitemvideo.py @@ -2,8 +2,9 @@ from __future__ import annotations from openrouter.types import BaseModel -from openrouter.utils import validate_const +from openrouter.utils import get_discriminator, validate_const import pydantic +from pydantic import Discriminator, Tag from pydantic.functional_validators import AfterValidator from typing import Literal, Union from typing_extensions import Annotated, TypeAliasType, TypedDict @@ -64,7 +65,10 @@ class ChatMessageContentItemVideoInputVideo(BaseModel): ) -ChatMessageContentItemVideo = TypeAliasType( - "ChatMessageContentItemVideo", - Union[ChatMessageContentItemVideoInputVideo, ChatMessageContentItemVideoVideoURL], -) +ChatMessageContentItemVideo = Annotated[ + Union[ + Annotated[ChatMessageContentItemVideoInputVideo, Tag("input_video")], + Annotated[ChatMessageContentItemVideoVideoURL, Tag("video_url")], + ], + Discriminator(lambda m: get_discriminator(m, "type", "type")), +] diff --git a/src/openrouter/components/chatresponsechoice.py b/src/openrouter/components/chatresponsechoice.py index e59f847..9592bf1 100644 --- a/src/openrouter/components/chatresponsechoice.py +++ b/src/openrouter/components/chatresponsechoice.py @@ -2,41 +2,24 @@ from __future__ import annotations from .assistantmessage import AssistantMessage, AssistantMessageTypedDict +from .chatcompletionfinishreason import ChatCompletionFinishReason from .chatmessagetokenlogprobs import ( ChatMessageTokenLogprobs, ChatMessageTokenLogprobsTypedDict, ) -from .chatstreamingmessagechunk import ( - ChatStreamingMessageChunk, - ChatStreamingMessageChunkTypedDict, -) from openrouter.types import ( BaseModel, Nullable, OptionalNullable, UNSET, UNSET_SENTINEL, - UnrecognizedStr, ) from openrouter.utils import validate_open_enum from pydantic import model_serializer from pydantic.functional_validators import PlainValidator -from typing import Literal, Union from typing_extensions import Annotated, NotRequired, TypedDict -ChatCompletionFinishReason = Union[ - Literal[ - "tool_calls", - "stop", - "length", - "content_filter", - "error", - ], - UnrecognizedStr, -] - - class ChatResponseChoiceTypedDict(TypedDict): finish_reason: Nullable[ChatCompletionFinishReason] index: float @@ -84,52 +67,3 @@ def serialize_model(self, handler): m[k] = val return m - - -class ChatStreamingChoiceTypedDict(TypedDict): - delta: ChatStreamingMessageChunkTypedDict - finish_reason: Nullable[ChatCompletionFinishReason] - index: float - logprobs: NotRequired[Nullable[ChatMessageTokenLogprobsTypedDict]] - - -class ChatStreamingChoice(BaseModel): - delta: ChatStreamingMessageChunk - - finish_reason: Annotated[ - Nullable[ChatCompletionFinishReason], PlainValidator(validate_open_enum(False)) - ] - - index: float - - logprobs: OptionalNullable[ChatMessageTokenLogprobs] = UNSET - - @model_serializer(mode="wrap") - def serialize_model(self, handler): - optional_fields = ["logprobs"] - nullable_fields = ["finish_reason", "logprobs"] - null_default_fields = [] - - serialized = handler(self) - - m = {} - - for n, f in type(self).model_fields.items(): - k = f.alias or n - val = serialized.get(k) - serialized.pop(k, None) - - optional_nullable = k in optional_fields and k in nullable_fields - is_set = ( - self.__pydantic_fields_set__.intersection({n}) - or k in null_default_fields - ) # pylint: disable=no-member - - if val is not None and val != UNSET_SENTINEL: - m[k] = val - elif val != UNSET_SENTINEL and ( - not k in optional_fields or (optional_nullable and is_set) - ): - m[k] = val - - return m diff --git a/src/openrouter/components/chatstreamingchoice.py b/src/openrouter/components/chatstreamingchoice.py new file mode 100644 index 0000000..b64b8ff --- /dev/null +++ b/src/openrouter/components/chatstreamingchoice.py @@ -0,0 +1,72 @@ +"""Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT.""" + +from __future__ import annotations +from .chatcompletionfinishreason import ChatCompletionFinishReason +from .chatmessagetokenlogprobs import ( + ChatMessageTokenLogprobs, + ChatMessageTokenLogprobsTypedDict, +) +from .chatstreamingmessagechunk import ( + ChatStreamingMessageChunk, + ChatStreamingMessageChunkTypedDict, +) +from openrouter.types import ( + BaseModel, + Nullable, + OptionalNullable, + UNSET, + UNSET_SENTINEL, +) +from openrouter.utils import validate_open_enum +from pydantic import model_serializer +from pydantic.functional_validators import PlainValidator +from typing_extensions import Annotated, NotRequired, TypedDict + + +class ChatStreamingChoiceTypedDict(TypedDict): + delta: ChatStreamingMessageChunkTypedDict + finish_reason: Nullable[ChatCompletionFinishReason] + index: float + logprobs: NotRequired[Nullable[ChatMessageTokenLogprobsTypedDict]] + + +class ChatStreamingChoice(BaseModel): + delta: ChatStreamingMessageChunk + + finish_reason: Annotated[ + Nullable[ChatCompletionFinishReason], PlainValidator(validate_open_enum(False)) + ] + + index: float + + logprobs: OptionalNullable[ChatMessageTokenLogprobs] = UNSET + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = ["logprobs"] + nullable_fields = ["finish_reason", "logprobs"] + null_default_fields = [] + + serialized = handler(self) + + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k) + serialized.pop(k, None) + + optional_nullable = k in optional_fields and k in nullable_fields + is_set = ( + self.__pydantic_fields_set__.intersection({n}) + or k in null_default_fields + ) # pylint: disable=no-member + + if val is not None and val != UNSET_SENTINEL: + m[k] = val + elif val != UNSET_SENTINEL and ( + not k in optional_fields or (optional_nullable and is_set) + ): + m[k] = val + + return m diff --git a/src/openrouter/components/chatstreamingresponsechunk.py b/src/openrouter/components/chatstreamingresponsechunk.py index 0ec76bc..7c32481 100644 --- a/src/openrouter/components/chatstreamingresponsechunk.py +++ b/src/openrouter/components/chatstreamingresponsechunk.py @@ -5,7 +5,7 @@ ChatGenerationTokenUsage, ChatGenerationTokenUsageTypedDict, ) -from .chatresponsechoice import ChatStreamingChoice, ChatStreamingChoiceTypedDict +from .chatstreamingchoice import ChatStreamingChoice, ChatStreamingChoiceTypedDict from openrouter.types import ( BaseModel, Nullable, diff --git a/src/openrouter/components/completionchoice.py b/src/openrouter/components/completionchoice.py index 3e68a00..21ce2b9 100644 --- a/src/openrouter/components/completionchoice.py +++ b/src/openrouter/components/completionchoice.py @@ -2,12 +2,19 @@ from __future__ import annotations from .completionlogprobs import CompletionLogprobs, CompletionLogprobsTypedDict -from openrouter.types import BaseModel, Nullable, UNSET_SENTINEL, UnrecognizedStr +from openrouter.types import ( + BaseModel, + Nullable, + OptionalNullable, + UNSET, + UNSET_SENTINEL, + UnrecognizedStr, +) from openrouter.utils import validate_open_enum from pydantic import model_serializer from pydantic.functional_validators import PlainValidator -from typing import Literal, Union -from typing_extensions import Annotated, TypedDict +from typing import Literal, Optional, Union +from typing_extensions import Annotated, NotRequired, TypedDict CompletionFinishReason = Union[ @@ -25,6 +32,8 @@ class CompletionChoiceTypedDict(TypedDict): index: float logprobs: Nullable[CompletionLogprobsTypedDict] finish_reason: Nullable[CompletionFinishReason] + native_finish_reason: NotRequired[str] + reasoning: NotRequired[Nullable[str]] class CompletionChoice(BaseModel): @@ -38,10 +47,14 @@ class CompletionChoice(BaseModel): Nullable[CompletionFinishReason], PlainValidator(validate_open_enum(False)) ] + native_finish_reason: Optional[str] = None + + reasoning: OptionalNullable[str] = UNSET + @model_serializer(mode="wrap") def serialize_model(self, handler): - optional_fields = [] - nullable_fields = ["logprobs", "finish_reason"] + optional_fields = ["native_finish_reason", "reasoning"] + nullable_fields = ["logprobs", "finish_reason", "reasoning"] null_default_fields = [] serialized = handler(self) diff --git a/src/openrouter/components/completioncreateparams.py b/src/openrouter/components/completioncreateparams.py index 3a81d2e..712feaa 100644 --- a/src/openrouter/components/completioncreateparams.py +++ b/src/openrouter/components/completioncreateparams.py @@ -16,9 +16,9 @@ UNSET, UNSET_SENTINEL, ) -from openrouter.utils import validate_const +from openrouter.utils import get_discriminator, validate_const import pydantic -from pydantic import model_serializer +from pydantic import Discriminator, Tag, model_serializer from pydantic.functional_validators import AfterValidator from typing import Dict, List, Literal, Optional, Union from typing_extensions import Annotated, NotRequired, TypeAliasType, TypedDict @@ -127,16 +127,16 @@ class CompletionCreateParamsResponseFormatText(BaseModel): ) -CompletionCreateParamsResponseFormatUnion = TypeAliasType( - "CompletionCreateParamsResponseFormatUnion", +CompletionCreateParamsResponseFormatUnion = Annotated[ Union[ - CompletionCreateParamsResponseFormatText, - CompletionCreateParamsResponseFormatJSONObject, - CompletionCreateParamsResponseFormatPython, - ResponseFormatJSONSchema, - ResponseFormatTextGrammar, + Annotated[CompletionCreateParamsResponseFormatText, Tag("text")], + Annotated[CompletionCreateParamsResponseFormatJSONObject, Tag("json_object")], + Annotated[ResponseFormatJSONSchema, Tag("json_schema")], + Annotated[ResponseFormatTextGrammar, Tag("grammar")], + Annotated[CompletionCreateParamsResponseFormatPython, Tag("python")], ], -) + Discriminator(lambda m: get_discriminator(m, "type", "type")), +] class CompletionCreateParamsTypedDict(TypedDict): diff --git a/src/openrouter/components/completionresponse.py b/src/openrouter/components/completionresponse.py index 12bde5f..dcdd529 100644 --- a/src/openrouter/components/completionresponse.py +++ b/src/openrouter/components/completionresponse.py @@ -17,6 +17,7 @@ class CompletionResponseTypedDict(TypedDict): model: str choices: List[CompletionChoiceTypedDict] object: Literal["text_completion"] + provider: NotRequired[str] system_fingerprint: NotRequired[str] usage: NotRequired[CompletionUsageTypedDict] @@ -38,6 +39,8 @@ class CompletionResponse(BaseModel): pydantic.Field(alias="object"), ] = "text_completion" + provider: Optional[str] = None + system_fingerprint: Optional[str] = None usage: Optional[CompletionUsage] = None diff --git a/src/openrouter/components/message.py b/src/openrouter/components/message.py index 676b497..8d92cba 100644 --- a/src/openrouter/components/message.py +++ b/src/openrouter/components/message.py @@ -10,8 +10,9 @@ from .toolresponsemessage import ToolResponseMessage, ToolResponseMessageTypedDict from .usermessage import UserMessage, UserMessageTypedDict from openrouter.types import BaseModel -from openrouter.utils import validate_const +from openrouter.utils import get_discriminator, validate_const import pydantic +from pydantic import Discriminator, Tag from pydantic.functional_validators import AfterValidator from typing import List, Literal, Optional, Union from typing_extensions import Annotated, NotRequired, TypeAliasType, TypedDict @@ -56,13 +57,13 @@ class MessageDeveloper(BaseModel): ) -Message = TypeAliasType( - "Message", +Message = Annotated[ Union[ - SystemMessage, - UserMessage, - MessageDeveloper, - ToolResponseMessage, - AssistantMessage, + Annotated[SystemMessage, Tag("system")], + Annotated[UserMessage, Tag("user")], + Annotated[MessageDeveloper, Tag("developer")], + Annotated[AssistantMessage, Tag("assistant")], + Annotated[ToolResponseMessage, Tag("tool")], ], -) + Discriminator(lambda m: get_discriminator(m, "role", "role")), +] diff --git a/src/openrouter/components/openairesponsesannotation.py b/src/openrouter/components/openairesponsesannotation.py index 315df01..1303f98 100644 --- a/src/openrouter/components/openairesponsesannotation.py +++ b/src/openrouter/components/openairesponsesannotation.py @@ -4,8 +4,10 @@ from .filecitation import FileCitation, FileCitationTypedDict from .filepath import FilePath, FilePathTypedDict from .urlcitation import URLCitation, URLCitationTypedDict +from openrouter.utils import get_discriminator +from pydantic import Discriminator, Tag from typing import Union -from typing_extensions import TypeAliasType +from typing_extensions import Annotated, TypeAliasType OpenAIResponsesAnnotationTypedDict = TypeAliasType( @@ -14,6 +16,11 @@ ) -OpenAIResponsesAnnotation = TypeAliasType( - "OpenAIResponsesAnnotation", Union[FilePath, FileCitation, URLCitation] -) +OpenAIResponsesAnnotation = Annotated[ + Union[ + Annotated[FileCitation, Tag("file_citation")], + Annotated[URLCitation, Tag("url_citation")], + Annotated[FilePath, Tag("file_path")], + ], + Discriminator(lambda m: get_discriminator(m, "type", "type")), +] diff --git a/src/openrouter/components/openresponsesnonstreamingresponse.py b/src/openrouter/components/openresponsesnonstreamingresponse.py index 6051f4e..4836d91 100644 --- a/src/openrouter/components/openresponsesnonstreamingresponse.py +++ b/src/openrouter/components/openresponsesnonstreamingresponse.py @@ -48,8 +48,8 @@ UNSET, UNSET_SENTINEL, ) -from openrouter.utils import validate_open_enum -from pydantic import model_serializer +from openrouter.utils import get_discriminator, validate_open_enum +from pydantic import Discriminator, Tag, model_serializer from pydantic.functional_validators import PlainValidator from typing import Any, Dict, List, Literal, Optional, Union from typing_extensions import Annotated, NotRequired, TypeAliasType, TypedDict @@ -127,16 +127,19 @@ def serialize_model(self, handler): ) -OpenResponsesNonStreamingResponseToolUnion = TypeAliasType( - "OpenResponsesNonStreamingResponseToolUnion", +OpenResponsesNonStreamingResponseToolUnion = Annotated[ Union[ - OpenResponsesWebSearchPreviewTool, - OpenResponsesWebSearchPreview20250311Tool, - OpenResponsesWebSearchTool, - OpenResponsesWebSearch20250826Tool, - OpenResponsesNonStreamingResponseToolFunction, + Annotated[OpenResponsesNonStreamingResponseToolFunction, Tag("function")], + Annotated[OpenResponsesWebSearchPreviewTool, Tag("web_search_preview")], + Annotated[ + OpenResponsesWebSearchPreview20250311Tool, + Tag("web_search_preview_2025_03_11"), + ], + Annotated[OpenResponsesWebSearchTool, Tag("web_search")], + Annotated[OpenResponsesWebSearch20250826Tool, Tag("web_search_2025_08_26")], ], -) + Discriminator(lambda m: get_discriminator(m, "type", "type")), +] class OpenResponsesNonStreamingResponseTypedDict(TypedDict): diff --git a/src/openrouter/components/openresponsesrequest.py b/src/openrouter/components/openresponsesrequest.py index 265f2d5..78f00d3 100644 --- a/src/openrouter/components/openresponsesrequest.py +++ b/src/openrouter/components/openresponsesrequest.py @@ -44,9 +44,10 @@ UNSET_SENTINEL, UnrecognizedStr, ) -from openrouter.utils import validate_open_enum -from pydantic import model_serializer -from pydantic.functional_validators import PlainValidator +from openrouter.utils import get_discriminator, validate_const, validate_open_enum +import pydantic +from pydantic import Discriminator, Tag, model_serializer +from pydantic.functional_validators import AfterValidator, PlainValidator from typing import Any, Dict, List, Literal, Optional, Union from typing_extensions import Annotated, NotRequired, TypeAliasType, TypedDict @@ -120,28 +121,22 @@ def serialize_model(self, handler): ) -OpenResponsesRequestToolUnion = TypeAliasType( - "OpenResponsesRequestToolUnion", +OpenResponsesRequestToolUnion = Annotated[ Union[ - OpenResponsesWebSearchPreviewTool, - OpenResponsesWebSearchPreview20250311Tool, - OpenResponsesWebSearchTool, - OpenResponsesWebSearch20250826Tool, - OpenResponsesRequestToolFunction, + Annotated[OpenResponsesRequestToolFunction, Tag("function")], + Annotated[OpenResponsesWebSearchPreviewTool, Tag("web_search_preview")], + Annotated[ + OpenResponsesWebSearchPreview20250311Tool, + Tag("web_search_preview_2025_03_11"), + ], + Annotated[OpenResponsesWebSearchTool, Tag("web_search")], + Annotated[OpenResponsesWebSearch20250826Tool, Tag("web_search_2025_08_26")], ], -) + Discriminator(lambda m: get_discriminator(m, "type", "type")), +] -ServiceTier = Union[ - Literal[ - "auto", - "default", - "flex", - "priority", - "scale", - ], - UnrecognizedStr, -] +ServiceTier = Literal["auto",] Truncation = Union[ @@ -353,6 +348,17 @@ def serialize_model(self, handler): return m +IDResponseHealing = Literal["response-healing",] + + +class PluginResponseHealingTypedDict(TypedDict): + id: IDResponseHealing + + +class PluginResponseHealing(BaseModel): + id: IDResponseHealing + + IDFileParser = Literal["file-parser",] @@ -434,11 +440,24 @@ class PluginModeration(BaseModel): PluginTypedDict = TypeAliasType( "PluginTypedDict", - Union[PluginModerationTypedDict, PluginFileParserTypedDict, PluginWebTypedDict], + Union[ + PluginModerationTypedDict, + PluginResponseHealingTypedDict, + PluginFileParserTypedDict, + PluginWebTypedDict, + ], ) -Plugin = TypeAliasType("Plugin", Union[PluginModeration, PluginFileParser, PluginWeb]) +Plugin = Annotated[ + Union[ + Annotated[PluginModeration, Tag("moderation")], + Annotated[PluginWeb, Tag("web")], + Annotated[PluginFileParser, Tag("file-parser")], + Annotated[PluginResponseHealing, Tag("response-healing")], + ], + Discriminator(lambda m: get_discriminator(m, "id", "id")), +] class OpenResponsesRequestTypedDict(TypedDict): @@ -468,8 +487,8 @@ class OpenResponsesRequestTypedDict(TypedDict): include: NotRequired[Nullable[List[OpenAIResponsesIncludable]]] background: NotRequired[Nullable[bool]] safety_identifier: NotRequired[Nullable[str]] - store: NotRequired[Nullable[bool]] - service_tier: NotRequired[Nullable[ServiceTier]] + store: Literal[False] + service_tier: NotRequired[ServiceTier] truncation: NotRequired[Nullable[Truncation]] stream: NotRequired[bool] provider: NotRequired[Nullable[ProviderTypedDict]] @@ -533,11 +552,12 @@ class OpenResponsesRequest(BaseModel): safety_identifier: OptionalNullable[str] = UNSET - store: OptionalNullable[bool] = UNSET + STORE: Annotated[ + Annotated[Optional[Literal[False]], AfterValidator(validate_const(False))], + pydantic.Field(alias="store"), + ] = False - service_tier: Annotated[ - OptionalNullable[ServiceTier], PlainValidator(validate_open_enum(False)) - ] = UNSET + service_tier: Optional[ServiceTier] = "auto" truncation: Annotated[ OptionalNullable[Truncation], PlainValidator(validate_open_enum(False)) @@ -599,8 +619,6 @@ def serialize_model(self, handler): "include", "background", "safety_identifier", - "store", - "service_tier", "truncation", "provider", ] diff --git a/src/openrouter/components/openresponsesstreamevent.py b/src/openrouter/components/openresponsesstreamevent.py index 6e423d8..8b21548 100644 --- a/src/openrouter/components/openresponsesstreamevent.py +++ b/src/openrouter/components/openresponsesstreamevent.py @@ -59,8 +59,10 @@ from .responseoutputtext import ResponseOutputText, ResponseOutputTextTypedDict from .responsesoutputitem import ResponsesOutputItem, ResponsesOutputItemTypedDict from openrouter.types import BaseModel +from openrouter.utils import get_discriminator +from pydantic import Discriminator, Tag from typing import List, Literal, Union -from typing_extensions import TypeAliasType, TypedDict +from typing_extensions import Annotated, TypeAliasType, TypedDict TypeResponseReasoningSummaryPartDone = Literal["response.reasoning_summary_part.done",] @@ -328,10 +330,14 @@ class OpenResponsesStreamEventResponseOutputTextDelta(BaseModel): ) -Part2 = TypeAliasType( - "Part2", - Union[ReasoningTextContent, OpenAIResponsesRefusalContent, ResponseOutputText], -) +Part2 = Annotated[ + Union[ + Annotated[ResponseOutputText, Tag("output_text")], + Annotated[ReasoningTextContent, Tag("reasoning_text")], + Annotated[OpenAIResponsesRefusalContent, Tag("refusal")], + ], + Discriminator(lambda m: get_discriminator(m, "type", "type")), +] class OpenResponsesStreamEventResponseContentPartDoneTypedDict(TypedDict): @@ -374,10 +380,14 @@ class OpenResponsesStreamEventResponseContentPartDone(BaseModel): ) -Part1 = TypeAliasType( - "Part1", - Union[ReasoningTextContent, OpenAIResponsesRefusalContent, ResponseOutputText], -) +Part1 = Annotated[ + Union[ + Annotated[ResponseOutputText, Tag("output_text")], + Annotated[ReasoningTextContent, Tag("reasoning_text")], + Annotated[OpenAIResponsesRefusalContent, Tag("refusal")], + ], + Discriminator(lambda m: get_discriminator(m, "type", "type")), +] class OpenResponsesStreamEventResponseContentPartAddedTypedDict(TypedDict): @@ -609,36 +619,97 @@ class OpenResponsesStreamEventResponseCreated(BaseModel): r"""Union of all possible event types emitted during response streaming""" -OpenResponsesStreamEvent = TypeAliasType( - "OpenResponsesStreamEvent", +OpenResponsesStreamEvent = Annotated[ Union[ - OpenResponsesStreamEventResponseCreated, - OpenResponsesStreamEventResponseInProgress, - OpenResponsesStreamEventResponseCompleted, - OpenResponsesStreamEventResponseIncomplete, - OpenResponsesStreamEventResponseFailed, - OpenResponsesStreamEventResponseOutputItemAdded, - OpenResponsesStreamEventResponseOutputItemDone, - OpenResponsesImageGenCallCompleted, - OpenResponsesImageGenCallGenerating, - OpenResponsesImageGenCallInProgress, - OpenResponsesErrorEvent, - OpenResponsesStreamEventResponseFunctionCallArgumentsDelta, - OpenResponsesStreamEventResponseRefusalDelta, - OpenResponsesReasoningSummaryPartAddedEvent, - OpenResponsesStreamEventResponseContentPartAdded, - OpenResponsesImageGenCallPartialImage, - OpenResponsesStreamEventResponseFunctionCallArgumentsDone, - OpenResponsesReasoningDeltaEvent, - OpenResponsesReasoningDoneEvent, - OpenResponsesStreamEventResponseRefusalDone, - OpenResponsesStreamEventResponseReasoningSummaryPartDone, - OpenResponsesReasoningSummaryTextDeltaEvent, - OpenResponsesReasoningSummaryTextDoneEvent, - OpenResponsesStreamEventResponseContentPartDone, - OpenResponsesStreamEventResponseOutputTextDelta, - OpenResponsesStreamEventResponseOutputTextDone, - OpenResponsesStreamEventResponseOutputTextAnnotationAdded, + Annotated[OpenResponsesStreamEventResponseCreated, Tag("response.created")], + Annotated[ + OpenResponsesStreamEventResponseInProgress, Tag("response.in_progress") + ], + Annotated[OpenResponsesStreamEventResponseCompleted, Tag("response.completed")], + Annotated[ + OpenResponsesStreamEventResponseIncomplete, Tag("response.incomplete") + ], + Annotated[OpenResponsesStreamEventResponseFailed, Tag("response.failed")], + Annotated[OpenResponsesErrorEvent, Tag("error")], + Annotated[ + OpenResponsesStreamEventResponseOutputItemAdded, + Tag("response.output_item.added"), + ], + Annotated[ + OpenResponsesStreamEventResponseOutputItemDone, + Tag("response.output_item.done"), + ], + Annotated[ + OpenResponsesStreamEventResponseContentPartAdded, + Tag("response.content_part.added"), + ], + Annotated[ + OpenResponsesStreamEventResponseContentPartDone, + Tag("response.content_part.done"), + ], + Annotated[ + OpenResponsesStreamEventResponseOutputTextDelta, + Tag("response.output_text.delta"), + ], + Annotated[ + OpenResponsesStreamEventResponseOutputTextDone, + Tag("response.output_text.done"), + ], + Annotated[ + OpenResponsesStreamEventResponseRefusalDelta, Tag("response.refusal.delta") + ], + Annotated[ + OpenResponsesStreamEventResponseRefusalDone, Tag("response.refusal.done") + ], + Annotated[ + OpenResponsesStreamEventResponseOutputTextAnnotationAdded, + Tag("response.output_text.annotation.added"), + ], + Annotated[ + OpenResponsesStreamEventResponseFunctionCallArgumentsDelta, + Tag("response.function_call_arguments.delta"), + ], + Annotated[ + OpenResponsesStreamEventResponseFunctionCallArgumentsDone, + Tag("response.function_call_arguments.done"), + ], + Annotated[ + OpenResponsesReasoningDeltaEvent, Tag("response.reasoning_text.delta") + ], + Annotated[OpenResponsesReasoningDoneEvent, Tag("response.reasoning_text.done")], + Annotated[ + OpenResponsesReasoningSummaryPartAddedEvent, + Tag("response.reasoning_summary_part.added"), + ], + Annotated[ + OpenResponsesStreamEventResponseReasoningSummaryPartDone, + Tag("response.reasoning_summary_part.done"), + ], + Annotated[ + OpenResponsesReasoningSummaryTextDeltaEvent, + Tag("response.reasoning_summary_text.delta"), + ], + Annotated[ + OpenResponsesReasoningSummaryTextDoneEvent, + Tag("response.reasoning_summary_text.done"), + ], + Annotated[ + OpenResponsesImageGenCallInProgress, + Tag("response.image_generation_call.in_progress"), + ], + Annotated[ + OpenResponsesImageGenCallGenerating, + Tag("response.image_generation_call.generating"), + ], + Annotated[ + OpenResponsesImageGenCallPartialImage, + Tag("response.image_generation_call.partial_image"), + ], + Annotated[ + OpenResponsesImageGenCallCompleted, + Tag("response.image_generation_call.completed"), + ], ], -) + Discriminator(lambda m: get_discriminator(m, "type", "type")), +] r"""Union of all possible event types emitted during response streaming""" diff --git a/src/openrouter/components/outputmessage.py b/src/openrouter/components/outputmessage.py index a956778..1304077 100644 --- a/src/openrouter/components/outputmessage.py +++ b/src/openrouter/components/outputmessage.py @@ -7,8 +7,10 @@ ) from .responseoutputtext import ResponseOutputText, ResponseOutputTextTypedDict from openrouter.types import BaseModel +from openrouter.utils import get_discriminator +from pydantic import Discriminator, Tag from typing import List, Literal, Optional, Union -from typing_extensions import NotRequired, TypeAliasType, TypedDict +from typing_extensions import Annotated, NotRequired, TypeAliasType, TypedDict OutputMessageRole = Literal["assistant",] @@ -52,9 +54,13 @@ ) -OutputMessageContent = TypeAliasType( - "OutputMessageContent", Union[OpenAIResponsesRefusalContent, ResponseOutputText] -) +OutputMessageContent = Annotated[ + Union[ + Annotated[ResponseOutputText, Tag("output_text")], + Annotated[OpenAIResponsesRefusalContent, Tag("refusal")], + ], + Discriminator(lambda m: get_discriminator(m, "type", "type")), +] class OutputMessageTypedDict(TypedDict): diff --git a/src/openrouter/components/providername.py b/src/openrouter/components/providername.py index 21f6345..62e892c 100644 --- a/src/openrouter/components/providername.py +++ b/src/openrouter/components/providername.py @@ -11,12 +11,14 @@ "AionLabs", "Alibaba", "Amazon Bedrock", + "Amazon Nova", "Anthropic", - "Arcee", + "Arcee AI", "AtlasCloud", "Avian", "Azure", "BaseTen", + "BytePlus", "Black Forest Labs", "Cerebras", "Chutes", @@ -31,6 +33,7 @@ "Fireworks", "Friendli", "GMICloud", + "GoPomelo", "Google", "Google AI Studio", "Groq", @@ -61,6 +64,7 @@ "SambaNova", "SiliconFlow", "Stealth", + "StreamLake", "Switchpoint", "Targon", "Together", diff --git a/src/openrouter/components/responseformattextconfig.py b/src/openrouter/components/responseformattextconfig.py index 7478c80..a82e125 100644 --- a/src/openrouter/components/responseformattextconfig.py +++ b/src/openrouter/components/responseformattextconfig.py @@ -10,8 +10,10 @@ ResponsesFormatTextJSONSchemaConfig, ResponsesFormatTextJSONSchemaConfigTypedDict, ) +from openrouter.utils import get_discriminator +from pydantic import Discriminator, Tag from typing import Union -from typing_extensions import TypeAliasType +from typing_extensions import Annotated, TypeAliasType ResponseFormatTextConfigTypedDict = TypeAliasType( @@ -25,12 +27,12 @@ r"""Text response format configuration""" -ResponseFormatTextConfig = TypeAliasType( - "ResponseFormatTextConfig", +ResponseFormatTextConfig = Annotated[ Union[ - ResponsesFormatText, - ResponsesFormatJSONObject, - ResponsesFormatTextJSONSchemaConfig, + Annotated[ResponsesFormatText, Tag("text")], + Annotated[ResponsesFormatJSONObject, Tag("json_object")], + Annotated[ResponsesFormatTextJSONSchemaConfig, Tag("json_schema")], ], -) + Discriminator(lambda m: get_discriminator(m, "type", "type")), +] r"""Text response format configuration""" diff --git a/src/openrouter/components/responsesoutputitem.py b/src/openrouter/components/responsesoutputitem.py index 63c4023..28966bd 100644 --- a/src/openrouter/components/responsesoutputitem.py +++ b/src/openrouter/components/responsesoutputitem.py @@ -25,8 +25,10 @@ ResponsesWebSearchCallOutput, ResponsesWebSearchCallOutputTypedDict, ) +from openrouter.utils import get_discriminator +from pydantic import Discriminator, Tag from typing import Union -from typing_extensions import TypeAliasType +from typing_extensions import Annotated, TypeAliasType ResponsesOutputItemTypedDict = TypeAliasType( @@ -43,15 +45,15 @@ r"""An output item from the response""" -ResponsesOutputItem = TypeAliasType( - "ResponsesOutputItem", +ResponsesOutputItem = Annotated[ Union[ - ResponsesWebSearchCallOutput, - ResponsesOutputItemFileSearchCall, - ResponsesImageGenerationCall, - ResponsesOutputMessage, - ResponsesOutputItemReasoning, - ResponsesOutputItemFunctionCall, + Annotated[ResponsesOutputMessage, Tag("message")], + Annotated[ResponsesOutputItemReasoning, Tag("reasoning")], + Annotated[ResponsesOutputItemFunctionCall, Tag("function_call")], + Annotated[ResponsesWebSearchCallOutput, Tag("web_search_call")], + Annotated[ResponsesOutputItemFileSearchCall, Tag("file_search_call")], + Annotated[ResponsesImageGenerationCall, Tag("image_generation_call")], ], -) + Discriminator(lambda m: get_discriminator(m, "type", "type")), +] r"""An output item from the response""" diff --git a/src/openrouter/components/responsesoutputmessage.py b/src/openrouter/components/responsesoutputmessage.py index 74126d4..6717a26 100644 --- a/src/openrouter/components/responsesoutputmessage.py +++ b/src/openrouter/components/responsesoutputmessage.py @@ -7,8 +7,10 @@ ) from .responseoutputtext import ResponseOutputText, ResponseOutputTextTypedDict from openrouter.types import BaseModel +from openrouter.utils import get_discriminator +from pydantic import Discriminator, Tag from typing import List, Literal, Optional, Union -from typing_extensions import NotRequired, TypeAliasType, TypedDict +from typing_extensions import Annotated, NotRequired, TypeAliasType, TypedDict ResponsesOutputMessageRole = Literal["assistant",] @@ -52,10 +54,13 @@ ) -ResponsesOutputMessageContent = TypeAliasType( - "ResponsesOutputMessageContent", - Union[OpenAIResponsesRefusalContent, ResponseOutputText], -) +ResponsesOutputMessageContent = Annotated[ + Union[ + Annotated[ResponseOutputText, Tag("output_text")], + Annotated[OpenAIResponsesRefusalContent, Tag("refusal")], + ], + Discriminator(lambda m: get_discriminator(m, "type", "type")), +] class ResponsesOutputMessageTypedDict(TypedDict): diff --git a/src/openrouter/credits.py b/src/openrouter/credits.py index ee3cb92..241f22f 100644 --- a/src/openrouter/credits.py +++ b/src/openrouter/credits.py @@ -51,6 +51,7 @@ def get_credits( accept_header_value="application/json", http_headers=http_headers, security=self.sdk_configuration.security, + allow_empty_value=None, timeout_ms=timeout_ms, ) @@ -147,6 +148,7 @@ async def get_credits_async( accept_header_value="application/json", http_headers=http_headers, security=self.sdk_configuration.security, + allow_empty_value=None, timeout_ms=timeout_ms, ) @@ -266,6 +268,7 @@ def create_coinbase_charge( get_serialized_body=lambda: utils.serialize_request_body( request, False, False, "json", components.CreateChargeRequest ), + allow_empty_value=None, timeout_ms=timeout_ms, ) @@ -390,6 +393,7 @@ async def create_coinbase_charge_async( get_serialized_body=lambda: utils.serialize_request_body( request, False, False, "json", components.CreateChargeRequest ), + allow_empty_value=None, timeout_ms=timeout_ms, ) diff --git a/src/openrouter/embeddings.py b/src/openrouter/embeddings.py index 0dec307..eaa52b0 100644 --- a/src/openrouter/embeddings.py +++ b/src/openrouter/embeddings.py @@ -1,7 +1,6 @@ """Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT.""" from .basesdk import BaseSDK -from enum import Enum from openrouter import components, errors, operations, utils from openrouter._hooks import HookContext from openrouter.types import OptionalNullable, UNSET @@ -10,11 +9,6 @@ from typing import Any, Mapping, Optional, Union -class GenerateAcceptEnum(str, Enum): - APPLICATION_JSON = "application/json" - TEXT_EVENT_STREAM = "text/event-stream" - - class Embeddings(BaseSDK): r"""Text embedding endpoints""" @@ -36,7 +30,6 @@ def generate( retries: OptionalNullable[utils.RetryConfig] = UNSET, server_url: Optional[str] = None, timeout_ms: Optional[int] = None, - accept_header_override: Optional[GenerateAcceptEnum] = None, http_headers: Optional[Mapping[str, str]] = None, ) -> operations.CreateEmbeddingsResponse: r"""Submit an embedding request @@ -53,7 +46,6 @@ def generate( :param retries: Override the default retry configuration for this method :param server_url: Override the default server URL for this method :param timeout_ms: Override the default request timeout configuration for this method in milliseconds - :param accept_header_override: Override the default accept header for this method :param http_headers: Additional headers to set or replace on requests. """ base_url = None @@ -88,14 +80,13 @@ def generate( request_has_path_params=False, request_has_query_params=True, user_agent_header="user-agent", - accept_header_value=accept_header_override.value - if accept_header_override is not None - else "application/json;q=1, text/event-stream;q=0", + accept_header_value="application/json", http_headers=http_headers, security=self.sdk_configuration.security, get_serialized_body=lambda: utils.serialize_request_body( request, False, False, "json", operations.CreateEmbeddingsRequest ), + allow_empty_value=None, timeout_ms=timeout_ms, ) @@ -138,10 +129,8 @@ def generate( response_data: Any = None if utils.match_response(http_res, "200", "application/json"): return unmarshal_json_response( - operations.CreateEmbeddingsResponseBody, http_res + operations.CreateEmbeddingsResponse, http_res ) - if utils.match_response(http_res, "200", "text/event-stream"): - return http_res.text if utils.match_response(http_res, "400", "application/json"): response_data = unmarshal_json_response( errors.BadRequestResponseErrorData, http_res @@ -223,7 +212,6 @@ async def generate_async( retries: OptionalNullable[utils.RetryConfig] = UNSET, server_url: Optional[str] = None, timeout_ms: Optional[int] = None, - accept_header_override: Optional[GenerateAcceptEnum] = None, http_headers: Optional[Mapping[str, str]] = None, ) -> operations.CreateEmbeddingsResponse: r"""Submit an embedding request @@ -240,7 +228,6 @@ async def generate_async( :param retries: Override the default retry configuration for this method :param server_url: Override the default server URL for this method :param timeout_ms: Override the default request timeout configuration for this method in milliseconds - :param accept_header_override: Override the default accept header for this method :param http_headers: Additional headers to set or replace on requests. """ base_url = None @@ -275,14 +262,13 @@ async def generate_async( request_has_path_params=False, request_has_query_params=True, user_agent_header="user-agent", - accept_header_value=accept_header_override.value - if accept_header_override is not None - else "application/json;q=1, text/event-stream;q=0", + accept_header_value="application/json", http_headers=http_headers, security=self.sdk_configuration.security, get_serialized_body=lambda: utils.serialize_request_body( request, False, False, "json", operations.CreateEmbeddingsRequest ), + allow_empty_value=None, timeout_ms=timeout_ms, ) @@ -325,10 +311,8 @@ async def generate_async( response_data: Any = None if utils.match_response(http_res, "200", "application/json"): return unmarshal_json_response( - operations.CreateEmbeddingsResponseBody, http_res + operations.CreateEmbeddingsResponse, http_res ) - if utils.match_response(http_res, "200", "text/event-stream"): - return http_res.text if utils.match_response(http_res, "400", "application/json"): response_data = unmarshal_json_response( errors.BadRequestResponseErrorData, http_res @@ -431,6 +415,7 @@ def list_models( accept_header_value="application/json", http_headers=http_headers, security=self.sdk_configuration.security, + allow_empty_value=None, timeout_ms=timeout_ms, ) @@ -522,6 +507,7 @@ async def list_models_async( accept_header_value="application/json", http_headers=http_headers, security=self.sdk_configuration.security, + allow_empty_value=None, timeout_ms=timeout_ms, ) diff --git a/src/openrouter/endpoints.py b/src/openrouter/endpoints.py index c6d8824..23f6842 100644 --- a/src/openrouter/endpoints.py +++ b/src/openrouter/endpoints.py @@ -59,6 +59,7 @@ def list( accept_header_value="application/json", http_headers=http_headers, security=self.sdk_configuration.security, + allow_empty_value=None, timeout_ms=timeout_ms, ) @@ -158,6 +159,7 @@ async def list_async( accept_header_value="application/json", http_headers=http_headers, security=self.sdk_configuration.security, + allow_empty_value=None, timeout_ms=timeout_ms, ) @@ -247,6 +249,7 @@ def list_zdr_endpoints( accept_header_value="application/json", http_headers=http_headers, security=self.sdk_configuration.security, + allow_empty_value=None, timeout_ms=timeout_ms, ) @@ -333,6 +336,7 @@ async def list_zdr_endpoints_async( accept_header_value="application/json", http_headers=http_headers, security=self.sdk_configuration.security, + allow_empty_value=None, timeout_ms=timeout_ms, ) diff --git a/src/openrouter/generations.py b/src/openrouter/generations.py index 2f75494..fd085ef 100644 --- a/src/openrouter/generations.py +++ b/src/openrouter/generations.py @@ -56,6 +56,7 @@ def get_generation( accept_header_value="application/json", http_headers=http_headers, security=self.sdk_configuration.security, + allow_empty_value=None, timeout_ms=timeout_ms, ) @@ -193,6 +194,7 @@ async def get_generation_async( accept_header_value="application/json", http_headers=http_headers, security=self.sdk_configuration.security, + allow_empty_value=None, timeout_ms=timeout_ms, ) diff --git a/src/openrouter/models/__init__.py b/src/openrouter/models/__init__.py new file mode 100644 index 0000000..726fc5e --- /dev/null +++ b/src/openrouter/models/__init__.py @@ -0,0 +1,3 @@ +"""Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT.""" + +# package diff --git a/src/openrouter/models/internal/__init__.py b/src/openrouter/models/internal/__init__.py new file mode 100644 index 0000000..e7070a1 --- /dev/null +++ b/src/openrouter/models/internal/__init__.py @@ -0,0 +1,54 @@ +"""Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT.""" + +from typing import TYPE_CHECKING +from importlib import import_module +import builtins +import sys + +if TYPE_CHECKING: + from .globals import Globals, GlobalsTypedDict + +__all__ = ["Globals", "GlobalsTypedDict"] + +_dynamic_imports: dict[str, str] = { + "Globals": ".globals", + "GlobalsTypedDict": ".globals", +} + + +def dynamic_import(modname, retries=3): + for attempt in range(retries): + try: + return import_module(modname, __package__) + except KeyError: + # Clear any half-initialized module and retry + sys.modules.pop(modname, None) + if attempt == retries - 1: + break + raise KeyError(f"Failed to import module '{modname}' after {retries} attempts") + + +def __getattr__(attr_name: str) -> object: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError( + f"No {attr_name} found in _dynamic_imports for module name -> {__name__} " + ) + + try: + module = dynamic_import(module_name) + result = getattr(module, attr_name) + return result + except ImportError as e: + raise ImportError( + f"Failed to import {attr_name} from {module_name}: {e}" + ) from e + except AttributeError as e: + raise AttributeError( + f"Failed to get {attr_name} from {module_name}: {e}" + ) from e + + +def __dir__(): + lazy_attrs = builtins.list(_dynamic_imports.keys()) + return builtins.sorted(lazy_attrs) diff --git a/src/openrouter/models/internal/globals.py b/src/openrouter/models/internal/globals.py new file mode 100644 index 0000000..248f13e --- /dev/null +++ b/src/openrouter/models/internal/globals.py @@ -0,0 +1,41 @@ +"""Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT.""" + +from __future__ import annotations +from openrouter.types import BaseModel +from openrouter.utils import FieldMetadata, HeaderMetadata +import pydantic +from typing import Optional +from typing_extensions import Annotated, NotRequired, TypedDict + + +class GlobalsTypedDict(TypedDict): + http_referer: NotRequired[str] + r"""The app identifier should be your app's URL and is used as the primary identifier for rankings. + This is used to track API usage per application. + + """ + x_title: NotRequired[str] + r"""The app display name allows you to customize how your app appears in OpenRouter's dashboard. + + """ + + +class Globals(BaseModel): + http_referer: Annotated[ + Optional[str], + pydantic.Field(alias="HTTP-Referer"), + FieldMetadata(header=HeaderMetadata(style="simple", explode=False)), + ] = None + r"""The app identifier should be your app's URL and is used as the primary identifier for rankings. + This is used to track API usage per application. + + """ + + x_title: Annotated[ + Optional[str], + pydantic.Field(alias="X-Title"), + FieldMetadata(header=HeaderMetadata(style="simple", explode=False)), + ] = None + r"""The app display name allows you to customize how your app appears in OpenRouter's dashboard. + + """ diff --git a/src/openrouter/models_.py b/src/openrouter/models_.py index 6ba885b..6b9e6e7 100644 --- a/src/openrouter/models_.py +++ b/src/openrouter/models_.py @@ -49,6 +49,7 @@ def count( accept_header_value="application/json", http_headers=http_headers, security=self.sdk_configuration.security, + allow_empty_value=None, timeout_ms=timeout_ms, ) @@ -133,6 +134,7 @@ async def count_async( accept_header_value="application/json", http_headers=http_headers, security=self.sdk_configuration.security, + allow_empty_value=None, timeout_ms=timeout_ms, ) @@ -227,6 +229,7 @@ def list( accept_header_value="application/json", http_headers=http_headers, security=self.sdk_configuration.security, + allow_empty_value=None, timeout_ms=timeout_ms, ) @@ -326,6 +329,7 @@ async def list_async( accept_header_value="application/json", http_headers=http_headers, security=self.sdk_configuration.security, + allow_empty_value=None, timeout_ms=timeout_ms, ) @@ -422,6 +426,7 @@ def list_for_user( security=utils.get_pydantic_model( security, operations.ListModelsUserSecurity ), + allow_empty_value=None, timeout_ms=timeout_ms, ) @@ -516,6 +521,7 @@ async def list_for_user_async( security=utils.get_pydantic_model( security, operations.ListModelsUserSecurity ), + allow_empty_value=None, timeout_ms=timeout_ms, ) diff --git a/src/openrouter/oauth.py b/src/openrouter/oauth.py index 50d2395..3fc3316 100644 --- a/src/openrouter/oauth.py +++ b/src/openrouter/oauth.py @@ -74,6 +74,7 @@ def exchange_auth_code_for_api_key( "json", operations.ExchangeAuthCodeForAPIKeyRequest, ), + allow_empty_value=None, timeout_ms=timeout_ms, ) @@ -194,6 +195,7 @@ async def exchange_auth_code_for_api_key_async( "json", operations.ExchangeAuthCodeForAPIKeyRequest, ), + allow_empty_value=None, timeout_ms=timeout_ms, ) @@ -316,6 +318,7 @@ def create_auth_code( get_serialized_body=lambda: utils.serialize_request_body( request, False, False, "json", operations.CreateAuthKeysCodeRequest ), + allow_empty_value=None, timeout_ms=timeout_ms, ) @@ -438,6 +441,7 @@ async def create_auth_code_async( get_serialized_body=lambda: utils.serialize_request_body( request, False, False, "json", operations.CreateAuthKeysCodeRequest ), + allow_empty_value=None, timeout_ms=timeout_ms, ) diff --git a/src/openrouter/operations/__init__.py b/src/openrouter/operations/__init__.py index 3ba8b45..82d7f51 100644 --- a/src/openrouter/operations/__init__.py +++ b/src/openrouter/operations/__init__.py @@ -45,8 +45,6 @@ CreateEmbeddingsRequest, CreateEmbeddingsRequestTypedDict, CreateEmbeddingsResponse, - CreateEmbeddingsResponseBody, - CreateEmbeddingsResponseBodyTypedDict, CreateEmbeddingsResponseTypedDict, Embedding, EmbeddingTypedDict, @@ -100,7 +98,12 @@ ExchangeAuthCodeForAPIKeyResponse, ExchangeAuthCodeForAPIKeyResponseTypedDict, ) - from .getcredits import GetCreditsResponse, GetCreditsResponseTypedDict + from .getcredits import ( + GetCreditsData, + GetCreditsDataTypedDict, + GetCreditsResponse, + GetCreditsResponseTypedDict, + ) from .getcurrentkey import ( GetCurrentKeyData, GetCurrentKeyDataTypedDict, @@ -216,8 +219,6 @@ "CreateEmbeddingsRequest", "CreateEmbeddingsRequestTypedDict", "CreateEmbeddingsResponse", - "CreateEmbeddingsResponseBody", - "CreateEmbeddingsResponseBodyTypedDict", "CreateEmbeddingsResponseTypedDict", "CreateKeysData", "CreateKeysDataTypedDict", @@ -242,6 +243,8 @@ "ExchangeAuthCodeForAPIKeyRequestTypedDict", "ExchangeAuthCodeForAPIKeyResponse", "ExchangeAuthCodeForAPIKeyResponseTypedDict", + "GetCreditsData", + "GetCreditsDataTypedDict", "GetCreditsResponse", "GetCreditsResponseTypedDict", "GetCurrentKeyData", @@ -370,8 +373,6 @@ "CreateEmbeddingsRequest": ".createembeddings", "CreateEmbeddingsRequestTypedDict": ".createembeddings", "CreateEmbeddingsResponse": ".createembeddings", - "CreateEmbeddingsResponseBody": ".createembeddings", - "CreateEmbeddingsResponseBodyTypedDict": ".createembeddings", "CreateEmbeddingsResponseTypedDict": ".createembeddings", "Embedding": ".createembeddings", "EmbeddingTypedDict": ".createembeddings", @@ -416,6 +417,8 @@ "ExchangeAuthCodeForAPIKeyRequestTypedDict": ".exchangeauthcodeforapikey", "ExchangeAuthCodeForAPIKeyResponse": ".exchangeauthcodeforapikey", "ExchangeAuthCodeForAPIKeyResponseTypedDict": ".exchangeauthcodeforapikey", + "GetCreditsData": ".getcredits", + "GetCreditsDataTypedDict": ".getcredits", "GetCreditsResponse": ".getcredits", "GetCreditsResponseTypedDict": ".getcredits", "GetCurrentKeyData": ".getcurrentkey", diff --git a/src/openrouter/operations/createembeddings.py b/src/openrouter/operations/createembeddings.py index ddd90c3..c200370 100644 --- a/src/openrouter/operations/createembeddings.py +++ b/src/openrouter/operations/createembeddings.py @@ -15,8 +15,8 @@ UNSET_SENTINEL, UnrecognizedStr, ) -from openrouter.utils import validate_open_enum -from pydantic import model_serializer +from openrouter.utils import get_discriminator, validate_open_enum +from pydantic import Discriminator, Tag, model_serializer from pydantic.functional_validators import PlainValidator from typing import Any, List, Literal, Optional, Union from typing_extensions import Annotated, NotRequired, TypeAliasType, TypedDict @@ -63,7 +63,13 @@ class ContentText(BaseModel): ) -Content = TypeAliasType("Content", Union[ContentText, ContentImageURL]) +Content = Annotated[ + Union[ + Annotated[ContentText, Tag("text")], + Annotated[ContentImageURL, Tag("image_url")], + ], + Discriminator(lambda m: get_discriminator(m, "type", "type")), +] class InputTypedDict(TypedDict): @@ -389,7 +395,7 @@ class Usage(BaseModel): cost: Optional[float] = None -class CreateEmbeddingsResponseBodyTypedDict(TypedDict): +class CreateEmbeddingsResponseTypedDict(TypedDict): r"""Embedding response""" object: Object @@ -399,7 +405,7 @@ class CreateEmbeddingsResponseBodyTypedDict(TypedDict): usage: NotRequired[UsageTypedDict] -class CreateEmbeddingsResponseBody(BaseModel): +class CreateEmbeddingsResponse(BaseModel): r"""Embedding response""" object: Object @@ -411,14 +417,3 @@ class CreateEmbeddingsResponseBody(BaseModel): id: Optional[str] = None usage: Optional[Usage] = None - - -CreateEmbeddingsResponseTypedDict = TypeAliasType( - "CreateEmbeddingsResponseTypedDict", - Union[CreateEmbeddingsResponseBodyTypedDict, str], -) - - -CreateEmbeddingsResponse = TypeAliasType( - "CreateEmbeddingsResponse", Union[CreateEmbeddingsResponseBody, str] -) diff --git a/src/openrouter/operations/getcredits.py b/src/openrouter/operations/getcredits.py index 9719016..8cab52d 100644 --- a/src/openrouter/operations/getcredits.py +++ b/src/openrouter/operations/getcredits.py @@ -5,9 +5,28 @@ from typing_extensions import TypedDict +class GetCreditsDataTypedDict(TypedDict): + total_credits: float + r"""Total credits purchased""" + total_usage: float + r"""Total credits used""" + + +class GetCreditsData(BaseModel): + total_credits: float + r"""Total credits purchased""" + + total_usage: float + r"""Total credits used""" + + class GetCreditsResponseTypedDict(TypedDict): r"""Total credits purchased and used""" + data: GetCreditsDataTypedDict + class GetCreditsResponse(BaseModel): r"""Total credits purchased and used""" + + data: GetCreditsData diff --git a/src/openrouter/operations/getparameters.py b/src/openrouter/operations/getparameters.py index 18da669..14349a8 100644 --- a/src/openrouter/operations/getparameters.py +++ b/src/openrouter/operations/getparameters.py @@ -38,12 +38,14 @@ class GetParametersSecurity(BaseModel): "AionLabs", "Alibaba", "Amazon Bedrock", + "Amazon Nova", "Anthropic", - "Arcee", + "Arcee AI", "AtlasCloud", "Avian", "Azure", "BaseTen", + "BytePlus", "Black Forest Labs", "Cerebras", "Chutes", @@ -58,6 +60,7 @@ class GetParametersSecurity(BaseModel): "Fireworks", "Friendli", "GMICloud", + "GoPomelo", "Google", "Google AI Studio", "Groq", @@ -88,6 +91,7 @@ class GetParametersSecurity(BaseModel): "SambaNova", "SiliconFlow", "Stealth", + "StreamLake", "Switchpoint", "Targon", "Together", diff --git a/src/openrouter/parameters.py b/src/openrouter/parameters.py index 5b4008a..5206f57 100644 --- a/src/openrouter/parameters.py +++ b/src/openrouter/parameters.py @@ -68,6 +68,7 @@ def get_parameters( security=utils.get_pydantic_model( security, operations.GetParametersSecurity ), + allow_empty_value=None, timeout_ms=timeout_ms, ) @@ -179,6 +180,7 @@ async def get_parameters_async( security=utils.get_pydantic_model( security, operations.GetParametersSecurity ), + allow_empty_value=None, timeout_ms=timeout_ms, ) diff --git a/src/openrouter/providers.py b/src/openrouter/providers.py index a5f8066..04c0598 100644 --- a/src/openrouter/providers.py +++ b/src/openrouter/providers.py @@ -49,6 +49,7 @@ def list( accept_header_value="application/json", http_headers=http_headers, security=self.sdk_configuration.security, + allow_empty_value=None, timeout_ms=timeout_ms, ) @@ -133,6 +134,7 @@ async def list_async( accept_header_value="application/json", http_headers=http_headers, security=self.sdk_configuration.security, + allow_empty_value=None, timeout_ms=timeout_ms, ) diff --git a/src/openrouter/responses.py b/src/openrouter/responses.py index 277e769..fba3823 100644 --- a/src/openrouter/responses.py +++ b/src/openrouter/responses.py @@ -69,8 +69,7 @@ def send( include: OptionalNullable[List[components.OpenAIResponsesIncludable]] = UNSET, background: OptionalNullable[bool] = UNSET, safety_identifier: OptionalNullable[str] = UNSET, - store: OptionalNullable[bool] = UNSET, - service_tier: OptionalNullable[components.ServiceTier] = UNSET, + service_tier: Optional[components.ServiceTier] = "auto", truncation: OptionalNullable[components.Truncation] = UNSET, stream: Union[Literal[False], None] = None, provider: OptionalNullable[ @@ -109,7 +108,6 @@ def send( :param include: :param background: :param safety_identifier: - :param store: :param service_tier: :param truncation: :param stream: @@ -174,8 +172,7 @@ def send( include: OptionalNullable[List[components.OpenAIResponsesIncludable]] = UNSET, background: OptionalNullable[bool] = UNSET, safety_identifier: OptionalNullable[str] = UNSET, - store: OptionalNullable[bool] = UNSET, - service_tier: OptionalNullable[components.ServiceTier] = UNSET, + service_tier: Optional[components.ServiceTier] = "auto", truncation: OptionalNullable[components.Truncation] = UNSET, stream: Literal[True], provider: OptionalNullable[ @@ -214,7 +211,6 @@ def send( :param include: :param background: :param safety_identifier: - :param store: :param service_tier: :param truncation: :param stream: @@ -278,8 +274,7 @@ def send( include: OptionalNullable[List[components.OpenAIResponsesIncludable]] = UNSET, background: OptionalNullable[bool] = UNSET, safety_identifier: OptionalNullable[str] = UNSET, - store: OptionalNullable[bool] = UNSET, - service_tier: OptionalNullable[components.ServiceTier] = UNSET, + service_tier: Optional[components.ServiceTier] = "auto", truncation: OptionalNullable[components.Truncation] = UNSET, stream: Optional[bool] = False, provider: OptionalNullable[ @@ -318,7 +313,6 @@ def send( :param include: :param background: :param safety_identifier: - :param store: :param service_tier: :param truncation: :param stream: @@ -374,7 +368,6 @@ def send( include=include, background=background, safety_identifier=safety_identifier, - store=store, service_tier=service_tier, truncation=truncation, stream=stream, @@ -403,6 +396,7 @@ def send( get_serialized_body=lambda: utils.serialize_request_body( request, False, False, "json", components.OpenResponsesRequest ), + allow_empty_value=None, timeout_ms=timeout_ms, ) @@ -626,8 +620,7 @@ async def send_async( include: OptionalNullable[List[components.OpenAIResponsesIncludable]] = UNSET, background: OptionalNullable[bool] = UNSET, safety_identifier: OptionalNullable[str] = UNSET, - store: OptionalNullable[bool] = UNSET, - service_tier: OptionalNullable[components.ServiceTier] = UNSET, + service_tier: Optional[components.ServiceTier] = "auto", truncation: OptionalNullable[components.Truncation] = UNSET, stream: Union[Literal[False], None] = None, provider: OptionalNullable[ @@ -666,7 +659,6 @@ async def send_async( :param include: :param background: :param safety_identifier: - :param store: :param service_tier: :param truncation: :param stream: @@ -731,8 +723,7 @@ async def send_async( include: OptionalNullable[List[components.OpenAIResponsesIncludable]] = UNSET, background: OptionalNullable[bool] = UNSET, safety_identifier: OptionalNullable[str] = UNSET, - store: OptionalNullable[bool] = UNSET, - service_tier: OptionalNullable[components.ServiceTier] = UNSET, + service_tier: Optional[components.ServiceTier] = "auto", truncation: OptionalNullable[components.Truncation] = UNSET, stream: Literal[True], provider: OptionalNullable[ @@ -771,7 +762,6 @@ async def send_async( :param include: :param background: :param safety_identifier: - :param store: :param service_tier: :param truncation: :param stream: @@ -835,8 +825,7 @@ async def send_async( include: OptionalNullable[List[components.OpenAIResponsesIncludable]] = UNSET, background: OptionalNullable[bool] = UNSET, safety_identifier: OptionalNullable[str] = UNSET, - store: OptionalNullable[bool] = UNSET, - service_tier: OptionalNullable[components.ServiceTier] = UNSET, + service_tier: Optional[components.ServiceTier] = "auto", truncation: OptionalNullable[components.Truncation] = UNSET, stream: Optional[bool] = False, provider: OptionalNullable[ @@ -875,7 +864,6 @@ async def send_async( :param include: :param background: :param safety_identifier: - :param store: :param service_tier: :param truncation: :param stream: @@ -931,7 +919,6 @@ async def send_async( include=include, background=background, safety_identifier=safety_identifier, - store=store, service_tier=service_tier, truncation=truncation, stream=stream, @@ -960,6 +947,7 @@ async def send_async( get_serialized_body=lambda: utils.serialize_request_body( request, False, False, "json", components.OpenResponsesRequest ), + allow_empty_value=None, timeout_ms=timeout_ms, ) diff --git a/src/openrouter/sdk.py b/src/openrouter/sdk.py index 0ad8870..a71e057 100644 --- a/src/openrouter/sdk.py +++ b/src/openrouter/sdk.py @@ -9,6 +9,7 @@ import importlib from openrouter import components, utils from openrouter._hooks import SDKHooks +from openrouter.models import internal from openrouter.types import OptionalNullable, UNSET import sys from typing import Any, Callable, Dict, Optional, TYPE_CHECKING, Union, cast @@ -77,6 +78,8 @@ class OpenRouter(BaseSDK): def __init__( self, api_key: Optional[Union[Optional[str], Callable[[], Optional[str]]]] = None, + http_referer: Optional[str] = None, + x_title: Optional[str] = None, server: Optional[str] = None, server_url: Optional[str] = None, url_params: Optional[Dict[str, str]] = None, @@ -89,6 +92,8 @@ def __init__( r"""Instantiates the SDK configuring it with the provided parameters. :param api_key: The api_key required for authentication + :param http_referer: Configures the http_referer parameter for all supported operations + :param x_title: Configures the x_title parameter for all supported operations :param server: The server by name to use for all methods :param server_url: The server URL to use for all methods :param url_params: Parameters to optionally template the server URL with @@ -129,6 +134,13 @@ def __init__( if url_params is not None: server_url = utils.template_url(server_url, url_params) + _globals = internal.Globals( + http_referer=utils.get_global_from_env( + http_referer, "OPENROUTER_HTTP_REFERER", str + ), + x_title=utils.get_global_from_env(x_title, "OPENROUTER_X_TITLE", str), + ) + BaseSDK.__init__( self, SDKConfiguration( @@ -136,6 +148,7 @@ def __init__( client_supplied=client_supplied, async_client=async_client, async_client_supplied=async_client_supplied, + globals=_globals, security=security, server_url=server_url, server=server, diff --git a/src/openrouter/sdkconfiguration.py b/src/openrouter/sdkconfiguration.py index 4f12a18..2643316 100644 --- a/src/openrouter/sdkconfiguration.py +++ b/src/openrouter/sdkconfiguration.py @@ -10,6 +10,7 @@ from .utils import Logger, RetryConfig, remove_suffix from dataclasses import dataclass from openrouter import components +from openrouter.models import internal from openrouter.types import OptionalNullable, UNSET from pydantic import Field from typing import Callable, Dict, Optional, Tuple, Union @@ -30,6 +31,7 @@ class SDKConfiguration: async_client: Union[AsyncHttpClient, None] async_client_supplied: bool debug_logger: Logger + globals: internal.Globals security: Optional[ Union[components.Security, Callable[[], components.Security]] ] = None diff --git a/src/openrouter/utils/forms.py b/src/openrouter/utils/forms.py index e873495..f961e76 100644 --- a/src/openrouter/utils/forms.py +++ b/src/openrouter/utils/forms.py @@ -142,16 +142,21 @@ def serialize_multipart_form( if field_metadata.file: if isinstance(val, List): # Handle array of files + array_field_name = f_name + "[]" for file_obj in val: if not _is_set(file_obj): continue - - file_name, content, content_type = _extract_file_properties(file_obj) + + file_name, content, content_type = _extract_file_properties( + file_obj + ) if content_type is not None: - files.append((f_name + "[]", (file_name, content, content_type))) + files.append( + (array_field_name, (file_name, content, content_type)) + ) else: - files.append((f_name + "[]", (file_name, content))) + files.append((array_field_name, (file_name, content))) else: # Handle single file file_name, content, content_type = _extract_file_properties(val) @@ -161,11 +166,16 @@ def serialize_multipart_form( else: files.append((f_name, (file_name, content))) elif field_metadata.json: - files.append((f_name, ( - None, - marshal_json(val, request_field_types[name]), - "application/json", - ))) + files.append( + ( + f_name, + ( + None, + marshal_json(val, request_field_types[name]), + "application/json", + ), + ) + ) else: if isinstance(val, List): values = [] @@ -175,7 +185,8 @@ def serialize_multipart_form( continue values.append(_val_to_string(value)) - form[f_name + "[]"] = values + array_field_name = f_name + "[]" + form[array_field_name] = values else: form[f_name] = _val_to_string(val) return media_type, form, files diff --git a/src/openrouter/utils/queryparams.py b/src/openrouter/utils/queryparams.py index 37a6e7f..c04e0db 100644 --- a/src/openrouter/utils/queryparams.py +++ b/src/openrouter/utils/queryparams.py @@ -27,12 +27,13 @@ def get_query_params( query_params: Any, gbls: Optional[Any] = None, + allow_empty_value: Optional[List[str]] = None, ) -> Dict[str, List[str]]: params: Dict[str, List[str]] = {} - globals_already_populated = _populate_query_params(query_params, gbls, params, []) + globals_already_populated = _populate_query_params(query_params, gbls, params, [], allow_empty_value) if _is_set(gbls): - _populate_query_params(gbls, None, params, globals_already_populated) + _populate_query_params(gbls, None, params, globals_already_populated, allow_empty_value) return params @@ -42,6 +43,7 @@ def _populate_query_params( gbls: Any, query_param_values: Dict[str, List[str]], skip_fields: List[str], + allow_empty_value: Optional[List[str]] = None, ) -> List[str]: globals_already_populated: List[str] = [] @@ -69,6 +71,16 @@ def _populate_query_params( globals_already_populated.append(name) f_name = field.alias if field.alias is not None else name + + allow_empty_set = set(allow_empty_value or []) + should_include_empty = f_name in allow_empty_set and ( + value is None or value == [] or value == "" + ) + + if should_include_empty: + query_param_values[f_name] = [""] + continue + serialization = metadata.serialization if serialization is not None: serialized_parms = _get_serialized_params( diff --git a/src/openrouter/utils/retries.py b/src/openrouter/utils/retries.py index 4d60867..88a91b1 100644 --- a/src/openrouter/utils/retries.py +++ b/src/openrouter/utils/retries.py @@ -3,7 +3,9 @@ import asyncio import random import time -from typing import List +from datetime import datetime +from email.utils import parsedate_to_datetime +from typing import List, Optional import httpx @@ -51,9 +53,11 @@ def __init__(self, config: RetryConfig, status_codes: List[str]): class TemporaryError(Exception): response: httpx.Response + retry_after: Optional[int] def __init__(self, response: httpx.Response): self.response = response + self.retry_after = _parse_retry_after_header(response) class PermanentError(Exception): @@ -63,6 +67,62 @@ def __init__(self, inner: Exception): self.inner = inner +def _parse_retry_after_header(response: httpx.Response) -> Optional[int]: + """Parse Retry-After header from response. + + Returns: + Retry interval in milliseconds, or None if header is missing or invalid. + """ + retry_after_header = response.headers.get("retry-after") + if not retry_after_header: + return None + + try: + seconds = float(retry_after_header) + return round(seconds * 1000) + except ValueError: + pass + + try: + retry_date = parsedate_to_datetime(retry_after_header) + delta = (retry_date - datetime.now(retry_date.tzinfo)).total_seconds() + return round(max(0, delta) * 1000) + except (ValueError, TypeError): + pass + + return None + + +def _get_sleep_interval( + exception: Exception, + initial_interval: int, + max_interval: int, + exponent: float, + retries: int, +) -> float: + """Get sleep interval for retry with exponential backoff. + + Args: + exception: The exception that triggered the retry. + initial_interval: Initial retry interval in milliseconds. + max_interval: Maximum retry interval in milliseconds. + exponent: Base for exponential backoff calculation. + retries: Current retry attempt count. + + Returns: + Sleep interval in seconds. + """ + if ( + isinstance(exception, TemporaryError) + and exception.retry_after is not None + and exception.retry_after > 0 + ): + return exception.retry_after / 1000 + + sleep = (initial_interval / 1000) * exponent**retries + random.uniform(0, 1) + return min(sleep, max_interval / 1000) + + def retry(func, retries: Retries): if retries.config.strategy == "backoff": @@ -183,8 +243,10 @@ def retry_with_backoff( return exception.response raise - sleep = (initial_interval / 1000) * exponent**retries + random.uniform(0, 1) - sleep = min(sleep, max_interval / 1000) + + sleep = _get_sleep_interval( + exception, initial_interval, max_interval, exponent, retries + ) time.sleep(sleep) retries += 1 @@ -211,7 +273,9 @@ async def retry_with_backoff_async( return exception.response raise - sleep = (initial_interval / 1000) * exponent**retries + random.uniform(0, 1) - sleep = min(sleep, max_interval / 1000) + + sleep = _get_sleep_interval( + exception, initial_interval, max_interval, exponent, retries + ) await asyncio.sleep(sleep) retries += 1 diff --git a/uv.lock b/uv.lock index d506269..7778462 100644 --- a/uv.lock +++ b/uv.lock @@ -211,7 +211,7 @@ wheels = [ [[package]] name = "openrouter" -version = "0.0.16" +version = "0.1.0" source = { editable = "." } dependencies = [ { name = "httpcore" },