HALO — HTTP API Language for Operations. FastAPI plugin for self-describing APIs for LLM agents.
Project description
halo-fastapi
The Python reference implementation of the HALO protocol — a server-side FastAPI plugin and an agent-side HTTP client.
What It Does
Server-side (HaloRegister) — A single line added to any FastAPI application makes every route HALO-compliant. The plugin introspects existing routes, Pydantic models, and dependency injection at startup to automatically generate application/llm+json schemas served via OPTIONS handlers.
Client-side (HaloClient) — A client that discovers and consumes any HALO-compliant API. Handles root manifest discovery, per-route schema fetching with caching, credential injection (bearer, API key, basic), and retry with exponential backoff.
Installation
uv add halo-fastapi
Optional Extras
The framework adapters have optional dependencies that conflict with each other (azure-ai-projects version mismatch), so they cannot be installed in the same environment:
# Agent Framework adapter
uv add "halo-fastapi[agent-framework]"
# Semantic Kernel adapter
uv add "halo-fastapi[semantic-kernel]"
Development (Monorepo)
halo-fastapi is a workspace member. To sync the core workspace:
uv sync --all-packages
The sample agent apps are excluded from the workspace due to conflicting transitive dependencies. Install them on demand:
# Agent Framework sample
uv pip install -e samples/agents/agent-framework
# Semantic Kernel sample
uv pip install -e samples/agents/semantic-kernel
Server Usage
from halo_fastapi import HaloRegister
from fastapi import FastAPI
app = FastAPI(title="My API", version="1.0.0")
HaloRegister(app)
Everything is derived automatically from your existing route definitions, Pydantic models, docstrings, and dependency injection. LLM-native fields (why, tags, effects) can be added via json_schema_extra on your Pydantic models.
Multi-Method Paths
When multiple HTTP methods share the same path (e.g. GET and POST on /api/books), each method gets its own HALO schema. OPTIONS /api/books returns an array — one schema per method.
GET Endpoints with Query Parameters
For GET endpoints using query parameters, use a Pydantic model via Depends() to carry LLM metadata:
from fastapi import Depends
class BookSearchRequest(BaseModel):
query: str | None = Field(None, description="Search term")
model_config = ConfigDict(json_schema_extra={"llm": {"why": "Search books", "tags": ["books", "read"]}})
@app.get("/api/books")
async def search_books(params: BookSearchRequest = Depends()):
...
The Pydantic model's json_schema_extra provides why, tags, and examples — the same pattern used for POST body models.
Auth Detection
HaloRegister walks the FastAPI dependency tree and maps security classes to HALO auth shapes:
| FastAPI Security Class | HALO Auth Type |
|---|---|
HTTPBearer |
bearer |
HTTPBasic |
basic |
APIKeyHeader |
apikey (with custom header name) |
OAuth2PasswordBearer |
oauth (with tokenUrl and scopes) |
Client Usage
from halo_fastapi import HaloClient
plugin = await HaloClient(
base_url="https://api.example.com",
bearer_token=os.getenv("API_KEY"),
).discover(tags=["payments"])
# Fetch a single tool schema (cached after first call)
schema = await plugin.get_tool("/api/payments/charge")
# Invoke a tool — credentials injected automatically
result = await plugin.invoke("/api/payments/charge", body={"amount": 1000})
Retry Behaviour
HaloClient retries failed requests with exponential backoff on connection errors, HTTP 429, and 5xx responses. Defaults: 5 retries, 0.5s base delay, 30s max delay — configurable via constructor parameters.
Agent Framework Integration (Optional)
HaloAgentFrameworkAdapter converts discovered HALO tools into Microsoft Agent Framework FunctionTool instances. Install with the optional extra:
uv add "halo-fastapi[agent-framework]"
from halo_fastapi import HaloClient, HaloAgentFrameworkAdapter
client = await HaloClient(base_url="https://api.example.com").discover()
adapter = HaloAgentFrameworkAdapter(client)
tools = await adapter.create_tools() # list[FunctionTool]
create_tools() fetches the full schema for each discovered tool via HaloClient.get_tool() and builds a FunctionTool from the result. Schemas are cached by HaloClient so subsequent invocations do not repeat the OPTIONS requests.
Semantic Kernel Integration (Optional)
HaloSemanticKernelAdapter converts discovered HALO tools into a Semantic Kernel KernelPlugin. Install with the optional extra (cannot coexist with halo-fastapi[agent-framework] due to transitive dependency conflicts):
uv add "halo-fastapi[semantic-kernel]"
from halo_fastapi import HaloClient, HaloSemanticKernelAdapter
client = await HaloClient(base_url="https://api.example.com").discover()
adapter = HaloSemanticKernelAdapter(client)
plugin = await adapter.create_plugin() # KernelPlugin
kernel.add_plugin(plugin)
create_plugin() wraps each discovered tool as a @kernel_function-decorated async function inside a KernelPlugin. Semantic Kernel handles automatic function calling when FunctionChoiceBehavior.Auto() is configured.
Licence
Project details
Release history Release notifications | RSS feed
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 halo_fastapi-0.3.0.tar.gz.
File metadata
- Download URL: halo_fastapi-0.3.0.tar.gz
- Upload date:
- Size: 24.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1fc0bd701bf43bf51874b08543e33a74add2ea6d600f0bcbb411ed902807c50c
|
|
| MD5 |
01669dc8b061e1d7298b99026b6205df
|
|
| BLAKE2b-256 |
495d92597ca5d138388e1db0dbecb1def76050a913ed6a5ac6ff142e88852d06
|
Provenance
The following attestation bundles were made for halo_fastapi-0.3.0.tar.gz:
Publisher:
publish.yml on irarainey/halo
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
halo_fastapi-0.3.0.tar.gz -
Subject digest:
1fc0bd701bf43bf51874b08543e33a74add2ea6d600f0bcbb411ed902807c50c - Sigstore transparency entry: 1081702601
- Sigstore integration time:
-
Permalink:
irarainey/halo@25bf97fbdd406bfd41ffbce6cf91281a6bec436b -
Branch / Tag:
refs/tags/0.3.0 - Owner: https://github.com/irarainey
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@25bf97fbdd406bfd41ffbce6cf91281a6bec436b -
Trigger Event:
release
-
Statement type:
File details
Details for the file halo_fastapi-0.3.0-py3-none-any.whl.
File metadata
- Download URL: halo_fastapi-0.3.0-py3-none-any.whl
- Upload date:
- Size: 18.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a4132be4f86e48f1347d97cec3035eaf0aa1b69281a51d0bac50dd992eab7a17
|
|
| MD5 |
1bc5c9cd8f6ff94ca4483addeaf8afc2
|
|
| BLAKE2b-256 |
c3f3f596373927377c742342128b9e9f10a129a186b76f56f35f50a8b5d6e9d1
|
Provenance
The following attestation bundles were made for halo_fastapi-0.3.0-py3-none-any.whl:
Publisher:
publish.yml on irarainey/halo
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
halo_fastapi-0.3.0-py3-none-any.whl -
Subject digest:
a4132be4f86e48f1347d97cec3035eaf0aa1b69281a51d0bac50dd992eab7a17 - Sigstore transparency entry: 1081702673
- Sigstore integration time:
-
Permalink:
irarainey/halo@25bf97fbdd406bfd41ffbce6cf91281a6bec436b -
Branch / Tag:
refs/tags/0.3.0 - Owner: https://github.com/irarainey
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@25bf97fbdd406bfd41ffbce6cf91281a6bec436b -
Trigger Event:
release
-
Statement type: