Swiss AI Hub REST API, WebSocket gateway, and MCP server (FastAPI) for the Swiss AI Agent Protocol.
Project description
swiss-ai-hub-api
The REST API, WebSocket gateway, and MCP server for Swiss AI Hub — the bridge between clients and the platform, composable from a set of controllers.
What is Swiss AI Hub?
Swiss AI Hub is an open-source, self-hosted AI platform for enterprises. One
docker compose up starts ~30 integrated containers — LLM gateway (LiteLLM), vector search (Milvus), data pipelines
(Dagster), SSO (Keycloak), observability (Langfuse), a chat UI (Open-WebUI), and more. Agents, processes, and pipelines
talk to each other over NATS using the Swiss AI Agent Protocol; this package is how the outside world talks to them.
What is this package?
swiss-ai-hub-api is a FastAPI application that translates between HTTP/WebSocket and
the platform's internal event protocol. It is assembled from controllers (agents, threads, knowledge, models, roles,
…) that you mount on an ApiRunner. Its defining feature: it doesn't hardcode agent endpoints — it discovers online
agents and processes over NATS and registers HTTP + streaming endpoints for them at runtime.
What you get out of the box:
- Dynamic agent & process endpoints — for every online agent, the API auto-registers
POST …/agents/classes/<Class>/instances/<id>/UserMessageEvent(and a/streamvariant). No code, no redeploy when agents come and go. - Real-time WebSocket — clients subscribe to a thread and receive agent display events (chunks, thoughts, LLM output) live.
- OpenAI-compatible endpoints —
/openai/chat/completions,/embeddings,/audio/*,/images/generations— point any OpenAI client at the platform. - MCP server —
runner.create_app()also mounts a Model Context Protocol server at/mcp. - Management endpoints — agents, threads, knowledge bases, models, roles, tokens, memory, dashboards — via the controllers you choose to mount.
It builds on swiss-ai-hub-core (installed automatically).
Should you use this package?
Probably not directly — most deployments use the pre-built Docker image, which ships the full API ready to go:
# docker-compose.yml
services:
api:
image: ghcr.io/bbvch-ai/aihub-core/api:latest
Use this PyPI package when you want to compose your own API — mount only the controllers you need, add your own controllers alongside the built-in ones, or embed the gateway inside a larger FastAPI application. It's an SDK for building a custom backend on top of Swiss AI Hub, not just a standalone server.
Installation
pip install swiss-ai-hub-api
# or
uv add swiss-ai-hub-api
Requires Python 3.13.
Quick start
An API is an ApiRunner with the controllers you mount. Mount a few — or all of them — and call create_app() to get a
standard ASGI app:
# app.py
from swiss_ai_hub.api.runners import ApiRunner
from swiss_ai_hub.api.routes import ApiHealthController, AgentController, ThreadController, EventController
from swiss_ai_hub.core.auth import TokenAndOauth2Handler
runner = ApiRunner()
auth = TokenAndOauth2Handler.from_auth_settings() # Keycloak/OIDC + static-token auth from env
runner.mount(
ApiHealthController(auth=auth).get_health().get_ready(),
AgentController(auth=auth).get_agent_classes().get_agent_class().get_agent_class_instances().create_agent_instance(),
ThreadController(auth=auth).get_user_threads().create_thread().get_thread(),
EventController(auth=auth).ws().get_agent_events_in_thread(), # WebSocket + event history
)
app = runner.create_app() # ASGI app — also mounts the MCP server at /mcp
Serve it like any ASGI app:
uvicorn app:app --host 0.0.0.0 --port 8000
The API mounts under /api/v1. Health is at /api/v1/health; the OpenAPI spec and Swagger UI at /api/v1/openapi.json
and /api/v1/docs. Even with the minimal mount above, the lifetime manager's discovery service registers the dynamic
agent endpoints automatically — they appear in the OpenAPI spec the moment an agent is online.
The full production API mounts ~25 controllers — see
app/main.pyfor the complete list, which doubles as the canonical example.
Talking to an agent
With an agent online and an instance configured, the chat round-trip is a single POST:
curl -X POST -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
http://localhost:8000/api/v1/default/agents/classes/LLMWrappingAgent/instances/my-agent/UserMessageEvent \
-d '{"messages":[{"role":"user","blocks":[{"block_type":"text","text":"Hello!"}]}]}'
The API publishes a StartEvent to the agent over NATS, the agent runs, and the response streams back (use the
/stream variant for token-by-token SSE, or subscribe over the WebSocket for the full event timeline).
Development
The dev stack runs the platform infrastructure (NATS, FerretDB, Valkey, Milvus, LiteLLM, …) in Docker and exposes it on
localhost, so you run the API directly on your host:
# 1. Start the platform infrastructure (from a Swiss AI Hub checkout)
docker compose --env-file .env -f infra/docker-compose.dev.yml up -d
# 2. Load the dev connection settings (localhost endpoints) into your shell
set -a && source .env && set +a
# 3. Run your API — it connects to the dockerized stack and serves on :8000
uvicorn app:app --host 0.0.0.0 --port 8000
The API starts, connects to all backing stores, and its discovery service finds any online agents — their endpoints show up in the served OpenAPI spec, and the admin UI and chat work against your locally-running gateway.
Settings are not auto-loaded from the environment. The SDK reads connection settings only when constructed, so make sure the variables above are exported in the process that runs the API (
set -a && source .env && set +a).
Production
In production the API runs as a container behind Traefik, reaching other services by container hostname.
1. Containerize it — install the SDK from PyPI and serve with Gunicorn + Uvicorn workers (how the platform ships it):
FROM python:3.13-slim
COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/
WORKDIR /app
COPY pyproject.toml uv.lock ./ # your project, depending on swiss-ai-hub-api
RUN uv sync --frozen --no-dev
COPY . .
ENV PATH="/app/.venv/bin:$PATH" PYTHONUNBUFFERED=1
EXPOSE 8000
ENTRYPOINT ["gunicorn", "-w", "4", "-k", "uvicorn.workers.UvicornWorker", \
"--forwarded-allow-ips=*", "-b", "0.0.0.0:8000", "app:app"]
2. Run it alongside the platform on the right networks. The API is the externally-facing gateway, so it joins
proxy (Traefik ingress) plus the three internal zones backend, data, storage to reach LiteLLM, NATS, the
databases, and S3:
# docker-compose.my-api.yml — deployed alongside the platform
services:
my-api:
image: registry.example.com/my-api:1.0.0
restart: always
environment:
NATS_ENDPOINT: nats://nats:4222
NATS_TOKEN: ${NATS_TOKEN}
REDIS_URL: redis://valkey:6379
REDIS_TOKEN: ${REDIS_TOKEN}
MONGO_CONNECTION_STRING: mongodb://${MONGO_USERNAME}:${MONGO_PASSWORD}@ferretdb:27017/
MILVUS_URL: http://milvus-standalone:19530
S3_STORAGE_ENDPOINT: http://seaweedfs-s3:9000
LITE_LLM_PROXY_BASE_URL: http://litellm:4000
LITE_LLM_PROXY_API_KEY: ${LITELLM_MASTER_KEY}
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8000/api/v1/health"]
interval: 30s
timeout: 10s
retries: 3
labels: # expose through the platform's Traefik (optional)
- "traefik.enable=true"
- "traefik.http.services.my-api.loadbalancer.server.port=8000"
networks: [proxy, backend, data, storage]
networks:
proxy: { external: true }
backend: { external: true }
data: { external: true }
storage: { external: true }
docker compose -f docker-compose.my-api.yml up -d
Reuse the platform's secrets (from its .env) for the ${…} values, and match the actual network names of your
deployment. Drop the proxy network and Traefik labels if you front the API with your own ingress instead.
Network reference.
proxy= external ingress via Traefik.data= NATS, Valkey, FerretDB, Milvus, Neo4j.backend= LiteLLM, OTEL collector.storage= SeaweedFS/S3.
Building your own endpoints
Endpoints follow a Controller → Service → DTO → Entity separation:
- Controller — extends
Controller(global) orTenantScopedController(mounted under/api/v1/{tenant_id}/). Definesname/description/iconclass attributes and exposes each route through a fluent method that returnsself. Auth, permission checks (user_with_permission("aihub.user.<resource>.{path_param}")), OpenTelemetry spans, and tenant scoping all come from the base class. - Service — stateless
@staticmethodbusiness logic; calls entities for persistence. - DTO — Pydantic v2 models with
from_entity()factories andin_locale(t)localization.
You then mount it next to the built-ins, exactly like the production app/main.py:
runner.mount(MyController(auth=auth).list_items().create_item().delete_item())
AgentController
is the canonical reference, and the documentation walks through the full
new-endpoint workflow (DTOs → service → controller → mount). Every controller's HTTP dependencies (NATS client, S3,
Milvus, the event distributors, the WebSocket manager) are injected via FastAPI Depends/Security.
Links
- Source & issues: https://github.com/bbvch-ai/aihub-core
- Documentation: https://bbvch-ai.github.io/aihub-core/
- The full SDK (meta package): https://pypi.org/project/swiss-ai-hub/
License
Apache-2.0 — see packages/api/LICENSE. For the full per-package license matrix, see LICENSES.md.
Part of Swiss AI Hub. Built in Switzerland by bbv Software Services.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file swiss_ai_hub_api-0.292.2.tar.gz.
File metadata
- Download URL: swiss_ai_hub_api-0.292.2.tar.gz
- Upload date:
- Size: 148.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.11.19 {"installer":{"name":"uv","version":"0.11.19","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7dfa7fb9b8d28f4dd913c37d233028dd6fa2595cf998607114dcce9b4144f2fe
|
|
| MD5 |
38c542d75b3a193b0d62a6dc7bde0c80
|
|
| BLAKE2b-256 |
2c305e9591420886d39b171ed7e764cd75e3434b74da55e133f30e95f2c9d7ce
|
File details
Details for the file swiss_ai_hub_api-0.292.2-py3-none-any.whl.
File metadata
- Download URL: swiss_ai_hub_api-0.292.2-py3-none-any.whl
- Upload date:
- Size: 277.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.11.19 {"installer":{"name":"uv","version":"0.11.19","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3436b11f8aef57899db367f4e3bbb385a7df162ca2a90f2d457c954c7d94f3aa
|
|
| MD5 |
29de83f419c95a7cf033333add2b78f9
|
|
| BLAKE2b-256 |
6cd463f5c1155d3b64e1dd1728dd38d55c37cccb682b327dd09e1d3c0c3d96e4
|