Skip to content

Commit aebf27f

Browse files
committed
Initial otari Python SDK extracted from any-llm gateway provider
OtariClient wraps AsyncOpenAI with gateway-specific auth handling (platform mode via Bearer token, non-platform mode via Otari-Key header) and typed error mapping. Mirrors the TypeScript SDK API surface. Includes: - Chat completions, Responses API, embeddings, list models - Batch operations (create, retrieve, cancel, list, results) - Full error hierarchy (9 exception classes) - 82 unit tests, ruff lint clean
0 parents  commit aebf27f

12 files changed

Lines changed: 1982 additions & 0 deletions

File tree

.gitignore

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# Python
2+
__pycache__/
3+
*.py[cod]
4+
*$py.class
5+
*.so
6+
*.egg-info/
7+
*.egg
8+
dist/
9+
build/
10+
.eggs/
11+
12+
# Virtual environments
13+
.venv/
14+
venv/
15+
env/
16+
17+
# IDE
18+
.idea/
19+
.vscode/
20+
*.swp
21+
*.swo
22+
*~
23+
24+
# Testing / Linting
25+
.pytest_cache/
26+
.coverage
27+
htmlcov/
28+
.mypy_cache/
29+
.ruff_cache/
30+
31+
# OS
32+
.DS_Store
33+
Thumbs.db

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# otari (Python)
2+
3+
Python client for the [otari gateway](https://github.com/mozilla-ai/otari).

pyproject.toml

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
[build-system]
2+
requires = ["hatchling"]
3+
build-backend = "hatchling.build"
4+
5+
[project]
6+
name = "otari"
7+
version = "0.1.0"
8+
description = "Python client for the otari gateway"
9+
readme = "README.md"
10+
license = "Apache-2.0"
11+
requires-python = ">=3.11"
12+
authors = [{ name = "Mozilla AI", email = "ai-engineering@mozilla.com" }]
13+
classifiers = [
14+
"Development Status :: 3 - Alpha",
15+
"Intended Audience :: Developers",
16+
"License :: OSI Approved :: Apache Software License",
17+
"Programming Language :: Python :: 3",
18+
"Programming Language :: Python :: 3.11",
19+
"Programming Language :: Python :: 3.12",
20+
"Programming Language :: Python :: 3.13",
21+
"Topic :: Scientific/Engineering :: Artificial Intelligence",
22+
"Typing :: Typed",
23+
]
24+
dependencies = [
25+
"openai>=1.99.3",
26+
"httpx>=0.25.0",
27+
]
28+
29+
[project.urls]
30+
Homepage = "https://github.com/mozilla-ai/otari-sdk-python"
31+
Documentation = "https://mozilla-ai.github.io/otari/"
32+
Repository = "https://github.com/mozilla-ai/otari-sdk-python"
33+
Issues = "https://github.com/mozilla-ai/otari-sdk-python/issues"
34+
35+
[project.optional-dependencies]
36+
dev = [
37+
"pytest>=8.0",
38+
"pytest-asyncio>=0.24",
39+
"ruff>=0.8",
40+
"mypy>=1.13",
41+
]
42+
43+
[tool.hatch.build.targets.wheel]
44+
packages = ["src/otari"]
45+
46+
[tool.pytest.ini_options]
47+
testpaths = ["tests"]
48+
asyncio_mode = "auto"
49+
50+
[tool.ruff]
51+
target-version = "py311"
52+
line-length = 120
53+
54+
[tool.ruff.lint]
55+
select = ["E", "F", "I", "N", "W", "UP", "S", "B", "A", "C4", "DTZ", "T10", "ISC", "ICN", "PIE", "PT", "RSE", "RET", "SIM", "TID", "TCH", "ARG", "PLC", "PLE", "PLW", "TRY", "FLY", "PERF", "RUF"]
56+
ignore = ["S101", "TRY003", "PLW0603"]
57+
58+
[tool.ruff.lint.isort]
59+
known-first-party = ["otari"]
60+
61+
[tool.mypy]
62+
python_version = "3.11"
63+
strict = true
64+
warn_return_any = true
65+
warn_unused_configs = true

src/otari/__init__.py

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
"""otari - Python client for the otari gateway.
2+
3+
Example::
4+
5+
from otari import OtariClient
6+
7+
client = OtariClient(
8+
api_base="http://localhost:8000",
9+
platform_token="your-token-here",
10+
)
11+
12+
response = await client.completion(
13+
model="openai:gpt-4o-mini",
14+
messages=[{"role": "user", "content": "Hello!"}],
15+
)
16+
print(response.choices[0].message.content)
17+
"""
18+
19+
from importlib.metadata import PackageNotFoundError, version
20+
21+
from otari.client import OtariClient
22+
from otari.errors import (
23+
AuthenticationError,
24+
BatchNotCompleteError,
25+
GatewayTimeoutError,
26+
InsufficientFundsError,
27+
ModelNotFoundError,
28+
OtariError,
29+
RateLimitError,
30+
UnsupportedCapabilityError,
31+
UpstreamProviderError,
32+
)
33+
from otari.types import (
34+
BatchRequestItem,
35+
BatchResult,
36+
BatchResultError,
37+
BatchResultItem,
38+
ChatCompletion,
39+
ChatCompletionChunk,
40+
ChatCompletionMessageParam,
41+
CreateBatchParams,
42+
CreateEmbeddingResponse,
43+
EmbeddingCreateParams,
44+
ListBatchesOptions,
45+
Model,
46+
OtariClientOptions,
47+
Response,
48+
ResponseStreamEvent,
49+
Stream,
50+
)
51+
52+
try:
53+
__version__ = version("otari")
54+
except PackageNotFoundError:
55+
__version__ = "0.0.0-dev"
56+
57+
58+
__all__ = [
59+
"AuthenticationError",
60+
"BatchNotCompleteError",
61+
"BatchRequestItem",
62+
"BatchResult",
63+
"BatchResultError",
64+
"BatchResultItem",
65+
"ChatCompletion",
66+
"ChatCompletionChunk",
67+
"ChatCompletionMessageParam",
68+
"CreateBatchParams",
69+
"CreateEmbeddingResponse",
70+
"EmbeddingCreateParams",
71+
"GatewayTimeoutError",
72+
"InsufficientFundsError",
73+
"ListBatchesOptions",
74+
"Model",
75+
"ModelNotFoundError",
76+
"OtariClient",
77+
"OtariClientOptions",
78+
"OtariError",
79+
"RateLimitError",
80+
"Response",
81+
"ResponseStreamEvent",
82+
"Stream",
83+
"UnsupportedCapabilityError",
84+
"UpstreamProviderError",
85+
]

0 commit comments

Comments
 (0)