Shared FastAPI surface for the H Agent Platform agents API
Project description
hai-agent-api
Shared FastAPI surface for the agents API. This package defines the HTTP routes, request/response models, and authentication dependencies. Your application supplies the backing logic by implementing the service protocols and passing them to create_router.
Install
uv pip install hai-agent-api
You also need an ASGI server to run locally, for example:
uv pip install "uvicorn[standard]"
Quick start
hai-agent-api does not ship a runnable server. Wire the router into your own FastAPI app and provide implementations for all five services.
# app.py
from agent_api import ApiConfig, AuthConfig, Services, create_router
from agent_api.user import ApiUser
from agent_interface.specs.skill import Skill
from agp_types import Page, PageRequest
from fastapi import FastAPI, HTTPException
# ---------------------------------------------------------------------------
# Example service implementations
# ---------------------------------------------------------------------------
class InMemorySkillService:
"""Minimal skill catalog backed by an in-memory dict."""
def __init__(self) -> None:
self._skills: dict[str, Skill] = {}
async def create_skill(self, user: ApiUser, create: Skill) -> Skill:
if create.name in self._skills:
raise HTTPException(status_code=409, detail=f"Skill {create.name!r} already exists.")
self._skills[create.name] = create
return create
async def get_page(
self,
user: ApiUser,
page_request: PageRequest,
*,
name: str | None = None,
search: str | None = None,
) -> Page[Skill]:
items = list(self._skills.values())
if name:
items = [s for s in items if name.lower() in s.name.lower()]
if search:
q = search.lower()
items = [s for s in items if q in s.name.lower() or q in (s.description or "").lower()]
return Page[Skill](items=items, page=page_request.page, size=page_request.size, total=len(items))
async def get_skill(self, user: ApiUser, name: str) -> Skill:
skill = self._skills.get(name)
if skill is None:
raise HTTPException(status_code=404, detail=f"Skill {name!r} not found.")
return skill
async def update_skill(self, user: ApiUser, name: str, update: Skill) -> Skill:
if update.name != name:
raise HTTPException(status_code=422, detail="update.name must match the path name.")
if name not in self._skills:
raise HTTPException(status_code=404, detail=f"Skill {name!r} not found.")
self._skills[name] = update
return update
async def delete_skill(self, user: ApiUser, name: str) -> None:
if name not in self._skills:
raise HTTPException(status_code=404, detail=f"Skill {name!r} not found.")
del self._skills[name]
class StubService:
"""Placeholder for services you have not implemented yet."""
def __getattr__(self, name: str):
async def _not_implemented(*_args, **_kwargs):
raise HTTPException(status_code=501, detail=f"{name} is not implemented.")
return _not_implemented
# ---------------------------------------------------------------------------
# FastAPI app
# ---------------------------------------------------------------------------
app = FastAPI(title="agents-api", version="0.0.0")
services = Services(
agents=StubService(),
environments=StubService(),
skills=InMemorySkillService(),
sessions=StubService(),
memories=StubService(),
)
app.include_router(
create_router(
services,
config=ApiConfig(auth=AuthConfig(mode="local")),
),
prefix="/api",
)
Run the server:
uvicorn app:app --reload --port 8000
Open the interactive docs at http://localhost:8000/docs. Skill endpoints are available under /api/v2/skills; unimplemented services return 501 Not Implemented.
Try a request
curl -s -X POST http://localhost:8000/api/v2/skills \
-H 'Content-Type: application/json' \
-d '{"name": "my-skill", "description": "Example", "body": "Do the thing."}' | jq
Service protocols
Implement these protocols (structural typing—no base class required) and pass them in a Services container:
| Protocol | Router prefix | Responsibility |
|---|---|---|
AgentServiceProtocol |
/v2/agents |
Agent catalog CRUD and spec resolution |
EnvironmentServiceProtocol |
/v2/environments |
Environment catalog CRUD |
SkillServiceProtocol |
/v2/skills |
Skill catalog CRUD |
SessionServiceProtocol |
/v2/sessions |
Agentic session lifecycle, events, quota |
MemoryServiceProtocol |
/v2/memories |
Per-org key/value memory (hidden from OpenAPI) |
Full method signatures live in agent_api.services.protocols. Raise fastapi.HTTPException for expected client errors (404, 409, 422, etc.). Session creation may raise agent_api.exceptions.IdempotencyKeyConflict; the sessions router maps that to HTTP 422.
Configuration
ApiConfig controls router behaviour:
| Field | Default | Purpose |
|---|---|---|
long_poll_max_wait_for_seconds |
60 |
Upper bound for long-poll on /sessions/{id}/changes |
session_share_url_template |
/share/api/v1/trajectories/{session_id} |
Template for public share links |
auth |
AuthConfig(mode="platform") |
How requests are authenticated (see below) |
Authentication
Pass AuthConfig via ApiConfig.auth when calling create_router:
| Mode | Use case | Behaviour |
|---|---|---|
platform |
Agent Platform (default) | Requires X-User-Sub and X-User-Org headers injected by the gateway; returns 401 when missing |
local |
Local development / standalone apps | Protected routes accept optional identity headers and fall back to default UUIDs when omitted |
Local mode defaults:
| Field | Default |
|---|---|
default_user_id |
00000000-0000-0000-0000-000000000001 |
default_org_id |
00000000-0000-0000-0000-000000000002 |
default_email |
local-dev@example.com |
Override headers when you need a specific identity:
curl -s http://localhost:8000/api/v2/skills \
-H 'X-User-Sub: 11111111-1111-1111-1111-111111111111' \
-H 'X-User-Org: 22222222-2222-2222-2222-222222222222'
Agent Platform keeps the default platform mode—no change required in production wiring.
API layout
create_router returns a router with this structure:
/v2/sessions/...
/v2/memories/...
/v2/skills/...
/v2/environments/...
/v2/agents/...
Mount it under your chosen prefix (commonly /api, yielding /api/v2/...).
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distributions
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 hai_agent_api-0.1.2-py3-none-any.whl.
File metadata
- Download URL: hai_agent_api-0.1.2-py3-none-any.whl
- Upload date:
- Size: 51.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6c966279f209f0ebcf32f1c3e1c53e7d20012adb7ba1a033c725869fcd1da93c
|
|
| MD5 |
a71bb533726602ca38a388e98bb95635
|
|
| BLAKE2b-256 |
9b9163da4f83380ff33fba07b0b7cbb4ba09e42334b2e2f5e59c90fc143a1c45
|
Provenance
The following attestation bundles were made for hai_agent_api-0.1.2-py3-none-any.whl:
Publisher:
publish-agent-api-pypi.yaml on hcompai/agent_platform
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
hai_agent_api-0.1.2-py3-none-any.whl -
Subject digest:
6c966279f209f0ebcf32f1c3e1c53e7d20012adb7ba1a033c725869fcd1da93c - Sigstore transparency entry: 1766568664
- Sigstore integration time:
-
Permalink:
hcompai/agent_platform@d34c76b156df98148db1c10ad52652746ef0bbdc -
Branch / Tag:
refs/heads/main - Owner: https://github.com/hcompai
-
Access:
internal
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
self-hosted -
Publication workflow:
publish-agent-api-pypi.yaml@d34c76b156df98148db1c10ad52652746ef0bbdc -
Trigger Event:
push
-
Statement type: