pyagentic-core 2.10.1.dev2__tar.gz → 2.11.1.dev1__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.10.1.dev2/pyagentic_core.egg-info → pyagentic_core-2.11.1.dev1}/PKG-INFO +1 -1
- {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/docs/api/creating-an-app.md +47 -0
- {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/docs/api/index.md +7 -3
- {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/docs/api/jobs.md +15 -1
- {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/docs/api/running.md +33 -7
- {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/pyagentic/__init__.py +13 -1
- {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/pyagentic/_base/_agent/_agent.py +2 -0
- pyagentic_core-2.11.1.dev1/pyagentic/_base/_depends.py +59 -0
- {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/pyagentic/_base/_metaclasses.py +105 -0
- {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/pyagentic/api/__init__.py +5 -2
- {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/pyagentic/api/_app.py +65 -8
- pyagentic_core-2.11.1.dev1/pyagentic/api/_build.py +205 -0
- {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/pyagentic/api/_models.py +11 -17
- {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/pyagentic/api/_sessions.py +23 -25
- {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/pyagentic/api/jobs/_models.py +4 -0
- {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/pyagentic/api/jobs/_orchestrator.py +7 -1
- {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/pyagentic/api/jobs/_routes.py +19 -3
- {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/pyagentic/api/jobs/backends/__init__.py +4 -0
- {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/pyagentic/api/jobs/backends/_in_process.py +11 -4
- {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/pyagentic/api/jobs/store/_sqlite.py +20 -11
- {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1/pyagentic_core.egg-info}/PKG-INFO +1 -1
- {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/pyagentic_core.egg-info/SOURCES.txt +5 -0
- pyagentic_core-2.11.1.dev1/tests/api/test_build_agent.py +118 -0
- pyagentic_core-2.11.1.dev1/tests/api/test_construct_model.py +93 -0
- pyagentic_core-2.11.1.dev1/tests/api/test_dependencies_integration.py +106 -0
- {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/tests/api/test_sessions.py +10 -10
- {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/.github/workflows/docs.yml +0 -0
- {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/.github/workflows/release.yml +0 -0
- {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/.github/workflows/testing.yml +0 -0
- {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/.gitignore +0 -0
- {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/CHANGELOG.md +0 -0
- {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/LICENSE +0 -0
- {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/README.md +0 -0
- {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/docs/agent-linking.md +0 -0
- {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/docs/api/deploying.md +0 -0
- {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/docs/diagrams/declaration.svg +0 -0
- {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/docs/diagrams/instantiation.svg +0 -0
- {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/docs/diagrams/runtime.svg +0 -0
- {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/docs/diagrams/source/README.md +0 -0
- {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/docs/diagrams/source/declaration.d2 +0 -0
- {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/docs/diagrams/source/instantiation.d2 +0 -0
- {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/docs/diagrams/source/runtime.d2 +0 -0
- {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/docs/execution-modes.md +0 -0
- {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/docs/getting-started.md +0 -0
- {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/docs/images/langfuse.png +0 -0
- {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/docs/index.md +0 -0
- {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/docs/inheritance.md +0 -0
- {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/docs/observability.md +0 -0
- {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/docs/phases.md +0 -0
- {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/docs/policies.md +0 -0
- {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/docs/reference/architecture.md +0 -0
- {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/docs/reference/modules.md +0 -0
- {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/docs/responses.md +0 -0
- {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/docs/states.md +0 -0
- {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/docs/structured-output.md +0 -0
- {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/docs/tools.md +0 -0
- {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/mkdocs.yml +0 -0
- {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/pyagentic/_base/__init__.py +0 -0
- {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/pyagentic/_base/_agent/__init__.py +0 -0
- {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/pyagentic/_base/_agent/_agent_linking.py +0 -0
- {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/pyagentic/_base/_agent/_agent_state.py +0 -0
- {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/pyagentic/_base/_exceptions.py +0 -0
- {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/pyagentic/_base/_info.py +0 -0
- {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/pyagentic/_base/_mcp.py +0 -0
- {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/pyagentic/_base/_ref.py +0 -0
- {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/pyagentic/_base/_spec.py +0 -0
- {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/pyagentic/_base/_state.py +0 -0
- {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/pyagentic/_base/_tool.py +0 -0
- {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/pyagentic/_base/_validation.py +0 -0
- {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/pyagentic/_utils/_typing.py +0 -0
- {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/pyagentic/_utils/_warnings.py +0 -0
- {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/pyagentic/_version_scheme.py +0 -0
- {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/pyagentic/api/_config.py +0 -0
- {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/pyagentic/api/_docker.py +0 -0
- {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/pyagentic/api/_mcp_server.py +0 -0
- {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/pyagentic/api/jobs/__init__.py +0 -0
- {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/pyagentic/api/jobs/backends/_base.py +0 -0
- {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/pyagentic/api/jobs/store/__init__.py +0 -0
- {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/pyagentic/api/jobs/store/_base.py +0 -0
- {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/pyagentic/llm/__init__.py +0 -0
- {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/pyagentic/llm/_anthropic.py +0 -0
- {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/pyagentic/llm/_gemini.py +0 -0
- {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/pyagentic/llm/_mock.py +0 -0
- {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/pyagentic/llm/_openai.py +0 -0
- {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/pyagentic/llm/_openaiv1.py +0 -0
- {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/pyagentic/llm/_provider.py +0 -0
- {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/pyagentic/logging.py +0 -0
- {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/pyagentic/models/llm.py +0 -0
- {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/pyagentic/models/response.py +0 -0
- {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/pyagentic/models/tracing.py +0 -0
- {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/pyagentic/policies/__init__.py +0 -0
- {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/pyagentic/policies/_events.py +0 -0
- {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/pyagentic/policies/_policy.py +0 -0
- {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/pyagentic/tracing/__init__.py +0 -0
- {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/pyagentic/tracing/_basic.py +0 -0
- {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/pyagentic/tracing/_langfuse.py +0 -0
- {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/pyagentic/tracing/_tracer.py +0 -0
- {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/pyagentic/updates.py +0 -0
- {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/pyagentic_core.egg-info/dependency_links.txt +0 -0
- {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/pyagentic_core.egg-info/requires.txt +0 -0
- {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/pyagentic_core.egg-info/top_level.txt +0 -0
- {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/pyproject.toml +0 -0
- {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/setup.cfg +0 -0
- {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/setup.py +0 -0
- {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/tests/__init__.py +0 -0
- {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/tests/_base/__init__.py +0 -0
- {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/tests/_base/test_agent.py +0 -0
- {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/tests/_base/test_agent_inheritance.py +0 -0
- {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/tests/_base/test_agent_linking.py +0 -0
- {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/tests/_base/test_agent_provider.py +0 -0
- {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/tests/_base/test_params.py +0 -0
- {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/tests/_base/test_phases.py +0 -0
- {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/tests/_base/test_state.py +0 -0
- {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/tests/_base/test_tool.py +0 -0
- {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/tests/_base/test_validator.py +0 -0
- {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/tests/api/__init__.py +0 -0
- {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/tests/api/jobs/__init__.py +0 -0
- {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/tests/api/jobs/test_orchestrator.py +0 -0
- {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/tests/api/jobs/test_routes.py +0 -0
- {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/tests/api/jobs/test_store_sqlite.py +0 -0
- {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/tests/api/test_app.py +0 -0
- {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/tests/api/test_config.py +0 -0
- {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/tests/api/test_docker.py +0 -0
- {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/tests/api/test_mcp_server.py +0 -0
- {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/tests/api/test_metaclass_models.py +0 -0
- {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/tests/api/test_router.py +0 -0
- {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/tests/conftest.py +0 -0
- {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/tests/models/test_responses.py +0 -0
- {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/tests/tracing/__init__.py +0 -0
- {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/tests/tracing/test_basic_tracer.py +0 -0
- {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/tests/tracing/test_models.py +0 -0
- {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/tests/tracing/test_tracer.py +0 -0
- {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/uv.lock +0 -0
|
@@ -59,6 +59,53 @@ app.include_router(create_router(MyAgent, model="openai::gpt-4o"), prefix="/bot"
|
|
|
59
59
|
The router owns its `SessionManager`, exposed as `router.sessions` so you can
|
|
60
60
|
share it (for example with `mount_mcp`).
|
|
61
61
|
|
|
62
|
+
## Dependencies
|
|
63
|
+
|
|
64
|
+
Agents often need resources that can't travel over HTTP — a database handle, an
|
|
65
|
+
HTTP client, a pre-configured provider. Declare these with `Depends[...]` in the
|
|
66
|
+
agent class, exactly where you'd otherwise put `State[...]`:
|
|
67
|
+
|
|
68
|
+
```python
|
|
69
|
+
from pyagentic import BaseAgent, State, Depends
|
|
70
|
+
|
|
71
|
+
class ResearchAgent(BaseAgent):
|
|
72
|
+
__system_message__ = "You research topics."
|
|
73
|
+
|
|
74
|
+
topic: State[Topic] # client-provided per session
|
|
75
|
+
db: Depends[Database] # injected server-side, never sent by clients
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
A `Depends[T]` field is **excluded** from the session/job request body. Instead
|
|
79
|
+
you supply it once when building the app, via `dependencies=` — a list of
|
|
80
|
+
instances or zero-arg factories, resolved **by type**:
|
|
81
|
+
|
|
82
|
+
```python
|
|
83
|
+
app = create_app(ResearchAgent, dependencies=[Database(dsn), make_client])
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
- An **instance** (`Database(dsn)`) is shared across every session.
|
|
87
|
+
- A **factory** — a zero-arg callable annotated with its return type
|
|
88
|
+
(`def make_client() -> Client: ...`) — is called fresh for each agent built,
|
|
89
|
+
so every session and job gets its own.
|
|
90
|
+
|
|
91
|
+
Each `Depends[T]` slot — including those declared on linked sub-agents — is
|
|
92
|
+
matched to a provider of type `T`. Missing or mismatched dependencies fail fast
|
|
93
|
+
when the app is built, not on the first request.
|
|
94
|
+
|
|
95
|
+
For a multi-agent app, pass a flat list (applied to every agent) or a dict keyed
|
|
96
|
+
by mount prefix to scope providers per agent:
|
|
97
|
+
|
|
98
|
+
```python
|
|
99
|
+
app = create_app(
|
|
100
|
+
{"/research": ResearchAgent, "/writer": WriterAgent},
|
|
101
|
+
dependencies={"/research": [Database(dsn)], "/writer": [make_client]},
|
|
102
|
+
)
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Linked agents are built from the request body — a session's body nests each
|
|
106
|
+
`Link[...]` sub-agent's construction under its field name — while their
|
|
107
|
+
`Depends[...]` fields are injected from the same `dependencies` list.
|
|
108
|
+
|
|
62
109
|
## Configuration: `agents.toml`
|
|
63
110
|
|
|
64
111
|
Put an `agents.toml` next to your `pyproject.toml`. `create_app` reads its
|
|
@@ -53,14 +53,18 @@ field and the schemas update with no manual OpenAPI maintenance:
|
|
|
53
53
|
|
|
54
54
|
| Route | Purpose |
|
|
55
55
|
|---|---|
|
|
56
|
-
| `GET /` | Agent metadata (name, tools, state fields, linked agents) |
|
|
56
|
+
| `GET /` | Agent metadata (name, tools, state fields, linked agents, dependencies) |
|
|
57
57
|
| `GET /health` | Liveness probe |
|
|
58
|
-
| `GET /schema` | JSON schemas for request/response/stream-event/state |
|
|
59
|
-
| `POST /sessions` | Create an isolated session |
|
|
58
|
+
| `GET /schema` | JSON schemas for construct/request/response/stream-event/state |
|
|
59
|
+
| `POST /sessions` | Create an isolated session (body mirrors the agent's constructor) |
|
|
60
60
|
| `POST /sessions/{id}/chat` | Send a message, get a complete response |
|
|
61
61
|
| `POST /sessions/{id}/chat/stream` | Stream typed SSE events as the agent works |
|
|
62
62
|
| `GET /sessions/{id}/state` | Current agent state for a session |
|
|
63
63
|
|
|
64
|
+
Creating a session constructs the agent: the body carries its state and any
|
|
65
|
+
linked sub-agents (`POST /sessions`), while non-serializable resources are
|
|
66
|
+
injected server-side with [`Depends[...]`](creating-an-app.md#dependencies).
|
|
67
|
+
|
|
64
68
|
## Guides
|
|
65
69
|
|
|
66
70
|
<div class="grid cards" markdown>
|
|
@@ -58,7 +58,7 @@ result = httpx.get(f"http://localhost:8000/jobs/{job_id}").json()
|
|
|
58
58
|
|
|
59
59
|
| Route | Purpose |
|
|
60
60
|
|---|---|
|
|
61
|
-
| `POST /jobs` | Submit a run. Body `{"input": {<chat fields>}, "session_id": <optional>}`. Returns `202 {job_id, status}`. |
|
|
61
|
+
| `POST /jobs` | Submit a run. Body `{"input": {<chat fields>}, "session_id": <optional>, "construct": <optional>}`. Returns `202 {job_id, status}`. |
|
|
62
62
|
| `GET /jobs` | List jobs, newest first. Filter with `?status=` and `?session_id=`. |
|
|
63
63
|
| `GET /jobs/{id}` | Job status and, when terminal, its result. |
|
|
64
64
|
| `GET /jobs/{id}/updates?since=<seq>` | The update log past a sequence cursor. |
|
|
@@ -69,6 +69,20 @@ The `input` object is your agent's normal chat request body. Pass a
|
|
|
69
69
|
`session_id` (from `POST /sessions`) to run the job against an existing session;
|
|
70
70
|
omit it for a one-off run.
|
|
71
71
|
|
|
72
|
+
For a one-off run, add a `construct` object — the same construction data as
|
|
73
|
+
`POST /sessions` (state and any nested linked agents) — to build a fresh agent
|
|
74
|
+
for the job. Its [dependencies](creating-an-app.md#dependencies) are injected
|
|
75
|
+
server-side, just like for sessions. Session-bound jobs reuse the session's
|
|
76
|
+
existing agent and ignore `construct`.
|
|
77
|
+
|
|
78
|
+
```python
|
|
79
|
+
# One-off job that builds its own agent from a construct payload
|
|
80
|
+
httpx.post("http://localhost:8000/jobs", json={
|
|
81
|
+
"input": {"user_input": "Summarize the latest results"},
|
|
82
|
+
"construct": {"topic": {"name": "kv-cache"}},
|
|
83
|
+
})
|
|
84
|
+
```
|
|
85
|
+
|
|
72
86
|
## Surviving timeouts: replay-from-cursor
|
|
73
87
|
|
|
74
88
|
Every update carries a monotonic, gapless sequence number (`seq`), emitted on the
|
|
@@ -30,31 +30,49 @@ agent's prefix, e.g. `/research/sessions`.)
|
|
|
30
30
|
### Info routes
|
|
31
31
|
|
|
32
32
|
```bash
|
|
33
|
-
# Agent metadata: name, version, tools, state fields, linked agents
|
|
33
|
+
# Agent metadata: name, version, tools, state fields, linked agents, dependencies
|
|
34
34
|
curl http://localhost:8000/
|
|
35
35
|
|
|
36
36
|
# Liveness probe
|
|
37
37
|
curl http://localhost:8000/health
|
|
38
38
|
|
|
39
|
-
# JSON schemas for request, response, stream event, and state models
|
|
39
|
+
# JSON schemas for construct, request, response, stream event, and state models
|
|
40
40
|
curl http://localhost:8000/schema
|
|
41
41
|
```
|
|
42
42
|
|
|
43
|
+
The `construct` schema describes the body for creating a session (or a
|
|
44
|
+
sessionless job) — see below.
|
|
45
|
+
|
|
43
46
|
### Session management
|
|
44
47
|
|
|
45
48
|
Sessions provide isolated agent instances with independent state and
|
|
46
49
|
conversation history. Each session holds its own agent, so state changes in one
|
|
47
50
|
session don't affect others.
|
|
48
51
|
|
|
52
|
+
Creating a session **constructs the agent**, so the request body mirrors the
|
|
53
|
+
constructor: the agent's `State[...]` fields, any `Link[...]` sub-agents
|
|
54
|
+
(nested, recursively), and an optional `model`/`api_key`. Fields without a
|
|
55
|
+
default are required; defaulted ones are optional. The exact shape is published
|
|
56
|
+
at `GET /schema` under `construct`.
|
|
57
|
+
|
|
49
58
|
```bash
|
|
50
|
-
#
|
|
59
|
+
# Agent with no required construction fields — empty body is fine
|
|
51
60
|
curl -X POST http://localhost:8000/sessions
|
|
52
61
|
|
|
53
|
-
#
|
|
62
|
+
# Provide state and a per-session model
|
|
54
63
|
curl -X POST http://localhost:8000/sessions \
|
|
55
64
|
-H "Content-Type: application/json" \
|
|
56
|
-
-d '{"
|
|
65
|
+
-d '{"topic": {"name": "transformers"}, "model": "openai::gpt-4o"}'
|
|
57
66
|
|
|
67
|
+
# A linked agent's construction nests under its field name
|
|
68
|
+
curl -X POST http://localhost:8000/sessions \
|
|
69
|
+
-H "Content-Type: application/json" \
|
|
70
|
+
-d '{"researcher": {"topic": {"name": "attention"}}}'
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Omitting a required field returns `422`. List and delete are unchanged:
|
|
74
|
+
|
|
75
|
+
```bash
|
|
58
76
|
# List all active session IDs
|
|
59
77
|
curl http://localhost:8000/sessions
|
|
60
78
|
|
|
@@ -64,6 +82,11 @@ curl -X DELETE http://localhost:8000/sessions/{session_id}
|
|
|
64
82
|
|
|
65
83
|
Sessions are stored in memory — restarting the server clears them.
|
|
66
84
|
|
|
85
|
+
!!! note "Dependencies aren't part of the body"
|
|
86
|
+
`Depends[...]` fields (database handles, clients, providers) are injected
|
|
87
|
+
server-side when the app is built, never sent by clients — see
|
|
88
|
+
[Dependencies](creating-an-app.md#dependencies).
|
|
89
|
+
|
|
67
90
|
### Chat
|
|
68
91
|
|
|
69
92
|
The chat request body is automatically derived from your agent's `__call__`
|
|
@@ -125,14 +148,17 @@ Returns the full state model, serialized using the agent's `__state_class__`.
|
|
|
125
148
|
`create_app` extracts the agent's metaclass-generated models and wires them to
|
|
126
149
|
the routes:
|
|
127
150
|
|
|
151
|
+
- `__construct_model__` → session create body (state + nested linked agents)
|
|
128
152
|
- `__request_model__` → chat request body
|
|
129
153
|
- `__response_model__` → chat response body
|
|
130
154
|
- `__stream_event_model__` → typed SSE events
|
|
131
155
|
- `__state_class__` → state endpoint
|
|
132
156
|
|
|
133
157
|
A `SessionManager` (an in-memory store holding one agent instance per session)
|
|
134
|
-
backs the session routes.
|
|
135
|
-
|
|
158
|
+
backs the session routes. Creating a session builds a fresh agent from the
|
|
159
|
+
construct body plus any server-injected [dependencies](creating-an-app.md#dependencies).
|
|
160
|
+
The streaming endpoint drives `agent.step()`; the synchronous endpoint calls
|
|
161
|
+
`agent(**kwargs)`.
|
|
136
162
|
|
|
137
163
|
## Next steps
|
|
138
164
|
|
|
@@ -30,6 +30,7 @@ Main Components:
|
|
|
30
30
|
- tool: Decorator for marking methods as LLM-callable tools
|
|
31
31
|
- State: Type annotation for persistent state fields
|
|
32
32
|
- Link: Type annotation for linking agents together
|
|
33
|
+
- Depends: Type annotation for dependency-injected agent fields
|
|
33
34
|
- spec: Configuration factory for state, params, and agent links
|
|
34
35
|
- ref: Dynamic state reference system for tool parameters
|
|
35
36
|
- AgentExtension: Base class for creating reusable agent mixins
|
|
@@ -42,6 +43,17 @@ from pyagentic._base._mcp import MCPLink
|
|
|
42
43
|
from pyagentic._base._tool import tool
|
|
43
44
|
|
|
44
45
|
from pyagentic._base._state import State
|
|
46
|
+
from pyagentic._base._depends import Depends
|
|
45
47
|
from pyagentic._base._ref import ref
|
|
46
48
|
|
|
47
|
-
__all__ = [
|
|
49
|
+
__all__ = [
|
|
50
|
+
"BaseAgent",
|
|
51
|
+
"AgentExtension",
|
|
52
|
+
"tool",
|
|
53
|
+
"spec",
|
|
54
|
+
"State",
|
|
55
|
+
"Link",
|
|
56
|
+
"Depends",
|
|
57
|
+
"MCPLink",
|
|
58
|
+
"ref",
|
|
59
|
+
]
|
|
@@ -163,6 +163,7 @@ class BaseAgent(metaclass=AgentMeta):
|
|
|
163
163
|
__state_defs__: ClassVar[dict[str, _StateDefinition]] # State field definitions
|
|
164
164
|
__linked_agents__: ClassVar[dict[str, "_LinkedAgentDefinition"]] # Linked agent definitions
|
|
165
165
|
__mcp_defs__: ClassVar[dict[str, "_MCPDefinition"]] # MCP server definitions
|
|
166
|
+
__dependencies__: ClassVar[dict[str, type]] # Depends[T] field -> dependency type
|
|
166
167
|
|
|
167
168
|
# User-set Class Attributes (defined in subclass)
|
|
168
169
|
__system_message__: ClassVar[str] # Required: system prompt for the agent
|
|
@@ -173,6 +174,7 @@ class BaseAgent(metaclass=AgentMeta):
|
|
|
173
174
|
|
|
174
175
|
# Generated Class Attributes (built by metaclass)
|
|
175
176
|
__request_model__: ClassVar[Type[BaseModel]] = None # Pydantic request model from __call__
|
|
177
|
+
__construct_model__: ClassVar[Type[BaseModel]] = None # Recursive constructor model
|
|
176
178
|
__response_model__: ClassVar[Type[AgentResponse]] = None # Pydantic response model
|
|
177
179
|
__stream_event_model__: ClassVar[Type[BaseModel]] = None # Typed SSE stream event union
|
|
178
180
|
__state_class__: ClassVar[Type[_AgentState]] = None # Generated state class
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
from typing import TypeVar, Generic
|
|
2
|
+
|
|
3
|
+
T = TypeVar("T")
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Depends(Generic[T]):
|
|
7
|
+
"""
|
|
8
|
+
Type annotation for declaring a dependency-injected agent field.
|
|
9
|
+
|
|
10
|
+
``Depends`` marks a field as a server-supplied dependency rather than
|
|
11
|
+
serializable per-session state. Use it in an agent's class body wherever you
|
|
12
|
+
would otherwise use :class:`State`, for resources that cannot (or should not)
|
|
13
|
+
travel over the wire — database handles, HTTP clients, pre-configured
|
|
14
|
+
providers, and similar.
|
|
15
|
+
|
|
16
|
+
Unlike ``State`` and ``Link``, a ``Depends`` field is **excluded** from the
|
|
17
|
+
agent's generated construct model: clients never provide it. Instead it is
|
|
18
|
+
supplied once by the developer at :func:`pyagentic.api.create_router` /
|
|
19
|
+
:func:`pyagentic.api.create_app` via the ``dependencies`` argument, and
|
|
20
|
+
resolved by type across the (possibly nested) agent tree.
|
|
21
|
+
|
|
22
|
+
Args:
|
|
23
|
+
T: The dependency type. Used both as the field's type hint and as the key
|
|
24
|
+
for by-type resolution at serve time.
|
|
25
|
+
|
|
26
|
+
Example:
|
|
27
|
+
```python
|
|
28
|
+
from pyagentic import BaseAgent, State, Depends
|
|
29
|
+
|
|
30
|
+
class ResearchAgent(BaseAgent):
|
|
31
|
+
__system_message__ = "You research topics"
|
|
32
|
+
|
|
33
|
+
topic: State[TopicState] # client-provided per session
|
|
34
|
+
db: Depends[Database] # injected by the developer, by type
|
|
35
|
+
|
|
36
|
+
@tool("Look up a record")
|
|
37
|
+
def lookup(self, key: str) -> str:
|
|
38
|
+
return self.db.get(key)
|
|
39
|
+
```
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
def __class_getitem__(cls, item):
|
|
43
|
+
"""
|
|
44
|
+
Creates a generic Depends type marker for a given dependency class.
|
|
45
|
+
|
|
46
|
+
Args:
|
|
47
|
+
item: The dependency class this field requires.
|
|
48
|
+
|
|
49
|
+
Returns:
|
|
50
|
+
type: Special marker type that the metaclass detects and records in
|
|
51
|
+
``__dependencies__`` (and excludes from the construct model).
|
|
52
|
+
"""
|
|
53
|
+
# Return a special marker type that the metaclass can detect, mirroring
|
|
54
|
+
# the State[T] / Link[T] markers.
|
|
55
|
+
return type(
|
|
56
|
+
f"Depends[{item.__name__}]",
|
|
57
|
+
(),
|
|
58
|
+
{"__origin__": Depends, "__args__": (item,), "__dependency_type__": item},
|
|
59
|
+
)
|
|
@@ -15,6 +15,7 @@ from pyagentic._base._agent._agent_state import _AgentState
|
|
|
15
15
|
from pyagentic._base._tool import _ToolDefinition, tool
|
|
16
16
|
from pyagentic._base._state import State, StateInfo, _StateDefinition
|
|
17
17
|
from pyagentic._base._agent._agent_linking import Link, _LinkedAgentDefinition
|
|
18
|
+
from pyagentic._base._depends import Depends
|
|
18
19
|
from pyagentic._base._mcp import MCPLink, _MCPDefinition
|
|
19
20
|
|
|
20
21
|
from pyagentic.models.response import AgentResponse, ToolResponse
|
|
@@ -323,6 +324,92 @@ class AgentMeta(type):
|
|
|
323
324
|
|
|
324
325
|
return MappingProxyType(mcp_defs)
|
|
325
326
|
|
|
327
|
+
@staticmethod
|
|
328
|
+
def _extract_dependencies(annotations, namespace) -> Mapping[str, type]:
|
|
329
|
+
"""Extracts dependency-injected fields declared with ``Depends[T]``.
|
|
330
|
+
|
|
331
|
+
Looks for annotations whose marker has ``__origin__ is Depends`` and
|
|
332
|
+
records the wrapped dependency type. These fields are injected at serve
|
|
333
|
+
time (by type) rather than supplied by clients, so they are excluded from
|
|
334
|
+
the generated construct model.
|
|
335
|
+
|
|
336
|
+
Args:
|
|
337
|
+
annotations (dict): Combined annotations from the class hierarchy.
|
|
338
|
+
namespace (dict): The class namespace.
|
|
339
|
+
|
|
340
|
+
Returns:
|
|
341
|
+
Mapping[str, type]: Immutable mapping of field name to dependency type.
|
|
342
|
+
"""
|
|
343
|
+
dependencies: dict[str, type] = {}
|
|
344
|
+
|
|
345
|
+
for attr_name, attr_type in annotations.items():
|
|
346
|
+
if getattr(attr_type, "__origin__", None) is Depends:
|
|
347
|
+
dependencies[attr_name] = attr_type.__dependency_type__
|
|
348
|
+
|
|
349
|
+
return MappingProxyType(dependencies)
|
|
350
|
+
|
|
351
|
+
@staticmethod
|
|
352
|
+
def _build_construct_model(cls) -> Type[BaseModel]:
|
|
353
|
+
"""Build a recursive Pydantic model mirroring the agent's constructor.
|
|
354
|
+
|
|
355
|
+
The construct model is the serializable construction contract for the
|
|
356
|
+
agent: it carries the data a client must supply to instantiate one, in a
|
|
357
|
+
way that mirrors writing the construction in Python.
|
|
358
|
+
|
|
359
|
+
- State fields → the raw per-field model, required when the StateInfo
|
|
360
|
+
has no default/default_factory, otherwise optional.
|
|
361
|
+
- Linked agents → that agent's own ``__construct_model__`` (nested),
|
|
362
|
+
optional when the AgentLink provides a default, otherwise required.
|
|
363
|
+
- ``model`` / ``api_key`` → optional scalars for provider selection.
|
|
364
|
+
|
|
365
|
+
``Depends`` and MCP fields are excluded — they are injected/configured
|
|
366
|
+
server-side, not provided by clients.
|
|
367
|
+
|
|
368
|
+
Args:
|
|
369
|
+
cls: The agent class whose constructor to mirror.
|
|
370
|
+
|
|
371
|
+
Returns:
|
|
372
|
+
Type[BaseModel]: A dynamically created Pydantic model.
|
|
373
|
+
"""
|
|
374
|
+
from typing import Optional
|
|
375
|
+
from pydantic import ConfigDict
|
|
376
|
+
|
|
377
|
+
fields: dict[str, Any] = {}
|
|
378
|
+
|
|
379
|
+
# State fields: type is the raw per-field model (e.g. UserProfile).
|
|
380
|
+
# Check for a default's *existence* without invoking factories.
|
|
381
|
+
for name, state_def in cls.__state_defs__.items():
|
|
382
|
+
info = state_def.info
|
|
383
|
+
if info.default_factory is not None:
|
|
384
|
+
fields[name] = (state_def.model, Field(default_factory=info.default_factory))
|
|
385
|
+
elif info.default is not None:
|
|
386
|
+
fields[name] = (state_def.model, Field(default=info.default))
|
|
387
|
+
else:
|
|
388
|
+
fields[name] = (state_def.model, Field(...))
|
|
389
|
+
|
|
390
|
+
# Linked agents: nest each child's construct model. A link is optional
|
|
391
|
+
# when its AgentLink declares any default (don't call the factory here).
|
|
392
|
+
for name, linked_def in cls.__linked_agents__.items():
|
|
393
|
+
child_model = linked_def.agent.__construct_model__
|
|
394
|
+
info = linked_def.info
|
|
395
|
+
has_default = info is not None and (
|
|
396
|
+
info.default is not None or info.default_factory is not None
|
|
397
|
+
)
|
|
398
|
+
if has_default:
|
|
399
|
+
fields[name] = (Optional[child_model], Field(default=None))
|
|
400
|
+
else:
|
|
401
|
+
fields[name] = (child_model, Field(...))
|
|
402
|
+
|
|
403
|
+
# Provider selection scalars.
|
|
404
|
+
fields["model"] = (Optional[str], Field(default=None))
|
|
405
|
+
fields["api_key"] = (Optional[str], Field(default=None))
|
|
406
|
+
|
|
407
|
+
return create_model(
|
|
408
|
+
f"{cls.__name__}Construct",
|
|
409
|
+
__config__=ConfigDict(arbitrary_types_allowed=True),
|
|
410
|
+
**fields,
|
|
411
|
+
)
|
|
412
|
+
|
|
326
413
|
@staticmethod
|
|
327
414
|
def _build_request_model(cls) -> Type[BaseModel]:
|
|
328
415
|
"""Build a Pydantic request model from the agent's __call__ signature.
|
|
@@ -458,6 +545,16 @@ class AgentMeta(type):
|
|
|
458
545
|
# MCP fields are config-only, not constructor args
|
|
459
546
|
elif field_name in agent_cls.__mcp_defs__:
|
|
460
547
|
continue
|
|
548
|
+
# Dependency-injected fields are optional (default None); they are
|
|
549
|
+
# supplied at serve time or passed explicitly for direct/test use.
|
|
550
|
+
elif field_name in agent_cls.__dependencies__:
|
|
551
|
+
param = inspect.Parameter(
|
|
552
|
+
field_name,
|
|
553
|
+
inspect.Parameter.POSITIONAL_OR_KEYWORD,
|
|
554
|
+
default=None,
|
|
555
|
+
annotation=field_type,
|
|
556
|
+
)
|
|
557
|
+
optional.append(param)
|
|
461
558
|
# Other annotated fields (like model, api_key, tracer, etc.)
|
|
462
559
|
else:
|
|
463
560
|
default = getattr(agent_cls, field_name, inspect._empty)
|
|
@@ -611,12 +708,14 @@ class AgentMeta(type):
|
|
|
611
708
|
annotations, inherited_namespace | namespace, mcs.__BaseAgent__
|
|
612
709
|
)
|
|
613
710
|
mcp_defs = mcs._extract_mcp_defs(annotations, inherited_namespace | namespace)
|
|
711
|
+
dependencies = mcs._extract_dependencies(annotations, inherited_namespace | namespace)
|
|
614
712
|
with mcs._lock:
|
|
615
713
|
cls.__tool_defs__ = tool_defs
|
|
616
714
|
cls.__annotations__ = annotations
|
|
617
715
|
cls.__state_defs__ = state_defs
|
|
618
716
|
cls.__linked_agents__ = linked_agents
|
|
619
717
|
cls.__mcp_defs__ = mcp_defs
|
|
718
|
+
cls.__dependencies__ = dependencies
|
|
620
719
|
|
|
621
720
|
# Create response models at class declaration time, giving the agent a predetermined
|
|
622
721
|
# output structure. This allows developers to know exactly what the output of the
|
|
@@ -668,10 +767,16 @@ class AgentMeta(type):
|
|
|
668
767
|
ResponseModel=ResponseModel,
|
|
669
768
|
)
|
|
670
769
|
|
|
770
|
+
# Build the recursive construct model mirroring the agent's constructor.
|
|
771
|
+
# Linked agents' construct models already exist because their classes are
|
|
772
|
+
# defined before this one (same guarantee relied on for __response_model__).
|
|
773
|
+
ConstructModel = mcs._build_construct_model(cls)
|
|
774
|
+
|
|
671
775
|
with mcs._lock:
|
|
672
776
|
cls.__tool_response_models__ = MappingProxyType(tool_response_models)
|
|
673
777
|
cls.__response_model__ = ResponseModel
|
|
674
778
|
cls.__request_model__ = RequestModel
|
|
779
|
+
cls.__construct_model__ = ConstructModel
|
|
675
780
|
cls.__stream_event_model__ = StreamEventModel
|
|
676
781
|
cls.__state_class__ = StateClass
|
|
677
782
|
|
|
@@ -11,7 +11,9 @@ Configuration lives in ``agents.toml`` beside your ``pyproject.toml`` (see
|
|
|
11
11
|
:class:`AgentsConfig`).
|
|
12
12
|
"""
|
|
13
13
|
|
|
14
|
+
from pyagentic._base._depends import Depends
|
|
14
15
|
from pyagentic.api._app import create_app, create_router
|
|
16
|
+
from pyagentic.api._build import build_agent, validate_dependencies
|
|
15
17
|
from pyagentic.api._config import (
|
|
16
18
|
AgentsConfig,
|
|
17
19
|
AppConfig,
|
|
@@ -25,7 +27,6 @@ from pyagentic.api._models import (
|
|
|
25
27
|
AgentInfo,
|
|
26
28
|
AppAgentEntry,
|
|
27
29
|
AppIndex,
|
|
28
|
-
CreateSessionRequest,
|
|
29
30
|
CreateSessionResponse,
|
|
30
31
|
DeleteSessionResponse,
|
|
31
32
|
HealthResponse,
|
|
@@ -38,6 +39,9 @@ __all__ = [
|
|
|
38
39
|
"create_app",
|
|
39
40
|
"create_router",
|
|
40
41
|
"mount_mcp",
|
|
42
|
+
"Depends",
|
|
43
|
+
"build_agent",
|
|
44
|
+
"validate_dependencies",
|
|
41
45
|
"generate_dockerfile",
|
|
42
46
|
"write_dockerfile",
|
|
43
47
|
"AgentsConfig",
|
|
@@ -50,7 +54,6 @@ __all__ = [
|
|
|
50
54
|
"AgentInfo",
|
|
51
55
|
"AppAgentEntry",
|
|
52
56
|
"AppIndex",
|
|
53
|
-
"CreateSessionRequest",
|
|
54
57
|
"CreateSessionResponse",
|
|
55
58
|
"DeleteSessionResponse",
|
|
56
59
|
"HealthResponse",
|