pyagentic-core 2.6.2rc1__tar.gz → 2.7.0a2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (159) hide show
  1. {pyagentic_core-2.6.2rc1/pyagentic_core.egg-info → pyagentic_core-2.7.0a2}/PKG-INFO +4 -8
  2. pyagentic_core-2.7.0a2/docs/api/creating-an-app.md +108 -0
  3. pyagentic_core-2.7.0a2/docs/api/deploying.md +80 -0
  4. pyagentic_core-2.7.0a2/docs/api/index.md +103 -0
  5. pyagentic_core-2.7.0a2/docs/api/jobs.md +115 -0
  6. pyagentic_core-2.7.0a2/docs/api/running.md +141 -0
  7. {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/docs/index.md +3 -3
  8. {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/mkdocs.yml +7 -6
  9. {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/pyagentic/_base/_agent/_agent.py +3 -1
  10. {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/pyagentic/_base/_info.py +12 -10
  11. {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/pyagentic/_base/_spec.py +7 -3
  12. pyagentic_core-2.7.0a2/pyagentic/api/__init__.py +38 -0
  13. pyagentic_core-2.7.0a2/pyagentic/api/_app.py +469 -0
  14. pyagentic_core-2.7.0a2/pyagentic/api/_config.py +191 -0
  15. pyagentic_core-2.7.0a2/pyagentic/api/_docker.py +76 -0
  16. pyagentic_core-2.7.0a2/pyagentic/api/_mcp_server.py +130 -0
  17. {pyagentic_core-2.6.2rc1/pyagentic/serve → pyagentic_core-2.7.0a2/pyagentic/api}/_sessions.py +31 -12
  18. pyagentic_core-2.7.0a2/pyagentic/api/jobs/__init__.py +35 -0
  19. pyagentic_core-2.7.0a2/pyagentic/api/jobs/_models.py +137 -0
  20. pyagentic_core-2.7.0a2/pyagentic/api/jobs/_orchestrator.py +462 -0
  21. pyagentic_core-2.7.0a2/pyagentic/api/jobs/_routes.py +240 -0
  22. pyagentic_core-2.7.0a2/pyagentic/api/jobs/backends/__init__.py +43 -0
  23. pyagentic_core-2.7.0a2/pyagentic/api/jobs/backends/_base.py +142 -0
  24. pyagentic_core-2.7.0a2/pyagentic/api/jobs/backends/_in_process.py +121 -0
  25. pyagentic_core-2.7.0a2/pyagentic/api/jobs/store/__init__.py +21 -0
  26. pyagentic_core-2.7.0a2/pyagentic/api/jobs/store/_base.py +162 -0
  27. pyagentic_core-2.7.0a2/pyagentic/api/jobs/store/_sqlite.py +371 -0
  28. {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/pyagentic/llm/_anthropic.py +0 -3
  29. {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2/pyagentic_core.egg-info}/PKG-INFO +4 -8
  30. {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/pyagentic_core.egg-info/SOURCES.txt +33 -35
  31. {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/pyagentic_core.egg-info/requires.txt +1 -5
  32. {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/pyproject.toml +1 -8
  33. {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/tests/_base/test_params.py +26 -0
  34. pyagentic_core-2.7.0a2/tests/api/jobs/test_orchestrator.py +269 -0
  35. pyagentic_core-2.7.0a2/tests/api/jobs/test_routes.py +269 -0
  36. pyagentic_core-2.7.0a2/tests/api/jobs/test_store_sqlite.py +190 -0
  37. pyagentic_core-2.7.0a2/tests/api/test_app.py +152 -0
  38. pyagentic_core-2.7.0a2/tests/api/test_config.py +96 -0
  39. pyagentic_core-2.7.0a2/tests/api/test_docker.py +52 -0
  40. pyagentic_core-2.7.0a2/tests/api/test_mcp_server.py +97 -0
  41. pyagentic_core-2.7.0a2/tests/api/test_router.py +81 -0
  42. {pyagentic_core-2.6.2rc1/tests/serve → pyagentic_core-2.7.0a2/tests/api}/test_sessions.py +19 -29
  43. {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/uv.lock +73 -81
  44. pyagentic_core-2.6.2rc1/docs/deploy/building.md +0 -166
  45. pyagentic_core-2.6.2rc1/docs/deploy/creating-a-project.md +0 -169
  46. pyagentic_core-2.6.2rc1/docs/deploy/index.md +0 -43
  47. pyagentic_core-2.6.2rc1/docs/deploy/running.md +0 -155
  48. pyagentic_core-2.6.2rc1/pyagentic/cli/__init__.py +0 -30
  49. pyagentic_core-2.6.2rc1/pyagentic/cli/__main__.py +0 -7
  50. pyagentic_core-2.6.2rc1/pyagentic/cli/_build.py +0 -55
  51. pyagentic_core-2.6.2rc1/pyagentic/cli/_init.py +0 -176
  52. pyagentic_core-2.6.2rc1/pyagentic/cli/_publish.py +0 -100
  53. pyagentic_core-2.6.2rc1/pyagentic/cli/_run.py +0 -174
  54. pyagentic_core-2.6.2rc1/pyagentic/cli/_templates.py +0 -138
  55. pyagentic_core-2.6.2rc1/pyagentic/serve/__init__.py +0 -32
  56. pyagentic_core-2.6.2rc1/pyagentic/serve/_agent_ref.py +0 -254
  57. pyagentic_core-2.6.2rc1/pyagentic/serve/_app.py +0 -251
  58. pyagentic_core-2.6.2rc1/pyagentic/serve/_client_session.py +0 -178
  59. pyagentic_core-2.6.2rc1/pyagentic/serve/_container.py +0 -407
  60. pyagentic_core-2.6.2rc1/pyagentic/serve/_discovery.py +0 -47
  61. pyagentic_core-2.6.2rc1/pyagentic/serve/_docker.py +0 -127
  62. pyagentic_core-2.6.2rc1/pyagentic/serve/_exceptions.py +0 -59
  63. pyagentic_core-2.6.2rc1/pyagentic/serve/_manifest.py +0 -150
  64. pyagentic_core-2.6.2rc1/pyagentic/serve/_mcp_server.py +0 -78
  65. pyagentic_core-2.6.2rc1/pyagentic/serve/_sse.py +0 -60
  66. pyagentic_core-2.6.2rc1/pyagentic_core.egg-info/entry_points.txt +0 -2
  67. pyagentic_core-2.6.2rc1/tests/cli/test_init.py +0 -106
  68. pyagentic_core-2.6.2rc1/tests/serve/test_agent_ref.py +0 -451
  69. pyagentic_core-2.6.2rc1/tests/serve/test_app.py +0 -151
  70. pyagentic_core-2.6.2rc1/tests/serve/test_client_session.py +0 -265
  71. pyagentic_core-2.6.2rc1/tests/serve/test_discovery.py +0 -46
  72. pyagentic_core-2.6.2rc1/tests/serve/test_docker.py +0 -74
  73. pyagentic_core-2.6.2rc1/tests/serve/test_manifest.py +0 -136
  74. {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  75. {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  76. {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/.github/workflows/docs.yml +0 -0
  77. {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/.github/workflows/release.yml +0 -0
  78. {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/.github/workflows/testing.yml +0 -0
  79. {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/.gitignore +0 -0
  80. {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/CHANGELOG.md +0 -0
  81. {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/LICENSE +0 -0
  82. {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/README.md +0 -0
  83. {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/docs/agent-linking.md +0 -0
  84. {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/docs/diagrams/declaration.svg +0 -0
  85. {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/docs/diagrams/instantiation.svg +0 -0
  86. {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/docs/diagrams/runtime.svg +0 -0
  87. {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/docs/diagrams/source/README.md +0 -0
  88. {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/docs/diagrams/source/declaration.d2 +0 -0
  89. {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/docs/diagrams/source/instantiation.d2 +0 -0
  90. {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/docs/diagrams/source/runtime.d2 +0 -0
  91. {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/docs/execution-modes.md +0 -0
  92. {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/docs/getting-started.md +0 -0
  93. {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/docs/images/langfuse.png +0 -0
  94. {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/docs/inheritance.md +0 -0
  95. {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/docs/observability.md +0 -0
  96. {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/docs/phases.md +0 -0
  97. {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/docs/policies.md +0 -0
  98. {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/docs/reference/architecture.md +0 -0
  99. {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/docs/reference/modules.md +0 -0
  100. {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/docs/responses.md +0 -0
  101. {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/docs/states.md +0 -0
  102. {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/docs/structured-output.md +0 -0
  103. {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/docs/tools.md +0 -0
  104. {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/pyagentic/__init__.py +0 -0
  105. {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/pyagentic/_base/__init__.py +0 -0
  106. {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/pyagentic/_base/_agent/__init__.py +0 -0
  107. {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/pyagentic/_base/_agent/_agent_linking.py +0 -0
  108. {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/pyagentic/_base/_agent/_agent_state.py +0 -0
  109. {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/pyagentic/_base/_exceptions.py +0 -0
  110. {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/pyagentic/_base/_mcp.py +0 -0
  111. {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/pyagentic/_base/_metaclasses.py +0 -0
  112. {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/pyagentic/_base/_ref.py +0 -0
  113. {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/pyagentic/_base/_state.py +0 -0
  114. {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/pyagentic/_base/_tool.py +0 -0
  115. {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/pyagentic/_base/_validation.py +0 -0
  116. {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/pyagentic/_utils/_typing.py +0 -0
  117. {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/pyagentic/_utils/_warnings.py +0 -0
  118. {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/pyagentic/_version_scheme.py +0 -0
  119. {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/pyagentic/llm/__init__.py +0 -0
  120. {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/pyagentic/llm/_gemini.py +0 -0
  121. {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/pyagentic/llm/_mock.py +0 -0
  122. {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/pyagentic/llm/_openai.py +0 -0
  123. {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/pyagentic/llm/_openaiv1.py +0 -0
  124. {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/pyagentic/llm/_provider.py +0 -0
  125. {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/pyagentic/logging.py +0 -0
  126. {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/pyagentic/models/llm.py +0 -0
  127. {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/pyagentic/models/response.py +0 -0
  128. {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/pyagentic/models/tracing.py +0 -0
  129. {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/pyagentic/policies/__init__.py +0 -0
  130. {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/pyagentic/policies/_events.py +0 -0
  131. {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/pyagentic/policies/_policy.py +0 -0
  132. {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/pyagentic/tracing/__init__.py +0 -0
  133. {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/pyagentic/tracing/_basic.py +0 -0
  134. {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/pyagentic/tracing/_langfuse.py +0 -0
  135. {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/pyagentic/tracing/_tracer.py +0 -0
  136. {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/pyagentic/updates.py +0 -0
  137. {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/pyagentic_core.egg-info/dependency_links.txt +0 -0
  138. {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/pyagentic_core.egg-info/top_level.txt +0 -0
  139. {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/setup.cfg +0 -0
  140. {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/setup.py +0 -0
  141. {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/tests/__init__.py +0 -0
  142. {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/tests/_base/__init__.py +0 -0
  143. {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/tests/_base/test_agent.py +0 -0
  144. {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/tests/_base/test_agent_inheritance.py +0 -0
  145. {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/tests/_base/test_agent_linking.py +0 -0
  146. {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/tests/_base/test_agent_provider.py +0 -0
  147. {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/tests/_base/test_phases.py +0 -0
  148. {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/tests/_base/test_state.py +0 -0
  149. {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/tests/_base/test_tool.py +0 -0
  150. {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/tests/_base/test_validator.py +0 -0
  151. {pyagentic_core-2.6.2rc1/tests/cli → pyagentic_core-2.7.0a2/tests/api}/__init__.py +0 -0
  152. {pyagentic_core-2.6.2rc1/tests/serve → pyagentic_core-2.7.0a2/tests/api/jobs}/__init__.py +0 -0
  153. {pyagentic_core-2.6.2rc1/tests/serve → pyagentic_core-2.7.0a2/tests/api}/test_metaclass_models.py +0 -0
  154. {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/tests/conftest.py +0 -0
  155. {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/tests/models/test_responses.py +0 -0
  156. {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/tests/tracing/__init__.py +0 -0
  157. {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/tests/tracing/test_basic_tracer.py +0 -0
  158. {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/tests/tracing/test_models.py +0 -0
  159. {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/tests/tracing/test_tracer.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyagentic-core
3
- Version: 2.6.2rc1
3
+ Version: 2.7.0a2
4
4
  Summary: Build LLM Agents in a Pythonic way
5
5
  Author-email: Ryan Mikulec <rmikulec.dev@gmail.com>
6
6
  License: MIT
@@ -26,13 +26,9 @@ Requires-Dist: jinja2>=3.1.6
26
26
  Requires-Dist: fastmcp>=3.4.0
27
27
  Provides-Extra: mcp
28
28
  Requires-Dist: fastmcp>=2.0.0; extra == "mcp"
29
- Provides-Extra: deploy
30
- Requires-Dist: typer>=0.15.0; extra == "deploy"
31
- Requires-Dist: fastapi>=0.115.0; extra == "deploy"
32
- Requires-Dist: uvicorn>=0.34.0; extra == "deploy"
33
- Requires-Dist: sse-starlette>=2.0.0; extra == "deploy"
34
- Requires-Dist: httpx>=0.28.0; extra == "deploy"
35
- Requires-Dist: fastmcp>=2.0.0; extra == "deploy"
29
+ Provides-Extra: api
30
+ Requires-Dist: fastapi>=0.115.0; extra == "api"
31
+ Requires-Dist: uvicorn>=0.34.0; extra == "api"
36
32
  Dynamic: license-file
37
33
 
38
34
  # PyAgentic
@@ -0,0 +1,108 @@
1
+ # Creating an App
2
+
3
+ `create_app` builds a standalone FastAPI application from one or more agents.
4
+ `create_router` builds an `APIRouter` you can mount into an app you already
5
+ have. Both derive their routes from the agent's metaclass-generated models.
6
+
7
+ ## A single agent
8
+
9
+ Pass the class; it's served at the root:
10
+
11
+ ```python
12
+ from pyagentic.api import create_app
13
+
14
+ app = create_app(MyAgent, model="openai::gpt-4o")
15
+ ```
16
+
17
+ `model` is the default LLM used for sessions that don't override it. `name`,
18
+ `version`, and `description` set the FastAPI metadata (and default from
19
+ `agents.toml`, see below).
20
+
21
+ ## Multiple agents
22
+
23
+ Pass a list and each agent is mounted under a prefix derived from its class name
24
+ (`ResearchAgent` → `/research`, `WriterAgent` → `/writer`). A top-level
25
+ `GET /` index lists what's mounted, and `GET /health` is added:
26
+
27
+ ```python
28
+ app = create_app([ResearchAgent, WriterAgent])
29
+ # /research/sessions, /research/chat, ...
30
+ # /writer/sessions, /writer/chat, ...
31
+ ```
32
+
33
+ For explicit prefixes, pass a `{prefix: agent}` mapping:
34
+
35
+ ```python
36
+ app = create_app({"/research": ResearchAgent, "/writer": WriterAgent})
37
+ ```
38
+
39
+ Each agent gets its own isolated `SessionManager` — sessions, state, and (if
40
+ enabled) jobs never cross between agents.
41
+
42
+ !!! note "Linked agents stay subordinate"
43
+ Agents connected with `Link[...]` are called through their parent agent's
44
+ tools, not exposed as separate endpoints. "Multiple agents" here means
45
+ several independent top-level agents on one app.
46
+
47
+ ## Mounting into an existing app
48
+
49
+ Use `create_router` to add an agent to a FastAPI app you already run:
50
+
51
+ ```python
52
+ from fastapi import FastAPI
53
+ from pyagentic.api import create_router
54
+
55
+ app = FastAPI()
56
+ app.include_router(create_router(MyAgent, model="openai::gpt-4o"), prefix="/bot")
57
+ ```
58
+
59
+ The router owns its `SessionManager`, exposed as `router.sessions` so you can
60
+ share it (for example with `mount_mcp`).
61
+
62
+ ## Configuration: `agents.toml`
63
+
64
+ Put an `agents.toml` next to your `pyproject.toml`. `create_app` reads its
65
+ `[app]` section for defaults; explicit keyword arguments override it.
66
+
67
+ ```toml
68
+ [app]
69
+ name = "my-agents"
70
+ version = "0.1.0"
71
+ description = "My agent service"
72
+ model = "openai::gpt-4o" # default model for sessions
73
+ ```
74
+
75
+ ```python
76
+ app = create_app(MyAgent) # name/version/model come from agents.toml
77
+ app = create_app(MyAgent, model="anthropic::claude-sonnet-4-6") # override
78
+ ```
79
+
80
+ `agents.toml` also has `[deploy]` (see [Deploying](deploying.md)) and `[jobs]`
81
+ (see [Async Jobs](jobs.md)) sections.
82
+
83
+ ## Exposing the agent over MCP
84
+
85
+ Pass `mcp=True` to also mount a [Model Context Protocol](https://modelcontextprotocol.io)
86
+ endpoint per agent at `<prefix>/mcp`, sharing the same sessions as the HTTP routes:
87
+
88
+ ```python
89
+ app = create_app(MyAgent, mcp=True) # MCP server at /mcp
90
+ ```
91
+
92
+ To mount MCP onto your own app, use `mount_mcp`:
93
+
94
+ ```python
95
+ from pyagentic.api import create_router, mount_mcp
96
+
97
+ router = create_router(MyAgent)
98
+ app.include_router(router, prefix="/bot")
99
+ mount_mcp(app, MyAgent, sessions=router.sessions, path="/bot/mcp")
100
+ ```
101
+
102
+ `mount_mcp` requires the `fastmcp` extra (`pip install pyagentic-core[mcp]`).
103
+
104
+ ## Next steps
105
+
106
+ - [Run your app](running.md) and explore the HTTP API
107
+ - Enable [durable async jobs](jobs.md) for long-running calls
108
+ - [Deploy](deploying.md) it as a container
@@ -0,0 +1,80 @@
1
+ # Deploying
2
+
3
+ Your app is a standard ASGI application, so it deploys like any FastAPI service:
4
+ run `uvicorn main:app` in a container. PyAgentic can generate a Dockerfile for
5
+ you from `agents.toml`.
6
+
7
+ ## The `[deploy]` section
8
+
9
+ Add a `[deploy]` section to `agents.toml` (beside your `pyproject.toml`):
10
+
11
+ ```toml
12
+ [deploy]
13
+ target = "main:app" # the ASGI app uvicorn serves
14
+ python_version = "3.13"
15
+ dependencies = [] # extra pip packages to install in the image
16
+ port = 8000
17
+ env = ["OPENAI_API_KEY"] # environment variables required at runtime
18
+ ```
19
+
20
+ ## Generating a Dockerfile
21
+
22
+ ```python
23
+ from pyagentic.api import write_dockerfile
24
+
25
+ write_dockerfile() # reads ./agents.toml, writes ./Dockerfile
26
+ ```
27
+
28
+ Or get the contents as a string with `generate_dockerfile()`. The generated
29
+ Dockerfile installs your project and runs it with uvicorn:
30
+
31
+ ```dockerfile
32
+ FROM python:3.13-slim
33
+ WORKDIR /app
34
+ RUN pip install uv
35
+
36
+ RUN uv pip install --system "pyagentic-core[api]"
37
+
38
+ COPY . .
39
+ EXPOSE 8000
40
+ CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
41
+ ```
42
+
43
+ Extra `[deploy].dependencies` are installed as an additional step:
44
+
45
+ ```toml
46
+ [deploy]
47
+ dependencies = ["pandas", "numpy"]
48
+ ```
49
+
50
+ ```dockerfile
51
+ # Added automatically:
52
+ RUN uv pip install --system "pandas" "numpy"
53
+ ```
54
+
55
+ ## Building and running
56
+
57
+ Build and run with your normal Docker tooling:
58
+
59
+ ```bash
60
+ docker build -t my-agents:0.1.0 .
61
+ docker run -p 8000:8000 --env-file .env my-agents:0.1.0
62
+ ```
63
+
64
+ Pass secrets via `--env-file` or `-e` (the variables you listed in
65
+ `[deploy].env`):
66
+
67
+ ```bash
68
+ docker run -p 8000:8000 -e OPENAI_API_KEY=sk-... my-agents:0.1.0
69
+ ```
70
+
71
+ !!! tip "Durable jobs in a container"
72
+ If you use [async jobs](jobs.md), set `[jobs].store` to a path on a mounted
73
+ volume so job records survive container restarts. The default
74
+ `.pyagentic/jobs.db` lives in the container's filesystem and is lost when the
75
+ container is removed.
76
+
77
+ ## Next steps
78
+
79
+ - Review [async jobs](jobs.md) for long-running deployments
80
+ - Explore [observability](../observability.md) to trace agents in production
@@ -0,0 +1,103 @@
1
+ # API
2
+
3
+ PyAgentic turns an agent class into a FastAPI application (or a router you mount
4
+ into your own app), with an HTTP API generated automatically from the agent's
5
+ tools, state, and input signature. Optionally expose it over MCP, run long calls
6
+ as durable background jobs, and generate a Dockerfile to ship it.
7
+
8
+ ## Installation
9
+
10
+ The serving tools ship as an optional extra:
11
+
12
+ ```bash
13
+ pip install pyagentic-core[api]
14
+ ```
15
+
16
+ This installs FastAPI and uvicorn alongside the core package.
17
+
18
+ ## Quickstart
19
+
20
+ Write a `main.py` that builds the app from your agent class:
21
+
22
+ ```python
23
+ # main.py
24
+ from pyagentic import BaseAgent, tool
25
+ from pyagentic.api import create_app
26
+
27
+
28
+ class AssistantAgent(BaseAgent):
29
+ __system_message__ = "You are a helpful assistant."
30
+
31
+ @tool("Add two numbers")
32
+ def add(self, a: int, b: int) -> str:
33
+ return str(a + b)
34
+
35
+
36
+ app = create_app(AssistantAgent, model="openai::gpt-4o")
37
+ ```
38
+
39
+ Run it with uvicorn:
40
+
41
+ ```bash
42
+ uvicorn main:app --reload
43
+ ```
44
+
45
+ That's the whole loop — no CLI, no scaffolding. Your agent is now served at
46
+ `http://localhost:8000` with session management, chat, streaming, and a schema
47
+ endpoint, all derived from the class.
48
+
49
+ ## What you get
50
+
51
+ The generated API always reflects your agent — add a tool parameter or state
52
+ field and the schemas update with no manual OpenAPI maintenance:
53
+
54
+ | Route | Purpose |
55
+ |---|---|
56
+ | `GET /` | Agent metadata (name, tools, state fields, linked agents) |
57
+ | `GET /health` | Liveness probe |
58
+ | `GET /schema` | JSON schemas for request/response/stream-event/state |
59
+ | `POST /sessions` | Create an isolated session |
60
+ | `POST /sessions/{id}/chat` | Send a message, get a complete response |
61
+ | `POST /sessions/{id}/chat/stream` | Stream typed SSE events as the agent works |
62
+ | `GET /sessions/{id}/state` | Current agent state for a session |
63
+
64
+ ## Guides
65
+
66
+ <div class="grid cards" markdown>
67
+
68
+ - :material-application-cog: **[Creating an App](creating-an-app.md)**
69
+
70
+ ---
71
+
72
+ Build an app or router from one or many agents, configure it with
73
+ `agents.toml`, and mount it into an existing FastAPI app.
74
+
75
+ [:octicons-arrow-right-24: Build it](creating-an-app.md)
76
+
77
+ - :material-play-circle: **[Running](running.md)**
78
+
79
+ ---
80
+
81
+ Run with uvicorn and explore the auto-generated HTTP API: sessions, chat,
82
+ streaming, and state.
83
+
84
+ [:octicons-arrow-right-24: Run locally](running.md)
85
+
86
+ - :material-clock-fast: **[Async Jobs](jobs.md)**
87
+
88
+ ---
89
+
90
+ Run agents that take longer than an HTTP request: submit a durable job and
91
+ stream its updates, surviving timeouts and reconnects.
92
+
93
+ [:octicons-arrow-right-24: Go async](jobs.md)
94
+
95
+ - :material-docker: **[Deploying](deploying.md)**
96
+
97
+ ---
98
+
99
+ Generate a Dockerfile from `agents.toml` and ship your agent as a container.
100
+
101
+ [:octicons-arrow-right-24: Ship it](deploying.md)
102
+
103
+ </div>
@@ -0,0 +1,115 @@
1
+ # Async Jobs
2
+
3
+ A `/chat` request runs the agent inside the HTTP request and returns when it's
4
+ done. That's fine for quick calls, but an agent that researches, calls tools,
5
+ and chains for several minutes will outlast client read timeouts, load-balancer
6
+ idle limits, and flaky connections.
7
+
8
+ The job system solves this. You **submit** an agent run, it executes in the
9
+ background as a **durable record**, and you **stream or poll** its updates
10
+ later — surviving timeouts, disconnects, and reconnects.
11
+
12
+ ## Enabling jobs
13
+
14
+ Jobs are opt-in. Turn them on in code:
15
+
16
+ ```python
17
+ from pyagentic.api import create_app
18
+
19
+ app = create_app(MyAgent, model="openai::gpt-4o", jobs=True)
20
+ ```
21
+
22
+ or in `agents.toml`:
23
+
24
+ ```toml
25
+ [jobs]
26
+ enabled = true
27
+ store = ".pyagentic/jobs.db" # SQLite path; ":memory:" for ephemeral
28
+ admission_cap = 16 # max jobs in-flight at once
29
+ max_concurrency = 8 # max concurrent agent runs
30
+ ttl = "24h" # how long terminal job records are kept
31
+ cleanup_interval_seconds = 300
32
+ ```
33
+
34
+ This mounts a `/jobs` API per agent (under each agent's prefix in a multi-agent
35
+ app) and persists job records and their update logs to SQLite, so they survive a
36
+ server restart.
37
+
38
+ ## The flow
39
+
40
+ ```python
41
+ import httpx
42
+
43
+ # 1. Submit — returns immediately with a job id (202)
44
+ r = httpx.post("http://localhost:8000/jobs",
45
+ json={"input": {"user_input": "Research AI safety"}})
46
+ job_id = r.json()["job_id"]
47
+
48
+ # 2. Stream updates as the agent works (resumable — see below)
49
+ with httpx.stream("GET", f"http://localhost:8000/jobs/{job_id}/stream") as s:
50
+ for line in s.iter_lines():
51
+ print(line)
52
+
53
+ # 3. Or just fetch the final result when you're ready
54
+ result = httpx.get(f"http://localhost:8000/jobs/{job_id}").json()
55
+ ```
56
+
57
+ ## Endpoints
58
+
59
+ | Route | Purpose |
60
+ |---|---|
61
+ | `POST /jobs` | Submit a run. Body `{"input": {<chat fields>}, "session_id": <optional>}`. Returns `202 {job_id, status}`. |
62
+ | `GET /jobs` | List jobs, newest first. Filter with `?status=` and `?session_id=`. |
63
+ | `GET /jobs/{id}` | Job status and, when terminal, its result. |
64
+ | `GET /jobs/{id}/updates?since=<seq>` | The update log past a sequence cursor. |
65
+ | `GET /jobs/{id}/stream` | SSE stream of updates: replay from cursor, then live tail. |
66
+ | `POST /jobs/{id}/cancel` | Cancel a queued or running job. |
67
+
68
+ The `input` object is your agent's normal chat request body. Pass a
69
+ `session_id` (from `POST /sessions`) to run the job against an existing session;
70
+ omit it for a one-off run.
71
+
72
+ ## Surviving timeouts: replay-from-cursor
73
+
74
+ Every update carries a monotonic, gapless sequence number (`seq`), emitted on the
75
+ SSE stream as the frame `id`. If the connection drops, reconnect with the
76
+ `Last-Event-ID` header (or `?since=<seq>`) and the stream replays **only** what
77
+ you missed, then resumes the live tail:
78
+
79
+ ```
80
+ event: llm_response
81
+ id: 0
82
+ data: {...}
83
+
84
+ event: tool_response
85
+ id: 1
86
+ data: {...}
87
+ ```
88
+
89
+ ```bash
90
+ # Reconnect after seq 1 — replays seq 2 onward, then tails live
91
+ curl -N http://localhost:8000/jobs/{id}/stream \
92
+ -H "Last-Event-ID: 1"
93
+ ```
94
+
95
+ The stream closes after a terminal event (`agent_response`, `job_failed`, or
96
+ `job_cancelled`). Because the store is the single source of truth, a client can
97
+ disconnect for any reason and pick up exactly where it left off — the agent run
98
+ keeps going server-side regardless.
99
+
100
+ ## How it works
101
+
102
+ - **Durable store** — a `JobStore` (SQLite by default) holds each job record and
103
+ its append-only update log. Restarting the server keeps finished jobs and
104
+ their logs; an in-flight `running` job is marked failed on restart (execution
105
+ is not resumed), and `queued` jobs are re-dispatched.
106
+ - **In-process backend** — runs `agent.step()` in the server's event loop and
107
+ emits each update. Session-bound jobs run on the session's live agent;
108
+ the orchestrator serializes a session's jobs FIFO (one in-flight per session).
109
+ - **Admission cap** — a single process-wide cap bounds concurrent runs; jobs
110
+ beyond it stay `queued` until a slot frees.
111
+
112
+ ## Next steps
113
+
114
+ - [Deploy](deploying.md) your app — set a persistent `[jobs].store` path in the container
115
+ - Revisit the [synchronous chat endpoints](running.md#chat) for quick calls
@@ -0,0 +1,141 @@
1
+ # Running
2
+
3
+ Your app is an ordinary ASGI application, so you run it with uvicorn (or any
4
+ ASGI server). The API is generated from your agent's class — tools, state, and
5
+ input signature all become typed endpoints with zero configuration.
6
+
7
+ ## Run with uvicorn
8
+
9
+ ```bash
10
+ uvicorn main:app --reload
11
+ ```
12
+
13
+ `main:app` points at the `app = create_app(...)` in your `main.py`. Use
14
+ `--reload` for auto-restart during development, and `--host`/`--port` to change
15
+ the bind address:
16
+
17
+ ```bash
18
+ uvicorn main:app --host 0.0.0.0 --port 3000
19
+ ```
20
+
21
+ The interactive OpenAPI docs are available at `http://localhost:8000/docs`.
22
+
23
+ ## API endpoints
24
+
25
+ All request and response schemas are derived from your agent's
26
+ metaclass-generated models — adding a tool parameter or state field
27
+ automatically updates the API. (For a multi-agent app, these live under each
28
+ agent's prefix, e.g. `/research/sessions`.)
29
+
30
+ ### Info routes
31
+
32
+ ```bash
33
+ # Agent metadata: name, version, tools, state fields, linked agents
34
+ curl http://localhost:8000/
35
+
36
+ # Liveness probe
37
+ curl http://localhost:8000/health
38
+
39
+ # JSON schemas for request, response, stream event, and state models
40
+ curl http://localhost:8000/schema
41
+ ```
42
+
43
+ ### Session management
44
+
45
+ Sessions provide isolated agent instances with independent state and
46
+ conversation history. Each session holds its own agent, so state changes in one
47
+ session don't affect others.
48
+
49
+ ```bash
50
+ # Create a new session (returns session_id)
51
+ curl -X POST http://localhost:8000/sessions
52
+
53
+ # Create with a model or API key override
54
+ curl -X POST http://localhost:8000/sessions \
55
+ -H "Content-Type: application/json" \
56
+ -d '{"model": "openai::gpt-4o", "api_key": "sk-..."}'
57
+
58
+ # List all active session IDs
59
+ curl http://localhost:8000/sessions
60
+
61
+ # Delete a session
62
+ curl -X DELETE http://localhost:8000/sessions/{session_id}
63
+ ```
64
+
65
+ Sessions are stored in memory — restarting the server clears them.
66
+
67
+ ### Chat
68
+
69
+ The chat request body is automatically derived from your agent's `__call__`
70
+ signature. With the default signature, the body is `{"user_input": "..."}`; if
71
+ you override `__call__` with custom parameters, the schema updates to match.
72
+
73
+ ```bash
74
+ curl -X POST http://localhost:8000/sessions/{session_id}/chat \
75
+ -H "Content-Type: application/json" \
76
+ -d '{"user_input": "Hello!"}'
77
+ ```
78
+
79
+ ### Streaming
80
+
81
+ The stream endpoint returns [Server-Sent Events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events)
82
+ with typed events as the agent works:
83
+
84
+ ```bash
85
+ curl -N -X POST http://localhost:8000/sessions/{session_id}/chat/stream \
86
+ -H "Content-Type: application/json" \
87
+ -d '{"user_input": "Research AI safety"}'
88
+ ```
89
+
90
+ ```
91
+ event: llm_response
92
+ data: {"event": "llm_response", "data": {...}}
93
+
94
+ event: tool_response
95
+ data: {"event": "tool_response", "data": {...}}
96
+
97
+ event: agent_response
98
+ data: {"event": "agent_response", "data": {...}}
99
+ ```
100
+
101
+ - `llm_response` — Fired after each LLM inference. The model's text and any tool calls.
102
+ - `tool_response` — Fired after each tool execution. The tool name, arguments, and result.
103
+ - `agent_response` — Fired once at the end with the complete `AgentResponse`.
104
+
105
+ This maps directly to the three response types from
106
+ [`agent.step()`](../execution-modes.md): `LLMResponse`, `ToolResponse`, and
107
+ `AgentResponse`.
108
+
109
+ !!! tip "Long-running agents"
110
+ `/chat` and `/chat/stream` run within the HTTP request — fine for quick
111
+ calls, but a multi-minute agent run can outlive client or proxy timeouts.
112
+ For those, submit a [durable job](jobs.md) instead and stream its updates.
113
+
114
+ ### State
115
+
116
+ ```bash
117
+ # Get the current agent state for a session
118
+ curl http://localhost:8000/sessions/{session_id}/state
119
+ ```
120
+
121
+ Returns the full state model, serialized using the agent's `__state_class__`.
122
+
123
+ ## How it works
124
+
125
+ `create_app` extracts the agent's metaclass-generated models and wires them to
126
+ the routes:
127
+
128
+ - `__request_model__` → chat request body
129
+ - `__response_model__` → chat response body
130
+ - `__stream_event_model__` → typed SSE events
131
+ - `__state_class__` → state endpoint
132
+
133
+ A `SessionManager` (an in-memory store holding one agent instance per session)
134
+ backs the session routes. The streaming endpoint drives `agent.step()`; the
135
+ synchronous endpoint calls `agent(**kwargs)`.
136
+
137
+ ## Next steps
138
+
139
+ - Add [durable async jobs](jobs.md) for long-running calls
140
+ - [Deploy](deploying.md) your app as a Docker image
141
+ - Review the [architecture reference](../reference/architecture.md) for runtime internals
@@ -104,13 +104,13 @@ Dive deeper into PyAgentic's powerful features:
104
104
 
105
105
  [:octicons-arrow-right-24: Trace behavior](observability.md)
106
106
 
107
- - :material-rocket-launch: **[Deploy](deploy/index.md)**
107
+ - :material-rocket-launch: **[API](api/index.md)**
108
108
 
109
109
  ---
110
110
 
111
- Scaffold, run, build, and publish agents with the PyAgentic CLI.
111
+ Turn agents into a FastAPI app or router, run async jobs, and ship a Docker image.
112
112
 
113
- [:octicons-arrow-right-24: Deploy your agent](deploy/index.md)
113
+ [:octicons-arrow-right-24: Serve your agent](api/index.md)
114
114
 
115
115
  </div>
116
116
 
@@ -18,12 +18,13 @@ nav:
18
18
  - MCP: mcp.md
19
19
  - Inheritance: inheritance.md
20
20
  - Observability: observability.md
21
- - Deploy:
22
- - Overview: deploy/index.md
23
- - Create a Project: deploy/creating-a-project.md
24
- - Run Your Project: deploy/running.md
25
- - Build & Deploy: deploy/building.md
26
- - API Reference:
21
+ - API:
22
+ - Overview: api/index.md
23
+ - Creating an App: api/creating-an-app.md
24
+ - Running: api/running.md
25
+ - Async Jobs: api/jobs.md
26
+ - Deploying: api/deploying.md
27
+ - Code Reference:
27
28
  - Architecture: reference/architecture.md
28
29
  - Modules: reference/modules.md
29
30
 
@@ -278,7 +278,9 @@ class BaseAgent(metaclass=AgentMeta):
278
278
  mcp_response_models = {}
279
279
 
280
280
  for field_name, mcp_def in self.__mcp_defs__.items():
281
- info = mcp_def.info
281
+ # Resolve StateRefs (e.g. ref.self.root in server/args) the same
282
+ # way _get_tool_defs resolves them in tool definitions.
283
+ info = mcp_def.info.resolve(self.agent_reference)
282
284
  server = info.server
283
285
 
284
286
  # Auto-detect transport
@@ -22,17 +22,19 @@ class _SpecInfo:
22
22
  return None
23
23
 
24
24
  def resolve(self, agent_reference: dict) -> Self:
25
+ def _resolve_value(value: Any) -> Any:
26
+ if isinstance(value, RefNode):
27
+ return value.resolve(agent_reference)
28
+ if isinstance(value, list):
29
+ # resolve refs nested in list fields, e.g. MCPInfo.args
30
+ return [_resolve_value(item) for item in value]
31
+ return value
32
+
25
33
  attrs: dict[str, Any] = {}
26
34
 
27
35
  # walk actual model fields, not the dumped / serialized version
28
- for name, value in self.__dict__.items():
29
- value = getattr(self, name)
30
-
31
- if isinstance(value, RefNode):
32
- resolved = value.resolve(agent_reference)
33
- attrs[name] = resolved
34
- else:
35
- attrs[name] = value
36
+ for name in self.__dict__:
37
+ attrs[name] = _resolve_value(getattr(self, name))
36
38
 
37
39
  # rebuild same class with resolved attrs
38
40
  return self.__class__(**attrs)
@@ -89,8 +91,8 @@ class ParamInfo(_SpecInfo):
89
91
  class MCPInfo(_SpecInfo):
90
92
  """Descriptor for configuring MCP server connections."""
91
93
 
92
- server: Any = None
93
- args: list[str] | None = None
94
+ server: MaybeRef[Any] = None
95
+ args: list[MaybeRef[str]] | None = None
94
96
  tools: list[str] | None = None
95
97
  exclude_tools: list[str] | None = None
96
98
  prefix: bool | str = True
@@ -1,6 +1,6 @@
1
1
  from typing import Any, Callable, Literal
2
2
 
3
- from pyagentic._base._info import StateInfo, ParamInfo, AgentInfo, MCPInfo
3
+ from pyagentic._base._info import StateInfo, ParamInfo, AgentInfo, MCPInfo, MaybeRef
4
4
  from pyagentic.policies._policy import Policy
5
5
 
6
6
 
@@ -141,9 +141,9 @@ class spec:
141
141
 
142
142
  @staticmethod
143
143
  def MCPLink(
144
- server: Any = None,
144
+ server: MaybeRef[Any] = None,
145
145
  *,
146
- args: list[str] | None = None,
146
+ args: list[MaybeRef[str]] | None = None,
147
147
  tools: list[str] | None = None,
148
148
  exclude_tools: list[str] | None = None,
149
149
  prefix: bool | str = True,
@@ -158,6 +158,10 @@ class spec:
158
158
  - ``str`` + ``args`` → stdio subprocess
159
159
  - ``FastMCP`` object → in-process
160
160
 
161
+ Both ``server`` and ``args`` entries may be StateRefs (e.g.
162
+ ``ref.self.root``); they are resolved against agent state when the
163
+ MCP connection is established.
164
+
161
165
  Args:
162
166
  server (Any): URL string, command string, or FastMCP server object.
163
167
  args (list[str], optional): Arguments for stdio subprocess mode.