galene-ai
Python SDK for the Galene.AI platform — a single,
typed client covering all 317 REST operations of the API, generated and
hand-crafted from the vendored OpenAPI spec (spec/openapi.json).
- Familiar ergonomics —
client.chat.create(...),client.files.upload(...), OpenAI-SDK-style. - Sync and async —
GaleneandAsyncGaleneshare one core and expose an identical API. - Typed —
msgspecmodels generated from the spec;mypy --strictclean. - Slim — two runtime dependencies:
httpxandmsgspec. - Production-ready — retries with backoff, pluggable auth (API key or username/password with auto-refresh), SSE streaming, cursor pagination.
Install
pip install galene-ai
Requires Python 3.12+.
Quickstart
from galene_ai import Galene
from galene_ai.models._generated import VectorStoreCreate, VectorStoreFileAdd, VectorStoreSearchRequest
client = Galene(api_key="sk-...") # or set GALENE_AI_API_KEY in the environment
# Chat completion
reply = client.chat.create(
model="Galene/LLM",
messages=[{"role": "user", "content": "Hi"}],
)
print(reply.choices[0].message.content)
# Upload a file
uploaded = client.files.upload(b"some file bytes", purpose="user_data", filename="doc.txt")
# Vector stores (RAG)
vector_store = client.vector_stores.create(VectorStoreCreate(name="kb"))
client.vector_stores.add_file(vector_store.id, VectorStoreFileAdd(file_id=uploaded.id))
results = client.vector_stores.search(vector_store.id, VectorStoreSearchRequest(query="pricing"))
client.close()
VectorStoreCreate, VectorStoreFileAdd, and VectorStoreSearchRequest are
generated request models (see
examples/vector_store_rag.py for the full,
runnable version). Galene is also a context manager:
with Galene(api_key="sk-...") as client:
... # client.close() is called automatically
Authentication
The default is a static API key, supplied explicitly or via environment variable:
from galene_ai import Galene
client = Galene(api_key="sk-...") # explicit
client = Galene() # reads GALENE_AI_API_KEY from the environment
Environment variables:
| Variable | Purpose | Default |
|---|---|---|
GALENE_AI_API_KEY |
API key used by the default ApiKeyAuth |
— |
GALENE_AI_BASE_URL |
API base URL | https://api.playground.galene.ai |
Both are optional — the base URL defaults to the public playground, so point it
at your own deployment via the env var or the base_url= argument. The SDK also auto-loads a .env file from the
working directory (real environment variables always win). Copy .env.example
to .env and fill it in:
cp .env.example .env # then set GALENE_AI_API_KEY / GALENE_AI_BASE_URL
For username/password login with transparent token refresh, pass a
SessionAuth instance as auth=:
from galene_ai import Galene, SessionAuth
client = Galene(auth=SessionAuth(username="me@example.com", password="hunter2"))
SessionAuth logs in via POST /login on the first request, attaches the
resulting access token to every request, and — on a 401 — transparently
calls POST /refresh-token and retries once. Auth is pluggable: any
httpx.Auth subclass can be passed as auth=.
Async usage
AsyncGalene mirrors Galene method-for-method:
import asyncio
from galene_ai import AsyncGalene
async def main() -> None:
async with AsyncGalene(api_key="sk-...") as client:
reply = await client.chat.create(
model="Galene/LLM",
messages=[{"role": "user", "content": "Hi"}],
)
print(reply.choices[0].message.content)
asyncio.run(main())
Streaming
Pass stream=True to chat.create (or responses.create) to get a
Stream[dict] (or AsyncStream[dict]) of raw SSE chunks — the spec defines no
fixed chunk schema, so chunks decode as plain dicts in the standard OpenAI
delta shape:
with client.chat.create(
model="Galene/LLM",
messages=[{"role": "user", "content": "Count to five."}],
stream=True,
) as stream:
for chunk in stream:
delta = (chunk.get("choices") or [{}])[0].get("delta") or {}
print(delta.get("content") or "", end="", flush=True)
The async equivalent uses async for chunk in stream: inside async with.
Pagination
List methods on cursor-paginated namespaces (files, vector_stores, ...)
return a CursorPage[T] (or AsyncCursorPage[T]) with .data, .has_more,
and .auto_paging_iter():
for vector_store in client.vector_stores.list().auto_paging_iter():
print(vector_store.id, vector_store.name)
Async (async for on an AsyncCursorPage already walks every page — no
separate auto_paging_iter() call needed):
async for vector_store in await client.vector_stores.list():
print(vector_store.id)
Namespaces that use offset pagination (e.g. observability) instead expose
plain limit/offset keyword arguments and return a list[...] directly.
Error handling
Every error raised by the SDK subclasses GaleneError. HTTP error responses
raise a status-specific subclass of APIStatusError, mirroring the OpenAI SDK
hierarchy:
from galene_ai import GaleneError, NotFoundError
try:
client.files.retrieve("file-does-not-exist")
except NotFoundError as e:
print(e.status_code, e.request_id, e.body)
except GaleneError as e:
print("Galene API error:", e)
GaleneError
├─ APIConnectionError (network failure)
│ └─ APITimeoutError
└─ APIStatusError (.status_code, .request_id, .response, .body)
├─ BadRequestError 400
├─ AuthenticationError 401
├─ PermissionDeniedError 403
├─ NotFoundError 404
├─ ConflictError 409
├─ UnprocessableEntityError 422
├─ RateLimitError 429
└─ InternalServerError 5xx
Connection failures and 408/409/429/500/502/503/504 responses are retried
automatically with exponential backoff (max_retries=2 by default,
configurable via Galene(max_retries=...)); non-idempotent generation
requests are never retried on a read timeout, since a timed-out generation
keeps running server-side.
Namespaces
Every operation is reachable as client.<namespace>.<method>(...)
(async: AsyncGalene, identical shape). Admin operations nest under
client.admin.<namespace>.<method>(...).
OpenAI-compatible surface (30 ops):
| Namespace | Ops | Namespace | Ops |
|---|---|---|---|
chat |
2 | files |
6 |
responses |
2 | vector_stores |
11 |
embeddings |
2 | audio |
4 |
models |
2 | moderations |
1 |
Platform surface (212 ops):
| Namespace | Ops | Namespace | Ops | Namespace | Ops |
|---|---|---|---|---|---|
agents |
46 | attachments |
10 | api_keys |
6 |
observability |
16 | conversations |
10 | groups |
6 |
tickets |
14 | shield |
9 | notifications |
5 |
users |
13 | mcp_servers |
8 | roles |
4 |
auth |
12 | health |
2 | ||
kb_sync |
12 | organizations |
2 | ||
tts |
12 | release_notes |
2 | ||
database_connectors |
11 | changelog |
1 | ||
kb_connectors |
11 |
Admin surface, nested under client.admin.* (74 ops):
| Namespace | Ops | Namespace | Ops |
|---|---|---|---|
admin.org_config |
33 | admin.tickets |
7 |
admin.global_config |
25 | admin.management |
5 |
admin.deployment |
1 | ||
admin.frontend |
1 | ||
admin.search |
1 | ||
admin.user_api_keys |
1 |
For example:
client.agents.list()
client.observability.list_traces(status="running")
client.admin.org_config.set_favicon(org_uuid, favicon_png_bytes)
observability exposes trace/observation listing, read-audit trails, CSV
exports, and aggregate metrics directly (no further sub-resource nesting);
conversations exposes message- and attachment-related helpers the same way.
Endpoints with mode-prefixed variants (chat, responses, embeddings,
models; mode ∈ direct/shield/appliance_shield) expose a
create_with_mode(mode, ...) (or list_with_mode(mode)) sibling method
alongside the plain /v1/... one.
The full, machine-readable operation → namespace/method mapping lives in
spec/operations.json.
Typing note: a handful of endpoints return dict
spec/openapi.json leaves the 200 response schema empty ({}, no $ref) for
a number of operations — most notably the entire agents namespace (46 ops)
and attachments namespace (10 ops), plus a few others scattered across the
SDK (models.list, audio.voices, the observability .csv export
endpoints, some vector_stores delete/batch responses). datamodel-code-generator
has nothing to generate a model from in those cases, so the SDK decodes the
response as a plain dict[str, Any] rather than guessing a shape that might
not match production.
To upgrade one of these to a typed msgspec.Struct:
- Run
scripts/probe_shapes.pyagainst a reachable Galene instance (GALENE_AI_API_KEY=... python scripts/probe_shapes.py). It only issues GET requests — it never mutates data — and writes the observed JSON shapes toprobe_output/<operationId>.json, plus a summary table to stdout. - Add a hand-written
msgspec.Structfor the observed shape tosrc/galene_ai/models/_extra.py(see that module's docstring for the existing pattern used forFile,VectorStore, etc.). - Update the resource method's
cast_to=argument to the new model.
Development
uv sync --extra dev
- Regenerate models (dev-only, output is committed):
uv run python scripts/generate_models.py - Regenerate the operation registry (dev-only, output is committed, drives
the drift test):
uv run python scripts/build_registry.py - Tests:
uv run pytest - Type-check:
uv run mypy - Lint / format:
uv run ruff check ./uv run ruff format .
tests/test_drift.py asserts that the set of operationIds implemented by
resource methods equals the set in spec/operations.json exactly — any API
addition, removal, or rename that isn't reflected in the SDK fails CI.
License
MIT
Metadata
Release files for galene-ai 1.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| galene_ai-1.1.0.tar.gz | 99.1 kB | Without provenance | Details for galene_ai-1.1.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| galene_ai-1.1.0-py3-none-any.whl | Python 3 | none | any | Details for galene_ai-1.1.0-py3-none-any.whl |
Total release size: 220.6 kB
Release files / galene_ai-1.1.0.tar.gz
| Download URL | galene_ai-1.1.0.tar.gz |
|---|---|
| Size | 99.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
2b00a577657b341e965a94ff816751c3857e960258c952b529eb6353e91cc8e9
|
|
BLAKE2b-256 checksum How to use checksums |
54f070bf98fb96763c47f63837a1aba36ae6ccf0e6e0a972bf1a37196c29ac42
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.9.17 {"installer":{"name":"uv","version":"0.9.17","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Release files / galene_ai-1.1.0-py3-none-any.whl
| Download URL | galene_ai-1.1.0-py3-none-any.whl |
|---|---|
| Size | 121.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
f70bd84375e39665cfba4eb1bed341b248263d132d6954509efd459e1c58d8da
|
|
BLAKE2b-256 checksum How to use checksums |
524e5a7789c9181e21fe272a3b159ecdfa4c3daa4903a51e9261539efeddb565
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.9.17 {"installer":{"name":"uv","version":"0.9.17","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Security for galene-ai 1.1.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.