pyagentic-core 2.2.0__tar.gz → 2.2.0a1__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.2.0 → pyagentic_core-2.2.0a1}/PKG-INFO +1 -1
- {pyagentic_core-2.2.0 → pyagentic_core-2.2.0a1}/pyagentic/_base/_agent/_agent.py +33 -21
- {pyagentic_core-2.2.0 → pyagentic_core-2.2.0a1}/pyagentic/_base/_agent/_agent_state.py +3 -42
- {pyagentic_core-2.2.0 → pyagentic_core-2.2.0a1}/pyagentic/_base/_tool.py +4 -5
- {pyagentic_core-2.2.0 → pyagentic_core-2.2.0a1}/pyagentic_core.egg-info/PKG-INFO +1 -1
- {pyagentic_core-2.2.0 → pyagentic_core-2.2.0a1}/pyproject.toml +1 -1
- {pyagentic_core-2.2.0 → pyagentic_core-2.2.0a1}/LICENSE +0 -0
- {pyagentic_core-2.2.0 → pyagentic_core-2.2.0a1}/README.md +0 -0
- {pyagentic_core-2.2.0 → pyagentic_core-2.2.0a1}/pyagentic/__init__.py +0 -0
- {pyagentic_core-2.2.0 → pyagentic_core-2.2.0a1}/pyagentic/_base/__init__.py +0 -0
- {pyagentic_core-2.2.0 → pyagentic_core-2.2.0a1}/pyagentic/_base/_agent/__init__.py +0 -0
- {pyagentic_core-2.2.0 → pyagentic_core-2.2.0a1}/pyagentic/_base/_agent/_agent_linking.py +0 -0
- {pyagentic_core-2.2.0 → pyagentic_core-2.2.0a1}/pyagentic/_base/_exceptions.py +0 -0
- {pyagentic_core-2.2.0 → pyagentic_core-2.2.0a1}/pyagentic/_base/_info.py +0 -0
- {pyagentic_core-2.2.0 → pyagentic_core-2.2.0a1}/pyagentic/_base/_metaclasses.py +0 -0
- {pyagentic_core-2.2.0 → pyagentic_core-2.2.0a1}/pyagentic/_base/_ref.py +0 -0
- {pyagentic_core-2.2.0 → pyagentic_core-2.2.0a1}/pyagentic/_base/_spec.py +0 -0
- {pyagentic_core-2.2.0 → pyagentic_core-2.2.0a1}/pyagentic/_base/_state.py +0 -0
- {pyagentic_core-2.2.0 → pyagentic_core-2.2.0a1}/pyagentic/_base/_validation.py +0 -0
- {pyagentic_core-2.2.0 → pyagentic_core-2.2.0a1}/pyagentic/_utils/_typing.py +0 -0
- {pyagentic_core-2.2.0 → pyagentic_core-2.2.0a1}/pyagentic/_utils/_warnings.py +0 -0
- {pyagentic_core-2.2.0 → pyagentic_core-2.2.0a1}/pyagentic/llm/__init__.py +0 -0
- {pyagentic_core-2.2.0 → pyagentic_core-2.2.0a1}/pyagentic/llm/_anthropic.py +0 -0
- {pyagentic_core-2.2.0 → pyagentic_core-2.2.0a1}/pyagentic/llm/_gemini.py +0 -0
- {pyagentic_core-2.2.0 → pyagentic_core-2.2.0a1}/pyagentic/llm/_mock.py +0 -0
- {pyagentic_core-2.2.0 → pyagentic_core-2.2.0a1}/pyagentic/llm/_openai.py +0 -0
- {pyagentic_core-2.2.0 → pyagentic_core-2.2.0a1}/pyagentic/llm/_openaiv1.py +0 -0
- {pyagentic_core-2.2.0 → pyagentic_core-2.2.0a1}/pyagentic/llm/_provider.py +0 -0
- {pyagentic_core-2.2.0 → pyagentic_core-2.2.0a1}/pyagentic/logging.py +0 -0
- {pyagentic_core-2.2.0 → pyagentic_core-2.2.0a1}/pyagentic/models/llm.py +0 -0
- {pyagentic_core-2.2.0 → pyagentic_core-2.2.0a1}/pyagentic/models/response.py +0 -0
- {pyagentic_core-2.2.0 → pyagentic_core-2.2.0a1}/pyagentic/models/tracing.py +0 -0
- {pyagentic_core-2.2.0 → pyagentic_core-2.2.0a1}/pyagentic/policies/__init__.py +0 -0
- {pyagentic_core-2.2.0 → pyagentic_core-2.2.0a1}/pyagentic/policies/_events.py +0 -0
- {pyagentic_core-2.2.0 → pyagentic_core-2.2.0a1}/pyagentic/policies/_policy.py +0 -0
- {pyagentic_core-2.2.0 → pyagentic_core-2.2.0a1}/pyagentic/tracing/__init__.py +0 -0
- {pyagentic_core-2.2.0 → pyagentic_core-2.2.0a1}/pyagentic/tracing/_basic.py +0 -0
- {pyagentic_core-2.2.0 → pyagentic_core-2.2.0a1}/pyagentic/tracing/_langfuse.py +0 -0
- {pyagentic_core-2.2.0 → pyagentic_core-2.2.0a1}/pyagentic/tracing/_tracer.py +0 -0
- {pyagentic_core-2.2.0 → pyagentic_core-2.2.0a1}/pyagentic/updates.py +0 -0
- {pyagentic_core-2.2.0 → pyagentic_core-2.2.0a1}/pyagentic_core.egg-info/SOURCES.txt +0 -0
- {pyagentic_core-2.2.0 → pyagentic_core-2.2.0a1}/pyagentic_core.egg-info/dependency_links.txt +0 -0
- {pyagentic_core-2.2.0 → pyagentic_core-2.2.0a1}/pyagentic_core.egg-info/requires.txt +0 -0
- {pyagentic_core-2.2.0 → pyagentic_core-2.2.0a1}/pyagentic_core.egg-info/top_level.txt +0 -0
- {pyagentic_core-2.2.0 → pyagentic_core-2.2.0a1}/setup.cfg +0 -0
|
@@ -169,6 +169,7 @@ class BaseAgent(metaclass=AgentMeta):
|
|
|
169
169
|
phases: ClassVar[list[tuple[str, str, Callable]]] = None
|
|
170
170
|
|
|
171
171
|
# Generated Class Attributes (built by metaclass)
|
|
172
|
+
__machine__: Machine | None = None
|
|
172
173
|
__response_model__: ClassVar[Type[AgentResponse]] = None # Pydantic response model
|
|
173
174
|
__state_class__: ClassVar[Type[_AgentState]] = None # Generated state class
|
|
174
175
|
__tool_response_models__: ClassVar[dict[str, Type[ToolResponse]]] # Tool response models
|
|
@@ -231,6 +232,34 @@ class BaseAgent(metaclass=AgentMeta):
|
|
|
231
232
|
if self.__tool_defs__ and not self.provider.__supports_tool_calls__:
|
|
232
233
|
raise Exception("Tools are not supported with this provider")
|
|
233
234
|
|
|
235
|
+
def _build_phase_machine(self) -> Machine:
|
|
236
|
+
if not self.phases:
|
|
237
|
+
return None
|
|
238
|
+
|
|
239
|
+
states = []
|
|
240
|
+
for to_, from_, _ in self.phases:
|
|
241
|
+
if to_ not in states:
|
|
242
|
+
states.append(to_)
|
|
243
|
+
if from_ not in states:
|
|
244
|
+
states.append(from_)
|
|
245
|
+
|
|
246
|
+
machine = Machine(states=states, initial=states[0])
|
|
247
|
+
|
|
248
|
+
for to_, from_, _ in self.phases:
|
|
249
|
+
machine.add_transition(
|
|
250
|
+
trigger=f"{to_}_to_{from_}",
|
|
251
|
+
source=to_,
|
|
252
|
+
dest=from_,
|
|
253
|
+
)
|
|
254
|
+
|
|
255
|
+
return machine
|
|
256
|
+
|
|
257
|
+
def _update_state_machine(self):
|
|
258
|
+
for to_, from_, condition in self.phases:
|
|
259
|
+
if condition(self.state):
|
|
260
|
+
trigger = f"{to_}_to_{from_}"
|
|
261
|
+
getattr(self.__machine__, trigger)()
|
|
262
|
+
|
|
234
263
|
def __post_init__(self):
|
|
235
264
|
"""
|
|
236
265
|
Post-initialization hook called after agent instance is created.
|
|
@@ -252,9 +281,7 @@ class BaseAgent(metaclass=AgentMeta):
|
|
|
252
281
|
```
|
|
253
282
|
"""
|
|
254
283
|
self._check_llm_provider()
|
|
255
|
-
|
|
256
|
-
if self.phases:
|
|
257
|
-
self.state._build_phase_machine(self.phases)
|
|
284
|
+
self.__machine__ = self._build_phase_machine()
|
|
258
285
|
|
|
259
286
|
# Use BasicTracer as default if no tracer provided
|
|
260
287
|
if not self.tracer:
|
|
@@ -320,14 +347,7 @@ class BaseAgent(metaclass=AgentMeta):
|
|
|
320
347
|
self.state._messages.append(
|
|
321
348
|
Message(role="assistant", content="Failed to generate a response")
|
|
322
349
|
)
|
|
323
|
-
response =
|
|
324
|
-
text=f"The LLM failed to generate a response: {e}", tool_calls=[]
|
|
325
|
-
)
|
|
326
|
-
|
|
327
|
-
if self.phases:
|
|
328
|
-
self.state._update_state_machine(phases=self.phases)
|
|
329
|
-
|
|
330
|
-
return response
|
|
350
|
+
return LLMResponse(text=f"The LLM failed to generate a response: {e}", tool_calls=[])
|
|
331
351
|
|
|
332
352
|
@traced(SpanKind.AGENT)
|
|
333
353
|
async def _process_agent_call(self, tool_call: ToolCall) -> AgentResponse:
|
|
@@ -374,8 +394,6 @@ class BaseAgent(metaclass=AgentMeta):
|
|
|
374
394
|
self.state._messages.append(
|
|
375
395
|
self.provider.to_tool_call_result_message(result=stringified_result, id_=tool_call.id)
|
|
376
396
|
)
|
|
377
|
-
if self.phases:
|
|
378
|
-
self.state._update_state_machine(phases=self.phases)
|
|
379
397
|
return response
|
|
380
398
|
|
|
381
399
|
@traced(SpanKind.TOOL)
|
|
@@ -440,8 +458,6 @@ class BaseAgent(metaclass=AgentMeta):
|
|
|
440
458
|
self.provider.to_tool_call_result_message(result=stringified_result, id_=tool_call.id)
|
|
441
459
|
)
|
|
442
460
|
|
|
443
|
-
if self.phases:
|
|
444
|
-
self.state._update_state_machine(phases=self.phases)
|
|
445
461
|
# Build and return the structured tool response
|
|
446
462
|
ToolResponseModel = self.__tool_response_models__[tool_call.name]
|
|
447
463
|
return ToolResponseModel(
|
|
@@ -463,12 +479,7 @@ class BaseAgent(metaclass=AgentMeta):
|
|
|
463
479
|
# Add all @tool decorated methods
|
|
464
480
|
for tool_def in self.__tool_defs__.values():
|
|
465
481
|
# Resolve StateRefs in parameters (e.g., ref.self.user_name -> actual value)
|
|
466
|
-
|
|
467
|
-
if self.phases and tool_def.phases:
|
|
468
|
-
if self.state.phase in tool_def.phases:
|
|
469
|
-
tool_defs.append(tool_def.resolve(self.agent_reference))
|
|
470
|
-
else:
|
|
471
|
-
tool_defs.append(tool_def.resolve(self.agent_reference))
|
|
482
|
+
tool_defs.append(tool_def.resolve(self.agent_reference))
|
|
472
483
|
|
|
473
484
|
# Add linked agents as tools
|
|
474
485
|
for name, linked_def in self.__linked_agents__.items():
|
|
@@ -568,6 +579,7 @@ class BaseAgent(metaclass=AgentMeta):
|
|
|
568
579
|
yield result
|
|
569
580
|
|
|
570
581
|
# Increment depth and continue loop (LLM will see tool results next iteration)
|
|
582
|
+
self._update_state_machine()
|
|
571
583
|
depth += 1
|
|
572
584
|
|
|
573
585
|
# If we exhausted max_call_depth without final text, get one more response
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import asyncio
|
|
2
2
|
import threading
|
|
3
3
|
from typing import Any, Type, Self, Optional, ClassVar
|
|
4
|
-
from pydantic import BaseModel, create_model, Field, PrivateAttr,
|
|
4
|
+
from pydantic import BaseModel, create_model, Field, PrivateAttr, ConfigDict
|
|
5
5
|
from jinja2 import Template
|
|
6
6
|
from typing import Optional, Literal, Callable
|
|
7
7
|
from transitions import Machine
|
|
@@ -31,41 +31,12 @@ class _AgentState(BaseModel):
|
|
|
31
31
|
|
|
32
32
|
instructions: str
|
|
33
33
|
input_template: Optional[str] = "{{ user_message }}"
|
|
34
|
-
_machine: Machine = PrivateAttr(default=None)
|
|
35
34
|
_messages: list[Message] = PrivateAttr(default_factory=list)
|
|
36
35
|
_instructions_template: Template = PrivateAttr(default_factory=lambda: Template(source=""))
|
|
37
36
|
_input_template: Template = PrivateAttr(
|
|
38
37
|
default_factory=lambda: Template(source="{{ user_message }}")
|
|
39
38
|
)
|
|
40
39
|
|
|
41
|
-
def _build_phase_machine(self, phases: list[tuple[str, str, Callable]]) -> Machine:
|
|
42
|
-
if not phases:
|
|
43
|
-
return None
|
|
44
|
-
|
|
45
|
-
states = []
|
|
46
|
-
for source, dest, _ in phases:
|
|
47
|
-
if source not in states:
|
|
48
|
-
states.append(source)
|
|
49
|
-
if dest not in states:
|
|
50
|
-
states.append(dest)
|
|
51
|
-
|
|
52
|
-
machine = Machine(states=states, initial=states[0])
|
|
53
|
-
|
|
54
|
-
for source, dest, _ in phases:
|
|
55
|
-
machine.add_transition(
|
|
56
|
-
trigger=f"{source}_to_{dest}",
|
|
57
|
-
source=source,
|
|
58
|
-
dest=dest,
|
|
59
|
-
)
|
|
60
|
-
|
|
61
|
-
self._machine = machine
|
|
62
|
-
|
|
63
|
-
def _update_state_machine(self, phases):
|
|
64
|
-
for to_, from_, condition in phases:
|
|
65
|
-
if condition(self):
|
|
66
|
-
trigger = f"{to_}_to_{from_}"
|
|
67
|
-
getattr(self._machine, trigger)()
|
|
68
|
-
|
|
69
40
|
def model_post_init(self, state):
|
|
70
41
|
self._instructions_template = Template(source=self.instructions)
|
|
71
42
|
if self.input_template:
|
|
@@ -251,10 +222,6 @@ class _AgentState(BaseModel):
|
|
|
251
222
|
# now build the dataclass
|
|
252
223
|
return create_model(f"AgentState[{name}]", __base__=cls, **pydantic_fields)
|
|
253
224
|
|
|
254
|
-
@property
|
|
255
|
-
def phase(self) -> str:
|
|
256
|
-
return self._machine.state if self._machine else None
|
|
257
|
-
|
|
258
225
|
@property
|
|
259
226
|
def recent_message(self) -> Message:
|
|
260
227
|
"""
|
|
@@ -276,10 +243,7 @@ class _AgentState(BaseModel):
|
|
|
276
243
|
# start with all the normal dataclass fields
|
|
277
244
|
|
|
278
245
|
# now format your instruction template
|
|
279
|
-
|
|
280
|
-
return self._instructions_template.render(phase=self.phase, **self.model_dump())
|
|
281
|
-
else:
|
|
282
|
-
return self._instructions_template.render(**self.model_dump())
|
|
246
|
+
return self._instructions_template.render(**self.model_dump())
|
|
283
247
|
|
|
284
248
|
@property
|
|
285
249
|
def messages(self) -> list[Message]:
|
|
@@ -307,10 +271,7 @@ class _AgentState(BaseModel):
|
|
|
307
271
|
if self.input_template:
|
|
308
272
|
data = self.model_dump()
|
|
309
273
|
data["user_message"] = message
|
|
310
|
-
|
|
311
|
-
content = self._input_template.render(phase=self.phase, **self.model_dump())
|
|
312
|
-
else:
|
|
313
|
-
content = self._input_template.render(**self.model_dump())
|
|
274
|
+
content = self._input_template.render(**data)
|
|
314
275
|
else:
|
|
315
276
|
content = message
|
|
316
277
|
self._messages.append(Message(role="user", content=content))
|
|
@@ -43,14 +43,12 @@ class _ToolDefinition:
|
|
|
43
43
|
parameters: dict[str, tuple[TypeVar, ParamInfo]],
|
|
44
44
|
return_type: Type[Any],
|
|
45
45
|
condition: Callable[[Any], bool] = None,
|
|
46
|
-
phases: list[str] = None,
|
|
47
46
|
):
|
|
48
47
|
self.name: str = name
|
|
49
48
|
self.description: str = description
|
|
50
49
|
self.parameters: dict[str, tuple[TypeVar, ParamInfo]] = parameters
|
|
51
50
|
self.condition = condition
|
|
52
51
|
self.return_type = return_type
|
|
53
|
-
self.phases = phases if phases else []
|
|
54
52
|
|
|
55
53
|
def resolve(self, agent_reference: dict) -> Self:
|
|
56
54
|
new_parameters = {}
|
|
@@ -69,7 +67,6 @@ class _ToolDefinition:
|
|
|
69
67
|
parameters=new_parameters,
|
|
70
68
|
condition=self.condition,
|
|
71
69
|
return_type=self.return_type,
|
|
72
|
-
phases=self.phases,
|
|
73
70
|
)
|
|
74
71
|
|
|
75
72
|
def to_openai_spec(self) -> dict:
|
|
@@ -213,7 +210,10 @@ class _ToolDefinition:
|
|
|
213
210
|
return compiled_args
|
|
214
211
|
|
|
215
212
|
|
|
216
|
-
def tool(
|
|
213
|
+
def tool(
|
|
214
|
+
description: str,
|
|
215
|
+
condition: Callable[[Any], bool] = None,
|
|
216
|
+
):
|
|
217
217
|
"""
|
|
218
218
|
Decorator to mark an agent method as a tool that the LLM can call.
|
|
219
219
|
|
|
@@ -281,7 +281,6 @@ def tool(description: str, condition: Callable[[Any], bool] = None, phases: list
|
|
|
281
281
|
parameters=params,
|
|
282
282
|
condition=condition,
|
|
283
283
|
return_type=return_type,
|
|
284
|
-
phases=phases,
|
|
285
284
|
)
|
|
286
285
|
return fn
|
|
287
286
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pyagentic_core-2.2.0 → pyagentic_core-2.2.0a1}/pyagentic_core.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|