Metadata-Version: 2.5
Name: prismal-server
Version: 0.2.0
Summary: Reference host for the prismal agent engine — a thin FastAPI/ASGI process serving prismal over REST/SSE and inbound A2A.
Project-URL: Homepage, https://github.com/prismal-ai/prismal-server
Project-URL: Repository, https://github.com/prismal-ai/prismal-server
Project-URL: Engine, https://github.com/prismal-ai/prismal
Project-URL: SDK, https://github.com/prismal-ai/prismal-sdk
Author-email: Ernesto Crespo <ecrespo@gmail.com>
License: MIT License
        
        Copyright (c) 2026 prismal-ai
        
        Permission is hereby granted, free of charge, to any person obtaining a copy
        of this software and associated documentation files (the "Software"), to deal
        in the Software without restriction, including without limitation the rights
        to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
        copies of the Software, and to permit persons to whom the Software is
        furnished to do so, subject to the following conditions:
        
        The above copyright notice and this permission notice shall be included in all
        copies or substantial portions of the Software.
        
        THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
        IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
        FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
        AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
        LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
        OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
        SOFTWARE.
License-File: LICENSE
Keywords: a2a,agent,asgi,fastapi,prismal,sse
Classifier: Development Status :: 3 - Alpha
Classifier: Framework :: FastAPI
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Internet :: WWW/HTTP :: HTTP Servers
Requires-Python: >=3.13
Requires-Dist: aiosqlite>=0.20
Requires-Dist: fastapi>=0.115
Requires-Dist: prismal-ai<4,>=3.17
Requires-Dist: pydantic-settings>=2.5
Requires-Dist: pydantic>=2.10
Requires-Dist: pyyaml>=6.0
Requires-Dist: uvicorn[standard]>=0.30
Provides-Extra: dev
Requires-Dist: httpx>=0.27; extra == 'dev'
Requires-Dist: mypy>=1.11; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.24; extra == 'dev'
Requires-Dist: pytest>=8.0; extra == 'dev'
Requires-Dist: ruff>=0.6; extra == 'dev'
Requires-Dist: types-pyyaml>=6.0; extra == 'dev'
Description-Content-Type: text/markdown

# prismal-server

Reference **host** for the [`prismal`](https://github.com/prismal-ai/prismal)
agent engine — a thin FastAPI/ASGI process that exposes prismal over
**REST · SSE · WebSocket** (opt-in, `host_ws_enabled`) plus the inbound **A2A** endpoints, so
non-Python clients and the front-end ecosystem can build against it.

`prismal` is a pure library (no web server by design). `prismal-server` is the
missing process that binds a port, boots the engine once, and serves it — while
**reimplementing none of its logic**.

## Status

**v0.2: usage, gateways and AG-UI.** On top of the v0.1 host (streaming chat,
inbound A2A, auth/tenancy seams, health, release plumbing), v0.2 adds per-tenant
engine config and gateways, `done.usage` plus `/v1/usage` and `/v1/budget`, AG-UI 1.0
streaming, the A2A v1.0 dual stack, an opt-in WebSocket transport, a deterministic
fake-LLM mode and read-only admin endpoints. Specs:
[`specs/reference-host-bootstrap/`](./specs/reference-host-bootstrap/) (v0.1) and
[`specs/host-v0.2-usage-gateways-agui/`](./specs/host-v0.2-usage-gateways-agui/)
(v0.2). Release notes are in [`CHANGELOG.md`](./CHANGELOG.md).

## Quickstart

```bash
uv pip install -e ".[dev]"
uvicorn prismal_server.app:app --reload
curl localhost:8000/healthz          # {"status":"ok"}
curl localhost:8000/readyz           # {"status":"ready"} once the runtime composes
```

Stream a chat turn (SSE):

```bash
tid=$(curl -sX POST localhost:8000/threads | python -c 'import sys,json;print(json.load(sys.stdin)["thread_id"])')
curl -N -X POST "localhost:8000/threads/$tid/messages" \
  -H 'content-type: application/json' \
  -d '{"content":"Hello!"}'
```

Or with Docker:

```bash
docker compose up --build
curl localhost:8000/healthz
```

> The image bundles the engine, which pulls heavy ML deps (embeddings, vector
> store) — the first build is large and slow. See
> [`docs/running.md`](./docs/running.md).

## HTTP surface

| Method | Path | Purpose |
|---|---|---|
| `GET` | `/healthz` · `/readyz` | Liveness / readiness |
| `POST` | `/threads` | Mint a new `thread_id` |
| `POST` | `/threads/{id}/messages` | Stream a chat turn (SSE: `token`/`tool_call`/`state`/`done`/`error`; `?protocol=ag-ui` for AG-UI) |
| `POST` | `/v1/agui` | AG-UI `RunAgentInput` → AG-UI event stream |
| `GET` | `/ws/threads/{id}` | WebSocket chat (opt-in, `host_ws_enabled`) |
| `GET` | `/v1/usage` · `/v1/budget` | Per-turn usage history · budget status and gateway quotas |
| `GET` | `/v1/admin/{tenants,audit,decisions,routing}` | Admin read endpoints (opt-in, admin scope) |
| `GET` | `/.well-known/agent-card.json` | A2A Agent Card (`?protocolVersion=0.3\|1.0`) |
| `POST` | `/a2a` · `{base}/{tenant}` | A2A JSON-RPC / SSE, v0.3 and v1.0 (delegates to `A2AServerHandler`) |
| `GET` | `/openapi.json` | OpenAPI with examples (for `prismal-sdk` generation) |

## Docs

| Doc | Covers |
|---|---|
| [`docs/running.md`](./docs/running.md) | Local run, Docker/compose, config, health/readiness |
| [`docs/auth.md`](./docs/auth.md) | The auth seam: `none` / `bearer` / `oidc`, strict mode |
| [`docs/tenancy.md`](./docs/tenancy.md) | `org_id` resolution (fail-closed), per-tenant runtimes, `tenant.yaml` and secrets |
| [`docs/usage.md`](./docs/usage.md) | `done.usage`, `/v1/usage`, `/v1/budget`, `402 budget_exceeded` |
| [`docs/agui.md`](./docs/agui.md) | AG-UI stream mode and `POST /v1/agui` |
| [`docs/websocket.md`](./docs/websocket.md) | WebSocket frames, close codes, handshake auth |
| [`docs/a2a.md`](./docs/a2a.md) | Inbound A2A: Agent Card, v0.3/v1.0 dual stack, tenant routes |
| [`docs/admin.md`](./docs/admin.md) | Admin read endpoints and their scopes |
| [`docs/fake-mode.md`](./docs/fake-mode.md) | Deterministic fake-LLM mode for contract tests |

## The contract with the engine

The host calls the engine only through its **public host read surface** (HRS §3)
plus the v0.1 entry points. Everything else it reads is a `RuntimeContext` method.
A static import-guard test enforces an exact per-module symbol allowlist
(`tests/unit/test_import_guard.py`); the rule and the review checklist live in
[`CLAUDE.md`](./CLAUDE.md).

- `build_runtime(...)` / `build_test_runtime(...)` → `RuntimeContext` (all ports, coordinated `aclose()`)
- `get_async_compiled_graph(...)` → the graph to `astream()`
- `A2AServerHandler` + `build_agent_card(...)` → inbound A2A
- `prismal.identity` `IdentityPort` / `OidcIdentityProvider` → request → identity
- `prismal.events.agui` (AG-UI translator) and `prismal.monitoring.get_run_summary` (usage wire)

## Development

```bash
uv pip install -e ".[dev]"
uv run pytest                      # unit suite (no live provider I/O)
uv run pytest -m contract          # opt-in: against a released engine
uv run ruff check . && uv run ruff format --check .
uv run mypy src
```

The unit suite runs fully offline against engine fakes (`build_test_runtime`); the
`contract` suite boots a real `build_runtime()`, serves a real Agent Card, runs the
v0.2 surfaces (native, AG-UI, usage, A2A v1) against the released engine in fake-LLM
mode, and, when an LLM provider credential is present in the environment,
round-trips a full chat turn over SSE.

## Related repos

- [`prismal-ai/prismal`](https://github.com/prismal-ai/prismal) — the engine (pip dependency `prismal-ai`, pinned `>=3.17,<4`).
- [`prismal-ai/prismal-sdk`](https://github.com/prismal-ai/prismal-sdk) — thin client generated against this host's wire contract (`/openapi.json` + the A2A surface).

## License

See [`LICENSE`](./LICENSE).
