From 15ce4685f33206626127e19ec00eda95e2d03c95 Mon Sep 17 00:00:00 2001 From: speakeasybot Date: Thu, 18 Jun 2026 01:00:24 +0000 Subject: [PATCH] ## Python SDK Changes: * `open_router.embeddings.list_models()`: `response.data.[].reasoning` **Added** * `open_router.models.get()`: `response.data.reasoning` **Added** * `open_router.models.list()`: `response.data.[].reasoning` **Added** * `open_router.models.list_for_user()`: `response.data.[].reasoning` **Added** --- .speakeasy/gen.lock | 55 ++++++---- .speakeasy/gen.yaml | 2 +- .speakeasy/out.openapi.yaml | 50 ++++++++- .speakeasy/workflow.lock | 12 +-- RELEASES.md | 12 ++- docs/components/model.md | 1 + pyproject.toml | 2 +- src/openrouter/_version.py | 4 +- src/openrouter/components/__init__.py | 7 ++ src/openrouter/components/model.py | 7 ++ src/openrouter/components/modelreasoning.py | 106 ++++++++++++++++++++ uv.lock | 2 +- 12 files changed, 228 insertions(+), 32 deletions(-) create mode 100644 src/openrouter/components/modelreasoning.py diff --git a/.speakeasy/gen.lock b/.speakeasy/gen.lock index 4249b4b..d69c794 100644 --- a/.speakeasy/gen.lock +++ b/.speakeasy/gen.lock @@ -1,19 +1,19 @@ lockVersion: 2.0.0 id: c48cf606-fb42-4a45-9c23-8f0555307828 management: - docChecksum: 8dd32184dc771115ba5e79b3c70e7f6f + docChecksum: cc6966b5800766370c195f5c42b10761 docVersion: 1.0.0 speakeasyVersion: 1.680.0 generationVersion: 2.788.4 - releaseVersion: 0.10.0 - configChecksum: 00a97c5bcdbddf7e591c0a1d7a45e98e + releaseVersion: 0.10.1 + configChecksum: b1687c127d87a1ac114b35dd9b492f3c repoURL: https://github.com/OpenRouterTeam/python-sdk.git installationURL: https://github.com/OpenRouterTeam/python-sdk.git published: true persistentEdits: - generation_id: b6cb6701-b1ae-453f-beb8-2064f09433f4 - pristine_commit_hash: ed0a00c99b7b3b76ed1de023bc8174696435c2cd - pristine_tree_hash: a5b3d8dc9253bb236d709d9f365f5766595d820b + generation_id: 8123354f-2376-451c-8dbd-6e7ed6d6980c + pristine_commit_hash: 48c154f879472adb6f282e81083593c020808530 + pristine_tree_hash: 3e5484ebb7e5c6660a037fb427b4aa9edd240571 features: python: acceptHeaders: 3.0.0 @@ -1448,6 +1448,10 @@ trackedFiles: id: 8d84fb41685d last_write_checksum: sha1:fcfbe6740c505b263b90f7de5281d4f506922b05 pristine_git_object: 870296bd3335da46ecdf4ec1722a3d47037a09c9 + docs/components/defaulteffort.md: + id: 08dabde07c1d + last_write_checksum: sha1:96f06eb8b68e44984b691338b99e746fef1253ac + pristine_git_object: f3f07f26e47692dd4e6a728e45f32cfe5795082e docs/components/defaultparameters.md: id: a84477afcaec last_write_checksum: sha1:a20e8b576e341dbcdb91884b8fc83cf4316ab106 @@ -2522,8 +2526,8 @@ trackedFiles: pristine_git_object: d5bbdc95f658be46eebf13f0ba8b9ae3716f72e2 docs/components/model.md: id: 673ad57400cd - last_write_checksum: sha1:517cbce221606bacb2cab7b97e53d316d09a5f17 - pristine_git_object: 8ce3d9d7c9ddc7c270da3a39df079148aa7ff207 + last_write_checksum: sha1:ea20bf9ca8f49a181142f516f1b36758cf5ceac6 + pristine_git_object: edb000fc276b3eec0dbe0bb1e82c4f5d7a72e8c9 docs/components/modelarchitecture.md: id: 79816051e6e8 last_write_checksum: sha1:158b04c79b7a32a2d95e3d3fda8fa6da0ab91cd9 @@ -2548,10 +2552,14 @@ trackedFiles: id: 74607e6a173d last_write_checksum: sha1:445ec227b130763e2509d1a181dd5e3e6d71420a pristine_git_object: 317190772f743d2aba63c4dad770827b46688f6a + docs/components/modelreasoning.md: + id: 47976b46e5b9 + last_write_checksum: sha1:07deec55fe155257ef07086b6dc2fbc7e012e031 + pristine_git_object: 306c0aeb39047768e6c79975996240584a97525b docs/components/modelresponse.md: id: 9d48c3f335b1 - last_write_checksum: sha1:5d8890c41a9f448341037e9915dd6e93df9140cb - pristine_git_object: 2a7433dc87413943d14220ffdfda8b25e59bc9c0 + last_write_checksum: sha1:442bb6c4d21debbceb5a48a9563b07704983123f + pristine_git_object: 2ad6e9dc16fa06d03318e98bc540c7aca0d497ed docs/components/modelscountresponse.md: id: abdad0c1a02a last_write_checksum: sha1:50ae205604eddcbdced268b8cd3e1ac06f0dd7c0 @@ -6042,8 +6050,8 @@ trackedFiles: pristine_git_object: 3e38f1a929f7d6b1d6de74604aa87e3d8f010544 pyproject.toml: id: 5d07e7d72637 - last_write_checksum: sha1:ccf5925a473a1eb527cec02639f862b79b601251 - pristine_git_object: 396bd51f9c1428f03560f3e7988edc99edc698e8 + last_write_checksum: sha1:8223abd8e7463dfa2b2c1097670bb28b2ec73e68 + pristine_git_object: b98c0280ffbb42638747b1064375fa36f467223c scripts/prepare_readme.py: id: e0c5957a6035 last_write_checksum: sha1:77f44b60b98bc126557ec27391f91dfba764bb54 @@ -6070,8 +6078,8 @@ trackedFiles: pristine_git_object: 07ace03229fd3267623c8f48665d2c3a67c3565d src/openrouter/_version.py: id: d8d15ad6c586 - last_write_checksum: sha1:017dc1ec43588b31eee23532d74d4421993dd874 - pristine_git_object: be619e3be1aa8d27deef5752ecc9ff09ccd4cd9f + last_write_checksum: sha1:856bdf17b7de36f6e1abd0b56f55bbc6093e4c68 + pristine_git_object: ade61995259031f3789ff71f0cfd1d6ae4b39395 src/openrouter/analytics.py: id: cb406b5aaabb last_write_checksum: sha1:6d47d1ab3da9cb4ac9f0a2bfe442f1a82a614f35 @@ -6102,8 +6110,8 @@ trackedFiles: pristine_git_object: a1894915bd32aee65f019885634eb7908970a046 src/openrouter/components/__init__.py: id: 81754e97b3f4 - last_write_checksum: sha1:56e0c315ac8fe611bf70b465a91341acbbe34534 - pristine_git_object: 2c07302c12f51cdf053e306906ac30862e925246 + last_write_checksum: sha1:a136f824b02becfa0584a28474bb5f308a6861dc + pristine_git_object: 9df66e9e82ebd373652cc654f23b104ef315f3c5 src/openrouter/components/aabenchmarkentry.py: id: e2e0f0b48c82 last_write_checksum: sha1:1b6b66672fb80dccdc39843a98bdef6d7d804364 @@ -7178,8 +7186,8 @@ trackedFiles: pristine_git_object: 1257b38d2001f34078cd1b052c20264e81a95e57 src/openrouter/components/model.py: id: cef3adaa772b - last_write_checksum: sha1:53eb4a044a7bf546225416bb08597df46a3b60a2 - pristine_git_object: 637b3fbe54ddcc454750d8de702045ee690c3333 + last_write_checksum: sha1:53f544fe39ae08cde33f99c5d0bb4b6cc1555301 + pristine_git_object: c0345109fb1717ea27010605984c27e1137426fd src/openrouter/components/modelarchitecture.py: id: b37ac27e512b last_write_checksum: sha1:e1dd41aa140b2ca2c3bbf15abf74e034d42b0166 @@ -7196,6 +7204,10 @@ trackedFiles: id: b8759a1a4787 last_write_checksum: sha1:69b2c991666848f4790e5136c67553733cc68a54 pristine_git_object: c197cb2c527296adff2c3cfd9e49bcbc48087ce7 + src/openrouter/components/modelreasoning.py: + id: 58bb3f2d75e0 + last_write_checksum: sha1:3da126ae596520b6d4f0edcc7fb8e4f786f686c7 + pristine_git_object: b71947b65e2054b23ff2094a270763720e740d96 src/openrouter/components/modelresponse.py: id: 023136e6dedf last_write_checksum: sha1:af84587984cbe6341f59f176b08df43096ac48c6 @@ -10043,4 +10055,9 @@ examples: "500": application/json: {"error": {"code": 500, "message": "Internal Server Error"}} examplesVersion: 1.0.2 -releaseNotes: "## Python SDK Changes:\n* `open_router.beta.responses.send()`: \n * `request` **Changed** **Breaking** :warning:\n * `response` **Changed** **Breaking** :warning:\n* `open_router.presets.create_presets_responses()`: `request` **Changed** **Breaking** :warning:\n* `open_router.presets.create_presets_chat_completions()`: `request` **Changed** **Breaking** :warning:\n* `open_router.chat.send()`: `request` **Changed** **Breaking** :warning:\n* `open_router.workspaces.set_budget()`: **Added**\n* `open_router.o_auth.create_auth_code()`: \n * `request.workspace_id` **Added**\n * `error.status[403]` **Added**\n* `open_router.files.download()`: **Added**\n* `open_router.models.get()`: **Added**\n* `open_router.workspaces.list_budgets()`: **Added**\n* `open_router.workspaces.delete_budget()`: **Added**\n* `open_router.datasets.get_benchmarks_artificial_analysis()`: **Added**\n* `open_router.beta.analytics.query_analytics()`: `response.data.warnings` **Added**\n* `open_router.files.delete()`: **Added**\n* `open_router.files.retrieve()`: **Added**\n* `open_router.files.upload()`: **Added**\n* `open_router.embeddings.list_models()`: `response.data.[].benchmarks` **Added**\n* `open_router.models.list()`: \n * `request` **Changed**\n * `response.data.[].benchmarks` **Added**\n* `open_router.models.list_for_user()`: `response.data.[].benchmarks` **Added**\n* `open_router.files.list()`: **Added**\n* `open_router.presets.create_presets_messages()`: `request` **Changed**\n* `open_router.datasets.get_benchmarks_design_arena()`: **Added**\n" +releaseNotes: | + ## Python SDK Changes: + * `open_router.embeddings.list_models()`: `response.data.[].reasoning` **Added** + * `open_router.models.get()`: `response.data.reasoning` **Added** + * `open_router.models.list()`: `response.data.[].reasoning` **Added** + * `open_router.models.list_for_user()`: `response.data.[].reasoning` **Added** diff --git a/.speakeasy/gen.yaml b/.speakeasy/gen.yaml index 652ef6c..dec9772 100644 --- a/.speakeasy/gen.yaml +++ b/.speakeasy/gen.yaml @@ -32,7 +32,7 @@ generation: skipResponseBodyAssertions: false preApplyUnionDiscriminators: true python: - version: 0.10.0 + version: 0.10.1 additionalDependencies: dev: {} main: {} diff --git a/.speakeasy/out.openapi.yaml b/.speakeasy/out.openapi.yaml index 0efe6ba..35fa2a0 100644 --- a/.speakeasy/out.openapi.yaml +++ b/.speakeasy/out.openapi.yaml @@ -12151,6 +12151,15 @@ components: image: '0' prompt: '0.00003' request: '0' + reasoning: + default_effort: medium + default_enabled: true + mandatory: false + supported_efforts: + - high + - medium + - low + - minimal supported_parameters: - temperature - top_p @@ -12213,6 +12222,8 @@ components: $ref: '#/components/schemas/PerRequestLimits' pricing: $ref: '#/components/schemas/PublicPricing' + reasoning: + $ref: '#/components/schemas/ModelReasoning' supported_parameters: description: List of supported parameters for this model items: @@ -12377,6 +12388,43 @@ components: description: Model to use for completion example: openai/gpt-4 type: string + ModelReasoning: + description: Reasoning effort configuration. Omitted for non-reasoning models and dynamic router models. + example: + default_effort: medium + default_enabled: true + mandatory: false + supported_efforts: + - high + - medium + - low + - minimal + properties: + default_effort: + allOf: + - $ref: '#/components/schemas/ReasoningEffort' + - description: >- + Default reasoning effort when the client enables reasoning without specifying effort. Maps to `reasoning.effort` in chat requests. When `"none"`, prefer omitting effort unless the user explicitly disables reasoning. + default_enabled: + description: Default reasoning enabled state when the client does not set `reasoning.enabled`. + type: boolean + mandatory: + description: When true, reasoning cannot be disabled and effort "none" is rejected. + type: boolean + supported_efforts: + description: >- + Allowed reasoning effort values for this model, in descending effort order (highest first). Null means no allowlist — all gateway effort values are accepted. + items: + $ref: '#/components/schemas/ReasoningEffort' + nullable: true + type: array + supports_max_tokens: + description: >- + Present and `true` when the model accepts `reasoning.max_tokens` in requests (Anthropic-style) instead of or in addition to `reasoning.effort`. Omitted otherwise. + type: boolean + required: + - mandatory + type: object ModelResponse: description: Single model response example: @@ -28725,7 +28773,7 @@ paths: id: openai/gpt-4 knowledge_cutoff: null links: - details: /api/v1/models/openai/gpt-5.4/endpoints + details: /api/v1/models/openai/gpt-4/endpoints name: GPT-4 per_request_limits: null pricing: diff --git a/.speakeasy/workflow.lock b/.speakeasy/workflow.lock index 8c16144..8e8a7d4 100644 --- a/.speakeasy/workflow.lock +++ b/.speakeasy/workflow.lock @@ -2,20 +2,20 @@ speakeasyVersion: 1.680.0 sources: OpenRouter API: sourceNamespace: open-router-chat-completions-api - sourceRevisionDigest: sha256:716ece4a1211eac2ee0e7e5836c7b484cff9bb002bd4e676d9ecad8317ee7941 - sourceBlobDigest: sha256:12d3dc8e7150ab2c4c5e3d613b01cb5e4dcce52b329eea89ca1fdc656d6fe045 + sourceRevisionDigest: sha256:b28471bf9c3ac9b7d9b816a09bfb5b5c58d98db2d82dbc971d64f5aa980f4e10 + sourceBlobDigest: sha256:8d445ea792f56fb2ebeb3f418757e6404802be1c21ab50520c66d10871bb960a tags: - latest - - speakeasy-sdk-regen-1781312282 + - speakeasy-sdk-regen-1781726096 - 1.0.0 targets: open-router: source: OpenRouter API sourceNamespace: open-router-chat-completions-api - sourceRevisionDigest: sha256:716ece4a1211eac2ee0e7e5836c7b484cff9bb002bd4e676d9ecad8317ee7941 - sourceBlobDigest: sha256:12d3dc8e7150ab2c4c5e3d613b01cb5e4dcce52b329eea89ca1fdc656d6fe045 + sourceRevisionDigest: sha256:b28471bf9c3ac9b7d9b816a09bfb5b5c58d98db2d82dbc971d64f5aa980f4e10 + sourceBlobDigest: sha256:8d445ea792f56fb2ebeb3f418757e6404802be1c21ab50520c66d10871bb960a codeSamplesNamespace: open-router-python-code-samples - codeSamplesRevisionDigest: sha256:e7d6f4de48a7c63cf15f3a8378f952277cd4c59373c28c61909497853fa769e2 + codeSamplesRevisionDigest: sha256:0bb2447eb2e776cdc9347c290ddcde4be0b9f9ded69c93e5b29231e2c54c3e5f workflow: workflowVersion: 1.0.0 speakeasyVersion: 1.680.0 diff --git a/RELEASES.md b/RELEASES.md index 3c7e1bf..cd89fff 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -28,4 +28,14 @@ Based on: ### Generated - [python v0.10.0] . ### Releases -- [PyPI v0.10.0] https://pypi.org/project/openrouter/0.10.0 - . \ No newline at end of file +- [PyPI v0.10.0] https://pypi.org/project/openrouter/0.10.0 - . + +## 2026-06-18 00:58:22 +### Changes +Based on: +- OpenAPI Doc +- Speakeasy CLI 1.680.0 (2.788.4) https://github.com/speakeasy-api/speakeasy +### Generated +- [python v0.10.1] . +### Releases +- [PyPI v0.10.1] https://pypi.org/project/openrouter/0.10.1 - . \ No newline at end of file diff --git a/docs/components/model.md b/docs/components/model.md index 8ce3d9d..edb000f 100644 --- a/docs/components/model.md +++ b/docs/components/model.md @@ -22,6 +22,7 @@ Information about an AI model available on OpenRouter | `name` | *str* | :heavy_check_mark: | Display name of the model | GPT-4 | | `per_request_limits` | [Nullable[components.PerRequestLimits]](../components/perrequestlimits.md) | :heavy_check_mark: | Per-request token limits | {
"completion_tokens": 1000,
"prompt_tokens": 1000
} | | `pricing` | [components.PublicPricing](../components/publicpricing.md) | :heavy_check_mark: | Pricing information for the model | {
"completion": "0.00006",
"image": "0",
"prompt": "0.00003",
"request": "0"
} | +| `reasoning` | [Optional[components.ModelReasoning]](../components/modelreasoning.md) | :heavy_minus_sign: | Reasoning effort configuration. Omitted for non-reasoning models and dynamic router models. | {
"default_effort": "medium",
"default_enabled": true,
"mandatory": false,
"supported_efforts": [
"high",
"medium",
"low",
"minimal"
]
} | | `supported_parameters` | List[[components.Parameter](../components/parameter.md)] | :heavy_check_mark: | List of supported parameters for this model | | | `supported_voices` | List[*str*] | :heavy_check_mark: | List of supported voice identifiers for TTS models. Null for non-TTS models. | | | `top_provider` | [components.TopProviderInfo](../components/topproviderinfo.md) | :heavy_check_mark: | Information about the top provider for this model | {
"context_length": 8192,
"is_moderated": true,
"max_completion_tokens": 4096
} | \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index 396bd51..b98c028 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "openrouter" -version = "0.10.0" +version = "0.10.1" 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 be619e3..ade6199 100644 --- a/src/openrouter/_version.py +++ b/src/openrouter/_version.py @@ -3,10 +3,10 @@ import importlib.metadata __title__: str = "openrouter" -__version__: str = "0.10.0" +__version__: str = "0.10.1" __openapi_doc_version__: str = "1.0.0" __gen_version__: str = "2.788.4" -__user_agent__: str = "speakeasy-sdk/python 0.10.0 2.788.4 1.0.0 openrouter" +__user_agent__: str = "speakeasy-sdk/python 0.10.1 2.788.4 1.0.0 openrouter" try: if __package__ is not None: diff --git a/src/openrouter/components/__init__.py b/src/openrouter/components/__init__.py index 2c07302..9df66e9 100644 --- a/src/openrouter/components/__init__.py +++ b/src/openrouter/components/__init__.py @@ -1436,6 +1436,7 @@ if TYPE_CHECKING: from .modelbenchmarks import ModelBenchmarks, ModelBenchmarksTypedDict from .modelgroup import ModelGroup from .modellinks import ModelLinks, ModelLinksTypedDict + from .modelreasoning import DefaultEffort, ModelReasoning, ModelReasoningTypedDict from .modelresponse import ModelResponse, ModelResponseTypedDict from .modelscountresponse import ( ModelsCountResponse, @@ -3076,6 +3077,7 @@ __all__ = [ "DatetimeServerToolConfigTypedDict", "DatetimeServerToolType", "DatetimeServerToolTypedDict", + "DefaultEffort", "DefaultParameters", "DefaultParametersTypedDict", "DeleteBYOKKeyResponse", @@ -3518,6 +3520,8 @@ __all__ = [ "ModelGroup", "ModelLinks", "ModelLinksTypedDict", + "ModelReasoning", + "ModelReasoningTypedDict", "ModelResponse", "ModelResponseTypedDict", "ModelTypedDict", @@ -5459,6 +5463,9 @@ _dynamic_imports: dict[str, str] = { "ModelGroup": ".modelgroup", "ModelLinks": ".modellinks", "ModelLinksTypedDict": ".modellinks", + "DefaultEffort": ".modelreasoning", + "ModelReasoning": ".modelreasoning", + "ModelReasoningTypedDict": ".modelreasoning", "ModelResponse": ".modelresponse", "ModelResponseTypedDict": ".modelresponse", "ModelsCountResponse": ".modelscountresponse", diff --git a/src/openrouter/components/model.py b/src/openrouter/components/model.py index 637b3fb..c034510 100644 --- a/src/openrouter/components/model.py +++ b/src/openrouter/components/model.py @@ -5,6 +5,7 @@ from .defaultparameters import DefaultParameters, DefaultParametersTypedDict from .modelarchitecture import ModelArchitecture, ModelArchitectureTypedDict from .modelbenchmarks import ModelBenchmarks, ModelBenchmarksTypedDict from .modellinks import ModelLinks, ModelLinksTypedDict +from .modelreasoning import ModelReasoning, ModelReasoningTypedDict from .parameter import Parameter from .perrequestlimits import PerRequestLimits, PerRequestLimitsTypedDict from .publicpricing import PublicPricing, PublicPricingTypedDict @@ -62,6 +63,8 @@ class ModelTypedDict(TypedDict): r"""Hugging Face model identifier, if applicable""" knowledge_cutoff: NotRequired[Nullable[str]] r"""The date up to which the model was trained on data. ISO 8601 date string (YYYY-MM-DD) or null if unknown.""" + reasoning: NotRequired[ModelReasoningTypedDict] + r"""Reasoning effort configuration. Omitted for non-reasoning models and dynamic router models.""" class Model(BaseModel): @@ -123,6 +126,9 @@ class Model(BaseModel): knowledge_cutoff: OptionalNullable[str] = UNSET r"""The date up to which the model was trained on data. ISO 8601 date string (YYYY-MM-DD) or null if unknown.""" + reasoning: Optional[ModelReasoning] = None + r"""Reasoning effort configuration. Omitted for non-reasoning models and dynamic router models.""" + @model_serializer(mode="wrap") def serialize_model(self, handler): optional_fields = [ @@ -131,6 +137,7 @@ class Model(BaseModel): "expiration_date", "hugging_face_id", "knowledge_cutoff", + "reasoning", ] nullable_fields = [ "context_length", diff --git a/src/openrouter/components/modelreasoning.py b/src/openrouter/components/modelreasoning.py new file mode 100644 index 0000000..b71947b --- /dev/null +++ b/src/openrouter/components/modelreasoning.py @@ -0,0 +1,106 @@ +"""Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT.""" + +from __future__ import annotations +from .reasoningeffort import ReasoningEffort +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 List, Literal, Optional, Union +from typing_extensions import Annotated, NotRequired, TypedDict + + +DefaultEffort = Union[ + Literal[ + "xhigh", + "high", + "medium", + "low", + "minimal", + "none", + ], + UnrecognizedStr, +] +r"""Default reasoning effort when the client enables reasoning without specifying effort. Maps to `reasoning.effort` in chat requests. When `\"none\"`, prefer omitting effort unless the user explicitly disables reasoning.""" + + +class ModelReasoningTypedDict(TypedDict): + r"""Reasoning effort configuration. Omitted for non-reasoning models and dynamic router models.""" + + mandatory: bool + r"""When true, reasoning cannot be disabled and effort \"none\" is rejected.""" + default_effort: NotRequired[Nullable[DefaultEffort]] + default_enabled: NotRequired[bool] + r"""Default reasoning enabled state when the client does not set `reasoning.enabled`.""" + supported_efforts: NotRequired[Nullable[List[Nullable[ReasoningEffort]]]] + r"""Allowed reasoning effort values for this model, in descending effort order (highest first). Null means no allowlist — all gateway effort values are accepted.""" + supports_max_tokens: NotRequired[bool] + r"""Present and `true` when the model accepts `reasoning.max_tokens` in requests (Anthropic-style) instead of or in addition to `reasoning.effort`. Omitted otherwise.""" + + +class ModelReasoning(BaseModel): + r"""Reasoning effort configuration. Omitted for non-reasoning models and dynamic router models.""" + + mandatory: bool + r"""When true, reasoning cannot be disabled and effort \"none\" is rejected.""" + + default_effort: Annotated[ + OptionalNullable[DefaultEffort], PlainValidator(validate_open_enum(False)) + ] = UNSET + + default_enabled: Optional[bool] = None + r"""Default reasoning enabled state when the client does not set `reasoning.enabled`.""" + + supported_efforts: OptionalNullable[ + List[ + Annotated[ + Nullable[ReasoningEffort], PlainValidator(validate_open_enum(False)) + ] + ] + ] = UNSET + r"""Allowed reasoning effort values for this model, in descending effort order (highest first). Null means no allowlist — all gateway effort values are accepted.""" + + supports_max_tokens: Optional[bool] = None + r"""Present and `true` when the model accepts `reasoning.max_tokens` in requests (Anthropic-style) instead of or in addition to `reasoning.effort`. Omitted otherwise.""" + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = [ + "default_effort", + "default_enabled", + "supported_efforts", + "supports_max_tokens", + ] + nullable_fields = ["default_effort", "supported_efforts"] + 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/uv.lock b/uv.lock index ac18dd7..0bfff32 100644 --- a/uv.lock +++ b/uv.lock @@ -220,7 +220,7 @@ wheels = [ [[package]] name = "openrouter" -version = "0.10.0" +version = "0.10.1" source = { editable = "." } dependencies = [ { name = "httpcore" },