pyagentic-core 2.10.1.dev2__py3-none-any.whl → 2.11.1.dev1__py3-none-any.whl

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/__init__.py CHANGED
@@ -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
 
pyagentic/api/__init__.py CHANGED
@@ -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",
pyagentic/api/_app.py CHANGED
@@ -23,17 +23,19 @@ from typing import Optional, Union
23
23
 
24
24
  from dotenv import load_dotenv
25
25
  from fastapi import APIRouter, FastAPI, HTTPException
26
+ from fastapi.exceptions import RequestValidationError
26
27
  from fastapi.responses import StreamingResponse
28
+ from pydantic import ValidationError
27
29
 
28
30
  from pyagentic._base._agent._agent import BaseAgent
29
31
  from pyagentic.models.llm import LLMResponse
30
32
  from pyagentic.models.response import AgentResponse, ToolResponse
33
+ from pyagentic.api._build import validate_dependencies
31
34
  from pyagentic.api._config import AgentsConfig, JobsConfig, load_config
32
35
  from pyagentic.api._models import (
33
36
  AgentInfo,
34
37
  AppAgentEntry,
35
38
  AppIndex,
36
- CreateSessionRequest,
37
39
  CreateSessionResponse,
38
40
  DeleteSessionResponse,
39
41
  HealthResponse,
@@ -49,12 +51,14 @@ AgentsArg = Union[type[BaseAgent], list[type[BaseAgent]], dict[str, type[BaseAge
49
51
 
50
52
  _REQUIRED_AGENT_ATTRS = (
51
53
  "__request_model__",
54
+ "__construct_model__",
52
55
  "__response_model__",
53
56
  "__stream_event_model__",
54
57
  "__state_class__",
55
58
  "__tool_defs__",
56
59
  "__state_defs__",
57
60
  "__linked_agents__",
61
+ "__dependencies__",
58
62
  )
59
63
 
60
64
 
@@ -140,6 +144,26 @@ def _normalize_agents(agents: AgentsArg) -> dict[str, type[BaseAgent]]:
140
144
  return mapping
141
145
 
142
146
 
147
+ def _deps_for(
148
+ prefix: str,
149
+ agent_class: type[BaseAgent],
150
+ dependencies: Optional[Union[list, dict]],
151
+ ) -> Optional[list]:
152
+ """Resolve the dependency list for one mounted agent.
153
+
154
+ A flat list applies to every agent; a dict is scoped by mount prefix (with or
155
+ without a leading slash) or by agent class.
156
+ """
157
+ if dependencies is None:
158
+ return None
159
+ if isinstance(dependencies, dict):
160
+ for key in (prefix, prefix.strip("/"), "/" + prefix.strip("/"), agent_class):
161
+ if key in dependencies:
162
+ return dependencies[key]
163
+ return None
164
+ return dependencies
165
+
166
+
143
167
  def create_router(
144
168
  agent_class: type[BaseAgent],
145
169
  *,
@@ -147,6 +171,7 @@ def create_router(
147
171
  name: Optional[str] = None,
148
172
  version: str = "0.1.0",
149
173
  tags: Optional[list[str]] = None,
174
+ dependencies: Optional[list] = None,
150
175
  sessions: bool = True,
151
176
  jobs: bool = False,
152
177
  jobs_config: Optional["JobsConfig"] = None,
@@ -185,6 +210,9 @@ def create_router(
185
210
  version (str): Version string for the info route.
186
211
  tags (Optional[list[str]]): OpenAPI tags applied to every route on the
187
212
  router (groups them in the docs). ``None`` leaves routes untagged.
213
+ dependencies (Optional[list]): Instances or factories satisfying the
214
+ agent tree's ``Depends[T]`` fields, resolved by type. Validated up
215
+ front so a missing dependency fails fast at router-build time.
188
216
  sessions (bool): If True, mount the session-based routes (create/list/
189
217
  delete sessions plus synchronous chat, streaming chat, and state).
190
218
  If False, those routes are omitted — interaction happens only via
@@ -203,18 +231,23 @@ def create_router(
203
231
 
204
232
  Raises:
205
233
  TypeError: If agent_class is missing required metaclass attributes.
234
+ ValueError: If a ``Depends[T]`` field in the agent tree has no provider.
206
235
  """
207
236
  _validate_agent_class(agent_class)
237
+ validate_dependencies(agent_class, dependencies)
208
238
 
209
239
  name = name or agent_class.__name__
210
240
  slug = _slugify(name)
211
241
  RequestModel = agent_class.__request_model__
242
+ ConstructModel = agent_class.__construct_model__
212
243
  ResponseModel = agent_class.__response_model__
213
244
  StreamEventModel = agent_class.__stream_event_model__
214
245
  StateModel = agent_class.__state_class__
215
246
 
216
247
  router = APIRouter(tags=tags)
217
- session_manager = SessionManager(agent_class, default_model=model)
248
+ session_manager = SessionManager(
249
+ agent_class, default_model=model, dependencies=dependencies
250
+ )
218
251
 
219
252
  # ---- info routes ----
220
253
 
@@ -228,6 +261,7 @@ def create_router(
228
261
  tools=list(agent_class.__tool_defs__.keys()),
229
262
  state_fields=list(agent_class.__state_defs__.keys()),
230
263
  linked_agents=list(agent_class.__linked_agents__.keys()),
264
+ dependencies=list(agent_class.__dependencies__.keys()),
231
265
  )
232
266
 
233
267
  @router.get("/health", response_model=HealthResponse, name=f"{slug}_health")
@@ -237,8 +271,9 @@ def create_router(
237
271
 
238
272
  @router.get("/schema", response_model=SchemaResponse, name=f"{slug}_schema")
239
273
  async def schema() -> SchemaResponse:
240
- """Return JSON schemas for request, response, stream event, and state models."""
274
+ """Return JSON schemas for construct, request, response, stream, and state models."""
241
275
  return SchemaResponse(
276
+ construct_schema=ConstructModel.model_json_schema(),
242
277
  request=RequestModel.model_json_schema(),
243
278
  response=ResponseModel.model_json_schema(),
244
279
  stream_event=StreamEventModel.model_json_schema(),
@@ -250,6 +285,7 @@ def create_router(
250
285
  router,
251
286
  slug,
252
287
  session_manager,
288
+ ConstructModel,
253
289
  RequestModel,
254
290
  ResponseModel,
255
291
  StreamEventModel,
@@ -268,6 +304,7 @@ def create_router(
268
304
  jobs_config or JobsConfig(),
269
305
  model,
270
306
  tags,
307
+ dependencies,
271
308
  )
272
309
  router.orchestrator = orchestrator
273
310
 
@@ -281,6 +318,7 @@ def _mount_jobs_router(
281
318
  jobs_config: "JobsConfig",
282
319
  default_model: Optional[str],
283
320
  tags: Optional[list[str]],
321
+ dependencies: Optional[list] = None,
284
322
  ) -> "JobOrchestrator":
285
323
  """Build a job orchestrator and include its /jobs router; return the orchestrator."""
286
324
  from pyagentic.api.jobs import JobOrchestrator, build_backend, build_store
@@ -292,6 +330,7 @@ def _mount_jobs_router(
292
330
  sessions,
293
331
  max_concurrency=jobs_config.max_concurrency,
294
332
  default_model=default_model,
333
+ dependencies=dependencies,
295
334
  )
296
335
  orchestrator = JobOrchestrator(store, backend, jobs_config, sessions)
297
336
  router.include_router(
@@ -304,6 +343,7 @@ def _register_session_routes(
304
343
  router: APIRouter,
305
344
  slug: str,
306
345
  sessions: SessionManager,
346
+ ConstructModel: type,
307
347
  RequestModel: type,
308
348
  ResponseModel: type,
309
349
  StreamEventModel: type,
@@ -320,12 +360,23 @@ def _register_session_routes(
320
360
  name=f"{slug}_create_session",
321
361
  )
322
362
  async def create_session(
323
- req: Optional[CreateSessionRequest] = None,
363
+ req: Optional[ConstructModel] = None, # type: ignore[valid-type]
324
364
  ) -> CreateSessionResponse:
325
- """Create a new agent session."""
326
- model = req.model if req else None
327
- api_key = req.api_key if req else None
328
- session_id = sessions.create(model=model, api_key=api_key)
365
+ """Create a new agent session.
366
+
367
+ The body is the agent's construct model — the serializable construction
368
+ data (state, nested linked-agent state, model/api_key). Required state
369
+ and linked agents (those without defaults) must be supplied here, exactly
370
+ as when constructing the agent in code. The body may be omitted only when
371
+ the agent has no required construction fields.
372
+ """
373
+ if req is None:
374
+ # No body: valid only when nothing is required; otherwise 422.
375
+ try:
376
+ req = ConstructModel()
377
+ except ValidationError as exc:
378
+ raise RequestValidationError(exc.errors())
379
+ session_id = sessions.create(construct_data=req)
329
380
  return CreateSessionResponse(session_id=session_id)
330
381
 
331
382
  @router.get(
@@ -483,6 +534,7 @@ def create_app(
483
534
  version: Optional[str] = None,
484
535
  description: Optional[str] = None,
485
536
  model: Optional[str] = None,
537
+ dependencies: Optional[Union[list, dict]] = None,
486
538
  mcp: bool = False,
487
539
  sessions: bool = True,
488
540
  jobs: bool = False,
@@ -512,6 +564,10 @@ def create_app(
512
564
  description (Optional[str]): App description. Overrides config.
513
565
  model (Optional[str]): Default model for all agents' sessions. Overrides
514
566
  config.
567
+ dependencies (Optional[Union[list, dict]]): Providers for the agents'
568
+ ``Depends[T]`` fields (instances or factories, resolved by type). A
569
+ flat list is applied to every mounted agent; a dict keyed by mount
570
+ prefix (or agent class) scopes providers per agent.
515
571
  mcp (bool): If True, mount an MCP endpoint per agent at ``<prefix>/mcp``.
516
572
  sessions (bool): If True (default), mount the session-based routes
517
573
  (sessions CRUD plus synchronous/streaming chat and state) for each
@@ -563,6 +619,7 @@ def create_app(
563
619
  model=model,
564
620
  name=agent_name,
565
621
  version=version,
622
+ dependencies=_deps_for(prefix, agent_class, dependencies),
566
623
  sessions=sessions,
567
624
  jobs=jobs_enabled,
568
625
  jobs_config=jobs_cfg,
@@ -0,0 +1,205 @@
1
+ """
2
+ Construct agents from a (recursive) construct-model payload plus developer-supplied
3
+ dependencies.
4
+
5
+ The metaclass generates ``agent_class.__construct_model__`` — a recursive Pydantic
6
+ model mirroring the constructor (state fields, nested linked-agent construct models,
7
+ and ``model``/``api_key``). ``build_agent`` turns an instance of that model into a
8
+ live agent, recursing into linked agents and injecting ``Depends[T]`` fields by type
9
+ from a flat ``dependencies`` list.
10
+
11
+ Dependencies are passed as bare values:
12
+ - an **instance** of the dependency type, or
13
+ - a zero-arg **factory** whose return annotation is (a subclass of) the type.
14
+
15
+ Resolution is by type. An instance match wins over a factory; factories are called
16
+ fresh for each agent built (so each session/job gets its own).
17
+ """
18
+
19
+ import inspect
20
+ from typing import Any, Optional
21
+
22
+ from pyagentic._base._agent._agent import BaseAgent
23
+
24
+
25
+ def _as_field_dict(construct_data: Any) -> dict:
26
+ """Normalize a construct payload (Pydantic model or dict) to a field dict.
27
+
28
+ Nested values (linked-agent construct models) are preserved as-is so the
29
+ recursion can descend into them.
30
+ """
31
+ if construct_data is None:
32
+ return {}
33
+ if isinstance(construct_data, dict):
34
+ return dict(construct_data)
35
+ # Pydantic model: iterate declared fields, keeping nested model instances.
36
+ model_fields = getattr(type(construct_data), "model_fields", None)
37
+ if model_fields is not None:
38
+ return {name: getattr(construct_data, name) for name in model_fields}
39
+ raise TypeError(f"Unsupported construct payload type: {type(construct_data)!r}")
40
+
41
+
42
+ def _matches_factory(value: Any, dep_type: type) -> bool:
43
+ """Return whether *value* is a zero-arg factory producing *dep_type*."""
44
+ if not callable(value) or isinstance(value, type):
45
+ # Plain classes are treated as instances/values, not factories — a
46
+ # factory is expected to be an annotated callable.
47
+ return False
48
+ try:
49
+ ret = inspect.signature(value).return_annotation
50
+ except (TypeError, ValueError):
51
+ return False
52
+ return isinstance(ret, type) and issubclass(ret, dep_type)
53
+
54
+
55
+ def _resolve_dependency(name: str, dep_type: type, dependencies: list) -> Any:
56
+ """Resolve a single ``Depends[T]`` slot from the dependency list.
57
+
58
+ Args:
59
+ name (str): The field name (used only for error messages).
60
+ dep_type (type): The declared dependency type ``T``.
61
+ dependencies (list): Provided instances/factories.
62
+
63
+ Returns:
64
+ Any: The resolved dependency instance.
65
+
66
+ Raises:
67
+ LookupError: If no provided value satisfies the slot.
68
+ """
69
+ # Instance match wins.
70
+ for value in dependencies:
71
+ if isinstance(value, dep_type):
72
+ return value
73
+ # Otherwise a factory whose return type matches; called fresh.
74
+ for value in dependencies:
75
+ if _matches_factory(value, dep_type):
76
+ return value()
77
+ raise LookupError(
78
+ f"No dependency provided for '{name}': {dep_type.__name__}. "
79
+ f"Pass an instance of {dep_type.__name__} or a factory returning it "
80
+ f"in create_router(..., dependencies=[...])."
81
+ )
82
+
83
+
84
+ def build_agent(
85
+ agent_class: type[BaseAgent],
86
+ construct_data: Any,
87
+ dependencies: Optional[list] = None,
88
+ *,
89
+ default_model: Optional[str] = None,
90
+ default_api_key: Optional[str] = None,
91
+ ) -> BaseAgent:
92
+ """Instantiate an agent from a construct-model payload and injected dependencies.
93
+
94
+ Linked agents are built first (depth-first) from their nested construct
95
+ submodels, then ``Depends[T]`` fields are resolved by type, and finally the
96
+ agent is constructed with state, ``model``/``api_key``, the built linked
97
+ agents, and the resolved dependencies. The ``default_model``/``default_api_key``
98
+ propagate down the whole tree, filling in any agent whose payload omits them.
99
+
100
+ Args:
101
+ agent_class (type[BaseAgent]): The agent class to instantiate.
102
+ construct_data (Any): An instance of ``agent_class.__construct_model__``
103
+ (or a dict with the same shape).
104
+ dependencies (Optional[list]): Instances or factories used to satisfy
105
+ ``Depends[T]`` fields across the agent tree. Defaults to ``[]``.
106
+ default_model (Optional[str]): Model applied to any agent in the tree that
107
+ does not specify its own ``model``.
108
+ default_api_key (Optional[str]): API key applied alongside ``default_model``
109
+ for any agent that does not specify its own.
110
+
111
+ Returns:
112
+ BaseAgent: The fully constructed agent instance.
113
+
114
+ Raises:
115
+ LookupError: If a required dependency cannot be resolved.
116
+ """
117
+ dependencies = dependencies or []
118
+ # A raw dict (e.g. a persisted job payload) is parsed through the construct
119
+ # model so nested state and linked-agent submodels are coerced to typed
120
+ # instances before construction.
121
+ if isinstance(construct_data, dict):
122
+ construct_data = agent_class.__construct_model__(**construct_data)
123
+ fields = _as_field_dict(construct_data)
124
+ kwargs: dict[str, Any] = {}
125
+
126
+ # Build nested linked agents first (defaults propagate down the tree).
127
+ for name, linked_def in agent_class.__linked_agents__.items():
128
+ child_data = fields.get(name)
129
+ if child_data is None:
130
+ # Omitted optional link: let __init__ apply the AgentLink default.
131
+ continue
132
+ kwargs[name] = build_agent(
133
+ linked_def.agent,
134
+ child_data,
135
+ dependencies,
136
+ default_model=default_model,
137
+ default_api_key=default_api_key,
138
+ )
139
+
140
+ # State fields supplied by the client.
141
+ for name in agent_class.__state_defs__:
142
+ if name in fields and fields[name] is not None:
143
+ kwargs[name] = fields[name]
144
+
145
+ # Provider selection scalars, falling back to the propagated defaults.
146
+ model = fields.get("model") or default_model
147
+ api_key = fields.get("api_key") or default_api_key
148
+ if model is not None:
149
+ kwargs["model"] = model
150
+ if api_key is not None:
151
+ kwargs["api_key"] = api_key
152
+
153
+ # Inject dependencies by type.
154
+ for name, dep_type in agent_class.__dependencies__.items():
155
+ kwargs[name] = _resolve_dependency(name, dep_type, dependencies)
156
+
157
+ return agent_class(**kwargs)
158
+
159
+
160
+ def _collect_dependency_slots(
161
+ agent_class: type[BaseAgent],
162
+ _seen: Optional[set] = None,
163
+ ) -> list[tuple[str, type]]:
164
+ """Walk the linked-agent tree collecting every ``Depends[T]`` slot."""
165
+ _seen = _seen if _seen is not None else set()
166
+ if agent_class in _seen:
167
+ return []
168
+ _seen.add(agent_class)
169
+
170
+ slots: list[tuple[str, type]] = list(agent_class.__dependencies__.items())
171
+ for linked_def in agent_class.__linked_agents__.values():
172
+ slots.extend(_collect_dependency_slots(linked_def.agent, _seen))
173
+ return slots
174
+
175
+
176
+ def validate_dependencies(
177
+ agent_class: type[BaseAgent],
178
+ dependencies: Optional[list] = None,
179
+ ) -> None:
180
+ """Verify every ``Depends[T]`` slot in the agent tree is satisfiable.
181
+
182
+ Intended to run once at ``create_router``/``create_app`` time so misconfigured
183
+ dependencies fail fast rather than per request. Factories are not invoked —
184
+ only their presence/return type is checked.
185
+
186
+ Args:
187
+ agent_class (type[BaseAgent]): The root agent class.
188
+ dependencies (Optional[list]): Provided instances/factories.
189
+
190
+ Raises:
191
+ ValueError: If any dependency slot has no matching provider.
192
+ """
193
+ dependencies = dependencies or []
194
+ missing: list[str] = []
195
+ for name, dep_type in _collect_dependency_slots(agent_class):
196
+ satisfiable = any(isinstance(v, dep_type) for v in dependencies) or any(
197
+ _matches_factory(v, dep_type) for v in dependencies
198
+ )
199
+ if not satisfiable:
200
+ missing.append(f"'{name}': {dep_type.__name__}")
201
+ if missing:
202
+ raise ValueError(
203
+ f"{agent_class.__name__} has unsatisfied dependencies: {', '.join(missing)}. "
204
+ f"Provide them via dependencies=[...] (an instance or a factory returning the type)."
205
+ )
pyagentic/api/_models.py CHANGED
@@ -8,22 +8,7 @@ endpoints are typed directly from each agent's metaclass-generated models
8
8
  ``__state_class__``), so they are not duplicated here.
9
9
  """
10
10
 
11
- from typing import Optional
12
-
13
- from pydantic import BaseModel, Field
14
-
15
-
16
- class CreateSessionRequest(BaseModel):
17
- """Request body for creating a new agent session.
18
-
19
- Attributes:
20
- model (Optional[str]): LLM model string override
21
- (e.g. ``'openai::gpt-4o'``).
22
- api_key (Optional[str]): API key for the model provider.
23
- """
24
-
25
- model: Optional[str] = None
26
- api_key: Optional[str] = None
11
+ from pydantic import BaseModel, ConfigDict, Field
27
12
 
28
13
 
29
14
  class CreateSessionResponse(BaseModel):
@@ -76,6 +61,7 @@ class AgentInfo(BaseModel):
76
61
  tools (list[str]): Names of the tools the agent exposes.
77
62
  state_fields (list[str]): Names of the agent's state fields.
78
63
  linked_agents (list[str]): Names of agents linked to this one.
64
+ dependencies (list[str]): Names of the agent's ``Depends[T]`` fields.
79
65
  """
80
66
 
81
67
  name: str
@@ -84,18 +70,26 @@ class AgentInfo(BaseModel):
84
70
  tools: list[str] = Field(default_factory=list)
85
71
  state_fields: list[str] = Field(default_factory=list)
86
72
  linked_agents: list[str] = Field(default_factory=list)
73
+ dependencies: list[str] = Field(default_factory=list)
87
74
 
88
75
 
89
76
  class SchemaResponse(BaseModel):
90
- """JSON schemas for an agent's request/response/stream/state models.
77
+ """JSON schemas for an agent's construct/request/response/stream/state models.
91
78
 
92
79
  Attributes:
80
+ construct (dict): JSON schema of the agent's construct model (the body
81
+ for creating a session / a sessionless job).
93
82
  request (dict): JSON schema of the agent's request model.
94
83
  response (dict): JSON schema of the agent's response model.
95
84
  stream_event (dict): JSON schema of the agent's stream event model.
96
85
  state (dict): JSON schema of the agent's state model.
97
86
  """
98
87
 
88
+ # Serialized as ``construct``; the Python attribute avoids shadowing the
89
+ # deprecated ``BaseModel.construct`` method (which pydantic warns about).
90
+ model_config = ConfigDict(populate_by_name=True)
91
+
92
+ construct_schema: dict = Field(serialization_alias="construct", validation_alias="construct")
99
93
  request: dict
100
94
  response: dict
101
95
  stream_event: dict
@@ -4,24 +4,28 @@ independent state and conversation history.
4
4
  """
5
5
 
6
6
  import uuid
7
- from typing import TYPE_CHECKING, Optional
7
+ from typing import TYPE_CHECKING, Any, Optional
8
+
9
+ from pyagentic.api._build import build_agent
8
10
 
9
11
  if TYPE_CHECKING:
10
12
  from pyagentic._base._agent._agent import BaseAgent
11
- from pyagentic.llm._provider import LLMProvider
12
13
 
13
14
 
14
15
  class SessionManager:
15
16
  """Manages agent sessions keyed by session ID.
16
17
 
17
18
  Each session holds its own agent instance, so state and conversation
18
- history are isolated per session.
19
+ history are isolated per session. Agents are constructed from a
20
+ construct-model payload (mirroring the constructor) plus developer-supplied
21
+ dependencies, via :func:`pyagentic.api._build.build_agent`.
19
22
  """
20
23
 
21
24
  def __init__(
22
25
  self,
23
26
  agent_class: "type[BaseAgent]",
24
27
  default_model: Optional[str] = None,
28
+ dependencies: Optional[list] = None,
25
29
  ) -> None:
26
30
  """Initialize the session manager.
27
31
 
@@ -29,42 +33,36 @@ class SessionManager:
29
33
  agent_class (type[BaseAgent]): The agent class to instantiate for
30
34
  each session.
31
35
  default_model (Optional[str]): Model string used when a session is
32
- created without one. ``None`` falls back to the agent class's
33
- own default model.
36
+ created without one in its construct payload. ``None`` falls
37
+ back to the agent class's own default model.
38
+ dependencies (Optional[list]): Instances or factories used to satisfy
39
+ the agent tree's ``Depends[T]`` fields (resolved by type).
34
40
  """
35
41
  self._agent_class = agent_class
36
42
  self._default_model = default_model
43
+ self._dependencies = dependencies or []
37
44
  self._sessions: dict[str, "BaseAgent"] = {}
38
45
 
39
- def create(
40
- self,
41
- *,
42
- model: Optional[str] = None,
43
- api_key: Optional[str] = None,
44
- provider: Optional["LLMProvider"] = None,
45
- ) -> str:
46
+ def create(self, *, construct_data: Any = None) -> str:
46
47
  """Create a new session with a fresh agent instance.
47
48
 
48
49
  Args:
49
- model (Optional[str]): LLM model string (e.g. 'openai::gpt-4o').
50
- Falls back to the manager's ``default_model``.
51
- api_key (Optional[str]): API key for the model provider.
52
- provider (Optional[LLMProvider]): Pre-configured LLMProvider
53
- instance. Overrides model/api_key.
50
+ construct_data (Any): An instance of the agent's
51
+ ``__construct_model__`` (or a dict of the same shape) carrying
52
+ the serializable construction data (state, nested linked-agent
53
+ state, ``model``/``api_key``). ``None`` builds with defaults.
54
54
 
55
55
  Returns:
56
56
  str: The new session ID.
57
57
  """
58
58
  session_id = uuid.uuid4().hex[:12]
59
59
 
60
- if provider is not None:
61
- agent = self._agent_class(provider=provider)
62
- else:
63
- kwargs: dict = {"api_key": api_key}
64
- resolved_model = model or self._default_model
65
- if resolved_model is not None:
66
- kwargs["model"] = resolved_model
67
- agent = self._agent_class(**kwargs)
60
+ agent = build_agent(
61
+ self._agent_class,
62
+ construct_data,
63
+ self._dependencies,
64
+ default_model=self._default_model,
65
+ )
68
66
 
69
67
  self._sessions[session_id] = agent
70
68
  return session_id
@@ -37,6 +37,9 @@ class JobRecord(BaseModel):
37
37
  session_id (Optional[str]): Session the job belongs to, if any.
38
38
  status (JobStatus): Current lifecycle state.
39
39
  request (dict): The submitted agent input kwargs.
40
+ construct_payload (Optional[dict]): Construct-model payload for a
41
+ sessionless job, used to build a fresh agent (and survive recovery).
42
+ ``None`` for session-bound jobs (which reuse the session's live agent).
40
43
  result_json (Optional[str]): Final agent response as a raw JSON
41
44
  string, set when the job succeeds.
42
45
  error (Optional[str]): Failure description, set when the job fails.
@@ -49,6 +52,7 @@ class JobRecord(BaseModel):
49
52
  session_id: Optional[str] = None
50
53
  status: JobStatus = JobStatus.QUEUED
51
54
  request: dict = Field(default_factory=dict)
55
+ construct_payload: Optional[dict] = None
52
56
  result_json: Optional[str] = None
53
57
  error: Optional[str] = None
54
58
  created_at: float = Field(default_factory=time.time)
@@ -138,7 +138,10 @@ class JobOrchestrator:
138
138
  # ---- submission & execution ----
139
139
 
140
140
  async def submit(
141
- self, request: dict, session_id: Optional[str] = None
141
+ self,
142
+ request: dict,
143
+ session_id: Optional[str] = None,
144
+ construct: Optional[dict] = None,
142
145
  ) -> JobRecord:
143
146
  """Create a durable job record and route it for execution.
144
147
 
@@ -150,6 +153,8 @@ class JobOrchestrator:
150
153
  Args:
151
154
  request (dict): Agent input kwargs.
152
155
  session_id (Optional[str]): Session to bind the job to.
156
+ construct (Optional[dict]): Construct-model payload for building a
157
+ fresh agent (sessionless jobs only). Persisted for recovery.
153
158
 
154
159
  Returns:
155
160
  JobRecord: The persisted record in its initial queued state.
@@ -160,6 +165,7 @@ class JobOrchestrator:
160
165
  session_id=session_id,
161
166
  status=JobStatus.QUEUED,
162
167
  request=request,
168
+ construct_payload=construct,
163
169
  )
164
170
  await self.store.create_job(job)
165
171
  self._next_seq[job.job_id] = 0
@@ -10,7 +10,7 @@ from typing import Optional
10
10
 
11
11
  from fastapi import APIRouter, HTTPException, Request
12
12
  from fastapi.responses import StreamingResponse
13
- from pydantic import create_model
13
+ from pydantic import ConfigDict, Field, create_model
14
14
 
15
15
  from pyagentic._base._agent._agent import BaseAgent
16
16
  from pyagentic.api._sessions import SessionManager
@@ -138,12 +138,17 @@ def build_jobs_router(
138
138
  """
139
139
  router = APIRouter()
140
140
  RequestModel = agent_class.__request_model__
141
+ ConstructModel = agent_class.__construct_model__
141
142
  ResponseModel = agent_class.__response_model__
142
143
 
144
+ # ``construct`` is the wire field name; the Python attribute is suffixed to
145
+ # avoid shadowing the deprecated ``BaseModel.construct`` method.
143
146
  SubmitModel = create_model(
144
147
  f"{agent_class.__name__}JobSubmitRequest",
148
+ __config__=ConfigDict(populate_by_name=True),
145
149
  input=(RequestModel, ...),
146
150
  session_id=(Optional[str], None),
151
+ construct_payload=(Optional[ConstructModel], Field(None, alias="construct")),
147
152
  )
148
153
 
149
154
  # Narrow the loosely-typed JobSnapshot.result to this agent's response model
@@ -156,14 +161,25 @@ def build_jobs_router(
156
161
 
157
162
  @router.post("/jobs", status_code=202, response_model=JobSubmitResponse)
158
163
  async def submit_job(req: SubmitModel) -> dict: # type: ignore[valid-type]
159
- """Submit an agent run as an async job."""
164
+ """Submit an agent run as an async job.
165
+
166
+ A session-bound job (``session_id`` set) runs on that session's live
167
+ agent. A sessionless job builds a fresh agent from the optional
168
+ ``construct`` payload (the agent's construct model) plus the server's
169
+ injected dependencies.
170
+ """
160
171
  await orchestrator.ensure_started()
161
172
  if req.session_id is not None and not sessions.exists(req.session_id):
162
173
  raise HTTPException(status_code=404, detail="Session not found")
163
174
  request = {
164
175
  field: getattr(req.input, field) for field in type(req.input).model_fields
165
176
  }
166
- job = await orchestrator.submit(request, session_id=req.session_id)
177
+ construct = None
178
+ if req.session_id is None and req.construct_payload is not None:
179
+ construct = req.construct_payload.model_dump()
180
+ job = await orchestrator.submit(
181
+ request, session_id=req.session_id, construct=construct
182
+ )
167
183
  return {"job_id": job.job_id, "status": job.status.value}
168
184
 
169
185
  @router.get("/jobs", response_model=JobListResponse)
@@ -23,6 +23,7 @@ def build_backend(
23
23
  *,
24
24
  max_concurrency: int = 8,
25
25
  default_model: Optional[str] = None,
26
+ dependencies: Optional[list] = None,
26
27
  ) -> ExecutionBackend:
27
28
  """Build the in-process ExecutionBackend.
28
29
 
@@ -31,6 +32,8 @@ def build_backend(
31
32
  sessions (SessionManager): Live session registry.
32
33
  max_concurrency (int): Max concurrent agent runs.
33
34
  default_model (Optional[str]): Model for sessionless agents.
35
+ dependencies (Optional[list]): Providers for ``Depends[T]`` fields used
36
+ when building a fresh agent for a sessionless job.
34
37
 
35
38
  Returns:
36
39
  ExecutionBackend: A configured :class:`InProcessBackend`.
@@ -40,4 +43,5 @@ def build_backend(
40
43
  sessions,
41
44
  max_concurrency=max_concurrency,
42
45
  default_model=default_model,
46
+ dependencies=dependencies,
43
47
  )
@@ -17,6 +17,7 @@ from pyagentic.api.jobs._models import (
17
17
  _build_prompt,
18
18
  )
19
19
  from pyagentic.api.jobs.backends._base import EmitFn, LocalExecutionBackend
20
+ from pyagentic.api._build import build_agent
20
21
 
21
22
  if TYPE_CHECKING:
22
23
  from pyagentic.api._sessions import SessionManager
@@ -37,6 +38,7 @@ class InProcessBackend(LocalExecutionBackend):
37
38
  *,
38
39
  max_concurrency: int = 8,
39
40
  default_model: Optional[str] = None,
41
+ dependencies: Optional[list] = None,
40
42
  ) -> None:
41
43
  """Create the backend.
42
44
 
@@ -46,11 +48,14 @@ class InProcessBackend(LocalExecutionBackend):
46
48
  sessions (SessionManager): Live session registry for session-bound jobs.
47
49
  max_concurrency (int): Max concurrent agent runs.
48
50
  default_model (Optional[str]): Model for sessionless agents.
51
+ dependencies (Optional[list]): Providers for the agent's ``Depends[T]``
52
+ fields when building a fresh agent for a sessionless job.
49
53
  """
50
54
  super().__init__()
51
55
  self._agent_class = agent_class
52
56
  self._sessions = sessions
53
57
  self._default_model = default_model
58
+ self._dependencies = dependencies or []
54
59
  self._max_concurrency = max_concurrency
55
60
  self._sem = asyncio.Semaphore(max_concurrency)
56
61
 
@@ -72,10 +77,12 @@ class InProcessBackend(LocalExecutionBackend):
72
77
  agent = self._sessions.get(session_id)
73
78
  own_agent = False
74
79
  else:
75
- kwargs = {}
76
- if self._default_model is not None:
77
- kwargs["model"] = self._default_model
78
- agent = self._agent_class(**kwargs)
80
+ agent = build_agent(
81
+ self._agent_class,
82
+ job.construct_payload,
83
+ self._dependencies,
84
+ default_model=self._default_model,
85
+ )
79
86
  own_agent = True
80
87
 
81
88
  prompt = _build_prompt(job.request)
@@ -23,15 +23,16 @@ from pyagentic.api.jobs.store._base import JobStore
23
23
 
24
24
  _SCHEMA = """
25
25
  CREATE TABLE IF NOT EXISTS jobs (
26
- job_id TEXT PRIMARY KEY,
27
- session_id TEXT,
28
- status TEXT NOT NULL,
29
- request_json TEXT NOT NULL,
30
- result_json TEXT,
31
- error TEXT,
32
- created_at REAL NOT NULL,
33
- started_at REAL,
34
- finished_at REAL
26
+ job_id TEXT PRIMARY KEY,
27
+ session_id TEXT,
28
+ status TEXT NOT NULL,
29
+ request_json TEXT NOT NULL,
30
+ construct_json TEXT,
31
+ result_json TEXT,
32
+ error TEXT,
33
+ created_at REAL NOT NULL,
34
+ started_at REAL,
35
+ finished_at REAL
35
36
  );
36
37
  CREATE INDEX IF NOT EXISTS idx_jobs_session ON jobs(session_id);
37
38
  CREATE INDEX IF NOT EXISTS idx_jobs_status ON jobs(status);
@@ -54,6 +55,9 @@ def _row_to_record(row: sqlite3.Row) -> JobRecord:
54
55
  session_id=row["session_id"],
55
56
  status=JobStatus(row["status"]),
56
57
  request=json.loads(row["request_json"]),
58
+ construct_payload=(
59
+ json.loads(row["construct_json"]) if row["construct_json"] else None
60
+ ),
57
61
  result_json=row["result_json"],
58
62
  error=row["error"],
59
63
  created_at=row["created_at"],
@@ -128,13 +132,18 @@ class SQLiteJobStore(JobStore):
128
132
  def _insert(conn: sqlite3.Connection) -> None:
129
133
  conn.execute(
130
134
  "INSERT INTO jobs (job_id, session_id, status, request_json, "
131
- "result_json, error, created_at, started_at, finished_at) "
132
- "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)",
135
+ "construct_json, result_json, error, created_at, started_at, finished_at) "
136
+ "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
133
137
  (
134
138
  job.job_id,
135
139
  job.session_id,
136
140
  job.status.value,
137
141
  json.dumps(job.request, default=str),
142
+ (
143
+ json.dumps(job.construct_payload, default=str)
144
+ if job.construct_payload is not None
145
+ else None
146
+ ),
138
147
  job.result_json,
139
148
  job.error,
140
149
  job.created_at,
@@ -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
@@ -1,40 +1,42 @@
1
- pyagentic/__init__.py,sha256=eMQX9My9kax7togSTBmQOUVIV_rCw_cxE321FgCNjCQ,1849
1
+ pyagentic/__init__.py,sha256=S1nJnbbDZzkH0AbKYWIAWCPhZJW2a2Ml2zdxi91WSPk,2012
2
2
  pyagentic/_version_scheme.py,sha256=hUYPvGhWz5L7RSuVxIqW3h10i4zNjfYXMofmoIk8ZBM,1569
3
3
  pyagentic/logging.py,sha256=vWLKGx0jZurWQT5pMr6fLH5NgJnZd_eaB97geBnqlwI,1718
4
4
  pyagentic/updates.py,sha256=JrBxbmTRxS-4tfSrai_wPK-dxSyCiNrYwC5MgrzGj54,870
5
5
  pyagentic/_base/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
+ pyagentic/_base/_depends.py,sha256=O59cvg4uc4feDrPjn9HqryrMGzqNyulyv8intAmQ8PE,2188
6
7
  pyagentic/_base/_exceptions.py,sha256=r_wcuWgc_Y3g_D7DwLQL8NbTphIAeeNpIp-WDHMvvOk,3746
7
8
  pyagentic/_base/_info.py,sha256=2bY9_Ua9N7m94hcv6gLJCBZ2guxXTJf3YVOuJAyG4ns,2995
8
9
  pyagentic/_base/_mcp.py,sha256=YPRaqVFrgKDA5BZtvVrcScyADjo66cdB4_h6nXpRJBI,8329
9
- pyagentic/_base/_metaclasses.py,sha256=xg-AFj45KCby0LLWgvvQDNPjaySQhn49pCCv5cR9X-k,29682
10
+ pyagentic/_base/_metaclasses.py,sha256=0PQ3ipidOPKo6xE5Ra2ZBhme74s3Ss9BLj9O0RehueA,34452
10
11
  pyagentic/_base/_ref.py,sha256=Ez1Iexxl7NLBK91frcgz_eFbrXfLZpm9fvkYHMFWA_w,3179
11
12
  pyagentic/_base/_spec.py,sha256=TH-DZTYFcxXmzxGympH4vATPPlpI2o88Wlv6S7UuNlQ,7112
12
13
  pyagentic/_base/_state.py,sha256=swjMl22rOgaIVEuqyL1bG7gpYpumuOZ6ytNImV_2lGw,1794
13
14
  pyagentic/_base/_tool.py,sha256=6Zkd20iwQX1uS-rw4i9IA8doM8qjchEZ3EmMk5Li9SE,11040
14
15
  pyagentic/_base/_validation.py,sha256=BBscZMrXB09LNNP17pYOAY6I4iCW8r2GBVStqBd-9f8,3833
15
16
  pyagentic/_base/_agent/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
16
- pyagentic/_base/_agent/_agent.py,sha256=HZJM27CPtXA_bNqNjMSvbmEbv1OY05JirGUkSacf1xo,39373
17
+ pyagentic/_base/_agent/_agent.py,sha256=P35dpRp4Ihpc7AzGLW8bo_aj1pR2orvxu3a5777NIL8,39549
17
18
  pyagentic/_base/_agent/_agent_linking.py,sha256=gSHQkbH-BeKG8moROKDZiLs0ktLzW9bOVo0DFq3UNs8,1841
18
19
  pyagentic/_base/_agent/_agent_state.py,sha256=gvx0rQ1gaWfZHHAONX4YNsO_HE0xYQ14_GBKMP4Af-o,11507
19
20
  pyagentic/_utils/_typing.py,sha256=5aRZZOVzR0ZgnBfejkw__1yoARhn70P5QzA81sxUkls,3681
20
21
  pyagentic/_utils/_warnings.py,sha256=mjlBiYHGnnh5599Gsq5ke8mBFS6WsdhNa7jk20TT9_U,1330
21
- pyagentic/api/__init__.py,sha256=v-jaDw3mPCxpcllfYAbBCOdyC16qBA3B4eegxcDk4iU,1590
22
- pyagentic/api/_app.py,sha256=BOaMk28CPBEUOVoiQeg3e8D6CTuHw6h3V7ti0s2h-m8,22558
22
+ pyagentic/api/__init__.py,sha256=7CklNYjxVj5XOyqTWvvWKZU6IYuU8CAfpOhNeGMH1d4,1712
23
+ pyagentic/api/_app.py,sha256=G34AdK-h88EEuJxzmzzNPkskR5dUGKRHz16PE0g8i3E,25146
24
+ pyagentic/api/_build.py,sha256=HfIAr9YREZk2BvyRwy4HmkMONQpnPptqytpxc2l_QB8,8133
23
25
  pyagentic/api/_config.py,sha256=2YaFBDW7aaRueemHPBd1XzW9m0oqVCBilt8MA-x6QqI,6547
24
26
  pyagentic/api/_docker.py,sha256=AoAQ66gT-Uf5WhiYpImKSpTPyboiTaqWvxG7mBLe54I,2229
25
27
  pyagentic/api/_mcp_server.py,sha256=9aOC18-FSv1qS7umqFHxMKV5FujblziR-_NDfNsXmFo,5032
26
- pyagentic/api/_models.py,sha256=1wI-GVYNwuNkDCktmmfcicMVsAgNEcUkJXnqN_ff4eo,3411
27
- pyagentic/api/_sessions.py,sha256=HU2pkCIA_b1syr7n6qIl0m2Ad5tvPzVplzYAeKS_9HM,3636
28
+ pyagentic/api/_models.py,sha256=ReDeuzQWIZ7YYDAwa8yfPYZNR6cPQlBYRMtsni_5crw,3633
29
+ pyagentic/api/_sessions.py,sha256=P-i67Jii3cAfCrotkWGxYnf-8LCkc96Vn9gnDgXrS6Y,3754
28
30
  pyagentic/api/jobs/__init__.py,sha256=SGZOE86xVwv4hX2zjpm8uLZZhizz0rv3EV5suUuiM8M,979
29
- pyagentic/api/jobs/_models.py,sha256=vIFZaES7v5hD68MvyPG-2D_miQDMNaWp_5IH_7TYxcw,6950
30
- pyagentic/api/jobs/_orchestrator.py,sha256=XgvFiQ9R-rLMGjkTGUUXhU6YsTg-x6nABfABvpfQX-E,17159
31
- pyagentic/api/jobs/_routes.py,sha256=At5nmn_7a3igaLPIgFnt3_xwTM3yYSsD_ZFRfmalymk,9764
32
- pyagentic/api/jobs/backends/__init__.py,sha256=LdlWjve6cdl04X09sJvlV-aUPK2euImC_cW73lg3tBM,1325
31
+ pyagentic/api/jobs/_models.py,sha256=Q0E69uVkEibEUItuS1U0C38YG5ixKnp8IzxNvLVkDk8,7234
32
+ pyagentic/api/jobs/_orchestrator.py,sha256=umt6IAIPpCJiDH7HYMSwT8X1SVDVUQ7lqxBpDiI1k4c,17415
33
+ pyagentic/api/jobs/_routes.py,sha256=1tR-_udgiBIwLVy_JIvdW9Ik5BiYMEzAlIbufArzATU,10593
34
+ pyagentic/api/jobs/backends/__init__.py,sha256=giGsZOH4Q2l5HU8ccO5q-sKsKdR23zOzWlECc_n4CwE,1544
33
35
  pyagentic/api/jobs/backends/_base.py,sha256=LITAW0FNWKAdEhmnixe5UakbBrxYY7rWbZD7jK6VZL8,4453
34
- pyagentic/api/jobs/backends/_in_process.py,sha256=5ltFfS39noeddXacp2GCUHW9A_SyMNw0xHVPbKcgtFI,4425
36
+ pyagentic/api/jobs/backends/_in_process.py,sha256=pMyewlmkAUTsIn_q29zmYHPyyKNZNi2zhqZS2gQvyf8,4755
35
37
  pyagentic/api/jobs/store/__init__.py,sha256=jxm7hEa2AiV44_1UfYuJ-RofeM6I-DFMCywzYHNk1Nc,507
36
38
  pyagentic/api/jobs/store/_base.py,sha256=Ao9mRSRiwEz44mJRFWfqM5aZkXbemULoNwUBDRNDYuM,4856
37
- pyagentic/api/jobs/store/_sqlite.py,sha256=nzw5vkmrbIeetZqbw385CCnixltWtrBWGvhuay5OSlU,12291
39
+ pyagentic/api/jobs/store/_sqlite.py,sha256=hX0MIo1PVYmlsw8Hc6WDb4qM8tyavHzYVd37uK0hRQ0,12675
38
40
  pyagentic/llm/__init__.py,sha256=4ABk1NnYmw_yf9qeS0WVZb8dLl3gC1xtoD7RmH9_G2U,966
39
41
  pyagentic/llm/_anthropic.py,sha256=oiyin7QCeHlo9hn8YGJri0QheOa_uvacQSvyS9djMsE,8372
40
42
  pyagentic/llm/_gemini.py,sha256=cC1borYCdL9EpdJ51j0kJRQuTnSdMsG7JN5ruq3w5Cg,10385
@@ -52,8 +54,8 @@ pyagentic/tracing/__init__.py,sha256=ddMOl_-Nf3WOhu-PYxSJ3kqHZ3eQXAOM5aySn9YmDtA
52
54
  pyagentic/tracing/_basic.py,sha256=jUBKdkOPveCjBzEhHKS_Rvp5l38iZg-5nFIl6xEwOVk,8906
53
55
  pyagentic/tracing/_langfuse.py,sha256=5b5tdT-gyRJP5HDcvCS4tn_k3AFDI6eNsXOd6On5gKQ,13986
54
56
  pyagentic/tracing/_tracer.py,sha256=HMisWfRLAQ9sdcjOvImiLij6OASd3siM8K-7nUfM3xs,7656
55
- pyagentic_core-2.10.1.dev2.dist-info/licenses/LICENSE,sha256=wcOzTj82hOc96HztJk2VzLB2hFHpdIGpjz8vvbpP1_s,1069
56
- pyagentic_core-2.10.1.dev2.dist-info/METADATA,sha256=oQsV2I4CSW0si5Q8YueIq1W2bqt3lNzJXSZmv787VAs,8212
57
- pyagentic_core-2.10.1.dev2.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
58
- pyagentic_core-2.10.1.dev2.dist-info/top_level.txt,sha256=lAWfd-ay434uEpSg2FM0ySN0o4vgNGE6D9dJkIhGyfY,10
59
- pyagentic_core-2.10.1.dev2.dist-info/RECORD,,
57
+ pyagentic_core-2.11.1.dev1.dist-info/licenses/LICENSE,sha256=wcOzTj82hOc96HztJk2VzLB2hFHpdIGpjz8vvbpP1_s,1069
58
+ pyagentic_core-2.11.1.dev1.dist-info/METADATA,sha256=GOf2IQSJPjEu105ZiXEf0WfET_7YC2la3d1LH1_Y77I,8212
59
+ pyagentic_core-2.11.1.dev1.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
60
+ pyagentic_core-2.11.1.dev1.dist-info/top_level.txt,sha256=lAWfd-ay434uEpSg2FM0ySN0o4vgNGE6D9dJkIhGyfY,10
61
+ pyagentic_core-2.11.1.dev1.dist-info/RECORD,,