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.
Files changed (135) hide show
  1. {pyagentic_core-2.10.1.dev2/pyagentic_core.egg-info → pyagentic_core-2.11.1.dev1}/PKG-INFO +1 -1
  2. {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/docs/api/creating-an-app.md +47 -0
  3. {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/docs/api/index.md +7 -3
  4. {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/docs/api/jobs.md +15 -1
  5. {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/docs/api/running.md +33 -7
  6. {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/pyagentic/__init__.py +13 -1
  7. {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/pyagentic/_base/_agent/_agent.py +2 -0
  8. pyagentic_core-2.11.1.dev1/pyagentic/_base/_depends.py +59 -0
  9. {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/pyagentic/_base/_metaclasses.py +105 -0
  10. {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/pyagentic/api/__init__.py +5 -2
  11. {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/pyagentic/api/_app.py +65 -8
  12. pyagentic_core-2.11.1.dev1/pyagentic/api/_build.py +205 -0
  13. {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/pyagentic/api/_models.py +11 -17
  14. {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/pyagentic/api/_sessions.py +23 -25
  15. {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/pyagentic/api/jobs/_models.py +4 -0
  16. {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/pyagentic/api/jobs/_orchestrator.py +7 -1
  17. {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/pyagentic/api/jobs/_routes.py +19 -3
  18. {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/pyagentic/api/jobs/backends/__init__.py +4 -0
  19. {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/pyagentic/api/jobs/backends/_in_process.py +11 -4
  20. {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/pyagentic/api/jobs/store/_sqlite.py +20 -11
  21. {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1/pyagentic_core.egg-info}/PKG-INFO +1 -1
  22. {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/pyagentic_core.egg-info/SOURCES.txt +5 -0
  23. pyagentic_core-2.11.1.dev1/tests/api/test_build_agent.py +118 -0
  24. pyagentic_core-2.11.1.dev1/tests/api/test_construct_model.py +93 -0
  25. pyagentic_core-2.11.1.dev1/tests/api/test_dependencies_integration.py +106 -0
  26. {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/tests/api/test_sessions.py +10 -10
  27. {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  28. {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  29. {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/.github/workflows/docs.yml +0 -0
  30. {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/.github/workflows/release.yml +0 -0
  31. {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/.github/workflows/testing.yml +0 -0
  32. {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/.gitignore +0 -0
  33. {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/CHANGELOG.md +0 -0
  34. {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/LICENSE +0 -0
  35. {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/README.md +0 -0
  36. {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/docs/agent-linking.md +0 -0
  37. {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/docs/api/deploying.md +0 -0
  38. {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/docs/diagrams/declaration.svg +0 -0
  39. {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/docs/diagrams/instantiation.svg +0 -0
  40. {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/docs/diagrams/runtime.svg +0 -0
  41. {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/docs/diagrams/source/README.md +0 -0
  42. {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/docs/diagrams/source/declaration.d2 +0 -0
  43. {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/docs/diagrams/source/instantiation.d2 +0 -0
  44. {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/docs/diagrams/source/runtime.d2 +0 -0
  45. {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/docs/execution-modes.md +0 -0
  46. {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/docs/getting-started.md +0 -0
  47. {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/docs/images/langfuse.png +0 -0
  48. {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/docs/index.md +0 -0
  49. {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/docs/inheritance.md +0 -0
  50. {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/docs/observability.md +0 -0
  51. {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/docs/phases.md +0 -0
  52. {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/docs/policies.md +0 -0
  53. {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/docs/reference/architecture.md +0 -0
  54. {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/docs/reference/modules.md +0 -0
  55. {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/docs/responses.md +0 -0
  56. {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/docs/states.md +0 -0
  57. {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/docs/structured-output.md +0 -0
  58. {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/docs/tools.md +0 -0
  59. {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/mkdocs.yml +0 -0
  60. {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/pyagentic/_base/__init__.py +0 -0
  61. {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/pyagentic/_base/_agent/__init__.py +0 -0
  62. {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/pyagentic/_base/_agent/_agent_linking.py +0 -0
  63. {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/pyagentic/_base/_agent/_agent_state.py +0 -0
  64. {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/pyagentic/_base/_exceptions.py +0 -0
  65. {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/pyagentic/_base/_info.py +0 -0
  66. {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/pyagentic/_base/_mcp.py +0 -0
  67. {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/pyagentic/_base/_ref.py +0 -0
  68. {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/pyagentic/_base/_spec.py +0 -0
  69. {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/pyagentic/_base/_state.py +0 -0
  70. {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/pyagentic/_base/_tool.py +0 -0
  71. {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/pyagentic/_base/_validation.py +0 -0
  72. {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/pyagentic/_utils/_typing.py +0 -0
  73. {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/pyagentic/_utils/_warnings.py +0 -0
  74. {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/pyagentic/_version_scheme.py +0 -0
  75. {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/pyagentic/api/_config.py +0 -0
  76. {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/pyagentic/api/_docker.py +0 -0
  77. {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/pyagentic/api/_mcp_server.py +0 -0
  78. {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/pyagentic/api/jobs/__init__.py +0 -0
  79. {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/pyagentic/api/jobs/backends/_base.py +0 -0
  80. {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/pyagentic/api/jobs/store/__init__.py +0 -0
  81. {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/pyagentic/api/jobs/store/_base.py +0 -0
  82. {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/pyagentic/llm/__init__.py +0 -0
  83. {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/pyagentic/llm/_anthropic.py +0 -0
  84. {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/pyagentic/llm/_gemini.py +0 -0
  85. {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/pyagentic/llm/_mock.py +0 -0
  86. {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/pyagentic/llm/_openai.py +0 -0
  87. {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/pyagentic/llm/_openaiv1.py +0 -0
  88. {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/pyagentic/llm/_provider.py +0 -0
  89. {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/pyagentic/logging.py +0 -0
  90. {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/pyagentic/models/llm.py +0 -0
  91. {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/pyagentic/models/response.py +0 -0
  92. {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/pyagentic/models/tracing.py +0 -0
  93. {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/pyagentic/policies/__init__.py +0 -0
  94. {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/pyagentic/policies/_events.py +0 -0
  95. {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/pyagentic/policies/_policy.py +0 -0
  96. {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/pyagentic/tracing/__init__.py +0 -0
  97. {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/pyagentic/tracing/_basic.py +0 -0
  98. {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/pyagentic/tracing/_langfuse.py +0 -0
  99. {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/pyagentic/tracing/_tracer.py +0 -0
  100. {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/pyagentic/updates.py +0 -0
  101. {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/pyagentic_core.egg-info/dependency_links.txt +0 -0
  102. {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/pyagentic_core.egg-info/requires.txt +0 -0
  103. {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/pyagentic_core.egg-info/top_level.txt +0 -0
  104. {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/pyproject.toml +0 -0
  105. {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/setup.cfg +0 -0
  106. {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/setup.py +0 -0
  107. {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/tests/__init__.py +0 -0
  108. {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/tests/_base/__init__.py +0 -0
  109. {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/tests/_base/test_agent.py +0 -0
  110. {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/tests/_base/test_agent_inheritance.py +0 -0
  111. {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/tests/_base/test_agent_linking.py +0 -0
  112. {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/tests/_base/test_agent_provider.py +0 -0
  113. {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/tests/_base/test_params.py +0 -0
  114. {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/tests/_base/test_phases.py +0 -0
  115. {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/tests/_base/test_state.py +0 -0
  116. {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/tests/_base/test_tool.py +0 -0
  117. {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/tests/_base/test_validator.py +0 -0
  118. {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/tests/api/__init__.py +0 -0
  119. {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/tests/api/jobs/__init__.py +0 -0
  120. {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/tests/api/jobs/test_orchestrator.py +0 -0
  121. {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/tests/api/jobs/test_routes.py +0 -0
  122. {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/tests/api/jobs/test_store_sqlite.py +0 -0
  123. {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/tests/api/test_app.py +0 -0
  124. {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/tests/api/test_config.py +0 -0
  125. {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/tests/api/test_docker.py +0 -0
  126. {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/tests/api/test_mcp_server.py +0 -0
  127. {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/tests/api/test_metaclass_models.py +0 -0
  128. {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/tests/api/test_router.py +0 -0
  129. {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/tests/conftest.py +0 -0
  130. {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/tests/models/test_responses.py +0 -0
  131. {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/tests/tracing/__init__.py +0 -0
  132. {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/tests/tracing/test_basic_tracer.py +0 -0
  133. {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/tests/tracing/test_models.py +0 -0
  134. {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/tests/tracing/test_tracer.py +0 -0
  135. {pyagentic_core-2.10.1.dev2 → pyagentic_core-2.11.1.dev1}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyagentic-core
3
- Version: 2.10.1.dev2
3
+ Version: 2.11.1.dev1
4
4
  Summary: Build LLM Agents in a Pythonic way
5
5
  Author-email: Ryan Mikulec <rmikulec.dev@gmail.com>
6
6
  License: MIT
@@ -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
- # Create a new session (returns session_id)
59
+ # Agent with no required construction fields — empty body is fine
51
60
  curl -X POST http://localhost:8000/sessions
52
61
 
53
- # Create with a model or API key override
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 '{"model": "openai::gpt-4o", "api_key": "sk-..."}'
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. The streaming endpoint drives `agent.step()`; the
135
- synchronous endpoint calls `agent(**kwargs)`.
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__ = ["BaseAgent", "AgentExtension", "tool", "spec", "State", "Link", "MCPLink", "ref"]
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",