fastapi-integration.mdx 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480
  1. ---
  2. title: "FastAPI Integration"
  3. description: "Serving your agencies and tools as APIs with FastAPI."
  4. icon: "server"
  5. ---
  6. 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.
  7. ## Installation
  8. FastAPI integration is an **optional installation**. To install all required dependencies, run:
  9. ```bash
  10. pip install "agency-swarm[fastapi]"
  11. ```
  12. ## Setting Up FastAPI Endpoints
  13. You can expose your agencies and tools as API endpoints using the `run_fastapi()` function.
  14. ### Example: Create an API endpoint for a single agency
  15. ```python
  16. from agency_swarm import Agency, Agent
  17. agent = Agent(
  18. name="Assistant",
  19. instructions="You are a helpful assistant."
  20. )
  21. agency = Agency(agent, name="test_agency")
  22. agency.run_fastapi()
  23. ```
  24. <Accordion title="run_fastapi parameters" defaultOpen={false}>
  25. | Param | Type | Default | Description |
  26. | --- | --- | --- | --- |
  27. | `host` | string | `"0.0.0.0"` | Interface to bind the server. |
  28. | `port` | integer | `8000` | Port to serve FastAPI on. |
  29. | `app_token_env` | string | `"APP_TOKEN"` | Env var name for the bearer token; if missing, auth is disabled. |
  30. | `return_app` | boolean | `false` | Return the FastAPI app instead of running the server. |
  31. | `cors_origins` | list | `["*"]` | Allowed CORS origins. When set to `["*"]`, `allow_credentials` is automatically set to `False` for security. |
  32. | `enable_agui` | boolean | `false` | Enable AG-UI streaming (hides the cancel endpoint). |
  33. | `enable_logging` | boolean | `false` | Track requests and expose `/get_logs` at the server root. |
  34. | `logs_dir` | string | `"activity-logs"` | Folder for request logs when logging is enabled. |
  35. | `allowed_local_file_dirs` | list | `None` | Allowlist for local `file_urls`; paths outside the list are rejected. |
  36. </Accordion>
  37. <Accordion title="Endpoint reference" defaultOpen={false}>
  38. - Agencies are served at:
  39. - `/your_agency_name/get_response` (POST)
  40. - `/your_agency_name/get_response_stream` (POST, streaming responses)
  41. - `/your_agency_name/cancel_response_stream` (POST; not registered when `enable_agui=True`)
  42. - `/your_agency_name/get_metadata` (GET)
  43. - `/get_logs` (GET; when `enable_logging=True`)
  44. - Tools registered via `tools=[...]` are available at `/tool/ToolClassName` (BaseTools) or `/tool/function_name` (function tools).
  45. - OpenAPI and interactive docs: `/openapi.json`, `/docs`, `/redoc`.
  46. </Accordion>
  47. <Accordion title="Response format" defaultOpen={false}>
  48. **Non-streaming (`/get_response`):**
  49. ```json
  50. {
  51. "response": "Hello! How can I help you today?",
  52. "new_messages": [
  53. {
  54. "type": "message",
  55. "role": "user",
  56. "content": [{"type": "input_text", "text": "Hello"}],
  57. "agent": "Assistant",
  58. "callerAgent": null,
  59. "timestamp": 1704067200000
  60. },
  61. {
  62. "type": "message",
  63. "role": "assistant",
  64. "content": [{"type": "output_text", "text": "Hello! How can I help you today?"}],
  65. "agent": "Assistant",
  66. "callerAgent": null,
  67. "timestamp": 1704067201000
  68. }
  69. ],
  70. "usage": {"input_tokens": 50, "output_tokens": 12, "total_cost": 0.0001},
  71. "file_ids_map": {"document.pdf": "file-abc123"}
  72. }
  73. ```
  74. **Streaming (`/get_response_stream`):**
  75. ```
  76. event: meta
  77. data: {"run_id": "550e8400-e29b-41d4-a716-446655440000"}
  78. data: {"data": {"data": {"type": "response.output_text.delta", "delta": "Hello"}}}
  79. data: {"data": {"data": {"type": "response.output_text.delta", "delta": "!"}}}
  80. event: messages
  81. data: {"new_messages": [...], "run_id": "...", "cancelled": false, "usage": {...}, "file_ids_map": {...}}
  82. event: end
  83. data: [DONE]
  84. ```
  85. **Cancel (`/cancel_response_stream`):**
  86. ```json
  87. {
  88. "ok": true,
  89. "run_id": "550e8400-e29b-41d4-a716-446655440000",
  90. "cancelled": true,
  91. "cancel_mode": "immediate",
  92. "new_messages": [...]
  93. }
  94. ```
  95. **Metadata (`/get_metadata`):**
  96. ```json
  97. {
  98. "nodes": [
  99. {
  100. "id": "Assistant",
  101. "type": "agent",
  102. "data": {
  103. "label": "Assistant",
  104. "description": "Main assistant agent",
  105. "conversationStarters": ["Support: I need help with billing"],
  106. "quickReplies": ["hi", "hello"],
  107. "isEntryPoint": true,
  108. "toolCount": 2,
  109. "tools": [
  110. {
  111. "name": "example_tool",
  112. "type": "function",
  113. "description": "...",
  114. "inputSchema": {"type": "object", "properties": {"text": {"type": "string"}}}
  115. }
  116. ]
  117. }
  118. }
  119. ],
  120. "edges": [
  121. {"id": "CEO->Developer", "source": "CEO", "target": "Developer", "type": "communication"}
  122. ],
  123. "metadata": {
  124. "agencyName": "my_agency",
  125. "totalAgents": 2,
  126. "totalTools": 4,
  127. "agents": ["CEO", "Developer"],
  128. "entryPoints": ["CEO"]
  129. },
  130. "agency_swarm_version": "1.0.0"
  131. }
  132. ```
  133. Conversation starters appear under `data.conversationStarters`, and quick-reply phrases appear under `data.quickReplies` when configured. See [Agent Overview](/core-framework/agents/overview).
  134. **Tool (`/tool/<name>`):**
  135. ```json
  136. {
  137. "response": "Output returned by the tool"
  138. }
  139. ```
  140. </Accordion>
  141. <Accordion title="Usage tracking" defaultOpen={false}>
  142. Responses include a `usage` object with token counts and cost by default. For streaming, the final `event: messages` payload includes the same `usage` object.
  143. To understand what's inside `usage`, see [Observability](/additional-features/observability#usage-payload).
  144. Usage tracking is configured on the agent (not on FastAPI):
  145. ```python
  146. from agency_swarm import Agent, ModelSettings
  147. agent = Agent(
  148. name="Assistant",
  149. instructions="You are a helpful assistant.",
  150. model_settings=ModelSettings(include_usage=False),
  151. )
  152. ```
  153. If you're using LiteLLM models and want usage in streaming responses, keep `include_usage=True`.
  154. </Accordion>
  155. <Note>
  156. 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.
  157. </Note>
  158. ### Authentication
  159. Set the environment variable named by `app_token_env` (default `APP_TOKEN`) to require `Authorization: Bearer <token>` on every endpoint. When the variable is absent, authentication is disabled.
  160. ### Implementation reference
  161. <Accordion title="Example: Serving Multiple Agencies and Tools" defaultOpen={false}>
  162. ```python
  163. from agency_swarm import Agency, Agent, function_tool, run_fastapi
  164. # Example tools using agents SDK
  165. @function_tool
  166. def example_tool(example_field: str) -> str:
  167. """Example tool with input field."""
  168. return f"Result of ExampleTool operation with {example_field}"
  169. @function_tool
  170. def test_tool(example_field: str) -> str:
  171. """Test tool with input field."""
  172. return f"Result of TestTool operation with {example_field}"
  173. # Create agents
  174. agent1 = Agent(name="Assistant1", instructions="You are assistant 1.")
  175. agent2 = Agent(name="Assistant2", instructions="You are assistant 2.")
  176. # Create agency factory functions for proper thread management
  177. def create_agency_1(load_threads_callback=None, save_threads_callback=None):
  178. return Agency(
  179. agent1,
  180. name="test_agency_1",
  181. load_threads_callback=load_threads_callback,
  182. save_threads_callback=save_threads_callback,
  183. )
  184. def create_agency_2(load_threads_callback=None, save_threads_callback=None):
  185. return Agency(
  186. agent2,
  187. name="test_agency_2",
  188. load_threads_callback=load_threads_callback,
  189. save_threads_callback=save_threads_callback,
  190. )
  191. run_fastapi(
  192. agencies={
  193. "test_agency_1": create_agency_1,
  194. "test_agency_2": create_agency_2,
  195. },
  196. tools=[example_tool, test_tool],
  197. )
  198. ```
  199. Endpoints follow the reference above; tool schemas mirror the definitions of `example_tool` and `test_tool`.
  200. </Accordion>
  201. ---
  202. ## API Usage Example
  203. You can interact with your agents and tools using HTTP requests:
  204. ```python
  205. import requests
  206. agency_url = "http://127.0.0.1:8000/test_agency_1/get_response"
  207. payload = {
  208. "message": "Hello",
  209. "client_config": {
  210. "base_url": "https://my-openai-gateway.example.com/v1",
  211. "api_key": "sk-...", # override per request
  212. },
  213. }
  214. headers = {
  215. "Authorization": "Bearer 123" # Replace with your actual token if needed
  216. }
  217. agency_response = requests.post(agency_url, json=payload, headers=headers)
  218. print("Status code:", agency_response.status_code)
  219. print("Response:", agency_response.json())
  220. ```
  221. <Accordion title="Request payload" defaultOpen={false}>
  222. | Field | Type | Required | Description |
  223. | --- | --- | --- | --- |
  224. | `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. |
  225. | `chat_history` | list | No | Flat list of prior messages with metadata (`agent`, `callerAgent`, `timestamp`) to preserve context. |
  226. | `recipient_agent` | string | No | Target agent when you want to direct the next turn. |
  227. | `file_ids` | list | No | IDs of already uploaded files to attach. |
  228. | `file_urls` | object | No | `{filename: url_or_absolute_path}` map. Local paths require `allowed_local_file_dirs` on `run_fastapi`. |
  229. | `additional_instructions` | string | No | Extra guidance for the current request. |
  230. | `user_context` | object | No | Structured data passed to [Agency Context](/additional-features/agency-context) without exposing it to the LLM. |
  231. | `client_config` | object | No | Override `base_url` / `api_key` for this request (and optional `litellm_keys` for `litellm/` models). |
  232. ### How user_context is applied
  233. - Merges with any `user_context` set on the agency instance.
  234. - Useful for structured data (ids, preferences, feature flags) you do not want in the prompt.
  235. - Accessible within tools for the duration of the run. See [Agency Context](/additional-features/agency-context).
  236. ### How client_config is applied
  237. - Applies only to **OpenAI models** (no prefix or `openai/...`) and **LiteLLM models** (`litellm/...`).
  238. - **Custom Model subclasses** are not modified; the request still runs, but the override is skipped.
  239. - For LiteLLM, you can provide `litellm_keys` to pass different keys per provider. Requires LiteLLM installed.
  240. ### client_config fields
  241. - `base_url` (string, optional): Override the API base URL for this request.
  242. - `api_key` (string, optional): Override the API key for this request.
  243. - `litellm_keys` (object, optional): Only for `litellm/...` models.
  244. - Map `provider_name` → `api_key`.
  245. - Example: `{"anthropic": "...", "gemini": "..."}`
  246. <Accordion title="client_config examples" defaultOpen={false}>
  247. **OpenAI model override (gpt-4o):**
  248. ```json
  249. {
  250. "message": "Hello",
  251. "client_config": {
  252. "base_url": "https://my-openai-gateway.example.com/v1",
  253. "api_key": "sk-..."
  254. }
  255. }
  256. ```
  257. **LiteLLM mixed providers (requires `openai-agents[litellm]`):**
  258. ```json
  259. {
  260. "message": "Hello",
  261. "client_config": {
  262. "base_url": "https://my-litellm-proxy.example.com",
  263. "litellm_keys": {
  264. "anthropic": "sk-ant-...",
  265. "gemini": "AIza..."
  266. }
  267. }
  268. }
  269. ```
  270. </Accordion>
  271. </Accordion>
  272. ## Cancelling Active Streams
  273. The streaming endpoint supports cancellation via two methods:
  274. ### 1. Automatic Cancellation on Disconnect
  275. When a client disconnects (tab close, refresh, network failure), the stream is automatically cancelled to preserve token costs.
  276. ### 2. Cancel Endpoint
  277. Call the cancel endpoint with the `run_id` received from the first event of the streaming response.
  278. This will allow you to retrieve intermediate results that were generated before the run cancellation.
  279. Optionally include `cancel_mode` (defaults to `immediate`):
  280. - `immediate` — stop right away and return messages that were fully generated; the in-progress message is discarded.
  281. - `after_turn` — finish the current turn, then stop.
  282. ```python
  283. import requests
  284. # Cancel an active stream
  285. cancel_url = "http://127.0.0.1:8000/test_agency/cancel_response_stream"
  286. payload = {"run_id": "your-run-id", "cancel_mode": "after_turn"} # cancel_mode is optional
  287. response = requests.post(cancel_url, json=payload)
  288. # Returns: {"ok": True, "run_id": "...", "cancelled": True, "cancel_mode": "after_turn", "new_messages": [...]}
  289. ```
  290. ---
  291. ## Serving Standalone Tools
  292. Expose tools as simple HTTP endpoints for external systems, webhooks, or other agents to call directly without agency orchestration.
  293. ```python
  294. from agency_swarm import BaseTool, run_fastapi
  295. class Address(BaseTool):
  296. street: str
  297. zip_code: int
  298. def run(self) -> str:
  299. return f"{self.street} {self.zip_code}"
  300. run_fastapi(tools=[Address], port=8080)
  301. ```
  302. This creates:
  303. - `POST /tool/Address` — execute the tool
  304. - `GET /openapi.json` — full OpenAPI schema
  305. - `GET /docs` — interactive Swagger UI
  306. <Accordion title="Generate OpenAPI schema programmatically" defaultOpen={false}>
  307. Use `ToolFactory.get_openapi_schema()` to generate the OpenAPI spec programmatically:
  308. ```python
  309. from agency_swarm.tools import ToolFactory
  310. schema = ToolFactory.get_openapi_schema(
  311. [Address],
  312. url="https://your-server.com",
  313. title="My Tools API"
  314. )
  315. print(schema) # Returns a JSON string
  316. ```
  317. </Accordion>
  318. ---
  319. ## File Attachments
  320. Attach files to agency requests using `file_ids` or `file_urls` in the payload:
  321. ```json
  322. {
  323. "message": "Summarize this document",
  324. "file_urls": {"report.pdf": "https://example.com/report.pdf"}
  325. }
  326. ```
  327. For inline Responses attachments that should not be uploaded through the Files API, pass a structured `message`:
  328. ```json
  329. {
  330. "message": [
  331. {
  332. "role": "user",
  333. "content": [
  334. {"type": "input_image", "image_url": "data:image/png;base64,...", "detail": "auto"},
  335. {"type": "input_text", "text": "Describe this image"}
  336. ]
  337. }
  338. ]
  339. }
  340. ```
  341. 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.
  342. The response includes `file_ids_map` with the uploaded file IDs:
  343. ```json
  344. {
  345. "response": "...",
  346. "file_ids_map": {"report.pdf": "file-abc123"}
  347. }
  348. ```
  349. 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.
  350. **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`
  351. <Accordion title="Local file paths" defaultOpen={false}>
  352. To support passing local filepaths in the `file_urls` field, set `allowed_local_file_dirs` on `run_fastapi`:
  353. ```python
  354. run_fastapi(agencies=..., allowed_local_file_dirs=["/data/uploads"])
  355. ```
  356. Then pass absolute file paths in `file_urls`:
  357. ```json
  358. {"file_urls": {"doc.pdf": "/data/uploads/doc.pdf"}}
  359. ```
  360. Invalid paths return an `error` field in the response body.
  361. <Note>
  362. `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.
  363. </Note>
  364. </Accordion>