--- title: "FastAPI Integration" description: "Serving your agencies and tools as APIs with FastAPI." icon: "server" --- Agency Swarm supports serving your agencies and tools as production-ready HTTP APIs using [FastAPI](https://fastapi.tiangolo.com/). This enables you to interact with your agents and tools over HTTP, integrate with other services, or connect it to web frontends. ## Installation FastAPI integration is an **optional installation**. To install all required dependencies, run: ```bash pip install "agency-swarm[fastapi]" ``` ## Setting Up FastAPI Endpoints You can expose your agencies and tools as API endpoints using the `run_fastapi()` function. ### Example: Create an API endpoint for a single agency ```python from agency_swarm import Agency, Agent agent = Agent( name="Assistant", instructions="You are a helpful assistant." ) agency = Agency(agent, name="test_agency") agency.run_fastapi() ``` | Param | Type | Default | Description | | --- | --- | --- | --- | | `host` | string | `"0.0.0.0"` | Interface to bind the server. | | `port` | integer | `8000` | Port to serve FastAPI on. | | `app_token_env` | string | `"APP_TOKEN"` | Env var name for the bearer token; if missing, auth is disabled. | | `return_app` | boolean | `false` | Return the FastAPI app instead of running the server. | | `cors_origins` | list | `["*"]` | Allowed CORS origins. When set to `["*"]`, `allow_credentials` is automatically set to `False` for security. | | `enable_agui` | boolean | `false` | Enable AG-UI streaming (hides the cancel endpoint). | | `enable_logging` | boolean | `false` | Track requests and expose `/get_logs` at the server root. | | `logs_dir` | string | `"activity-logs"` | Folder for request logs when logging is enabled. | | `allowed_local_file_dirs` | list | `None` | Allowlist for local `file_urls`; paths outside the list are rejected. | - Agencies are served at: - `/your_agency_name/get_response` (POST) - `/your_agency_name/get_response_stream` (POST, streaming responses) - `/your_agency_name/cancel_response_stream` (POST; not registered when `enable_agui=True`) - `/your_agency_name/get_metadata` (GET) - `/get_logs` (GET; when `enable_logging=True`) - Tools registered via `tools=[...]` are available at `/tool/ToolClassName` (BaseTools) or `/tool/function_name` (function tools). - OpenAPI and interactive docs: `/openapi.json`, `/docs`, `/redoc`. **Non-streaming (`/get_response`):** ```json { "response": "Hello! How can I help you today?", "new_messages": [ { "type": "message", "role": "user", "content": [{"type": "input_text", "text": "Hello"}], "agent": "Assistant", "callerAgent": null, "timestamp": 1704067200000 }, { "type": "message", "role": "assistant", "content": [{"type": "output_text", "text": "Hello! How can I help you today?"}], "agent": "Assistant", "callerAgent": null, "timestamp": 1704067201000 } ], "usage": {"input_tokens": 50, "output_tokens": 12, "total_cost": 0.0001}, "file_ids_map": {"document.pdf": "file-abc123"} } ``` **Streaming (`/get_response_stream`):** ``` event: meta data: {"run_id": "550e8400-e29b-41d4-a716-446655440000"} data: {"data": {"data": {"type": "response.output_text.delta", "delta": "Hello"}}} data: {"data": {"data": {"type": "response.output_text.delta", "delta": "!"}}} event: messages data: {"new_messages": [...], "run_id": "...", "cancelled": false, "usage": {...}, "file_ids_map": {...}} event: end data: [DONE] ``` **Cancel (`/cancel_response_stream`):** ```json { "ok": true, "run_id": "550e8400-e29b-41d4-a716-446655440000", "cancelled": true, "cancel_mode": "immediate", "new_messages": [...] } ``` **Metadata (`/get_metadata`):** ```json { "nodes": [ { "id": "Assistant", "type": "agent", "data": { "label": "Assistant", "description": "Main assistant agent", "conversationStarters": ["Support: I need help with billing"], "quickReplies": ["hi", "hello"], "isEntryPoint": true, "toolCount": 2, "tools": [ { "name": "example_tool", "type": "function", "description": "...", "inputSchema": {"type": "object", "properties": {"text": {"type": "string"}}} } ] } } ], "edges": [ {"id": "CEO->Developer", "source": "CEO", "target": "Developer", "type": "communication"} ], "metadata": { "agencyName": "my_agency", "totalAgents": 2, "totalTools": 4, "agents": ["CEO", "Developer"], "entryPoints": ["CEO"] }, "agency_swarm_version": "1.0.0" } ``` Conversation starters appear under `data.conversationStarters`, and quick-reply phrases appear under `data.quickReplies` when configured. See [Agent Overview](/core-framework/agents/overview). **Tool (`/tool/`):** ```json { "response": "Output returned by the tool" } ``` Responses include a `usage` object with token counts and cost by default. For streaming, the final `event: messages` payload includes the same `usage` object. To understand what's inside `usage`, see [Observability](/additional-features/observability#usage-payload). Usage tracking is configured on the agent (not on FastAPI): ```python from agency_swarm import Agent, ModelSettings agent = Agent( name="Assistant", instructions="You are a helpful assistant.", model_settings=ModelSettings(include_usage=False), ) ``` If you're using LiteLLM models and want usage in streaming responses, keep `include_usage=True`. Stream cancellation uses an in-memory registry per process. Use single-worker deployments (e.g., uvicorn `workers=1`) or sticky routing so cancel requests reach the same worker. ### Authentication Set the environment variable named by `app_token_env` (default `APP_TOKEN`) to require `Authorization: Bearer ` on every endpoint. When the variable is absent, authentication is disabled. ### Implementation reference ```python from agency_swarm import Agency, Agent, function_tool, run_fastapi # Example tools using agents SDK @function_tool def example_tool(example_field: str) -> str: """Example tool with input field.""" return f"Result of ExampleTool operation with {example_field}" @function_tool def test_tool(example_field: str) -> str: """Test tool with input field.""" return f"Result of TestTool operation with {example_field}" # Create agents agent1 = Agent(name="Assistant1", instructions="You are assistant 1.") agent2 = Agent(name="Assistant2", instructions="You are assistant 2.") # Create agency factory functions for proper thread management def create_agency_1(load_threads_callback=None, save_threads_callback=None): return Agency( agent1, name="test_agency_1", load_threads_callback=load_threads_callback, save_threads_callback=save_threads_callback, ) def create_agency_2(load_threads_callback=None, save_threads_callback=None): return Agency( agent2, name="test_agency_2", load_threads_callback=load_threads_callback, save_threads_callback=save_threads_callback, ) run_fastapi( agencies={ "test_agency_1": create_agency_1, "test_agency_2": create_agency_2, }, tools=[example_tool, test_tool], ) ``` Endpoints follow the reference above; tool schemas mirror the definitions of `example_tool` and `test_tool`. --- ## API Usage Example You can interact with your agents and tools using HTTP requests: ```python import requests agency_url = "http://127.0.0.1:8000/test_agency_1/get_response" payload = { "message": "Hello", "client_config": { "base_url": "https://my-openai-gateway.example.com/v1", "api_key": "sk-...", # override per request }, } headers = { "Authorization": "Bearer 123" # Replace with your actual token if needed } agency_response = requests.post(agency_url, json=payload, headers=headers) print("Status code:", agency_response.status_code) print("Response:", agency_response.json()) ``` | Field | Type | Required | Description | | --- | --- | --- | --- | | `message` | string or structured list | Yes | User message to start or continue the conversation. Use a structured Responses input list for inline `input_text`, `input_image`, or `input_file` content. | | `chat_history` | list | No | Flat list of prior messages with metadata (`agent`, `callerAgent`, `timestamp`) to preserve context. | | `recipient_agent` | string | No | Target agent when you want to direct the next turn. | | `file_ids` | list | No | IDs of already uploaded files to attach. | | `file_urls` | object | No | `{filename: url_or_absolute_path}` map. Local paths require `allowed_local_file_dirs` on `run_fastapi`. | | `additional_instructions` | string | No | Extra guidance for the current request. | | `user_context` | object | No | Structured data passed to [Agency Context](/additional-features/agency-context) without exposing it to the LLM. | | `client_config` | object | No | Override `base_url` / `api_key` for this request (and optional `litellm_keys` for `litellm/` models). | ### How user_context is applied - Merges with any `user_context` set on the agency instance. - Useful for structured data (ids, preferences, feature flags) you do not want in the prompt. - Accessible within tools for the duration of the run. See [Agency Context](/additional-features/agency-context). ### How client_config is applied - Applies only to **OpenAI models** (no prefix or `openai/...`) and **LiteLLM models** (`litellm/...`). - **Custom Model subclasses** are not modified; the request still runs, but the override is skipped. - For LiteLLM, you can provide `litellm_keys` to pass different keys per provider. Requires LiteLLM installed. ### client_config fields - `base_url` (string, optional): Override the API base URL for this request. - `api_key` (string, optional): Override the API key for this request. - `litellm_keys` (object, optional): Only for `litellm/...` models. - Map `provider_name` → `api_key`. - Example: `{"anthropic": "...", "gemini": "..."}` **OpenAI model override (gpt-4o):** ```json { "message": "Hello", "client_config": { "base_url": "https://my-openai-gateway.example.com/v1", "api_key": "sk-..." } } ``` **LiteLLM mixed providers (requires `openai-agents[litellm]`):** ```json { "message": "Hello", "client_config": { "base_url": "https://my-litellm-proxy.example.com", "litellm_keys": { "anthropic": "sk-ant-...", "gemini": "AIza..." } } } ``` ## Cancelling Active Streams The streaming endpoint supports cancellation via two methods: ### 1. Automatic Cancellation on Disconnect When a client disconnects (tab close, refresh, network failure), the stream is automatically cancelled to preserve token costs. ### 2. Cancel Endpoint Call the cancel endpoint with the `run_id` received from the first event of the streaming response. This will allow you to retrieve intermediate results that were generated before the run cancellation. Optionally include `cancel_mode` (defaults to `immediate`): - `immediate` — stop right away and return messages that were fully generated; the in-progress message is discarded. - `after_turn` — finish the current turn, then stop. ```python import requests # Cancel an active stream cancel_url = "http://127.0.0.1:8000/test_agency/cancel_response_stream" payload = {"run_id": "your-run-id", "cancel_mode": "after_turn"} # cancel_mode is optional response = requests.post(cancel_url, json=payload) # Returns: {"ok": True, "run_id": "...", "cancelled": True, "cancel_mode": "after_turn", "new_messages": [...]} ``` --- ## Serving Standalone Tools Expose tools as simple HTTP endpoints for external systems, webhooks, or other agents to call directly without agency orchestration. ```python from agency_swarm import BaseTool, run_fastapi class Address(BaseTool): street: str zip_code: int def run(self) -> str: return f"{self.street} {self.zip_code}" run_fastapi(tools=[Address], port=8080) ``` This creates: - `POST /tool/Address` — execute the tool - `GET /openapi.json` — full OpenAPI schema - `GET /docs` — interactive Swagger UI Use `ToolFactory.get_openapi_schema()` to generate the OpenAPI spec programmatically: ```python from agency_swarm.tools import ToolFactory schema = ToolFactory.get_openapi_schema( [Address], url="https://your-server.com", title="My Tools API" ) print(schema) # Returns a JSON string ``` --- ## File Attachments Attach files to agency requests using `file_ids` or `file_urls` in the payload: ```json { "message": "Summarize this document", "file_urls": {"report.pdf": "https://example.com/report.pdf"} } ``` For inline Responses attachments that should not be uploaded through the Files API, pass a structured `message`: ```json { "message": [ { "role": "user", "content": [ {"type": "input_image", "image_url": "data:image/png;base64,...", "detail": "auto"}, {"type": "input_text", "text": "Describe this image"} ] } ] } ``` With manual `chat_history`, clients can keep structured attachment parts in history so follow-up turns work without reattaching. This may resend inline attachment content or references; it does not reuse server-managed state, `previous_response_id`, Conversations, or `file_id` values. The response includes `file_ids_map` with the uploaded file IDs: ```json { "response": "...", "file_ids_map": {"report.pdf": "file-abc123"} } ``` When `file_urls` is used, Agency Swarm also prepends a `system` message for that turn that records the original source string for each attached file. That system message is included in `new_messages`, so if your client persists `new_messages` as chat history, later turns will keep the original attachment source URL or local path in model context. **Supported filetypes:** `.pdf`, `.jpeg`, `.jpg`, `.gif`, `.png`, `.c`, `.cs`, `.cpp`, `.csv`, `.html`, `.java`, `.json`, `.php`, `.py`, `.rb`, `.css`, `.js`, `.sh`, `.ts`, `.pkl`, `.tar`, `.xlsx`, `.xml`, `.zip`, `.doc`, `.docx`, `.md`, `.pptx`, `.tex`, `.txt` To support passing local filepaths in the `file_urls` field, set `allowed_local_file_dirs` on `run_fastapi`: ```python run_fastapi(agencies=..., allowed_local_file_dirs=["/data/uploads"]) ``` Then pass absolute file paths in `file_urls`: ```json {"file_urls": {"doc.pdf": "/data/uploads/doc.pdf"}} ``` Invalid paths return an `error` field in the response body. `allowed_local_file_dirs` uses strict request-time validation for invalid entries: if an entry exists but is not a directory, local `file_urls` requests fail with an error. Missing directories are skipped and can be created later. The `/get_metadata` field `allowed_local_file_dirs` lists only currently usable directory entries.