diff --git a/.speakeasy/gen.lock b/.speakeasy/gen.lock index a41dc4f..4ab78ce 100644 --- a/.speakeasy/gen.lock +++ b/.speakeasy/gen.lock @@ -1,12 +1,12 @@ lockVersion: 2.0.0 id: 03e733ff-14c6-48f8-87a5-37a1a4588594 management: - docChecksum: f629b0ca18ec064975608b5eea7aebfd + docChecksum: 7849dd264d8d3676743d325682310ed7 docVersion: 1.0.0 - speakeasyVersion: 1.617.1 - generationVersion: 2.701.8 + speakeasyVersion: 1.618.1 + generationVersion: 2.702.0 releaseVersion: 0.1.0 - configChecksum: ee7ed66772b5663ad6d381d1b4142fe2 + configChecksum: 9b2fc6a03dd7103d87270af55bb29a50 features: python: acceptHeaders: 3.0.0 @@ -27,11 +27,13 @@ features: methodArguments: 1.0.2 nameOverrides: 3.0.1 nullables: 1.0.1 + openEnums: 1.0.1 responseFormat: 1.0.1 retries: 3.0.2 sdkHooks: 1.1.0 serverEvents: 1.0.9 serverEventsSentinels: 0.1.0 + serverIDs: 3.0.0 unions: 3.0.4 generatedFiles: - .devcontainer/README.md diff --git a/.speakeasy/gen.yaml b/.speakeasy/gen.yaml index 6104105..b21f199 100644 --- a/.speakeasy/gen.yaml +++ b/.speakeasy/gen.yaml @@ -39,7 +39,7 @@ python: baseErrorName: OpenRouterError clientServerStatusCodesAsErrors: true defaultErrorName: OpenRouterDefaultError - description: Python Client SDK Generated by Speakeasy. + description: The OpenRouter Python SDK. enableCustomCodeRegions: true enumFormat: union envVarPrefix: OPENROUTER diff --git a/.speakeasy/in.openapi.yaml b/.speakeasy/in.openapi.yaml index 72fe017..748098b 100644 --- a/.speakeasy/in.openapi.yaml +++ b/.speakeasy/in.openapi.yaml @@ -12,6 +12,7 @@ info: url: https://opensource.org/licenses/MIT servers: - url: https://{provider_url}/api/v1 + x-speakeasy-server-id: production variables: provider_url: default: openrouter.ai @@ -77,6 +78,7 @@ components: type: string nullable: true format: + x-speakeasy-unknown-values: allow type: string nullable: true enum: @@ -103,6 +105,7 @@ components: type: string nullable: true format: + x-speakeasy-unknown-values: allow type: string nullable: true enum: @@ -133,6 +136,7 @@ components: type: string nullable: true format: + x-speakeasy-unknown-values: allow type: string nullable: true enum: @@ -351,6 +355,7 @@ components: type: object properties: finish_reason: + x-speakeasy-unknown-values: allow type: string nullable: true enum: @@ -541,6 +546,7 @@ components: delta: $ref: '#/components/schemas/ChatCompletionChunkChoiceDelta' finish_reason: + x-speakeasy-unknown-values: allow type: string nullable: true enum: @@ -649,6 +655,7 @@ components: type: string description: Base64 encoded audio data format: + x-speakeasy-unknown-values: allow type: string enum: - wav diff --git a/.speakeasy/out.openapi.yaml b/.speakeasy/out.openapi.yaml index 72fe017..748098b 100644 --- a/.speakeasy/out.openapi.yaml +++ b/.speakeasy/out.openapi.yaml @@ -12,6 +12,7 @@ info: url: https://opensource.org/licenses/MIT servers: - url: https://{provider_url}/api/v1 + x-speakeasy-server-id: production variables: provider_url: default: openrouter.ai @@ -77,6 +78,7 @@ components: type: string nullable: true format: + x-speakeasy-unknown-values: allow type: string nullable: true enum: @@ -103,6 +105,7 @@ components: type: string nullable: true format: + x-speakeasy-unknown-values: allow type: string nullable: true enum: @@ -133,6 +136,7 @@ components: type: string nullable: true format: + x-speakeasy-unknown-values: allow type: string nullable: true enum: @@ -351,6 +355,7 @@ components: type: object properties: finish_reason: + x-speakeasy-unknown-values: allow type: string nullable: true enum: @@ -541,6 +546,7 @@ components: delta: $ref: '#/components/schemas/ChatCompletionChunkChoiceDelta' finish_reason: + x-speakeasy-unknown-values: allow type: string nullable: true enum: @@ -649,6 +655,7 @@ components: type: string description: Base64 encoded audio data format: + x-speakeasy-unknown-values: allow type: string enum: - wav diff --git a/.speakeasy/workflow.lock b/.speakeasy/workflow.lock index 261f881..f151bac 100644 --- a/.speakeasy/workflow.lock +++ b/.speakeasy/workflow.lock @@ -1,9 +1,9 @@ -speakeasyVersion: 1.617.1 +speakeasyVersion: 1.618.1 sources: OpenRouter Chat Completions API: sourceNamespace: open-router-chat-completions-api - sourceRevisionDigest: sha256:a7746404e672bbb6dcd8354532d0c627d24522844c39d58011d4cd9c7e8ebd20 - sourceBlobDigest: sha256:67c793fef9c039a2c351fc6ba7ed9a6119059023fe3fbd1022645d7494c2c63c + sourceRevisionDigest: sha256:74ae52780983745871d33efc788abba37d1d8b271f358771b180ddb8546d6f7f + sourceBlobDigest: sha256:2fd2132e64b77adae1fc045512364d6517f47916c553e757226e5b9012d2170d tags: - latest - 1.0.0 @@ -11,10 +11,10 @@ targets: open-router: source: OpenRouter Chat Completions API sourceNamespace: open-router-chat-completions-api - sourceRevisionDigest: sha256:a7746404e672bbb6dcd8354532d0c627d24522844c39d58011d4cd9c7e8ebd20 - sourceBlobDigest: sha256:67c793fef9c039a2c351fc6ba7ed9a6119059023fe3fbd1022645d7494c2c63c + sourceRevisionDigest: sha256:74ae52780983745871d33efc788abba37d1d8b271f358771b180ddb8546d6f7f + sourceBlobDigest: sha256:2fd2132e64b77adae1fc045512364d6517f47916c553e757226e5b9012d2170d codeSamplesNamespace: open-router-chat-completions-api-python-code-samples - codeSamplesRevisionDigest: sha256:fb054934c4d0de58a48ca8cdfde7b4210f71c425fb9dfad02e8824b8a1848774 + codeSamplesRevisionDigest: sha256:fd8ed35536e452d20ff16d6c33d311ebd405bf7e0f9188cfafcba4cd81539d96 workflow: workflowVersion: 1.0.0 speakeasyVersion: latest diff --git a/README.md b/README.md index 502567a..af4444c 100644 --- a/README.md +++ b/README.md @@ -401,9 +401,15 @@ with OpenRouter( ## Server Selection -### Server Variables +### Select Server by Name -The default server `https://{provider_url}/api/v1` contains variables and is set to `https://openrouter.ai/api/v1` by default. To override default values, the following parameters are available when initializing the SDK client instance: +You can override the default server globally by passing a server name to the `server: str` optional parameter when initializing the SDK client instance. The selected server will then be used as the default on the operations that use it. This table lists the names associated with the available servers: + +| Name | Server | Variables | Description | +| ------------ | ------------------------------- | -------------- | ----------------- | +| `production` | `https://{provider_url}/api/v1` | `provider_url` | Production server | + +If the selected server has variables, you may override its default values through the additional parameters made available in the SDK constructor: | Variable | Parameter | Default | Description | | -------------- | ------------------- | ----------------- | ----------- | @@ -417,6 +423,7 @@ import os with OpenRouter( + server="production", provider_url="https://ruddy-guacamole.info/" api_key=os.getenv("OPENROUTER_API_KEY", ""), ) as open_router: @@ -437,7 +444,7 @@ with OpenRouter( ### Override Server URL Per-Client -The default server can be overridden globally by passing a URL to the `server_url: str` optional parameter when initializing the SDK client instance. For example: +The default server can also be overridden globally by passing a URL to the `server_url: str` optional parameter when initializing the SDK client instance. For example: ```python from openrouter import OpenRouter import os diff --git a/pyproject.toml b/pyproject.toml index 16fef11..93097a5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,7 +1,7 @@ [project] name = "openrouter" version = "0.1.0" -description = "Python Client SDK Generated by Speakeasy." +description = "The OpenRouter Python SDK." authors = [{ name = "Speakeasy" },] readme = "README.md" requires-python = ">=3.9.2" diff --git a/src/openrouter/_version.py b/src/openrouter/_version.py index fd7d4cb..e345743 100644 --- a/src/openrouter/_version.py +++ b/src/openrouter/_version.py @@ -5,8 +5,8 @@ import importlib.metadata __title__: str = "openrouter" __version__: str = "0.1.0" __openapi_doc_version__: str = "1.0.0" -__gen_version__: str = "2.701.8" -__user_agent__: str = "speakeasy-sdk/python 0.1.0 2.701.8 1.0.0 openrouter" +__gen_version__: str = "2.702.0" +__user_agent__: str = "speakeasy-sdk/python 0.1.0 2.702.0 1.0.0 openrouter" try: if __package__ is not None: diff --git a/src/openrouter/models/chatcompletionchoice.py b/src/openrouter/models/chatcompletionchoice.py index f670b8a..506d967 100644 --- a/src/openrouter/models/chatcompletionchoice.py +++ b/src/openrouter/models/chatcompletionchoice.py @@ -12,14 +12,17 @@ from openrouter.types import ( OptionalNullable, UNSET, UNSET_SENTINEL, + UnrecognizedStr, ) +from openrouter.utils import validate_open_enum from pydantic import model_serializer -from typing import Literal -from typing_extensions import NotRequired, TypedDict +from pydantic.functional_validators import PlainValidator +from typing import Literal, Union +from typing_extensions import Annotated, NotRequired, TypedDict -ChatCompletionChoiceFinishReason = Literal[ - "tool_calls", "stop", "length", "content_filter", "error" +ChatCompletionChoiceFinishReason = Union[ + Literal["tool_calls", "stop", "length", "content_filter", "error"], UnrecognizedStr ] r"""Reason the completion finished""" @@ -40,7 +43,10 @@ class ChatCompletionChoiceTypedDict(TypedDict): class ChatCompletionChoice(BaseModel): r"""Chat completion choice""" - finish_reason: Nullable[ChatCompletionChoiceFinishReason] + finish_reason: Annotated[ + Nullable[ChatCompletionChoiceFinishReason], + PlainValidator(validate_open_enum(False)), + ] r"""Reason the completion finished""" index: float diff --git a/src/openrouter/models/chatcompletionchunkchoice.py b/src/openrouter/models/chatcompletionchunkchoice.py index 3e212e4..8ef1a32 100644 --- a/src/openrouter/models/chatcompletionchunkchoice.py +++ b/src/openrouter/models/chatcompletionchunkchoice.py @@ -15,14 +15,17 @@ from openrouter.types import ( OptionalNullable, UNSET, UNSET_SENTINEL, + UnrecognizedStr, ) +from openrouter.utils import validate_open_enum from pydantic import model_serializer -from typing import Literal -from typing_extensions import NotRequired, TypedDict +from pydantic.functional_validators import PlainValidator +from typing import Literal, Union +from typing_extensions import Annotated, NotRequired, TypedDict -ChatCompletionChunkChoiceFinishReason = Literal[ - "tool_calls", "stop", "length", "content_filter", "error" +ChatCompletionChunkChoiceFinishReason = Union[ + Literal["tool_calls", "stop", "length", "content_filter", "error"], UnrecognizedStr ] @@ -43,7 +46,10 @@ class ChatCompletionChunkChoice(BaseModel): delta: ChatCompletionChunkChoiceDelta r"""Delta changes in streaming response""" - finish_reason: Nullable[ChatCompletionChunkChoiceFinishReason] + finish_reason: Annotated[ + Nullable[ChatCompletionChunkChoiceFinishReason], + PlainValidator(validate_open_enum(False)), + ] index: float diff --git a/src/openrouter/models/chatcompletioncontentpartaudio.py b/src/openrouter/models/chatcompletioncontentpartaudio.py index 21bfb58..cd3349f 100644 --- a/src/openrouter/models/chatcompletioncontentpartaudio.py +++ b/src/openrouter/models/chatcompletioncontentpartaudio.py @@ -1,16 +1,18 @@ """Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT.""" from __future__ import annotations -from openrouter.types import BaseModel +from openrouter.types import BaseModel, UnrecognizedStr +from openrouter.utils import validate_open_enum import pydantic -from typing import Literal +from pydantic.functional_validators import PlainValidator +from typing import Literal, Union from typing_extensions import Annotated, TypedDict ChatCompletionContentPartAudioType = Literal["input_audio"] -ChatCompletionContentPartAudioFormat = Literal[ - "wav", "mp3", "flac", "m4a", "ogg", "pcm16", "pcm24" +ChatCompletionContentPartAudioFormat = Union[ + Literal["wav", "mp3", "flac", "m4a", "ogg", "pcm16", "pcm24"], UnrecognizedStr ] r"""Audio format""" @@ -27,7 +29,11 @@ class InputAudio(BaseModel): r"""Base64 encoded audio data""" format_: Annotated[ - ChatCompletionContentPartAudioFormat, pydantic.Field(alias="format") + Annotated[ + ChatCompletionContentPartAudioFormat, + PlainValidator(validate_open_enum(False)), + ], + pydantic.Field(alias="format"), ] r"""Audio format""" diff --git a/src/openrouter/models/reasoningdetailencrypted.py b/src/openrouter/models/reasoningdetailencrypted.py index d17bd47..a5b6f64 100644 --- a/src/openrouter/models/reasoningdetailencrypted.py +++ b/src/openrouter/models/reasoningdetailencrypted.py @@ -7,17 +7,20 @@ from openrouter.types import ( OptionalNullable, UNSET, UNSET_SENTINEL, + UnrecognizedStr, ) +from openrouter.utils import validate_open_enum import pydantic from pydantic import model_serializer -from typing import Literal, Optional +from pydantic.functional_validators import PlainValidator +from typing import Literal, Optional, Union from typing_extensions import Annotated, NotRequired, TypedDict ReasoningDetailEncryptedType = Literal["reasoning.encrypted"] -ReasoningDetailEncryptedFormat = Literal[ - "unknown", "openai-responses-v1", "anthropic-claude-v1" +ReasoningDetailEncryptedFormat = Union[ + Literal["unknown", "openai-responses-v1", "anthropic-claude-v1"], UnrecognizedStr ] @@ -41,7 +44,11 @@ class ReasoningDetailEncrypted(BaseModel): id: OptionalNullable[str] = UNSET format_: Annotated[ - OptionalNullable[ReasoningDetailEncryptedFormat], pydantic.Field(alias="format") + Annotated[ + OptionalNullable[ReasoningDetailEncryptedFormat], + PlainValidator(validate_open_enum(False)), + ], + pydantic.Field(alias="format"), ] = "anthropic-claude-v1" index: Optional[float] = None diff --git a/src/openrouter/models/reasoningdetailsummary.py b/src/openrouter/models/reasoningdetailsummary.py index ac2c376..5c5d5af 100644 --- a/src/openrouter/models/reasoningdetailsummary.py +++ b/src/openrouter/models/reasoningdetailsummary.py @@ -7,17 +7,20 @@ from openrouter.types import ( OptionalNullable, UNSET, UNSET_SENTINEL, + UnrecognizedStr, ) +from openrouter.utils import validate_open_enum import pydantic from pydantic import model_serializer -from typing import Literal, Optional +from pydantic.functional_validators import PlainValidator +from typing import Literal, Optional, Union from typing_extensions import Annotated, NotRequired, TypedDict ReasoningDetailSummaryType = Literal["reasoning.summary"] -ReasoningDetailSummaryFormat = Literal[ - "unknown", "openai-responses-v1", "anthropic-claude-v1" +ReasoningDetailSummaryFormat = Union[ + Literal["unknown", "openai-responses-v1", "anthropic-claude-v1"], UnrecognizedStr ] @@ -41,7 +44,11 @@ class ReasoningDetailSummary(BaseModel): id: OptionalNullable[str] = UNSET format_: Annotated[ - OptionalNullable[ReasoningDetailSummaryFormat], pydantic.Field(alias="format") + Annotated[ + OptionalNullable[ReasoningDetailSummaryFormat], + PlainValidator(validate_open_enum(False)), + ], + pydantic.Field(alias="format"), ] = "anthropic-claude-v1" index: Optional[float] = None diff --git a/src/openrouter/models/reasoningdetailtext.py b/src/openrouter/models/reasoningdetailtext.py index f1beee6..0389731 100644 --- a/src/openrouter/models/reasoningdetailtext.py +++ b/src/openrouter/models/reasoningdetailtext.py @@ -7,17 +7,20 @@ from openrouter.types import ( OptionalNullable, UNSET, UNSET_SENTINEL, + UnrecognizedStr, ) +from openrouter.utils import validate_open_enum import pydantic from pydantic import model_serializer -from typing import Literal, Optional +from pydantic.functional_validators import PlainValidator +from typing import Literal, Optional, Union from typing_extensions import Annotated, NotRequired, TypedDict ReasoningDetailTextType = Literal["reasoning.text"] -ReasoningDetailTextFormat = Literal[ - "unknown", "openai-responses-v1", "anthropic-claude-v1" +ReasoningDetailTextFormat = Union[ + Literal["unknown", "openai-responses-v1", "anthropic-claude-v1"], UnrecognizedStr ] @@ -44,7 +47,11 @@ class ReasoningDetailText(BaseModel): id: OptionalNullable[str] = UNSET format_: Annotated[ - OptionalNullable[ReasoningDetailTextFormat], pydantic.Field(alias="format") + Annotated[ + OptionalNullable[ReasoningDetailTextFormat], + PlainValidator(validate_open_enum(False)), + ], + pydantic.Field(alias="format"), ] = "anthropic-claude-v1" index: Optional[float] = None diff --git a/src/openrouter/sdk.py b/src/openrouter/sdk.py index ab87f24..0c66cb0 100644 --- a/src/openrouter/sdk.py +++ b/src/openrouter/sdk.py @@ -11,7 +11,7 @@ from openrouter import models, utils from openrouter._hooks import SDKHooks from openrouter.types import OptionalNullable, UNSET import sys -from typing import Any, Callable, Dict, List, Optional, TYPE_CHECKING, Union, cast +from typing import Any, Callable, Dict, Optional, TYPE_CHECKING, Union, cast import weakref if TYPE_CHECKING: @@ -32,7 +32,7 @@ class OpenRouter(BaseSDK): self, api_key: Optional[Union[Optional[str], Callable[[], Optional[str]]]] = None, provider_url: Optional[str] = None, - server_idx: Optional[int] = None, + server: Optional[str] = None, server_url: Optional[str] = None, url_params: Optional[Dict[str, str]] = None, client: Optional[HttpClient] = None, @@ -45,7 +45,7 @@ class OpenRouter(BaseSDK): :param api_key: The api_key required for authentication :param provider_url: Allows setting the provider_url variable for url substitution - :param server_idx: The index of the server to use for all methods + :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 :param client: The HTTP client to use for all synchronous methods @@ -84,11 +84,12 @@ class OpenRouter(BaseSDK): if server_url is not None: if url_params is not None: server_url = utils.template_url(server_url, url_params) - server_defaults: List[Dict[str, str]] = [ - { + + server_defaults: Dict[str, Dict[str, str]] = { + "production": { "provider_url": provider_url or "openrouter.ai", }, - ] + } BaseSDK.__init__( self, @@ -99,7 +100,7 @@ class OpenRouter(BaseSDK): async_client_supplied=async_client_supplied, security=security, server_url=server_url, - server_idx=server_idx, + server=server, server_defaults=server_defaults, retry_config=retry_config, timeout_ms=timeout_ms, diff --git a/src/openrouter/sdkconfiguration.py b/src/openrouter/sdkconfiguration.py index ba1e868..4b63ab1 100644 --- a/src/openrouter/sdkconfiguration.py +++ b/src/openrouter/sdkconfiguration.py @@ -12,13 +12,14 @@ from dataclasses import dataclass, field from openrouter import models from openrouter.types import OptionalNullable, UNSET from pydantic import Field -from typing import Callable, Dict, List, Optional, Tuple, Union +from typing import Callable, Dict, Optional, Tuple, Union -SERVERS = [ - "https://{provider_url}/api/v1", - # Production server -] +SERVER_PRODUCTION = "production" +r"""Production server""" +SERVERS = { + SERVER_PRODUCTION: "https://{provider_url}/api/v1", +} """Contains the list of servers available to the SDK""" @@ -31,8 +32,8 @@ class SDKConfiguration: debug_logger: Logger security: Optional[Union[models.Security, Callable[[], models.Security]]] = None server_url: Optional[str] = "" - server_idx: Optional[int] = 0 - server_defaults: List[Dict[str, str]] = field(default_factory=List) + server: Optional[str] = "" + server_defaults: Dict[str, Dict[str, str]] = field(default_factory=Dict) language: str = "python" openapi_doc_version: str = __openapi_doc_version__ sdk_version: str = __version__ @@ -44,7 +45,10 @@ class SDKConfiguration: def get_server_details(self) -> Tuple[str, Dict[str, str]]: if self.server_url is not None and self.server_url: return remove_suffix(self.server_url, "/"), {} - if self.server_idx is None: - self.server_idx = 0 + if not self.server: + self.server = SERVER_PRODUCTION - return SERVERS[self.server_idx], self.server_defaults[self.server_idx] + if self.server not in SERVERS: + raise ValueError(f'Invalid server "{self.server}"') + + return SERVERS[self.server], self.server_defaults.get(self.server, {})