add open enums, server selection, update description

This commit is contained in:
Sheldon Vaughn
2025-09-18 07:54:13 -05:00
parent 540701c0a3
commit 5d06b0a7b4
16 changed files with 128 additions and 61 deletions
+6 -4
View File
@@ -1,12 +1,12 @@
lockVersion: 2.0.0 lockVersion: 2.0.0
id: 03e733ff-14c6-48f8-87a5-37a1a4588594 id: 03e733ff-14c6-48f8-87a5-37a1a4588594
management: management:
docChecksum: f629b0ca18ec064975608b5eea7aebfd docChecksum: 7849dd264d8d3676743d325682310ed7
docVersion: 1.0.0 docVersion: 1.0.0
speakeasyVersion: 1.617.1 speakeasyVersion: 1.618.1
generationVersion: 2.701.8 generationVersion: 2.702.0
releaseVersion: 0.1.0 releaseVersion: 0.1.0
configChecksum: ee7ed66772b5663ad6d381d1b4142fe2 configChecksum: 9b2fc6a03dd7103d87270af55bb29a50
features: features:
python: python:
acceptHeaders: 3.0.0 acceptHeaders: 3.0.0
@@ -27,11 +27,13 @@ features:
methodArguments: 1.0.2 methodArguments: 1.0.2
nameOverrides: 3.0.1 nameOverrides: 3.0.1
nullables: 1.0.1 nullables: 1.0.1
openEnums: 1.0.1
responseFormat: 1.0.1 responseFormat: 1.0.1
retries: 3.0.2 retries: 3.0.2
sdkHooks: 1.1.0 sdkHooks: 1.1.0
serverEvents: 1.0.9 serverEvents: 1.0.9
serverEventsSentinels: 0.1.0 serverEventsSentinels: 0.1.0
serverIDs: 3.0.0
unions: 3.0.4 unions: 3.0.4
generatedFiles: generatedFiles:
- .devcontainer/README.md - .devcontainer/README.md
+1 -1
View File
@@ -39,7 +39,7 @@ python:
baseErrorName: OpenRouterError baseErrorName: OpenRouterError
clientServerStatusCodesAsErrors: true clientServerStatusCodesAsErrors: true
defaultErrorName: OpenRouterDefaultError defaultErrorName: OpenRouterDefaultError
description: Python Client SDK Generated by Speakeasy. description: The OpenRouter Python SDK.
enableCustomCodeRegions: true enableCustomCodeRegions: true
enumFormat: union enumFormat: union
envVarPrefix: OPENROUTER envVarPrefix: OPENROUTER
+7
View File
@@ -12,6 +12,7 @@ info:
url: https://opensource.org/licenses/MIT url: https://opensource.org/licenses/MIT
servers: servers:
- url: https://{provider_url}/api/v1 - url: https://{provider_url}/api/v1
x-speakeasy-server-id: production
variables: variables:
provider_url: provider_url:
default: openrouter.ai default: openrouter.ai
@@ -77,6 +78,7 @@ components:
type: string type: string
nullable: true nullable: true
format: format:
x-speakeasy-unknown-values: allow
type: string type: string
nullable: true nullable: true
enum: enum:
@@ -103,6 +105,7 @@ components:
type: string type: string
nullable: true nullable: true
format: format:
x-speakeasy-unknown-values: allow
type: string type: string
nullable: true nullable: true
enum: enum:
@@ -133,6 +136,7 @@ components:
type: string type: string
nullable: true nullable: true
format: format:
x-speakeasy-unknown-values: allow
type: string type: string
nullable: true nullable: true
enum: enum:
@@ -351,6 +355,7 @@ components:
type: object type: object
properties: properties:
finish_reason: finish_reason:
x-speakeasy-unknown-values: allow
type: string type: string
nullable: true nullable: true
enum: enum:
@@ -541,6 +546,7 @@ components:
delta: delta:
$ref: '#/components/schemas/ChatCompletionChunkChoiceDelta' $ref: '#/components/schemas/ChatCompletionChunkChoiceDelta'
finish_reason: finish_reason:
x-speakeasy-unknown-values: allow
type: string type: string
nullable: true nullable: true
enum: enum:
@@ -649,6 +655,7 @@ components:
type: string type: string
description: Base64 encoded audio data description: Base64 encoded audio data
format: format:
x-speakeasy-unknown-values: allow
type: string type: string
enum: enum:
- wav - wav
+7
View File
@@ -12,6 +12,7 @@ info:
url: https://opensource.org/licenses/MIT url: https://opensource.org/licenses/MIT
servers: servers:
- url: https://{provider_url}/api/v1 - url: https://{provider_url}/api/v1
x-speakeasy-server-id: production
variables: variables:
provider_url: provider_url:
default: openrouter.ai default: openrouter.ai
@@ -77,6 +78,7 @@ components:
type: string type: string
nullable: true nullable: true
format: format:
x-speakeasy-unknown-values: allow
type: string type: string
nullable: true nullable: true
enum: enum:
@@ -103,6 +105,7 @@ components:
type: string type: string
nullable: true nullable: true
format: format:
x-speakeasy-unknown-values: allow
type: string type: string
nullable: true nullable: true
enum: enum:
@@ -133,6 +136,7 @@ components:
type: string type: string
nullable: true nullable: true
format: format:
x-speakeasy-unknown-values: allow
type: string type: string
nullable: true nullable: true
enum: enum:
@@ -351,6 +355,7 @@ components:
type: object type: object
properties: properties:
finish_reason: finish_reason:
x-speakeasy-unknown-values: allow
type: string type: string
nullable: true nullable: true
enum: enum:
@@ -541,6 +546,7 @@ components:
delta: delta:
$ref: '#/components/schemas/ChatCompletionChunkChoiceDelta' $ref: '#/components/schemas/ChatCompletionChunkChoiceDelta'
finish_reason: finish_reason:
x-speakeasy-unknown-values: allow
type: string type: string
nullable: true nullable: true
enum: enum:
@@ -649,6 +655,7 @@ components:
type: string type: string
description: Base64 encoded audio data description: Base64 encoded audio data
format: format:
x-speakeasy-unknown-values: allow
type: string type: string
enum: enum:
- wav - wav
+6 -6
View File
@@ -1,9 +1,9 @@
speakeasyVersion: 1.617.1 speakeasyVersion: 1.618.1
sources: sources:
OpenRouter Chat Completions API: OpenRouter Chat Completions API:
sourceNamespace: open-router-chat-completions-api sourceNamespace: open-router-chat-completions-api
sourceRevisionDigest: sha256:a7746404e672bbb6dcd8354532d0c627d24522844c39d58011d4cd9c7e8ebd20 sourceRevisionDigest: sha256:74ae52780983745871d33efc788abba37d1d8b271f358771b180ddb8546d6f7f
sourceBlobDigest: sha256:67c793fef9c039a2c351fc6ba7ed9a6119059023fe3fbd1022645d7494c2c63c sourceBlobDigest: sha256:2fd2132e64b77adae1fc045512364d6517f47916c553e757226e5b9012d2170d
tags: tags:
- latest - latest
- 1.0.0 - 1.0.0
@@ -11,10 +11,10 @@ targets:
open-router: open-router:
source: OpenRouter Chat Completions API source: OpenRouter Chat Completions API
sourceNamespace: open-router-chat-completions-api sourceNamespace: open-router-chat-completions-api
sourceRevisionDigest: sha256:a7746404e672bbb6dcd8354532d0c627d24522844c39d58011d4cd9c7e8ebd20 sourceRevisionDigest: sha256:74ae52780983745871d33efc788abba37d1d8b271f358771b180ddb8546d6f7f
sourceBlobDigest: sha256:67c793fef9c039a2c351fc6ba7ed9a6119059023fe3fbd1022645d7494c2c63c sourceBlobDigest: sha256:2fd2132e64b77adae1fc045512364d6517f47916c553e757226e5b9012d2170d
codeSamplesNamespace: open-router-chat-completions-api-python-code-samples codeSamplesNamespace: open-router-chat-completions-api-python-code-samples
codeSamplesRevisionDigest: sha256:fb054934c4d0de58a48ca8cdfde7b4210f71c425fb9dfad02e8824b8a1848774 codeSamplesRevisionDigest: sha256:fd8ed35536e452d20ff16d6c33d311ebd405bf7e0f9188cfafcba4cd81539d96
workflow: workflow:
workflowVersion: 1.0.0 workflowVersion: 1.0.0
speakeasyVersion: latest speakeasyVersion: latest
+10 -3
View File
@@ -401,9 +401,15 @@ with OpenRouter(
<!-- Start Server Selection [server] --> <!-- Start Server Selection [server] -->
## Server Selection ## 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 | | Variable | Parameter | Default | Description |
| -------------- | ------------------- | ----------------- | ----------- | | -------------- | ------------------- | ----------------- | ----------- |
@@ -417,6 +423,7 @@ import os
with OpenRouter( with OpenRouter(
server="production",
provider_url="https://ruddy-guacamole.info/" provider_url="https://ruddy-guacamole.info/"
api_key=os.getenv("OPENROUTER_API_KEY", ""), api_key=os.getenv("OPENROUTER_API_KEY", ""),
) as open_router: ) as open_router:
@@ -437,7 +444,7 @@ with OpenRouter(
### Override Server URL Per-Client ### 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 ```python
from openrouter import OpenRouter from openrouter import OpenRouter
import os import os
+1 -1
View File
@@ -1,7 +1,7 @@
[project] [project]
name = "openrouter" name = "openrouter"
version = "0.1.0" version = "0.1.0"
description = "Python Client SDK Generated by Speakeasy." description = "The OpenRouter Python SDK."
authors = [{ name = "Speakeasy" },] authors = [{ name = "Speakeasy" },]
readme = "README.md" readme = "README.md"
requires-python = ">=3.9.2" requires-python = ">=3.9.2"
+2 -2
View File
@@ -5,8 +5,8 @@ import importlib.metadata
__title__: str = "openrouter" __title__: str = "openrouter"
__version__: str = "0.1.0" __version__: str = "0.1.0"
__openapi_doc_version__: str = "1.0.0" __openapi_doc_version__: str = "1.0.0"
__gen_version__: str = "2.701.8" __gen_version__: str = "2.702.0"
__user_agent__: str = "speakeasy-sdk/python 0.1.0 2.701.8 1.0.0 openrouter" __user_agent__: str = "speakeasy-sdk/python 0.1.0 2.702.0 1.0.0 openrouter"
try: try:
if __package__ is not None: if __package__ is not None:
+11 -5
View File
@@ -12,14 +12,17 @@ from openrouter.types import (
OptionalNullable, OptionalNullable,
UNSET, UNSET,
UNSET_SENTINEL, UNSET_SENTINEL,
UnrecognizedStr,
) )
from openrouter.utils import validate_open_enum
from pydantic import model_serializer from pydantic import model_serializer
from typing import Literal from pydantic.functional_validators import PlainValidator
from typing_extensions import NotRequired, TypedDict from typing import Literal, Union
from typing_extensions import Annotated, NotRequired, TypedDict
ChatCompletionChoiceFinishReason = Literal[ ChatCompletionChoiceFinishReason = Union[
"tool_calls", "stop", "length", "content_filter", "error" Literal["tool_calls", "stop", "length", "content_filter", "error"], UnrecognizedStr
] ]
r"""Reason the completion finished""" r"""Reason the completion finished"""
@@ -40,7 +43,10 @@ class ChatCompletionChoiceTypedDict(TypedDict):
class ChatCompletionChoice(BaseModel): class ChatCompletionChoice(BaseModel):
r"""Chat completion choice""" r"""Chat completion choice"""
finish_reason: Nullable[ChatCompletionChoiceFinishReason] finish_reason: Annotated[
Nullable[ChatCompletionChoiceFinishReason],
PlainValidator(validate_open_enum(False)),
]
r"""Reason the completion finished""" r"""Reason the completion finished"""
index: float index: float
@@ -15,14 +15,17 @@ from openrouter.types import (
OptionalNullable, OptionalNullable,
UNSET, UNSET,
UNSET_SENTINEL, UNSET_SENTINEL,
UnrecognizedStr,
) )
from openrouter.utils import validate_open_enum
from pydantic import model_serializer from pydantic import model_serializer
from typing import Literal from pydantic.functional_validators import PlainValidator
from typing_extensions import NotRequired, TypedDict from typing import Literal, Union
from typing_extensions import Annotated, NotRequired, TypedDict
ChatCompletionChunkChoiceFinishReason = Literal[ ChatCompletionChunkChoiceFinishReason = Union[
"tool_calls", "stop", "length", "content_filter", "error" Literal["tool_calls", "stop", "length", "content_filter", "error"], UnrecognizedStr
] ]
@@ -43,7 +46,10 @@ class ChatCompletionChunkChoice(BaseModel):
delta: ChatCompletionChunkChoiceDelta delta: ChatCompletionChunkChoiceDelta
r"""Delta changes in streaming response""" r"""Delta changes in streaming response"""
finish_reason: Nullable[ChatCompletionChunkChoiceFinishReason] finish_reason: Annotated[
Nullable[ChatCompletionChunkChoiceFinishReason],
PlainValidator(validate_open_enum(False)),
]
index: float index: float
@@ -1,16 +1,18 @@
"""Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT.""" """Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT."""
from __future__ import annotations from __future__ import annotations
from openrouter.types import BaseModel from openrouter.types import BaseModel, UnrecognizedStr
from openrouter.utils import validate_open_enum
import pydantic import pydantic
from typing import Literal from pydantic.functional_validators import PlainValidator
from typing import Literal, Union
from typing_extensions import Annotated, TypedDict from typing_extensions import Annotated, TypedDict
ChatCompletionContentPartAudioType = Literal["input_audio"] ChatCompletionContentPartAudioType = Literal["input_audio"]
ChatCompletionContentPartAudioFormat = Literal[ ChatCompletionContentPartAudioFormat = Union[
"wav", "mp3", "flac", "m4a", "ogg", "pcm16", "pcm24" Literal["wav", "mp3", "flac", "m4a", "ogg", "pcm16", "pcm24"], UnrecognizedStr
] ]
r"""Audio format""" r"""Audio format"""
@@ -27,7 +29,11 @@ class InputAudio(BaseModel):
r"""Base64 encoded audio data""" r"""Base64 encoded audio data"""
format_: Annotated[ format_: Annotated[
ChatCompletionContentPartAudioFormat, pydantic.Field(alias="format") Annotated[
ChatCompletionContentPartAudioFormat,
PlainValidator(validate_open_enum(False)),
],
pydantic.Field(alias="format"),
] ]
r"""Audio format""" r"""Audio format"""
@@ -7,17 +7,20 @@ from openrouter.types import (
OptionalNullable, OptionalNullable,
UNSET, UNSET,
UNSET_SENTINEL, UNSET_SENTINEL,
UnrecognizedStr,
) )
from openrouter.utils import validate_open_enum
import pydantic import pydantic
from pydantic import model_serializer 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 from typing_extensions import Annotated, NotRequired, TypedDict
ReasoningDetailEncryptedType = Literal["reasoning.encrypted"] ReasoningDetailEncryptedType = Literal["reasoning.encrypted"]
ReasoningDetailEncryptedFormat = Literal[ ReasoningDetailEncryptedFormat = Union[
"unknown", "openai-responses-v1", "anthropic-claude-v1" Literal["unknown", "openai-responses-v1", "anthropic-claude-v1"], UnrecognizedStr
] ]
@@ -41,7 +44,11 @@ class ReasoningDetailEncrypted(BaseModel):
id: OptionalNullable[str] = UNSET id: OptionalNullable[str] = UNSET
format_: Annotated[ format_: Annotated[
OptionalNullable[ReasoningDetailEncryptedFormat], pydantic.Field(alias="format") Annotated[
OptionalNullable[ReasoningDetailEncryptedFormat],
PlainValidator(validate_open_enum(False)),
],
pydantic.Field(alias="format"),
] = "anthropic-claude-v1" ] = "anthropic-claude-v1"
index: Optional[float] = None index: Optional[float] = None
@@ -7,17 +7,20 @@ from openrouter.types import (
OptionalNullable, OptionalNullable,
UNSET, UNSET,
UNSET_SENTINEL, UNSET_SENTINEL,
UnrecognizedStr,
) )
from openrouter.utils import validate_open_enum
import pydantic import pydantic
from pydantic import model_serializer 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 from typing_extensions import Annotated, NotRequired, TypedDict
ReasoningDetailSummaryType = Literal["reasoning.summary"] ReasoningDetailSummaryType = Literal["reasoning.summary"]
ReasoningDetailSummaryFormat = Literal[ ReasoningDetailSummaryFormat = Union[
"unknown", "openai-responses-v1", "anthropic-claude-v1" Literal["unknown", "openai-responses-v1", "anthropic-claude-v1"], UnrecognizedStr
] ]
@@ -41,7 +44,11 @@ class ReasoningDetailSummary(BaseModel):
id: OptionalNullable[str] = UNSET id: OptionalNullable[str] = UNSET
format_: Annotated[ format_: Annotated[
OptionalNullable[ReasoningDetailSummaryFormat], pydantic.Field(alias="format") Annotated[
OptionalNullable[ReasoningDetailSummaryFormat],
PlainValidator(validate_open_enum(False)),
],
pydantic.Field(alias="format"),
] = "anthropic-claude-v1" ] = "anthropic-claude-v1"
index: Optional[float] = None index: Optional[float] = None
+11 -4
View File
@@ -7,17 +7,20 @@ from openrouter.types import (
OptionalNullable, OptionalNullable,
UNSET, UNSET,
UNSET_SENTINEL, UNSET_SENTINEL,
UnrecognizedStr,
) )
from openrouter.utils import validate_open_enum
import pydantic import pydantic
from pydantic import model_serializer 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 from typing_extensions import Annotated, NotRequired, TypedDict
ReasoningDetailTextType = Literal["reasoning.text"] ReasoningDetailTextType = Literal["reasoning.text"]
ReasoningDetailTextFormat = Literal[ ReasoningDetailTextFormat = Union[
"unknown", "openai-responses-v1", "anthropic-claude-v1" Literal["unknown", "openai-responses-v1", "anthropic-claude-v1"], UnrecognizedStr
] ]
@@ -44,7 +47,11 @@ class ReasoningDetailText(BaseModel):
id: OptionalNullable[str] = UNSET id: OptionalNullable[str] = UNSET
format_: Annotated[ format_: Annotated[
OptionalNullable[ReasoningDetailTextFormat], pydantic.Field(alias="format") Annotated[
OptionalNullable[ReasoningDetailTextFormat],
PlainValidator(validate_open_enum(False)),
],
pydantic.Field(alias="format"),
] = "anthropic-claude-v1" ] = "anthropic-claude-v1"
index: Optional[float] = None index: Optional[float] = None
+8 -7
View File
@@ -11,7 +11,7 @@ from openrouter import models, utils
from openrouter._hooks import SDKHooks from openrouter._hooks import SDKHooks
from openrouter.types import OptionalNullable, UNSET from openrouter.types import OptionalNullable, UNSET
import sys 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 import weakref
if TYPE_CHECKING: if TYPE_CHECKING:
@@ -32,7 +32,7 @@ class OpenRouter(BaseSDK):
self, self,
api_key: Optional[Union[Optional[str], Callable[[], Optional[str]]]] = None, api_key: Optional[Union[Optional[str], Callable[[], Optional[str]]]] = None,
provider_url: Optional[str] = None, provider_url: Optional[str] = None,
server_idx: Optional[int] = None, server: Optional[str] = None,
server_url: Optional[str] = None, server_url: Optional[str] = None,
url_params: Optional[Dict[str, str]] = None, url_params: Optional[Dict[str, str]] = None,
client: Optional[HttpClient] = None, client: Optional[HttpClient] = None,
@@ -45,7 +45,7 @@ class OpenRouter(BaseSDK):
:param api_key: The api_key required for authentication :param api_key: The api_key required for authentication
:param provider_url: Allows setting the provider_url variable for url substitution :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 server_url: The server URL to use for all methods
:param url_params: Parameters to optionally template the server URL with :param url_params: Parameters to optionally template the server URL with
:param client: The HTTP client to use for all synchronous methods :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 server_url is not None:
if url_params is not None: if url_params is not None:
server_url = utils.template_url(server_url, url_params) 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", "provider_url": provider_url or "openrouter.ai",
}, },
] }
BaseSDK.__init__( BaseSDK.__init__(
self, self,
@@ -99,7 +100,7 @@ class OpenRouter(BaseSDK):
async_client_supplied=async_client_supplied, async_client_supplied=async_client_supplied,
security=security, security=security,
server_url=server_url, server_url=server_url,
server_idx=server_idx, server=server,
server_defaults=server_defaults, server_defaults=server_defaults,
retry_config=retry_config, retry_config=retry_config,
timeout_ms=timeout_ms, timeout_ms=timeout_ms,
+14 -10
View File
@@ -12,13 +12,14 @@ from dataclasses import dataclass, field
from openrouter import models from openrouter import models
from openrouter.types import OptionalNullable, UNSET from openrouter.types import OptionalNullable, UNSET
from pydantic import Field from pydantic import Field
from typing import Callable, Dict, List, Optional, Tuple, Union from typing import Callable, Dict, Optional, Tuple, Union
SERVERS = [ SERVER_PRODUCTION = "production"
"https://{provider_url}/api/v1", r"""Production server"""
# Production server SERVERS = {
] SERVER_PRODUCTION: "https://{provider_url}/api/v1",
}
"""Contains the list of servers available to the SDK""" """Contains the list of servers available to the SDK"""
@@ -31,8 +32,8 @@ class SDKConfiguration:
debug_logger: Logger debug_logger: Logger
security: Optional[Union[models.Security, Callable[[], models.Security]]] = None security: Optional[Union[models.Security, Callable[[], models.Security]]] = None
server_url: Optional[str] = "" server_url: Optional[str] = ""
server_idx: Optional[int] = 0 server: Optional[str] = ""
server_defaults: List[Dict[str, str]] = field(default_factory=List) server_defaults: Dict[str, Dict[str, str]] = field(default_factory=Dict)
language: str = "python" language: str = "python"
openapi_doc_version: str = __openapi_doc_version__ openapi_doc_version: str = __openapi_doc_version__
sdk_version: str = __version__ sdk_version: str = __version__
@@ -44,7 +45,10 @@ class SDKConfiguration:
def get_server_details(self) -> Tuple[str, Dict[str, str]]: def get_server_details(self) -> Tuple[str, Dict[str, str]]:
if self.server_url is not None and self.server_url: if self.server_url is not None and self.server_url:
return remove_suffix(self.server_url, "/"), {} return remove_suffix(self.server_url, "/"), {}
if self.server_idx is None: if not self.server:
self.server_idx = 0 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, {})