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.
- {pyagentic_core-2.6.2rc1/pyagentic_core.egg-info → pyagentic_core-2.7.0a2}/PKG-INFO +4 -8
- pyagentic_core-2.7.0a2/docs/api/creating-an-app.md +108 -0
- pyagentic_core-2.7.0a2/docs/api/deploying.md +80 -0
- pyagentic_core-2.7.0a2/docs/api/index.md +103 -0
- pyagentic_core-2.7.0a2/docs/api/jobs.md +115 -0
- pyagentic_core-2.7.0a2/docs/api/running.md +141 -0
- {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/docs/index.md +3 -3
- {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/mkdocs.yml +7 -6
- {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/pyagentic/_base/_agent/_agent.py +3 -1
- {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/pyagentic/_base/_info.py +12 -10
- {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/pyagentic/_base/_spec.py +7 -3
- pyagentic_core-2.7.0a2/pyagentic/api/__init__.py +38 -0
- pyagentic_core-2.7.0a2/pyagentic/api/_app.py +469 -0
- pyagentic_core-2.7.0a2/pyagentic/api/_config.py +191 -0
- pyagentic_core-2.7.0a2/pyagentic/api/_docker.py +76 -0
- pyagentic_core-2.7.0a2/pyagentic/api/_mcp_server.py +130 -0
- {pyagentic_core-2.6.2rc1/pyagentic/serve → pyagentic_core-2.7.0a2/pyagentic/api}/_sessions.py +31 -12
- pyagentic_core-2.7.0a2/pyagentic/api/jobs/__init__.py +35 -0
- pyagentic_core-2.7.0a2/pyagentic/api/jobs/_models.py +137 -0
- pyagentic_core-2.7.0a2/pyagentic/api/jobs/_orchestrator.py +462 -0
- pyagentic_core-2.7.0a2/pyagentic/api/jobs/_routes.py +240 -0
- pyagentic_core-2.7.0a2/pyagentic/api/jobs/backends/__init__.py +43 -0
- pyagentic_core-2.7.0a2/pyagentic/api/jobs/backends/_base.py +142 -0
- pyagentic_core-2.7.0a2/pyagentic/api/jobs/backends/_in_process.py +121 -0
- pyagentic_core-2.7.0a2/pyagentic/api/jobs/store/__init__.py +21 -0
- pyagentic_core-2.7.0a2/pyagentic/api/jobs/store/_base.py +162 -0
- pyagentic_core-2.7.0a2/pyagentic/api/jobs/store/_sqlite.py +371 -0
- {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/pyagentic/llm/_anthropic.py +0 -3
- {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2/pyagentic_core.egg-info}/PKG-INFO +4 -8
- {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/pyagentic_core.egg-info/SOURCES.txt +33 -35
- {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/pyagentic_core.egg-info/requires.txt +1 -5
- {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/pyproject.toml +1 -8
- {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/tests/_base/test_params.py +26 -0
- pyagentic_core-2.7.0a2/tests/api/jobs/test_orchestrator.py +269 -0
- pyagentic_core-2.7.0a2/tests/api/jobs/test_routes.py +269 -0
- pyagentic_core-2.7.0a2/tests/api/jobs/test_store_sqlite.py +190 -0
- pyagentic_core-2.7.0a2/tests/api/test_app.py +152 -0
- pyagentic_core-2.7.0a2/tests/api/test_config.py +96 -0
- pyagentic_core-2.7.0a2/tests/api/test_docker.py +52 -0
- pyagentic_core-2.7.0a2/tests/api/test_mcp_server.py +97 -0
- pyagentic_core-2.7.0a2/tests/api/test_router.py +81 -0
- {pyagentic_core-2.6.2rc1/tests/serve → pyagentic_core-2.7.0a2/tests/api}/test_sessions.py +19 -29
- {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/uv.lock +73 -81
- pyagentic_core-2.6.2rc1/docs/deploy/building.md +0 -166
- pyagentic_core-2.6.2rc1/docs/deploy/creating-a-project.md +0 -169
- pyagentic_core-2.6.2rc1/docs/deploy/index.md +0 -43
- pyagentic_core-2.6.2rc1/docs/deploy/running.md +0 -155
- pyagentic_core-2.6.2rc1/pyagentic/cli/__init__.py +0 -30
- pyagentic_core-2.6.2rc1/pyagentic/cli/__main__.py +0 -7
- pyagentic_core-2.6.2rc1/pyagentic/cli/_build.py +0 -55
- pyagentic_core-2.6.2rc1/pyagentic/cli/_init.py +0 -176
- pyagentic_core-2.6.2rc1/pyagentic/cli/_publish.py +0 -100
- pyagentic_core-2.6.2rc1/pyagentic/cli/_run.py +0 -174
- pyagentic_core-2.6.2rc1/pyagentic/cli/_templates.py +0 -138
- pyagentic_core-2.6.2rc1/pyagentic/serve/__init__.py +0 -32
- pyagentic_core-2.6.2rc1/pyagentic/serve/_agent_ref.py +0 -254
- pyagentic_core-2.6.2rc1/pyagentic/serve/_app.py +0 -251
- pyagentic_core-2.6.2rc1/pyagentic/serve/_client_session.py +0 -178
- pyagentic_core-2.6.2rc1/pyagentic/serve/_container.py +0 -407
- pyagentic_core-2.6.2rc1/pyagentic/serve/_discovery.py +0 -47
- pyagentic_core-2.6.2rc1/pyagentic/serve/_docker.py +0 -127
- pyagentic_core-2.6.2rc1/pyagentic/serve/_exceptions.py +0 -59
- pyagentic_core-2.6.2rc1/pyagentic/serve/_manifest.py +0 -150
- pyagentic_core-2.6.2rc1/pyagentic/serve/_mcp_server.py +0 -78
- pyagentic_core-2.6.2rc1/pyagentic/serve/_sse.py +0 -60
- pyagentic_core-2.6.2rc1/pyagentic_core.egg-info/entry_points.txt +0 -2
- pyagentic_core-2.6.2rc1/tests/cli/test_init.py +0 -106
- pyagentic_core-2.6.2rc1/tests/serve/test_agent_ref.py +0 -451
- pyagentic_core-2.6.2rc1/tests/serve/test_app.py +0 -151
- pyagentic_core-2.6.2rc1/tests/serve/test_client_session.py +0 -265
- pyagentic_core-2.6.2rc1/tests/serve/test_discovery.py +0 -46
- pyagentic_core-2.6.2rc1/tests/serve/test_docker.py +0 -74
- pyagentic_core-2.6.2rc1/tests/serve/test_manifest.py +0 -136
- {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/.github/workflows/docs.yml +0 -0
- {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/.github/workflows/release.yml +0 -0
- {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/.github/workflows/testing.yml +0 -0
- {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/.gitignore +0 -0
- {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/CHANGELOG.md +0 -0
- {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/LICENSE +0 -0
- {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/README.md +0 -0
- {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/docs/agent-linking.md +0 -0
- {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/docs/diagrams/declaration.svg +0 -0
- {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/docs/diagrams/instantiation.svg +0 -0
- {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/docs/diagrams/runtime.svg +0 -0
- {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/docs/diagrams/source/README.md +0 -0
- {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/docs/diagrams/source/declaration.d2 +0 -0
- {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/docs/diagrams/source/instantiation.d2 +0 -0
- {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/docs/diagrams/source/runtime.d2 +0 -0
- {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/docs/execution-modes.md +0 -0
- {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/docs/getting-started.md +0 -0
- {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/docs/images/langfuse.png +0 -0
- {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/docs/inheritance.md +0 -0
- {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/docs/observability.md +0 -0
- {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/docs/phases.md +0 -0
- {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/docs/policies.md +0 -0
- {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/docs/reference/architecture.md +0 -0
- {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/docs/reference/modules.md +0 -0
- {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/docs/responses.md +0 -0
- {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/docs/states.md +0 -0
- {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/docs/structured-output.md +0 -0
- {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/docs/tools.md +0 -0
- {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/pyagentic/__init__.py +0 -0
- {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/pyagentic/_base/__init__.py +0 -0
- {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/pyagentic/_base/_agent/__init__.py +0 -0
- {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/pyagentic/_base/_agent/_agent_linking.py +0 -0
- {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/pyagentic/_base/_agent/_agent_state.py +0 -0
- {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/pyagentic/_base/_exceptions.py +0 -0
- {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/pyagentic/_base/_mcp.py +0 -0
- {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/pyagentic/_base/_metaclasses.py +0 -0
- {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/pyagentic/_base/_ref.py +0 -0
- {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/pyagentic/_base/_state.py +0 -0
- {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/pyagentic/_base/_tool.py +0 -0
- {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/pyagentic/_base/_validation.py +0 -0
- {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/pyagentic/_utils/_typing.py +0 -0
- {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/pyagentic/_utils/_warnings.py +0 -0
- {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/pyagentic/_version_scheme.py +0 -0
- {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/pyagentic/llm/__init__.py +0 -0
- {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/pyagentic/llm/_gemini.py +0 -0
- {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/pyagentic/llm/_mock.py +0 -0
- {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/pyagentic/llm/_openai.py +0 -0
- {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/pyagentic/llm/_openaiv1.py +0 -0
- {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/pyagentic/llm/_provider.py +0 -0
- {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/pyagentic/logging.py +0 -0
- {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/pyagentic/models/llm.py +0 -0
- {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/pyagentic/models/response.py +0 -0
- {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/pyagentic/models/tracing.py +0 -0
- {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/pyagentic/policies/__init__.py +0 -0
- {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/pyagentic/policies/_events.py +0 -0
- {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/pyagentic/policies/_policy.py +0 -0
- {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/pyagentic/tracing/__init__.py +0 -0
- {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/pyagentic/tracing/_basic.py +0 -0
- {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/pyagentic/tracing/_langfuse.py +0 -0
- {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/pyagentic/tracing/_tracer.py +0 -0
- {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/pyagentic/updates.py +0 -0
- {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/pyagentic_core.egg-info/dependency_links.txt +0 -0
- {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/pyagentic_core.egg-info/top_level.txt +0 -0
- {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/setup.cfg +0 -0
- {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/setup.py +0 -0
- {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/tests/__init__.py +0 -0
- {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/tests/_base/__init__.py +0 -0
- {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/tests/_base/test_agent.py +0 -0
- {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/tests/_base/test_agent_inheritance.py +0 -0
- {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/tests/_base/test_agent_linking.py +0 -0
- {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/tests/_base/test_agent_provider.py +0 -0
- {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/tests/_base/test_phases.py +0 -0
- {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/tests/_base/test_state.py +0 -0
- {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/tests/_base/test_tool.py +0 -0
- {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/tests/_base/test_validator.py +0 -0
- {pyagentic_core-2.6.2rc1/tests/cli → pyagentic_core-2.7.0a2/tests/api}/__init__.py +0 -0
- {pyagentic_core-2.6.2rc1/tests/serve → pyagentic_core-2.7.0a2/tests/api/jobs}/__init__.py +0 -0
- {pyagentic_core-2.6.2rc1/tests/serve → pyagentic_core-2.7.0a2/tests/api}/test_metaclass_models.py +0 -0
- {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/tests/conftest.py +0 -0
- {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/tests/models/test_responses.py +0 -0
- {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/tests/tracing/__init__.py +0 -0
- {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/tests/tracing/test_basic_tracer.py +0 -0
- {pyagentic_core-2.6.2rc1 → pyagentic_core-2.7.0a2}/tests/tracing/test_models.py +0 -0
- {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.
|
|
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:
|
|
30
|
-
Requires-Dist:
|
|
31
|
-
Requires-Dist:
|
|
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: **[
|
|
107
|
+
- :material-rocket-launch: **[API](api/index.md)**
|
|
108
108
|
|
|
109
109
|
---
|
|
110
110
|
|
|
111
|
-
|
|
111
|
+
Turn agents into a FastAPI app or router, run async jobs, and ship a Docker image.
|
|
112
112
|
|
|
113
|
-
[:octicons-arrow-right-24:
|
|
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
|
-
-
|
|
22
|
-
- Overview:
|
|
23
|
-
-
|
|
24
|
-
-
|
|
25
|
-
-
|
|
26
|
-
|
|
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
|
-
|
|
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
|
|
29
|
-
|
|
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.
|