llmswitch-sdk 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. llmswitch_sdk-0.1.0/LICENSE +21 -0
  2. llmswitch_sdk-0.1.0/PKG-INFO +214 -0
  3. llmswitch_sdk-0.1.0/README.md +176 -0
  4. llmswitch_sdk-0.1.0/pyproject.toml +74 -0
  5. llmswitch_sdk-0.1.0/setup.cfg +4 -0
  6. llmswitch_sdk-0.1.0/src/llmswitch/__init__.py +47 -0
  7. llmswitch_sdk-0.1.0/src/llmswitch/base.py +39 -0
  8. llmswitch_sdk-0.1.0/src/llmswitch/config.py +115 -0
  9. llmswitch_sdk-0.1.0/src/llmswitch/constants.py +1 -0
  10. llmswitch_sdk-0.1.0/src/llmswitch/core/__init__.py +7 -0
  11. llmswitch_sdk-0.1.0/src/llmswitch/core/async_client.py +132 -0
  12. llmswitch_sdk-0.1.0/src/llmswitch/core/common.py +122 -0
  13. llmswitch_sdk-0.1.0/src/llmswitch/core/registry.py +66 -0
  14. llmswitch_sdk-0.1.0/src/llmswitch/core/sync_client.py +131 -0
  15. llmswitch_sdk-0.1.0/src/llmswitch/errors.py +333 -0
  16. llmswitch_sdk-0.1.0/src/llmswitch/models.py +343 -0
  17. llmswitch_sdk-0.1.0/src/llmswitch/providers/_shared.py +208 -0
  18. llmswitch_sdk-0.1.0/src/llmswitch/providers/anthropic_provider.py +1000 -0
  19. llmswitch_sdk-0.1.0/src/llmswitch/providers/azurefoundry_provider.py +749 -0
  20. llmswitch_sdk-0.1.0/src/llmswitch/providers/azureopenai_provider.py +599 -0
  21. llmswitch_sdk-0.1.0/src/llmswitch/providers/openai_provider.py +589 -0
  22. llmswitch_sdk-0.1.0/src/llmswitch/providers/vertexai_provider.py +817 -0
  23. llmswitch_sdk-0.1.0/src/llmswitch/py.typed +0 -0
  24. llmswitch_sdk-0.1.0/src/llmswitch/structured.py +246 -0
  25. llmswitch_sdk-0.1.0/src/llmswitch_sdk.egg-info/PKG-INFO +214 -0
  26. llmswitch_sdk-0.1.0/src/llmswitch_sdk.egg-info/SOURCES.txt +32 -0
  27. llmswitch_sdk-0.1.0/src/llmswitch_sdk.egg-info/dependency_links.txt +1 -0
  28. llmswitch_sdk-0.1.0/src/llmswitch_sdk.egg-info/requires.txt +22 -0
  29. llmswitch_sdk-0.1.0/src/llmswitch_sdk.egg-info/top_level.txt +1 -0
  30. llmswitch_sdk-0.1.0/tests/test_client_async.py +99 -0
  31. llmswitch_sdk-0.1.0/tests/test_client_options.py +67 -0
  32. llmswitch_sdk-0.1.0/tests/test_client_sync.py +203 -0
  33. llmswitch_sdk-0.1.0/tests/test_config.py +66 -0
  34. llmswitch_sdk-0.1.0/tests/test_providers.py +199 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Rituraj Raman
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,214 @@
1
+ Metadata-Version: 2.4
2
+ Name: llmswitch-sdk
3
+ Version: 0.1.0
4
+ Summary: One Python client for chat completions and embeddings across OpenAI, Azure OpenAI, Azure AI Foundry, Anthropic and Vertex AI.
5
+ Author: Rituraj Raman
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/RITURAJRAMAN/llmswitch
8
+ Project-URL: Repository, https://github.com/RITURAJRAMAN/llmswitch
9
+ Project-URL: Issues, https://github.com/RITURAJRAMAN/llmswitch/issues
10
+ Project-URL: Changelog, https://github.com/RITURAJRAMAN/llmswitch/blob/main/CHANGELOG.md
11
+ Keywords: llm,openai,azure,anthropic,vertex-ai,embeddings,chat-completions
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3 :: Only
14
+ Classifier: Programming Language :: Python :: 3.11
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Classifier: Typing :: Typed
17
+ Requires-Python: >=3.11
18
+ Description-Content-Type: text/markdown
19
+ License-File: LICENSE
20
+ Requires-Dist: pydantic<3,>=2.14.1
21
+ Requires-Dist: openai<4.0.0,>=3.36.1
22
+ Provides-Extra: anthropic
23
+ Requires-Dist: anthropic<3,>=2.1.0; extra == "anthropic"
24
+ Provides-Extra: azure-foundry
25
+ Requires-Dist: azure-ai-inference==1.0.0b9; extra == "azure-foundry"
26
+ Requires-Dist: aiohttp<4.0.0,>=3.14.5; extra == "azure-foundry"
27
+ Provides-Extra: vertexai
28
+ Requires-Dist: google-genai<3,>=2.32.0; extra == "vertexai"
29
+ Provides-Extra: all
30
+ Requires-Dist: llmswitch-sdk[anthropic,azure-foundry,vertexai]; extra == "all"
31
+ Provides-Extra: dev
32
+ Requires-Dist: build>=1.6.1; extra == "dev"
33
+ Requires-Dist: pytest>=9.1.1; extra == "dev"
34
+ Requires-Dist: pytest-asyncio>=1.4.0; extra == "dev"
35
+ Requires-Dist: ruff>=0.17.1; extra == "dev"
36
+ Requires-Dist: twine>=7.0.0; extra == "dev"
37
+ Dynamic: license-file
38
+
39
+ # llmswitch
40
+
41
+ One Python client for chat completions and embeddings across OpenAI, Azure OpenAI,
42
+ Azure AI Foundry, Anthropic and Vertex AI. Switch providers by changing the constructor
43
+ arguments; the call sites stay the same.
44
+
45
+ Supported in 0.1.0: synchronous and asynchronous chat completions (including streaming and
46
+ structured output) and embeddings.
47
+
48
+ ## Installation
49
+
50
+ ```bash
51
+ pip install llmswitch-sdk # core: OpenAI and Azure OpenAI
52
+ pip install "llmswitch-sdk[anthropic]" # adds Anthropic
53
+ pip install "llmswitch-sdk[azure-foundry]" # adds Azure AI Foundry
54
+ pip install "llmswitch-sdk[vertexai]" # adds Vertex AI
55
+ pip install "llmswitch-sdk[all]" # every provider
56
+ ```
57
+
58
+ Requires Python 3.11 or later.
59
+
60
+ ## Quick start
61
+
62
+ ```python
63
+ from llmswitch import LLMSwitch
64
+
65
+ client = LLMSwitch(provider="openai", api_key="sk-...", model="gpt-4o")
66
+
67
+ reply = client.chat.completions.create(
68
+ messages=[{"role": "user", "content": "Say hello in French."}],
69
+ )
70
+ print(reply.choices[0].message.content)
71
+
72
+ vectors = client.embeddings.create(["first text", "second text"], model="text-embedding-3-small")
73
+ print(len(vectors.data), len(vectors.data[0].embedding))
74
+ ```
75
+
76
+ Async:
77
+
78
+ ```python
79
+ import asyncio
80
+ from llmswitch import AsyncLLMSwitch
81
+
82
+ async def main():
83
+ async with AsyncLLMSwitch(provider="anthropic", api_key="sk-ant-...", model="claude-opus-4-5") as client:
84
+ reply = await client.chat.completions.create(
85
+ messages=[{"role": "user", "content": "Hello"}],
86
+ )
87
+ print(reply.choices[0].message.content)
88
+
89
+ asyncio.run(main())
90
+ ```
91
+
92
+ Streaming returns an iterator of chunks:
93
+
94
+ ```python
95
+ for chunk in client.chat.completions.create(messages=messages, stream=True):
96
+ if chunk.choices and chunk.choices[0].delta.content:
97
+ print(chunk.choices[0].delta.content, end="")
98
+ ```
99
+
100
+ With `AsyncLLMSwitch`, await the call first, then iterate with `async for`:
101
+
102
+ ```python
103
+ stream = await client.chat.completions.create(messages=messages, stream=True)
104
+ async for chunk in stream:
105
+ if chunk.choices and chunk.choices[0].delta.content:
106
+ print(chunk.choices[0].delta.content, end="")
107
+ ```
108
+
109
+ Structured output takes a Pydantic model class:
110
+
111
+ ```python
112
+ from pydantic import BaseModel
113
+
114
+ class City(BaseModel):
115
+ name: str
116
+ country: str
117
+
118
+ parsed = client.chat.completions.parse(
119
+ messages=[{"role": "user", "content": "Largest city in Japan?"}],
120
+ response_format=City,
121
+ )
122
+ print(parsed.choices[0].message.parsed)
123
+ ```
124
+
125
+ ## Providers
126
+
127
+ | `provider` | Required settings | Chat (sync / async) | Embeddings |
128
+ |---|---|---|---|
129
+ | `openai` | `api_key` | yes / yes | yes |
130
+ | `azure_openai` | `api_key`, `endpoint`, `api_version`, `deployment_name` | yes / yes | yes |
131
+ | `azure_foundry` | `api_key`, `endpoint`, `api_version`, `deployment_name` | yes / yes | yes |
132
+ | `anthropic` | `api_key` | yes / yes | no |
133
+ | `vertexai` | `location`, `credentials_path` | yes / no | no |
134
+
135
+ Optional settings: `timeout` (seconds) for every provider; `endpoint`, `organization` and
136
+ `project` for `openai`; `endpoint` and `enable_prompt_caching` (default `True`) for `anthropic`;
137
+ `scopes` for `vertexai`.
138
+
139
+ Azure OpenAI example:
140
+
141
+ ```python
142
+ client = LLMSwitch(
143
+ provider="azure_openai",
144
+ api_key="...",
145
+ endpoint="https://your-resource.openai.azure.com/",
146
+ api_version="2024-10-21",
147
+ deployment_name="your-gpt4o-deployment",
148
+ )
149
+ ```
150
+
151
+ When `deployment_name` is set it is always used as the model. A per-call `model` argument
152
+ only overrides the configured model when no deployment is configured.
153
+
154
+ Vertex AI example:
155
+
156
+ ```python
157
+ client = LLMSwitch(
158
+ provider="vertexai",
159
+ location="us-central1",
160
+ credentials_path="/path/to/service_account.json",
161
+ model="gemini-2.5-pro",
162
+ )
163
+ ```
164
+
165
+ Credentials are never read from the environment or from `.env` files by the library. Load
166
+ them in your application and pass them to the constructor.
167
+
168
+ ## Native SDK options
169
+
170
+ Pass extra arguments for the provider's native client with `client_options`. They are
171
+ given to the SDK client constructor as keyword arguments:
172
+
173
+ ```python
174
+ client = LLMSwitch(
175
+ provider="openai",
176
+ api_key="sk-...",
177
+ model="gpt-4o",
178
+ client_options={"max_retries": 3, "default_headers": {"X-Team": "search"}},
179
+ )
180
+ ```
181
+
182
+ Options that the unified constructor sets itself are rejected with a `ConfigurationError`:
183
+ `api_key`, `api_version`, `azure_endpoint`, `azure_deployment`, `base_url`, `endpoint`,
184
+ `organization`, `project`, `timeout`, `credential`, `credentials`, `location`, `vertexai`
185
+ and `http_options`. Use the unified argument for those instead.
186
+
187
+ Per-call keyword arguments are passed to the provider request unchanged, so provider-specific
188
+ parameters such as `seed`, `top_k` or `extra_body` can be given on `create()`.
189
+
190
+ ## Errors
191
+
192
+ All errors derive from `LLMSwitchError`:
193
+
194
+ - `ConfigurationError`: invalid or missing constructor settings.
195
+ - `ChatCompletionError`, `EmbeddingGenerationError`: provider failures. `status_code` carries the HTTP status when known.
196
+ - `AuthenticationError`, `APIConnectionError`, `RateLimitExceededError`, `TimeOutError`, `ContentFilterError`, `ModelNotFoundError`, `InvalidResponseError`.
197
+
198
+ Calling an operation a provider does not support (for example embeddings on `anthropic`)
199
+ raises `LLMSwitchError` before any request is sent.
200
+
201
+ ## Development
202
+
203
+ ```bash
204
+ python -m venv .venv && source .venv/bin/activate
205
+ pip install -e ".[all,dev]"
206
+ pytest
207
+ ruff check src tests
208
+ ```
209
+
210
+ The tests use mocks and do not contact any provider. Live tests are not part of 0.1.0.
211
+
212
+ ## License
213
+
214
+ MIT. See [LICENSE](LICENSE).
@@ -0,0 +1,176 @@
1
+ # llmswitch
2
+
3
+ One Python client for chat completions and embeddings across OpenAI, Azure OpenAI,
4
+ Azure AI Foundry, Anthropic and Vertex AI. Switch providers by changing the constructor
5
+ arguments; the call sites stay the same.
6
+
7
+ Supported in 0.1.0: synchronous and asynchronous chat completions (including streaming and
8
+ structured output) and embeddings.
9
+
10
+ ## Installation
11
+
12
+ ```bash
13
+ pip install llmswitch-sdk # core: OpenAI and Azure OpenAI
14
+ pip install "llmswitch-sdk[anthropic]" # adds Anthropic
15
+ pip install "llmswitch-sdk[azure-foundry]" # adds Azure AI Foundry
16
+ pip install "llmswitch-sdk[vertexai]" # adds Vertex AI
17
+ pip install "llmswitch-sdk[all]" # every provider
18
+ ```
19
+
20
+ Requires Python 3.11 or later.
21
+
22
+ ## Quick start
23
+
24
+ ```python
25
+ from llmswitch import LLMSwitch
26
+
27
+ client = LLMSwitch(provider="openai", api_key="sk-...", model="gpt-4o")
28
+
29
+ reply = client.chat.completions.create(
30
+ messages=[{"role": "user", "content": "Say hello in French."}],
31
+ )
32
+ print(reply.choices[0].message.content)
33
+
34
+ vectors = client.embeddings.create(["first text", "second text"], model="text-embedding-3-small")
35
+ print(len(vectors.data), len(vectors.data[0].embedding))
36
+ ```
37
+
38
+ Async:
39
+
40
+ ```python
41
+ import asyncio
42
+ from llmswitch import AsyncLLMSwitch
43
+
44
+ async def main():
45
+ async with AsyncLLMSwitch(provider="anthropic", api_key="sk-ant-...", model="claude-opus-4-5") as client:
46
+ reply = await client.chat.completions.create(
47
+ messages=[{"role": "user", "content": "Hello"}],
48
+ )
49
+ print(reply.choices[0].message.content)
50
+
51
+ asyncio.run(main())
52
+ ```
53
+
54
+ Streaming returns an iterator of chunks:
55
+
56
+ ```python
57
+ for chunk in client.chat.completions.create(messages=messages, stream=True):
58
+ if chunk.choices and chunk.choices[0].delta.content:
59
+ print(chunk.choices[0].delta.content, end="")
60
+ ```
61
+
62
+ With `AsyncLLMSwitch`, await the call first, then iterate with `async for`:
63
+
64
+ ```python
65
+ stream = await client.chat.completions.create(messages=messages, stream=True)
66
+ async for chunk in stream:
67
+ if chunk.choices and chunk.choices[0].delta.content:
68
+ print(chunk.choices[0].delta.content, end="")
69
+ ```
70
+
71
+ Structured output takes a Pydantic model class:
72
+
73
+ ```python
74
+ from pydantic import BaseModel
75
+
76
+ class City(BaseModel):
77
+ name: str
78
+ country: str
79
+
80
+ parsed = client.chat.completions.parse(
81
+ messages=[{"role": "user", "content": "Largest city in Japan?"}],
82
+ response_format=City,
83
+ )
84
+ print(parsed.choices[0].message.parsed)
85
+ ```
86
+
87
+ ## Providers
88
+
89
+ | `provider` | Required settings | Chat (sync / async) | Embeddings |
90
+ |---|---|---|---|
91
+ | `openai` | `api_key` | yes / yes | yes |
92
+ | `azure_openai` | `api_key`, `endpoint`, `api_version`, `deployment_name` | yes / yes | yes |
93
+ | `azure_foundry` | `api_key`, `endpoint`, `api_version`, `deployment_name` | yes / yes | yes |
94
+ | `anthropic` | `api_key` | yes / yes | no |
95
+ | `vertexai` | `location`, `credentials_path` | yes / no | no |
96
+
97
+ Optional settings: `timeout` (seconds) for every provider; `endpoint`, `organization` and
98
+ `project` for `openai`; `endpoint` and `enable_prompt_caching` (default `True`) for `anthropic`;
99
+ `scopes` for `vertexai`.
100
+
101
+ Azure OpenAI example:
102
+
103
+ ```python
104
+ client = LLMSwitch(
105
+ provider="azure_openai",
106
+ api_key="...",
107
+ endpoint="https://your-resource.openai.azure.com/",
108
+ api_version="2024-10-21",
109
+ deployment_name="your-gpt4o-deployment",
110
+ )
111
+ ```
112
+
113
+ When `deployment_name` is set it is always used as the model. A per-call `model` argument
114
+ only overrides the configured model when no deployment is configured.
115
+
116
+ Vertex AI example:
117
+
118
+ ```python
119
+ client = LLMSwitch(
120
+ provider="vertexai",
121
+ location="us-central1",
122
+ credentials_path="/path/to/service_account.json",
123
+ model="gemini-2.5-pro",
124
+ )
125
+ ```
126
+
127
+ Credentials are never read from the environment or from `.env` files by the library. Load
128
+ them in your application and pass them to the constructor.
129
+
130
+ ## Native SDK options
131
+
132
+ Pass extra arguments for the provider's native client with `client_options`. They are
133
+ given to the SDK client constructor as keyword arguments:
134
+
135
+ ```python
136
+ client = LLMSwitch(
137
+ provider="openai",
138
+ api_key="sk-...",
139
+ model="gpt-4o",
140
+ client_options={"max_retries": 3, "default_headers": {"X-Team": "search"}},
141
+ )
142
+ ```
143
+
144
+ Options that the unified constructor sets itself are rejected with a `ConfigurationError`:
145
+ `api_key`, `api_version`, `azure_endpoint`, `azure_deployment`, `base_url`, `endpoint`,
146
+ `organization`, `project`, `timeout`, `credential`, `credentials`, `location`, `vertexai`
147
+ and `http_options`. Use the unified argument for those instead.
148
+
149
+ Per-call keyword arguments are passed to the provider request unchanged, so provider-specific
150
+ parameters such as `seed`, `top_k` or `extra_body` can be given on `create()`.
151
+
152
+ ## Errors
153
+
154
+ All errors derive from `LLMSwitchError`:
155
+
156
+ - `ConfigurationError`: invalid or missing constructor settings.
157
+ - `ChatCompletionError`, `EmbeddingGenerationError`: provider failures. `status_code` carries the HTTP status when known.
158
+ - `AuthenticationError`, `APIConnectionError`, `RateLimitExceededError`, `TimeOutError`, `ContentFilterError`, `ModelNotFoundError`, `InvalidResponseError`.
159
+
160
+ Calling an operation a provider does not support (for example embeddings on `anthropic`)
161
+ raises `LLMSwitchError` before any request is sent.
162
+
163
+ ## Development
164
+
165
+ ```bash
166
+ python -m venv .venv && source .venv/bin/activate
167
+ pip install -e ".[all,dev]"
168
+ pytest
169
+ ruff check src tests
170
+ ```
171
+
172
+ The tests use mocks and do not contact any provider. Live tests are not part of 0.1.0.
173
+
174
+ ## License
175
+
176
+ MIT. See [LICENSE](LICENSE).
@@ -0,0 +1,74 @@
1
+ [build-system]
2
+ requires = ["setuptools>=84.2.1"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "llmswitch-sdk"
7
+ version = "0.1.0"
8
+ description = "One Python client for chat completions and embeddings across OpenAI, Azure OpenAI, Azure AI Foundry, Anthropic and Vertex AI."
9
+ readme = "README.md"
10
+ license = "MIT"
11
+ license-files = ["LICENSE"]
12
+ requires-python = ">=3.11"
13
+ authors = [{ name = "Rituraj Raman" }]
14
+ keywords = ["llm", "openai", "azure", "anthropic", "vertex-ai", "embeddings", "chat-completions"]
15
+ classifiers = [
16
+ "Programming Language :: Python :: 3",
17
+ "Programming Language :: Python :: 3 :: Only",
18
+ "Programming Language :: Python :: 3.11",
19
+ "Programming Language :: Python :: 3.12",
20
+ "Typing :: Typed",
21
+ ]
22
+
23
+ dependencies = [
24
+ "pydantic>=2.14.1,<3",
25
+ "openai>=3.36.1,<4.0.0",
26
+ ]
27
+
28
+ [project.urls]
29
+ Homepage = "https://github.com/RITURAJRAMAN/llmswitch"
30
+ Repository = "https://github.com/RITURAJRAMAN/llmswitch"
31
+ Issues = "https://github.com/RITURAJRAMAN/llmswitch/issues"
32
+ Changelog = "https://github.com/RITURAJRAMAN/llmswitch/blob/main/CHANGELOG.md"
33
+
34
+ [project.optional-dependencies]
35
+ anthropic = [
36
+ "anthropic>=2.1.0,<3",
37
+ ]
38
+ azure-foundry = [
39
+ "azure-ai-inference==1.0.0b9",
40
+ "aiohttp>=3.14.5,<4.0.0",
41
+ ]
42
+ vertexai = [
43
+ "google-genai>=2.32.0,<3",
44
+ ]
45
+ all = [
46
+ "llmswitch-sdk[anthropic,azure-foundry,vertexai]",
47
+ ]
48
+ dev = [
49
+ "build>=1.6.1",
50
+ "pytest>=9.1.1",
51
+ "pytest-asyncio>=1.4.0",
52
+ "ruff>=0.17.1",
53
+ "twine>=7.0.0",
54
+ ]
55
+
56
+ [tool.setuptools.packages.find]
57
+ where = ["src"]
58
+ include = ["llmswitch*"]
59
+
60
+ [tool.setuptools.package-data]
61
+ llmswitch = ["py.typed"]
62
+
63
+ [tool.pytest.ini_options]
64
+ testpaths = ["tests"]
65
+ asyncio_mode = "auto"
66
+
67
+ [tool.ruff]
68
+ line-length = 120
69
+ target-version = "py311"
70
+ src = ["src"]
71
+
72
+ [tool.ruff.lint]
73
+ select = ["E", "F", "W", "I", "B"]
74
+ ignore = ["E501"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,47 @@
1
+ """
2
+ llmswitch
3
+
4
+ One client interface for chat completions and embeddings across OpenAI, Azure
5
+ OpenAI, Azure AI Foundry, Anthropic and Vertex AI.
6
+
7
+ from llmswitch import LLMSwitch
8
+ """
9
+ import logging
10
+
11
+ __version__ = "0.1.0"
12
+
13
+ logging.getLogger(__name__).addHandler(logging.NullHandler())
14
+
15
+ from .core import AsyncLLMSwitch, LLMSwitch # noqa: E402
16
+ from .errors import ( # noqa: E402
17
+ APIConnectionError,
18
+ AuthenticationError,
19
+ ChatCompletionError,
20
+ ConfigurationError,
21
+ ContentFilterError,
22
+ EmbeddingGenerationError,
23
+ InvalidResponseError,
24
+ LLMSwitchError,
25
+ ModelNotFoundError,
26
+ RateLimitExceededError,
27
+ TimeOutError,
28
+ )
29
+ from .models import ProviderExtensions # noqa: E402
30
+
31
+ __all__ = [
32
+ "__version__",
33
+ "LLMSwitch",
34
+ "AsyncLLMSwitch",
35
+ "LLMSwitchError",
36
+ "ConfigurationError",
37
+ "ModelNotFoundError",
38
+ "APIConnectionError",
39
+ "AuthenticationError",
40
+ "RateLimitExceededError",
41
+ "ChatCompletionError",
42
+ "EmbeddingGenerationError",
43
+ "InvalidResponseError",
44
+ "ContentFilterError",
45
+ "TimeOutError",
46
+ "ProviderExtensions",
47
+ ]
@@ -0,0 +1,39 @@
1
+ from abc import ABC, abstractmethod
2
+
3
+
4
+ class BaseChatProvider(ABC):
5
+ """
6
+ Abstract base class for chat providers.
7
+ """
8
+
9
+ @abstractmethod
10
+ def create(self, messages, **kwargs):
11
+ """
12
+ Create a chat completion.
13
+
14
+ Args:
15
+ messages: List of message dicts or objects.
16
+ **kwargs: Additional parameters for provider.
17
+
18
+ Returns:
19
+ ChatCompletion or generator of ChatCompletionChunk.
20
+ """
21
+
22
+
23
+ class BaseEmbeddingProvider(ABC):
24
+ """
25
+ Abstract base class for embedding providers.
26
+ """
27
+
28
+ @abstractmethod
29
+ def create(self, inputs, **kwargs):
30
+ """
31
+ Create embeddings for the given inputs.
32
+
33
+ Args:
34
+ inputs: List of input strings or objects.
35
+ **kwargs: Additional parameters for provider.
36
+
37
+ Returns:
38
+ Embedding response object.
39
+ """
@@ -0,0 +1,115 @@
1
+ """
2
+ config.py
3
+
4
+ Pydantic configuration models, one per supported provider. ``CONFIG_REGISTRY``
5
+ maps each provider name to its configuration class.
6
+ """
7
+
8
+ from typing import Any, Optional
9
+
10
+ from pydantic import BaseModel, ConfigDict, Field, ValidationInfo, field_validator
11
+
12
+ # Native-SDK client arguments that the unified constructor sets itself. They cannot
13
+ # be overridden through ``client_options``; pass them as the unified arguments instead.
14
+ RESERVED_CLIENT_OPTIONS = frozenset({
15
+ "api_key", "api_version", "azure_endpoint", "azure_deployment", "base_url", "endpoint",
16
+ "organization", "project", "timeout", "credential", "credentials", "location",
17
+ "vertexai", "http_options",
18
+ })
19
+
20
+
21
+ class BaseLLMConfig(BaseModel):
22
+ """Fields shared by every provider. Provider configs inherit from this class."""
23
+
24
+ # The unified constructor accepts the union of provider-specific fields;
25
+ # each provider reads what it needs and ignores the rest.
26
+ model_config = ConfigDict(extra="allow", str_strip_whitespace=True)
27
+
28
+ provider: str = Field(..., min_length=1, description="Provider name (openai, azure_openai, ...)")
29
+ model: Optional[str] = Field(default=None, description="Model name or ID")
30
+ deployment_name: Optional[str] = Field(
31
+ default=None,
32
+ description="Deployment or model alias; when set it takes precedence over the per-call model",
33
+ )
34
+ timeout: Optional[float] = Field(default=None, gt=0, allow_inf_nan=False,
35
+ description="Request timeout in seconds")
36
+ client_options: Optional[dict[str, Any]] = Field(
37
+ default=None,
38
+ description="Extra keyword arguments passed to the native SDK client constructor, "
39
+ "for example default_headers or max_retries",
40
+ )
41
+
42
+ @field_validator("client_options")
43
+ @classmethod
44
+ def reject_reserved_client_options(cls, value: Optional[dict[str, Any]]) -> Optional[dict[str, Any]]:
45
+ if value is None:
46
+ return None
47
+ reserved = sorted(RESERVED_CLIENT_OPTIONS.intersection(value))
48
+ if reserved:
49
+ raise ValueError(
50
+ f"client_options cannot set {', '.join(reserved)}; use the unified constructor arguments instead"
51
+ )
52
+ return value
53
+
54
+ @field_validator("provider", mode="before")
55
+ @classmethod
56
+ def required_string_fields(cls, v, info: ValidationInfo):
57
+ if v is None or (isinstance(v, str) and not v.strip()):
58
+ raise ValueError(f"{info.field_name} is required and cannot be empty")
59
+ return v
60
+
61
+
62
+ class AzureOpenAIConfig(BaseLLMConfig):
63
+ """Azure OpenAI: API key, endpoint, API version and deployment name are required."""
64
+
65
+ api_key: str = Field(..., min_length=1, description="API key for authentication")
66
+ endpoint: str = Field(..., min_length=1, description="Azure OpenAI resource endpoint")
67
+ api_version: str = Field(..., min_length=1, description="Azure OpenAI API version")
68
+ deployment_name: str = Field(..., min_length=1, description="Azure OpenAI deployment name")
69
+
70
+
71
+ class AzureFoundryConfig(BaseLLMConfig):
72
+ """Azure AI Foundry: API key, endpoint, API version and deployment name are required."""
73
+
74
+ api_key: str = Field(..., min_length=1, description="API key for authentication")
75
+ endpoint: str = Field(..., min_length=1, description="Azure AI Foundry endpoint")
76
+ deployment_name: str = Field(..., min_length=1, description="Deployment or model name")
77
+ api_version: str = Field(..., min_length=1, description="Azure AI Foundry API version")
78
+
79
+
80
+ class VertexAIConfig(BaseLLMConfig):
81
+ """Vertex AI: location and service-account credentials are required."""
82
+
83
+ location: str = Field(..., min_length=1, description="GCP location (e.g. us-central1)")
84
+ credentials_path: str = Field(..., min_length=1, description="Path to GCP credentials JSON file")
85
+ scopes: list[str] = Field(default_factory=lambda: ["https://www.googleapis.com/auth/cloud-platform"],
86
+ description="OAuth scopes for GCP authentication")
87
+
88
+
89
+ class OpenAIConfig(BaseLLMConfig):
90
+ """Native OpenAI: API key is required; endpoint, organization and project are optional."""
91
+
92
+ api_key: str = Field(..., min_length=1, description="OpenAI API key")
93
+ endpoint: Optional[str] = Field(default=None, description="Custom base URL for compatible APIs")
94
+ organization: Optional[str] = Field(default=None, description="OpenAI organization ID")
95
+ project: Optional[str] = Field(default=None, description="OpenAI project ID")
96
+
97
+
98
+ class AnthropicConfig(BaseLLMConfig):
99
+ """Anthropic (Claude): API key is required. Prompt caching is on by default."""
100
+
101
+ api_key: str = Field(..., min_length=1, description="Anthropic API key")
102
+ endpoint: Optional[str] = Field(default=None, description="Custom base URL for an Anthropic-compatible gateway")
103
+ enable_prompt_caching: bool = Field(
104
+ default=True,
105
+ description="Inject cache_control on system messages and tool definitions",
106
+ )
107
+
108
+
109
+ CONFIG_REGISTRY = {
110
+ "azure_openai": AzureOpenAIConfig,
111
+ "azure_foundry": AzureFoundryConfig,
112
+ "vertexai": VertexAIConfig,
113
+ "openai": OpenAIConfig,
114
+ "anthropic": AnthropicConfig,
115
+ }