hammad-python 0.0.29__py3-none-any.whl → 0.0.31__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.
- ham/__init__.py +10 -0
- {hammad_python-0.0.29.dist-info → hammad_python-0.0.31.dist-info}/METADATA +6 -32
- hammad_python-0.0.31.dist-info/RECORD +6 -0
- hammad/__init__.py +0 -84
- hammad/_internal.py +0 -256
- hammad/_main.py +0 -226
- hammad/cache/__init__.py +0 -40
- hammad/cache/base_cache.py +0 -181
- hammad/cache/cache.py +0 -169
- hammad/cache/decorators.py +0 -261
- hammad/cache/file_cache.py +0 -80
- hammad/cache/ttl_cache.py +0 -74
- hammad/cli/__init__.py +0 -33
- hammad/cli/animations.py +0 -573
- hammad/cli/plugins.py +0 -867
- hammad/cli/styles/__init__.py +0 -55
- hammad/cli/styles/settings.py +0 -139
- hammad/cli/styles/types.py +0 -358
- hammad/cli/styles/utils.py +0 -634
- hammad/data/__init__.py +0 -90
- hammad/data/collections/__init__.py +0 -49
- hammad/data/collections/collection.py +0 -326
- hammad/data/collections/indexes/__init__.py +0 -37
- hammad/data/collections/indexes/qdrant/__init__.py +0 -1
- hammad/data/collections/indexes/qdrant/index.py +0 -723
- hammad/data/collections/indexes/qdrant/settings.py +0 -94
- hammad/data/collections/indexes/qdrant/utils.py +0 -210
- hammad/data/collections/indexes/tantivy/__init__.py +0 -1
- hammad/data/collections/indexes/tantivy/index.py +0 -426
- hammad/data/collections/indexes/tantivy/settings.py +0 -40
- hammad/data/collections/indexes/tantivy/utils.py +0 -176
- hammad/data/configurations/__init__.py +0 -35
- hammad/data/configurations/configuration.py +0 -564
- hammad/data/models/__init__.py +0 -50
- hammad/data/models/extensions/__init__.py +0 -4
- hammad/data/models/extensions/pydantic/__init__.py +0 -42
- hammad/data/models/extensions/pydantic/converters.py +0 -759
- hammad/data/models/fields.py +0 -546
- hammad/data/models/model.py +0 -1078
- hammad/data/models/utils.py +0 -280
- hammad/data/sql/__init__.py +0 -24
- hammad/data/sql/database.py +0 -576
- hammad/data/sql/types.py +0 -127
- hammad/data/types/__init__.py +0 -75
- hammad/data/types/file.py +0 -431
- hammad/data/types/multimodal/__init__.py +0 -36
- hammad/data/types/multimodal/audio.py +0 -200
- hammad/data/types/multimodal/image.py +0 -182
- hammad/data/types/text.py +0 -1308
- hammad/formatting/__init__.py +0 -33
- hammad/formatting/json/__init__.py +0 -27
- hammad/formatting/json/converters.py +0 -158
- hammad/formatting/text/__init__.py +0 -63
- hammad/formatting/text/converters.py +0 -723
- hammad/formatting/text/markdown.py +0 -131
- hammad/formatting/yaml/__init__.py +0 -26
- hammad/formatting/yaml/converters.py +0 -5
- hammad/genai/__init__.py +0 -217
- hammad/genai/a2a/__init__.py +0 -32
- hammad/genai/a2a/workers.py +0 -552
- hammad/genai/agents/__init__.py +0 -59
- hammad/genai/agents/agent.py +0 -1973
- hammad/genai/agents/run.py +0 -1024
- hammad/genai/agents/types/__init__.py +0 -42
- hammad/genai/agents/types/agent_context.py +0 -13
- hammad/genai/agents/types/agent_event.py +0 -128
- hammad/genai/agents/types/agent_hooks.py +0 -220
- hammad/genai/agents/types/agent_messages.py +0 -31
- hammad/genai/agents/types/agent_response.py +0 -125
- hammad/genai/agents/types/agent_stream.py +0 -327
- hammad/genai/graphs/__init__.py +0 -125
- hammad/genai/graphs/_utils.py +0 -190
- hammad/genai/graphs/base.py +0 -1828
- hammad/genai/graphs/plugins.py +0 -316
- hammad/genai/graphs/types.py +0 -638
- hammad/genai/models/__init__.py +0 -1
- hammad/genai/models/embeddings/__init__.py +0 -43
- hammad/genai/models/embeddings/model.py +0 -226
- hammad/genai/models/embeddings/run.py +0 -163
- hammad/genai/models/embeddings/types/__init__.py +0 -37
- hammad/genai/models/embeddings/types/embedding_model_name.py +0 -75
- hammad/genai/models/embeddings/types/embedding_model_response.py +0 -76
- hammad/genai/models/embeddings/types/embedding_model_run_params.py +0 -66
- hammad/genai/models/embeddings/types/embedding_model_settings.py +0 -47
- hammad/genai/models/language/__init__.py +0 -57
- hammad/genai/models/language/model.py +0 -1098
- hammad/genai/models/language/run.py +0 -878
- hammad/genai/models/language/types/__init__.py +0 -40
- hammad/genai/models/language/types/language_model_instructor_mode.py +0 -47
- hammad/genai/models/language/types/language_model_messages.py +0 -28
- hammad/genai/models/language/types/language_model_name.py +0 -239
- hammad/genai/models/language/types/language_model_request.py +0 -127
- hammad/genai/models/language/types/language_model_response.py +0 -217
- hammad/genai/models/language/types/language_model_response_chunk.py +0 -56
- hammad/genai/models/language/types/language_model_settings.py +0 -89
- hammad/genai/models/language/types/language_model_stream.py +0 -600
- hammad/genai/models/language/utils/__init__.py +0 -28
- hammad/genai/models/language/utils/requests.py +0 -421
- hammad/genai/models/language/utils/structured_outputs.py +0 -135
- hammad/genai/models/model_provider.py +0 -4
- hammad/genai/models/multimodal.py +0 -47
- hammad/genai/models/reranking.py +0 -26
- hammad/genai/types/__init__.py +0 -1
- hammad/genai/types/base.py +0 -215
- hammad/genai/types/history.py +0 -290
- hammad/genai/types/tools.py +0 -507
- hammad/logging/__init__.py +0 -35
- hammad/logging/decorators.py +0 -834
- hammad/logging/logger.py +0 -1018
- hammad/mcp/__init__.py +0 -53
- hammad/mcp/client/__init__.py +0 -35
- hammad/mcp/client/client.py +0 -624
- hammad/mcp/client/client_service.py +0 -400
- hammad/mcp/client/settings.py +0 -178
- hammad/mcp/servers/__init__.py +0 -26
- hammad/mcp/servers/launcher.py +0 -1161
- hammad/runtime/__init__.py +0 -32
- hammad/runtime/decorators.py +0 -142
- hammad/runtime/run.py +0 -299
- hammad/service/__init__.py +0 -49
- hammad/service/create.py +0 -527
- hammad/service/decorators.py +0 -283
- hammad/types.py +0 -288
- hammad/typing/__init__.py +0 -435
- hammad/web/__init__.py +0 -43
- hammad/web/http/__init__.py +0 -1
- hammad/web/http/client.py +0 -944
- hammad/web/models.py +0 -275
- hammad/web/openapi/__init__.py +0 -1
- hammad/web/openapi/client.py +0 -740
- hammad/web/search/__init__.py +0 -1
- hammad/web/search/client.py +0 -1023
- hammad/web/utils.py +0 -472
- hammad_python-0.0.29.dist-info/RECORD +0 -135
- {hammad → ham}/py.typed +0 -0
- {hammad_python-0.0.29.dist-info → hammad_python-0.0.31.dist-info}/WHEEL +0 -0
- {hammad_python-0.0.29.dist-info → hammad_python-0.0.31.dist-info}/licenses/LICENSE +0 -0
hammad/genai/agents/agent.py
DELETED
@@ -1,1973 +0,0 @@
|
|
1
|
-
"""hammad.genai.agents.agent"""
|
2
|
-
|
3
|
-
from typing import (
|
4
|
-
Any,
|
5
|
-
Callable,
|
6
|
-
Generic,
|
7
|
-
Literal,
|
8
|
-
List,
|
9
|
-
Type,
|
10
|
-
TypeVar,
|
11
|
-
Optional,
|
12
|
-
Union,
|
13
|
-
Dict,
|
14
|
-
TypeAlias,
|
15
|
-
overload,
|
16
|
-
TYPE_CHECKING,
|
17
|
-
)
|
18
|
-
from pydantic import BaseModel, Field, create_model
|
19
|
-
from dataclasses import dataclass, field
|
20
|
-
from enum import Enum
|
21
|
-
import json
|
22
|
-
|
23
|
-
from ...logging.logger import _get_internal_logger
|
24
|
-
|
25
|
-
from ..types.base import BaseGenAIModel, BaseGenAIModelSettings
|
26
|
-
from ..models.language.model import LanguageModel
|
27
|
-
from ..models.language.types import (
|
28
|
-
LanguageModelResponse,
|
29
|
-
LanguageModelName,
|
30
|
-
LanguageModelInstructorMode,
|
31
|
-
)
|
32
|
-
from ..types.tools import (
|
33
|
-
Tool,
|
34
|
-
define_tool,
|
35
|
-
execute_tools_from_language_model_response,
|
36
|
-
)
|
37
|
-
from ..models.language.utils.requests import (
|
38
|
-
parse_messages_input as parse_messages,
|
39
|
-
consolidate_system_messages,
|
40
|
-
)
|
41
|
-
from ...formatting.text.converters import convert_to_text
|
42
|
-
from .types.agent_response import (
|
43
|
-
AgentResponse,
|
44
|
-
_create_agent_response_from_language_model_response,
|
45
|
-
)
|
46
|
-
from .types.agent_stream import AgentStream
|
47
|
-
from .types.agent_context import AgentContext
|
48
|
-
from .types.agent_event import AgentEvent
|
49
|
-
from .types.agent_hooks import HookManager, HookDecorator
|
50
|
-
from .types.agent_messages import AgentMessages
|
51
|
-
|
52
|
-
if TYPE_CHECKING:
|
53
|
-
try:
|
54
|
-
from fasta2a import FastA2A
|
55
|
-
except ImportError:
|
56
|
-
FastA2A: TypeAlias = Any
|
57
|
-
|
58
|
-
|
59
|
-
T = TypeVar("T")
|
60
|
-
|
61
|
-
|
62
|
-
logger = _get_internal_logger(__name__)
|
63
|
-
|
64
|
-
|
65
|
-
@dataclass
|
66
|
-
class AgentSettings:
|
67
|
-
"""Settings object that controls the default behavior of an agent's run."""
|
68
|
-
|
69
|
-
max_steps: int = field(default=10)
|
70
|
-
"""The maximum amount of steps the agent can take before stopping."""
|
71
|
-
|
72
|
-
add_name_to_instructions: bool = field(default=True)
|
73
|
-
"""Whether to add the agent name to the instructions."""
|
74
|
-
|
75
|
-
context_format: Literal["json", "python", "markdown"] = field(default="json")
|
76
|
-
"""Format for context in instructions."""
|
77
|
-
|
78
|
-
# Context management settings
|
79
|
-
context_updates: Optional[
|
80
|
-
Union[List[Literal["before", "after"]], Literal["before", "after"]]
|
81
|
-
] = field(default=None)
|
82
|
-
"""When to update context ('before', 'after', or both)."""
|
83
|
-
|
84
|
-
context_confirm: bool = field(default=False)
|
85
|
-
"""Whether to confirm context updates."""
|
86
|
-
|
87
|
-
context_strategy: Literal["selective", "all"] = field(default="all")
|
88
|
-
"""Strategy for context updates."""
|
89
|
-
|
90
|
-
context_max_retries: int = field(default=3)
|
91
|
-
"""Maximum retries for context updates."""
|
92
|
-
|
93
|
-
context_confirm_instructions: Optional[str] = field(default=None)
|
94
|
-
"""Custom instructions for context confirmation."""
|
95
|
-
|
96
|
-
context_selection_instructions: Optional[str] = field(default=None)
|
97
|
-
"""Custom instructions for context selection."""
|
98
|
-
|
99
|
-
context_update_instructions: Optional[str] = field(default=None)
|
100
|
-
"""Custom instructions for context updates."""
|
101
|
-
|
102
|
-
|
103
|
-
class AgentModelSettings(BaseGenAIModelSettings):
|
104
|
-
"""Agent-specific model settings that extend the base model settings."""
|
105
|
-
|
106
|
-
instructor_mode: Optional[LanguageModelInstructorMode] = None
|
107
|
-
"""Instructor mode for structured outputs."""
|
108
|
-
|
109
|
-
max_steps: int = 10
|
110
|
-
"""Maximum number of steps the agent can take."""
|
111
|
-
|
112
|
-
add_name_to_instructions: bool = True
|
113
|
-
"""Whether to add the agent name to the instructions."""
|
114
|
-
|
115
|
-
context_format: Literal["json", "python", "markdown"] = "json"
|
116
|
-
"""Format for context in instructions."""
|
117
|
-
|
118
|
-
# Context management settings
|
119
|
-
context_updates: Optional[
|
120
|
-
Union[List[Literal["before", "after"]], Literal["before", "after"]]
|
121
|
-
] = None
|
122
|
-
"""When to update context ('before', 'after', or both)."""
|
123
|
-
|
124
|
-
context_confirm: bool = False
|
125
|
-
"""Whether to confirm context updates."""
|
126
|
-
|
127
|
-
context_strategy: Literal["selective", "all"] = "all"
|
128
|
-
"""Strategy for context updates."""
|
129
|
-
|
130
|
-
context_max_retries: int = 3
|
131
|
-
"""Maximum retries for context updates."""
|
132
|
-
|
133
|
-
context_confirm_instructions: Optional[str] = None
|
134
|
-
"""Custom instructions for context confirmation."""
|
135
|
-
|
136
|
-
context_selection_instructions: Optional[str] = None
|
137
|
-
"""Custom instructions for context selection."""
|
138
|
-
|
139
|
-
context_update_instructions: Optional[str] = None
|
140
|
-
"""Custom instructions for context updates."""
|
141
|
-
|
142
|
-
|
143
|
-
def _build_tools(tools: List[Tool] | Callable | None) -> List[Tool]:
|
144
|
-
"""Builds a list of tools from a list of tools or a callable that returns a list of tools."""
|
145
|
-
if tools is None:
|
146
|
-
return []
|
147
|
-
if callable(tools):
|
148
|
-
return [define_tool(tools)]
|
149
|
-
|
150
|
-
processed_tools = []
|
151
|
-
for tool in tools:
|
152
|
-
if not isinstance(tool, Tool):
|
153
|
-
tool = define_tool(tool)
|
154
|
-
processed_tools.append(tool)
|
155
|
-
|
156
|
-
return processed_tools
|
157
|
-
|
158
|
-
|
159
|
-
def _get_instructions(
|
160
|
-
name: str,
|
161
|
-
instructions: Optional[str],
|
162
|
-
add_name_to_instructions: bool,
|
163
|
-
) -> Optional[str]:
|
164
|
-
"""Gets the instructions for an agent."""
|
165
|
-
if add_name_to_instructions and name:
|
166
|
-
base_instructions = instructions or ""
|
167
|
-
return f"You are {name}.\n\n{base_instructions}".strip()
|
168
|
-
return instructions
|
169
|
-
|
170
|
-
|
171
|
-
def _format_context_for_instructions(
|
172
|
-
context: AgentContext | None,
|
173
|
-
context_format: Literal["json", "python", "markdown"] = "json",
|
174
|
-
) -> str:
|
175
|
-
"""Format context object for inclusion in instructions."""
|
176
|
-
if context is None:
|
177
|
-
return ""
|
178
|
-
|
179
|
-
if context_format == "json":
|
180
|
-
if isinstance(context, BaseModel):
|
181
|
-
return context.model_dump_json(indent=2)
|
182
|
-
elif isinstance(context, dict):
|
183
|
-
return json.dumps(context, indent=2)
|
184
|
-
else:
|
185
|
-
return json.dumps(str(context), indent=2)
|
186
|
-
|
187
|
-
elif context_format == "python":
|
188
|
-
if hasattr(context, "__repr__"):
|
189
|
-
return repr(context)
|
190
|
-
elif hasattr(context, "__str__"):
|
191
|
-
return str(context)
|
192
|
-
else:
|
193
|
-
return str(context)
|
194
|
-
|
195
|
-
elif context_format == "markdown":
|
196
|
-
return convert_to_text(context)
|
197
|
-
|
198
|
-
return str(context)
|
199
|
-
|
200
|
-
|
201
|
-
def _update_context_object(
|
202
|
-
context: AgentContext, updates: Dict[str, Any]
|
203
|
-
) -> AgentContext:
|
204
|
-
"""Update a context object with new values."""
|
205
|
-
if isinstance(context, BaseModel):
|
206
|
-
# For Pydantic models, create a copy with updated values
|
207
|
-
return context.model_copy(update=updates)
|
208
|
-
elif isinstance(context, dict):
|
209
|
-
# For dictionaries, update in place
|
210
|
-
updated_context = context.copy()
|
211
|
-
updated_context.update(updates)
|
212
|
-
return updated_context
|
213
|
-
else:
|
214
|
-
raise ValueError(f"Cannot update context of type {type(context)}")
|
215
|
-
|
216
|
-
|
217
|
-
def mark_complete() -> None:
|
218
|
-
"""If you feel you are ready to respond to the user, or have completed
|
219
|
-
the task given to you, call this function to mark your response as
|
220
|
-
complete."""
|
221
|
-
return "complete"
|
222
|
-
|
223
|
-
|
224
|
-
class Agent(BaseGenAIModel, Generic[T]):
|
225
|
-
"""A generative AI agent that can execute tools, generate structured outputs,
|
226
|
-
and maintain context across multiple conversation steps.
|
227
|
-
"""
|
228
|
-
|
229
|
-
model: LanguageModelName = "openai/gpt-4o-mini"
|
230
|
-
"""The language model to use for the agent."""
|
231
|
-
|
232
|
-
name: str = "agent"
|
233
|
-
"""The name of the agent."""
|
234
|
-
|
235
|
-
description: Optional[str] = None
|
236
|
-
"""A description of the agent."""
|
237
|
-
|
238
|
-
instructions: Optional[str] = None
|
239
|
-
"""System instructions for the agent."""
|
240
|
-
|
241
|
-
tools: List[Tool] = Field(default_factory=list)
|
242
|
-
"""List of tools available to the agent."""
|
243
|
-
|
244
|
-
settings: AgentSettings = Field(default_factory=AgentSettings)
|
245
|
-
"""Agent-specific settings."""
|
246
|
-
|
247
|
-
instructor_mode: Optional[LanguageModelInstructorMode] = None
|
248
|
-
"""Instructor mode for structured outputs."""
|
249
|
-
|
250
|
-
def __init__(
|
251
|
-
self,
|
252
|
-
name: str = "agent",
|
253
|
-
instructions: Optional[str] = None,
|
254
|
-
model: Union[LanguageModel, LanguageModelName] = "openai/gpt-4o-mini",
|
255
|
-
description: Optional[str] = None,
|
256
|
-
tools: Union[List[Tool], Callable, None] = None,
|
257
|
-
settings: Optional[AgentSettings] = None,
|
258
|
-
instructor_mode: Optional[LanguageModelInstructorMode] = None,
|
259
|
-
# Defaults
|
260
|
-
max_steps: int = 10,
|
261
|
-
# End Strategy
|
262
|
-
end_strategy: Literal["tool"] | None = None,
|
263
|
-
end_tool: Callable = mark_complete,
|
264
|
-
# Context management parameters
|
265
|
-
context_updates: Optional[
|
266
|
-
Union[List[Literal["before", "after"]], Literal["before", "after"]]
|
267
|
-
] = None,
|
268
|
-
context_confirm: bool = False,
|
269
|
-
context_strategy: Literal["selective", "all"] = "all",
|
270
|
-
context_max_retries: int = 3,
|
271
|
-
context_confirm_instructions: Optional[str] = None,
|
272
|
-
context_selection_instructions: Optional[str] = None,
|
273
|
-
context_update_instructions: Optional[str] = None,
|
274
|
-
context_format: Literal["json", "python", "markdown"] = "json",
|
275
|
-
verbose: bool = False,
|
276
|
-
debug: bool = False,
|
277
|
-
**kwargs: Any,
|
278
|
-
):
|
279
|
-
"""Create a new AI agent with specified capabilities and behavior.
|
280
|
-
|
281
|
-
An agent is an intelligent assistant that can use tools, follow instructions,
|
282
|
-
and maintain context across conversations. It combines a language model with
|
283
|
-
additional capabilities like tool execution and structured output generation.
|
284
|
-
|
285
|
-
Args:
|
286
|
-
name: A human-readable name for the agent (default: "agent")
|
287
|
-
instructions: System instructions that define the agent's behavior and personality
|
288
|
-
model: The language model to use - either a LanguageModel instance or model name string
|
289
|
-
description: Optional description of what the agent does
|
290
|
-
tools: List of tools/functions the agent can call, or a single callable
|
291
|
-
settings: AgentSettings object to customize default behavior
|
292
|
-
instructor_mode: Mode for structured output generation
|
293
|
-
max_steps: Default ,aximum number of steps the agent can take before stopping
|
294
|
-
end_strategy: Optional alternative strategy to provide an end tool for determining agent's final
|
295
|
-
response.
|
296
|
-
end_tool: The tool the agent will call to determine if it should stop.
|
297
|
-
This is only used if end_strategy is set to "tool".
|
298
|
-
context_updates: When to update context - "before", "after", or both
|
299
|
-
context_confirm: Whether to confirm context updates with the user
|
300
|
-
context_strategy: How to select context updates - "selective" or "all"
|
301
|
-
context_max_retries: Maximum attempts for context update operations
|
302
|
-
context_confirm_instructions: Custom instructions for context confirmation
|
303
|
-
context_selection_instructions: Custom instructions for context selection
|
304
|
-
context_update_instructions: Custom instructions for context updates
|
305
|
-
context_format: Format for context display - "json", "python", or "markdown"
|
306
|
-
verbose: If True, set logger to INFO level for detailed output
|
307
|
-
debug: If True, set logger to DEBUG level for maximum verbosity
|
308
|
-
**kwargs: Additional parameters passed to the underlying language model
|
309
|
-
|
310
|
-
Example:
|
311
|
-
Basic agent:
|
312
|
-
>>> agent = Agent(name="assistant", instructions="You are helpful")
|
313
|
-
|
314
|
-
Agent with tools:
|
315
|
-
>>> def calculator(x: int, y: int) -> int:
|
316
|
-
... return x + y
|
317
|
-
>>> agent = Agent(tools=[calculator])
|
318
|
-
|
319
|
-
Agent with custom settings:
|
320
|
-
>>> settings = AgentSettings(max_steps=5)
|
321
|
-
>>> agent = Agent(settings=settings, model="gpt-4")
|
322
|
-
"""
|
323
|
-
# Initialize BaseGenAIModel with basic parameters
|
324
|
-
super().__init__(
|
325
|
-
model=model if isinstance(model, str) else model.model, **kwargs
|
326
|
-
)
|
327
|
-
|
328
|
-
# Agent-specific initialization
|
329
|
-
self.name = name
|
330
|
-
self.description = description
|
331
|
-
self.tools = _build_tools(tools)
|
332
|
-
self.settings = settings or AgentSettings()
|
333
|
-
self.instructor_mode = instructor_mode
|
334
|
-
|
335
|
-
# Store max_steps as instance variable (overrides settings if provided)
|
336
|
-
self.max_steps = max_steps if max_steps is not None else self.settings.max_steps
|
337
|
-
|
338
|
-
# Store end strategy parameters
|
339
|
-
self.end_strategy = end_strategy
|
340
|
-
self.end_tool = end_tool if end_tool is not None else mark_complete
|
341
|
-
|
342
|
-
# Add end_tool to tools if end_strategy is 'tool'
|
343
|
-
if self.end_strategy == "tool":
|
344
|
-
self.tools.append(define_tool(self.end_tool))
|
345
|
-
|
346
|
-
# Process instructions
|
347
|
-
self.instructions = _get_instructions(
|
348
|
-
name=name,
|
349
|
-
instructions=instructions,
|
350
|
-
add_name_to_instructions=self.settings.add_name_to_instructions,
|
351
|
-
)
|
352
|
-
|
353
|
-
# Store verbose/debug settings
|
354
|
-
self.verbose = verbose
|
355
|
-
self.debug = debug
|
356
|
-
|
357
|
-
# Set logger level based on verbose/debug flags
|
358
|
-
if debug:
|
359
|
-
logger.level = "debug"
|
360
|
-
elif verbose:
|
361
|
-
logger.level = "info"
|
362
|
-
|
363
|
-
# Initialize the language model
|
364
|
-
if isinstance(model, LanguageModel):
|
365
|
-
self._language_model = model
|
366
|
-
else:
|
367
|
-
self._language_model = LanguageModel(
|
368
|
-
model=model, verbose=verbose, debug=debug, **kwargs
|
369
|
-
)
|
370
|
-
|
371
|
-
# Context management settings
|
372
|
-
self.context_updates = context_updates
|
373
|
-
self.context_confirm = context_confirm
|
374
|
-
self.context_strategy = context_strategy
|
375
|
-
self.context_max_retries = context_max_retries
|
376
|
-
self.context_confirm_instructions = context_confirm_instructions
|
377
|
-
self.context_selection_instructions = context_selection_instructions
|
378
|
-
self.context_update_instructions = context_update_instructions
|
379
|
-
self.context_format = context_format
|
380
|
-
|
381
|
-
# Hook system
|
382
|
-
self.hook_manager = HookManager()
|
383
|
-
self.on = HookDecorator(self.hook_manager)
|
384
|
-
|
385
|
-
@property
|
386
|
-
def language_model(self) -> LanguageModel:
|
387
|
-
"""Get the underlying language model."""
|
388
|
-
return self._language_model
|
389
|
-
|
390
|
-
def _get_effective_context_settings(
|
391
|
-
self,
|
392
|
-
context_updates: Optional[
|
393
|
-
Union[List[Literal["before", "after"]], Literal["before", "after"]]
|
394
|
-
] = None,
|
395
|
-
context_confirm: Optional[bool] = None,
|
396
|
-
context_strategy: Optional[Literal["selective", "all"]] = None,
|
397
|
-
context_max_retries: Optional[int] = None,
|
398
|
-
context_confirm_instructions: Optional[str] = None,
|
399
|
-
context_selection_instructions: Optional[str] = None,
|
400
|
-
context_update_instructions: Optional[str] = None,
|
401
|
-
context_format: Optional[Literal["json", "python", "markdown"]] = None,
|
402
|
-
) -> dict:
|
403
|
-
"""Get effective context settings, using provided parameters or defaults."""
|
404
|
-
return {
|
405
|
-
"context_updates": context_updates
|
406
|
-
if context_updates is not None
|
407
|
-
else self.context_updates,
|
408
|
-
"context_confirm": context_confirm
|
409
|
-
if context_confirm is not None
|
410
|
-
else self.context_confirm,
|
411
|
-
"context_strategy": context_strategy
|
412
|
-
if context_strategy is not None
|
413
|
-
else self.context_strategy,
|
414
|
-
"context_max_retries": context_max_retries
|
415
|
-
if context_max_retries is not None
|
416
|
-
else self.context_max_retries,
|
417
|
-
"context_confirm_instructions": context_confirm_instructions
|
418
|
-
if context_confirm_instructions is not None
|
419
|
-
else self.context_confirm_instructions,
|
420
|
-
"context_selection_instructions": context_selection_instructions
|
421
|
-
if context_selection_instructions is not None
|
422
|
-
else self.context_selection_instructions,
|
423
|
-
"context_update_instructions": context_update_instructions
|
424
|
-
if context_update_instructions is not None
|
425
|
-
else self.context_update_instructions,
|
426
|
-
"context_format": context_format
|
427
|
-
if context_format is not None
|
428
|
-
else self.context_format,
|
429
|
-
}
|
430
|
-
|
431
|
-
def _should_update_context(
|
432
|
-
self,
|
433
|
-
context: AgentContext,
|
434
|
-
timing: Literal["before", "after"],
|
435
|
-
context_updates=None,
|
436
|
-
) -> bool:
|
437
|
-
"""Determine if context should be updated based on timing and configuration."""
|
438
|
-
effective_context_updates = (
|
439
|
-
context_updates if context_updates is not None else self.context_updates
|
440
|
-
)
|
441
|
-
|
442
|
-
if not effective_context_updates:
|
443
|
-
return False
|
444
|
-
|
445
|
-
if isinstance(effective_context_updates, str):
|
446
|
-
return effective_context_updates == timing
|
447
|
-
else:
|
448
|
-
return timing in effective_context_updates
|
449
|
-
|
450
|
-
def _create_context_confirm_model(self):
|
451
|
-
"""Create IsUpdateRequired model for context confirmation."""
|
452
|
-
return create_model("IsUpdateRequired", decision=(bool, ...))
|
453
|
-
|
454
|
-
def _create_context_selection_model(self, context: AgentContext):
|
455
|
-
"""Create FieldsToUpdate model for selective context updates."""
|
456
|
-
if isinstance(context, BaseModel):
|
457
|
-
field_names = list(context.model_fields.keys())
|
458
|
-
elif isinstance(context, dict):
|
459
|
-
field_names = list(context.keys())
|
460
|
-
else:
|
461
|
-
raise ValueError(
|
462
|
-
f"Cannot create selection model for context type {type(context)}"
|
463
|
-
)
|
464
|
-
|
465
|
-
FieldEnum = Enum("FieldEnum", {name: name for name in field_names})
|
466
|
-
return create_model("FieldsToUpdate", fields=(List[FieldEnum], ...))
|
467
|
-
|
468
|
-
def _create_context_update_model(
|
469
|
-
self, context: AgentContext, field_name: str = None
|
470
|
-
):
|
471
|
-
"""Create update model for context updates."""
|
472
|
-
if field_name:
|
473
|
-
# Single field update
|
474
|
-
if isinstance(context, BaseModel):
|
475
|
-
field_type = context.__class__.model_fields[field_name].annotation
|
476
|
-
field_info = context.__class__.model_fields[field_name]
|
477
|
-
description = getattr(
|
478
|
-
field_info, "description", f"Update the {field_name} field"
|
479
|
-
)
|
480
|
-
elif isinstance(context, dict):
|
481
|
-
field_type = type(context[field_name])
|
482
|
-
description = f"Update the {field_name} field"
|
483
|
-
else:
|
484
|
-
field_type = Any
|
485
|
-
description = f"Update the {field_name} field"
|
486
|
-
|
487
|
-
return create_model(
|
488
|
-
f"Update{field_name.capitalize()}",
|
489
|
-
**{field_name: (field_type, Field(description=description))},
|
490
|
-
)
|
491
|
-
else:
|
492
|
-
# All fields update - create a model with the exact same fields as the context
|
493
|
-
if isinstance(context, BaseModel):
|
494
|
-
# Create a model with the same fields as the context
|
495
|
-
field_definitions = {}
|
496
|
-
for field_name, field_info in context.model_fields.items():
|
497
|
-
field_type = field_info.annotation
|
498
|
-
current_value = getattr(context, field_name)
|
499
|
-
description = getattr(
|
500
|
-
field_info, "description", f"Current value: {current_value}"
|
501
|
-
)
|
502
|
-
field_definitions[field_name] = (
|
503
|
-
field_type,
|
504
|
-
Field(description=description),
|
505
|
-
)
|
506
|
-
|
507
|
-
return create_model("ContextUpdate", **field_definitions)
|
508
|
-
elif isinstance(context, dict):
|
509
|
-
# Create a model with the same keys as the dict
|
510
|
-
field_definitions = {}
|
511
|
-
for key, value in context.items():
|
512
|
-
field_type = type(value)
|
513
|
-
description = f"Current value: {value}"
|
514
|
-
field_definitions[key] = (
|
515
|
-
field_type,
|
516
|
-
Field(description=description),
|
517
|
-
)
|
518
|
-
|
519
|
-
return create_model("ContextUpdate", **field_definitions)
|
520
|
-
else:
|
521
|
-
# Fallback to generic updates
|
522
|
-
return create_model(
|
523
|
-
"ContextUpdate",
|
524
|
-
updates=(
|
525
|
-
Dict[str, Any],
|
526
|
-
Field(description="Dictionary of field updates"),
|
527
|
-
),
|
528
|
-
)
|
529
|
-
|
530
|
-
def _perform_context_update(
|
531
|
-
self,
|
532
|
-
context: AgentContext,
|
533
|
-
model: LanguageModel,
|
534
|
-
current_messages: List[Dict[str, Any]],
|
535
|
-
timing: Literal["before", "after"],
|
536
|
-
effective_settings: Optional[dict] = None,
|
537
|
-
) -> AgentContext:
|
538
|
-
"""Perform context update with retries and error handling."""
|
539
|
-
updated_context = context
|
540
|
-
|
541
|
-
# Use effective settings or defaults
|
542
|
-
if effective_settings is None:
|
543
|
-
effective_settings = {
|
544
|
-
"context_confirm": self.context_confirm,
|
545
|
-
"context_strategy": self.context_strategy,
|
546
|
-
"context_max_retries": self.context_max_retries,
|
547
|
-
"context_confirm_instructions": self.context_confirm_instructions,
|
548
|
-
"context_selection_instructions": self.context_selection_instructions,
|
549
|
-
"context_update_instructions": self.context_update_instructions,
|
550
|
-
"context_format": self.context_format,
|
551
|
-
}
|
552
|
-
|
553
|
-
for attempt in range(effective_settings["context_max_retries"]):
|
554
|
-
try:
|
555
|
-
# Check if update is needed (if confirmation is enabled)
|
556
|
-
if effective_settings["context_confirm"]:
|
557
|
-
confirm_model = self._create_context_confirm_model()
|
558
|
-
|
559
|
-
# Create detailed instructions with context structure
|
560
|
-
context_structure = _format_context_for_instructions(
|
561
|
-
updated_context, effective_settings["context_format"]
|
562
|
-
)
|
563
|
-
confirm_instructions = f"""Based on the conversation, determine if the context should be updated {timing} processing.
|
564
|
-
|
565
|
-
Current context structure:
|
566
|
-
{context_structure}
|
567
|
-
|
568
|
-
Should the context be updated based on the new information provided in the conversation?"""
|
569
|
-
|
570
|
-
if effective_settings["context_confirm_instructions"]:
|
571
|
-
confirm_instructions += f"\n\nAdditional instructions: {effective_settings['context_confirm_instructions']}"
|
572
|
-
|
573
|
-
confirm_response = model.run(
|
574
|
-
messages=current_messages
|
575
|
-
+ [{"role": "user", "content": confirm_instructions}],
|
576
|
-
type=confirm_model,
|
577
|
-
instructor_mode=self.instructor_mode,
|
578
|
-
)
|
579
|
-
|
580
|
-
if not confirm_response.output.decision:
|
581
|
-
return updated_context
|
582
|
-
|
583
|
-
# Perform the update based on strategy
|
584
|
-
if effective_settings["context_strategy"] == "selective":
|
585
|
-
# Get fields to update
|
586
|
-
selection_model = self._create_context_selection_model(
|
587
|
-
updated_context
|
588
|
-
)
|
589
|
-
|
590
|
-
# Create detailed instructions with context structure
|
591
|
-
context_structure = _format_context_for_instructions(
|
592
|
-
updated_context, effective_settings["context_format"]
|
593
|
-
)
|
594
|
-
selection_instructions = f"""Select which fields in the context should be updated {timing} processing based on the conversation.
|
595
|
-
|
596
|
-
Current context structure:
|
597
|
-
{context_structure}
|
598
|
-
|
599
|
-
Choose only the fields that need to be updated based on the new information provided in the conversation."""
|
600
|
-
|
601
|
-
if effective_settings["context_selection_instructions"]:
|
602
|
-
selection_instructions += f"\n\nAdditional instructions: {effective_settings['context_selection_instructions']}"
|
603
|
-
|
604
|
-
selection_response = model.run(
|
605
|
-
messages=current_messages
|
606
|
-
+ [{"role": "user", "content": selection_instructions}],
|
607
|
-
type=selection_model,
|
608
|
-
instructor_mode=self.instructor_mode,
|
609
|
-
)
|
610
|
-
|
611
|
-
# Update each selected field
|
612
|
-
for field_enum in selection_response.output.fields:
|
613
|
-
field_name = field_enum.value
|
614
|
-
field_model = self._create_context_update_model(
|
615
|
-
updated_context, field_name
|
616
|
-
)
|
617
|
-
# Get current field value for context
|
618
|
-
current_value = (
|
619
|
-
getattr(updated_context, field_name)
|
620
|
-
if isinstance(updated_context, BaseModel)
|
621
|
-
else updated_context.get(field_name)
|
622
|
-
)
|
623
|
-
|
624
|
-
field_instructions = f"""Update the {field_name} field in the context based on the conversation.
|
625
|
-
|
626
|
-
Current value of {field_name}: {current_value}
|
627
|
-
|
628
|
-
Please provide the new value for {field_name} based on the information from the conversation."""
|
629
|
-
|
630
|
-
if effective_settings["context_update_instructions"]:
|
631
|
-
field_instructions += f"\n\nAdditional instructions: {effective_settings['context_update_instructions']}"
|
632
|
-
|
633
|
-
field_response = model.run(
|
634
|
-
messages=current_messages
|
635
|
-
+ [{"role": "user", "content": field_instructions}],
|
636
|
-
type=field_model,
|
637
|
-
instructor_mode=self.instructor_mode,
|
638
|
-
)
|
639
|
-
|
640
|
-
# Apply the update
|
641
|
-
field_updates = {
|
642
|
-
field_name: getattr(field_response.output, field_name)
|
643
|
-
}
|
644
|
-
updated_context = _update_context_object(
|
645
|
-
updated_context, field_updates
|
646
|
-
)
|
647
|
-
|
648
|
-
else: # strategy == "all"
|
649
|
-
# Update all fields at once
|
650
|
-
update_model = self._create_context_update_model(updated_context)
|
651
|
-
|
652
|
-
# Create detailed instructions with context structure
|
653
|
-
context_structure = _format_context_for_instructions(
|
654
|
-
updated_context, effective_settings["context_format"]
|
655
|
-
)
|
656
|
-
update_instructions = f"""Update the context {timing} processing based on the conversation.
|
657
|
-
|
658
|
-
Current context structure:
|
659
|
-
{context_structure}
|
660
|
-
|
661
|
-
Please update the appropriate fields based on the conversation. Only update fields that need to be changed based on the new information provided."""
|
662
|
-
|
663
|
-
if effective_settings["context_update_instructions"]:
|
664
|
-
update_instructions += f"\n\nAdditional instructions: {effective_settings['context_update_instructions']}"
|
665
|
-
|
666
|
-
update_response = model.run(
|
667
|
-
messages=current_messages
|
668
|
-
+ [{"role": "user", "content": update_instructions}],
|
669
|
-
type=update_model,
|
670
|
-
instructor_mode=self.instructor_mode,
|
671
|
-
)
|
672
|
-
|
673
|
-
# Apply the updates
|
674
|
-
if hasattr(update_response.output, "updates"):
|
675
|
-
# Legacy fallback for generic updates
|
676
|
-
updated_context = _update_context_object(
|
677
|
-
updated_context, update_response.output.updates
|
678
|
-
)
|
679
|
-
else:
|
680
|
-
# New approach - extract field values directly from the response
|
681
|
-
updates_dict = {}
|
682
|
-
for field_name in (
|
683
|
-
context.model_fields.keys()
|
684
|
-
if isinstance(context, BaseModel)
|
685
|
-
else context.keys()
|
686
|
-
):
|
687
|
-
if hasattr(update_response.output, field_name):
|
688
|
-
updates_dict[field_name] = getattr(
|
689
|
-
update_response.output, field_name
|
690
|
-
)
|
691
|
-
updated_context = _update_context_object(
|
692
|
-
updated_context, updates_dict
|
693
|
-
)
|
694
|
-
|
695
|
-
# Trigger context update hooks
|
696
|
-
self.hook_manager.trigger_hooks("context_update", updated_context)
|
697
|
-
|
698
|
-
return updated_context
|
699
|
-
|
700
|
-
except Exception as e:
|
701
|
-
if attempt == self.context_max_retries - 1:
|
702
|
-
# Last attempt failed, return original context
|
703
|
-
return updated_context
|
704
|
-
# Continue to next attempt
|
705
|
-
continue
|
706
|
-
|
707
|
-
return updated_context
|
708
|
-
|
709
|
-
def _format_messages_with_context(
|
710
|
-
self, messages: List[Dict[str, Any]], context: Optional[AgentContext] = None
|
711
|
-
) -> List[Dict[str, Any]]:
|
712
|
-
"""Format messages with instructions and context."""
|
713
|
-
formatted_messages = messages.copy()
|
714
|
-
|
715
|
-
if self.instructions:
|
716
|
-
system_content = self.instructions
|
717
|
-
|
718
|
-
# Add context if provided
|
719
|
-
if context is not None:
|
720
|
-
context_str = _format_context_for_instructions(
|
721
|
-
context, self.context_format
|
722
|
-
)
|
723
|
-
if context_str:
|
724
|
-
system_content += f"\n\nContext:\n{context_str}"
|
725
|
-
|
726
|
-
system_message = {"role": "system", "content": system_content}
|
727
|
-
formatted_messages = [system_message] + formatted_messages
|
728
|
-
|
729
|
-
return consolidate_system_messages(formatted_messages)
|
730
|
-
|
731
|
-
# Overloaded run methods for streaming support
|
732
|
-
@overload
|
733
|
-
def run(
|
734
|
-
self,
|
735
|
-
messages: AgentMessages,
|
736
|
-
model: Optional[Union[LanguageModel, LanguageModelName]] = None,
|
737
|
-
max_steps: Optional[int] = None,
|
738
|
-
context: Optional[AgentContext] = None,
|
739
|
-
output_type: Optional[Type[T]] = None,
|
740
|
-
end_strategy: Optional[Literal["tool"]] = None,
|
741
|
-
end_tool: Optional[Callable] = None,
|
742
|
-
context_updates: Optional[
|
743
|
-
Union[List[Literal["before", "after"]], Literal["before", "after"]]
|
744
|
-
] = None,
|
745
|
-
context_confirm: Optional[bool] = None,
|
746
|
-
context_strategy: Optional[Literal["selective", "all"]] = None,
|
747
|
-
context_max_retries: Optional[int] = None,
|
748
|
-
context_confirm_instructions: Optional[str] = None,
|
749
|
-
context_selection_instructions: Optional[str] = None,
|
750
|
-
context_update_instructions: Optional[str] = None,
|
751
|
-
context_format: Optional[Literal["json", "python", "markdown"]] = None,
|
752
|
-
verbose: Optional[bool] = None,
|
753
|
-
debug: Optional[bool] = None,
|
754
|
-
*,
|
755
|
-
stream: Literal[False] = False,
|
756
|
-
**kwargs: Any,
|
757
|
-
) -> AgentResponse[T, AgentContext]: ...
|
758
|
-
|
759
|
-
@overload
|
760
|
-
def run(
|
761
|
-
self,
|
762
|
-
messages: AgentMessages,
|
763
|
-
model: Optional[Union[LanguageModel, LanguageModelName]] = None,
|
764
|
-
max_steps: Optional[int] = None,
|
765
|
-
context: Optional[AgentContext] = None,
|
766
|
-
output_type: Optional[Type[T]] = None,
|
767
|
-
end_strategy: Optional[Literal["tool"]] = None,
|
768
|
-
end_tool: Optional[Callable] = None,
|
769
|
-
context_updates: Optional[
|
770
|
-
Union[List[Literal["before", "after"]], Literal["before", "after"]]
|
771
|
-
] = None,
|
772
|
-
context_confirm: Optional[bool] = None,
|
773
|
-
context_strategy: Optional[Literal["selective", "all"]] = None,
|
774
|
-
context_max_retries: Optional[int] = None,
|
775
|
-
context_confirm_instructions: Optional[str] = None,
|
776
|
-
context_selection_instructions: Optional[str] = None,
|
777
|
-
context_update_instructions: Optional[str] = None,
|
778
|
-
context_format: Optional[Literal["json", "python", "markdown"]] = None,
|
779
|
-
verbose: Optional[bool] = None,
|
780
|
-
debug: Optional[bool] = None,
|
781
|
-
*,
|
782
|
-
stream: Literal[True],
|
783
|
-
**kwargs: Any,
|
784
|
-
) -> AgentStream[T, AgentContext]: ...
|
785
|
-
|
786
|
-
def run(
|
787
|
-
self,
|
788
|
-
messages: AgentMessages,
|
789
|
-
model: Optional[Union[LanguageModel, LanguageModelName]] = None,
|
790
|
-
max_steps: Optional[int] = None,
|
791
|
-
context: Optional[AgentContext] = None,
|
792
|
-
output_type: Optional[Type[T]] = None,
|
793
|
-
end_strategy: Optional[Literal["tool"]] = None,
|
794
|
-
end_tool: Optional[Callable] = None,
|
795
|
-
context_updates: Optional[
|
796
|
-
Union[List[Literal["before", "after"]], Literal["before", "after"]]
|
797
|
-
] = None,
|
798
|
-
context_confirm: Optional[bool] = None,
|
799
|
-
context_strategy: Optional[Literal["selective", "all"]] = None,
|
800
|
-
context_max_retries: Optional[int] = None,
|
801
|
-
context_confirm_instructions: Optional[str] = None,
|
802
|
-
context_selection_instructions: Optional[str] = None,
|
803
|
-
context_update_instructions: Optional[str] = None,
|
804
|
-
context_format: Optional[Literal["json", "python", "markdown"]] = None,
|
805
|
-
verbose: Optional[bool] = None,
|
806
|
-
debug: Optional[bool] = None,
|
807
|
-
stream: bool = False,
|
808
|
-
**kwargs: Any,
|
809
|
-
) -> Union[AgentResponse[T, AgentContext], AgentStream[T, AgentContext]]:
|
810
|
-
"""Runs this agent and returns a final agent response or stream.
|
811
|
-
|
812
|
-
You can override defaults assigned to this agent from this function directly.
|
813
|
-
|
814
|
-
Args:
|
815
|
-
messages: The messages to process. Can be:
|
816
|
-
- A single string: "What's the weather like?"
|
817
|
-
- A list of message dicts: [{"role": "user", "content": "Hello"}]
|
818
|
-
- A list of strings: ["Hello", "How are you?"]
|
819
|
-
model: The model to use for this run (overrides default).
|
820
|
-
- Can be a LanguageModel instance or model name string like "gpt-4"
|
821
|
-
max_steps: Maximum number of steps to execute (overrides default).
|
822
|
-
- Useful for limiting tool usage or preventing infinite loops
|
823
|
-
context: Context object for the agent (overrides default).
|
824
|
-
- Any object that provides additional context for the conversation
|
825
|
-
output_type: The expected output type (overrides default).
|
826
|
-
- Use for structured outputs: output_type=MyPydanticModel
|
827
|
-
- Defaults to str for unstructured text responses
|
828
|
-
stream: Whether to return a stream instead of a final response.
|
829
|
-
- If True, returns AgentStream for real-time processing
|
830
|
-
- If False, returns complete AgentResponse
|
831
|
-
**kwargs: Additional keyword arguments passed to the language model.
|
832
|
-
- Examples: temperature=0.7, top_p=0.9, presence_penalty=0.1
|
833
|
-
|
834
|
-
Returns:
|
835
|
-
AgentResponse or AgentStream depending on stream parameter.
|
836
|
-
- AgentResponse: Contains final output, steps taken, and metadata
|
837
|
-
- AgentStream: Iterator yielding intermediate steps and final result
|
838
|
-
|
839
|
-
Examples:
|
840
|
-
Basic text conversation:
|
841
|
-
>>> agent = Agent()
|
842
|
-
>>> response = agent.run("Hello, how are you?")
|
843
|
-
>>> print(response.output)
|
844
|
-
"Hello! I'm doing well, thank you for asking."
|
845
|
-
|
846
|
-
With custom model and parameters:
|
847
|
-
>>> response = agent.run(
|
848
|
-
... messages="Explain quantum computing",
|
849
|
-
... model="gpt-4",
|
850
|
-
... max_steps=5,
|
851
|
-
... temperature=0.3
|
852
|
-
... )
|
853
|
-
|
854
|
-
Structured output with Pydantic model:
|
855
|
-
>>> from pydantic import BaseModel
|
856
|
-
>>> class Summary(BaseModel):
|
857
|
-
... title: str
|
858
|
-
... key_points: List[str]
|
859
|
-
>>> response = agent.run(
|
860
|
-
... "Summarize the benefits of renewable energy",
|
861
|
-
... output_type=Summary
|
862
|
-
... )
|
863
|
-
>>> print(response.output.title)
|
864
|
-
>>> print(response.output.key_points)
|
865
|
-
|
866
|
-
Streaming for real-time results:
|
867
|
-
>>> stream = agent.run(
|
868
|
-
... "Write a long story about space exploration",
|
869
|
-
... stream=True
|
870
|
-
... )
|
871
|
-
>>> for chunk in stream:
|
872
|
-
... print(chunk.output, end="", flush=True)
|
873
|
-
|
874
|
-
With context for additional information:
|
875
|
-
>>> context = {"user_preferences": "technical explanations"}
|
876
|
-
>>> response = agent.run(
|
877
|
-
... "How does machine learning work?",
|
878
|
-
... context=context
|
879
|
-
... )
|
880
|
-
"""
|
881
|
-
# Handle streaming
|
882
|
-
if stream:
|
883
|
-
return AgentStream(
|
884
|
-
agent=self,
|
885
|
-
messages=messages,
|
886
|
-
model=model,
|
887
|
-
max_steps=max_steps,
|
888
|
-
context=context,
|
889
|
-
output_type=output_type,
|
890
|
-
stream=stream,
|
891
|
-
**kwargs,
|
892
|
-
)
|
893
|
-
|
894
|
-
# Set logger level for this request if specified
|
895
|
-
original_level = logger.level
|
896
|
-
if debug or (debug is None and self.debug):
|
897
|
-
logger.level = "debug"
|
898
|
-
elif verbose or (verbose is None and self.verbose):
|
899
|
-
logger.level = "info"
|
900
|
-
|
901
|
-
# Log agent execution start
|
902
|
-
logger.info(f"Starting agent '{self.name}' execution")
|
903
|
-
logger.debug(
|
904
|
-
f"Agent settings: max_steps={max_steps or self.max_steps}, tools={len(self.tools)}"
|
905
|
-
)
|
906
|
-
|
907
|
-
try:
|
908
|
-
# Use provided model or default
|
909
|
-
if model is None:
|
910
|
-
working_model = self.language_model
|
911
|
-
elif isinstance(model, str):
|
912
|
-
working_model = LanguageModel(
|
913
|
-
model=model,
|
914
|
-
verbose=verbose or self.verbose,
|
915
|
-
debug=debug or self.debug,
|
916
|
-
)
|
917
|
-
else:
|
918
|
-
working_model = model
|
919
|
-
|
920
|
-
# Use provided max_steps or default from instance
|
921
|
-
if max_steps is None:
|
922
|
-
max_steps = self.max_steps
|
923
|
-
|
924
|
-
# Use provided end_strategy or default from instance
|
925
|
-
effective_end_strategy = (
|
926
|
-
end_strategy if end_strategy is not None else self.end_strategy
|
927
|
-
)
|
928
|
-
effective_end_tool = end_tool if end_tool is not None else self.end_tool
|
929
|
-
|
930
|
-
# Create working tools list with end_tool if needed
|
931
|
-
working_tools = self.tools.copy()
|
932
|
-
if effective_end_strategy == "tool" and effective_end_tool is not None:
|
933
|
-
end_tool_obj = define_tool(effective_end_tool)
|
934
|
-
# Only add if not already present
|
935
|
-
if not any(tool.name == end_tool_obj.name for tool in working_tools):
|
936
|
-
working_tools.append(end_tool_obj)
|
937
|
-
|
938
|
-
# Get effective context settings
|
939
|
-
effective_context_settings = self._get_effective_context_settings(
|
940
|
-
context_updates=context_updates,
|
941
|
-
context_confirm=context_confirm,
|
942
|
-
context_strategy=context_strategy,
|
943
|
-
context_max_retries=context_max_retries,
|
944
|
-
context_confirm_instructions=context_confirm_instructions,
|
945
|
-
context_selection_instructions=context_selection_instructions,
|
946
|
-
context_update_instructions=context_update_instructions,
|
947
|
-
context_format=context_format,
|
948
|
-
)
|
949
|
-
|
950
|
-
# Parse initial messages
|
951
|
-
parsed_messages = parse_messages(messages)
|
952
|
-
current_messages = parsed_messages.copy()
|
953
|
-
steps: List[LanguageModelResponse[str]] = []
|
954
|
-
|
955
|
-
# RUN MAIN AGENTIC LOOP
|
956
|
-
logger.debug(f"Starting agentic loop with max_steps={max_steps}")
|
957
|
-
for step in range(max_steps):
|
958
|
-
logger.debug(f"Agent step {step + 1}/{max_steps}")
|
959
|
-
# Update context before processing if configured
|
960
|
-
if context and self._should_update_context(
|
961
|
-
context, "before", effective_context_settings["context_updates"]
|
962
|
-
):
|
963
|
-
context = self._perform_context_update(
|
964
|
-
context=context,
|
965
|
-
model=working_model,
|
966
|
-
current_messages=current_messages,
|
967
|
-
timing="before",
|
968
|
-
effective_settings=effective_context_settings,
|
969
|
-
)
|
970
|
-
|
971
|
-
# Format messages with instructions and context for first step only
|
972
|
-
if step == 0:
|
973
|
-
formatted_messages = self._format_messages_with_context(
|
974
|
-
messages=current_messages,
|
975
|
-
context=context,
|
976
|
-
)
|
977
|
-
else:
|
978
|
-
formatted_messages = current_messages
|
979
|
-
|
980
|
-
# Prepare kwargs for language model
|
981
|
-
model_kwargs = kwargs.copy()
|
982
|
-
# Don't add output_type for intermediate steps - only for final response
|
983
|
-
if self.instructor_mode:
|
984
|
-
model_kwargs["instructor_mode"] = self.instructor_mode
|
985
|
-
|
986
|
-
# Get language model response
|
987
|
-
response = working_model.run(
|
988
|
-
messages=formatted_messages,
|
989
|
-
tools=[tool.to_dict() for tool in working_tools]
|
990
|
-
if working_tools
|
991
|
-
else None,
|
992
|
-
**model_kwargs,
|
993
|
-
)
|
994
|
-
|
995
|
-
# Check if response has tool calls
|
996
|
-
if response.has_tool_calls():
|
997
|
-
logger.info(
|
998
|
-
f"Agent '{self.name}' making tool calls: {len(response.tool_calls)} tools"
|
999
|
-
)
|
1000
|
-
for tool_call in response.tool_calls:
|
1001
|
-
logger.debug(
|
1002
|
-
f"Tool call: {tool_call.function.name}({tool_call.function.arguments})"
|
1003
|
-
)
|
1004
|
-
|
1005
|
-
# Add response to message history (with tool calls)
|
1006
|
-
current_messages.append(response.to_message())
|
1007
|
-
|
1008
|
-
# Execute tools and add their responses to messages
|
1009
|
-
tool_responses = execute_tools_from_language_model_response(
|
1010
|
-
tools=working_tools, response=response
|
1011
|
-
)
|
1012
|
-
# Add tool responses to message history
|
1013
|
-
for tool_resp in tool_responses:
|
1014
|
-
current_messages.append(tool_resp.to_dict())
|
1015
|
-
|
1016
|
-
# This is not the final step, add to steps
|
1017
|
-
steps.append(response)
|
1018
|
-
else:
|
1019
|
-
# No tool calls - check if this is actually the final step based on end_strategy
|
1020
|
-
if effective_end_strategy == "tool":
|
1021
|
-
# Check if the end_tool was called
|
1022
|
-
end_tool_called = (
|
1023
|
-
any(
|
1024
|
-
tool_call.function.name == effective_end_tool.__name__
|
1025
|
-
for tool_call in response.tool_calls
|
1026
|
-
)
|
1027
|
-
if response.tool_calls
|
1028
|
-
else False
|
1029
|
-
)
|
1030
|
-
|
1031
|
-
if not end_tool_called:
|
1032
|
-
# End tool not called, continue the conversation
|
1033
|
-
logger.debug(
|
1034
|
-
f"Agent '{self.name}' step {step + 1}: No end tool called, continuing..."
|
1035
|
-
)
|
1036
|
-
|
1037
|
-
# Add the response to history
|
1038
|
-
current_messages.append(response.to_message())
|
1039
|
-
|
1040
|
-
# Add system message instructing agent to call the end tool
|
1041
|
-
current_messages.append(
|
1042
|
-
{
|
1043
|
-
"role": "system",
|
1044
|
-
"content": f"You must call the {effective_end_tool.__name__} tool to complete your response. Do not provide a final answer until you have called this tool.",
|
1045
|
-
}
|
1046
|
-
)
|
1047
|
-
|
1048
|
-
# Add user message to continue
|
1049
|
-
current_messages.append(
|
1050
|
-
{"role": "user", "content": "continue"}
|
1051
|
-
)
|
1052
|
-
|
1053
|
-
# Remove the continue message and append assistant content
|
1054
|
-
current_messages.pop() # Remove "continue" message
|
1055
|
-
|
1056
|
-
# This is not the final step, add to steps and continue
|
1057
|
-
steps.append(response)
|
1058
|
-
continue
|
1059
|
-
|
1060
|
-
# This is the final step (either no end_strategy or end_tool was called)
|
1061
|
-
logger.info(
|
1062
|
-
f"Agent '{self.name}' completed execution in {step + 1} steps"
|
1063
|
-
)
|
1064
|
-
# Now we can make the final call with the output_type if specified
|
1065
|
-
# Only make structured output call for non-str types
|
1066
|
-
if output_type and output_type != str:
|
1067
|
-
# Make a final call with the structured output type
|
1068
|
-
final_model_kwargs = kwargs.copy()
|
1069
|
-
final_model_kwargs["type"] = output_type
|
1070
|
-
if self.instructor_mode:
|
1071
|
-
final_model_kwargs["instructor_mode"] = self.instructor_mode
|
1072
|
-
|
1073
|
-
# Create a clean conversation history for structured output
|
1074
|
-
# Include the original messages and the final response content
|
1075
|
-
clean_messages = []
|
1076
|
-
# Add original user messages (excluding tool calls/responses)
|
1077
|
-
for msg in formatted_messages:
|
1078
|
-
if isinstance(msg, dict) and msg.get("role") not in [
|
1079
|
-
"tool",
|
1080
|
-
"assistant",
|
1081
|
-
]:
|
1082
|
-
clean_messages.append(msg)
|
1083
|
-
elif hasattr(msg, "role") and msg.role not in [
|
1084
|
-
"tool",
|
1085
|
-
"assistant",
|
1086
|
-
]:
|
1087
|
-
clean_messages.append(msg.to_dict())
|
1088
|
-
|
1089
|
-
# Add the final assistant response content
|
1090
|
-
clean_messages.append(
|
1091
|
-
{"role": "assistant", "content": response.get_content()}
|
1092
|
-
)
|
1093
|
-
|
1094
|
-
# Use the clean conversation history to generate structured output
|
1095
|
-
final_response = working_model.run(
|
1096
|
-
messages=clean_messages,
|
1097
|
-
**final_model_kwargs,
|
1098
|
-
)
|
1099
|
-
|
1100
|
-
# Update context after processing if configured
|
1101
|
-
if context and self._should_update_context(
|
1102
|
-
context,
|
1103
|
-
"after",
|
1104
|
-
effective_context_settings["context_updates"],
|
1105
|
-
):
|
1106
|
-
context = self._perform_context_update(
|
1107
|
-
context=context,
|
1108
|
-
model=working_model,
|
1109
|
-
current_messages=current_messages,
|
1110
|
-
timing="after",
|
1111
|
-
effective_settings=effective_context_settings,
|
1112
|
-
)
|
1113
|
-
return _create_agent_response_from_language_model_response(
|
1114
|
-
response=final_response, steps=steps, context=context
|
1115
|
-
)
|
1116
|
-
else:
|
1117
|
-
# Update context after processing if configured
|
1118
|
-
if context and self._should_update_context(
|
1119
|
-
context,
|
1120
|
-
"after",
|
1121
|
-
effective_context_settings["context_updates"],
|
1122
|
-
):
|
1123
|
-
context = self._perform_context_update(
|
1124
|
-
context=context,
|
1125
|
-
model=working_model,
|
1126
|
-
current_messages=current_messages,
|
1127
|
-
timing="after",
|
1128
|
-
effective_settings=effective_context_settings,
|
1129
|
-
)
|
1130
|
-
return _create_agent_response_from_language_model_response(
|
1131
|
-
response=response, steps=steps, context=context
|
1132
|
-
)
|
1133
|
-
|
1134
|
-
# Max steps reached - return last response
|
1135
|
-
if steps:
|
1136
|
-
final_response = steps[-1]
|
1137
|
-
# If we have an output_type, make a final structured call (but not for str)
|
1138
|
-
if output_type and output_type != str:
|
1139
|
-
final_model_kwargs = kwargs.copy()
|
1140
|
-
final_model_kwargs["type"] = output_type
|
1141
|
-
if self.instructor_mode:
|
1142
|
-
final_model_kwargs["instructor_mode"] = self.instructor_mode
|
1143
|
-
|
1144
|
-
# Create clean messages for structured output
|
1145
|
-
clean_messages = []
|
1146
|
-
formatted_messages = self._format_messages_with_context(
|
1147
|
-
messages=current_messages,
|
1148
|
-
context=context,
|
1149
|
-
)
|
1150
|
-
|
1151
|
-
# Add original user messages (excluding tool calls/responses)
|
1152
|
-
for msg in formatted_messages:
|
1153
|
-
if isinstance(msg, dict) and msg.get("role") not in [
|
1154
|
-
"tool",
|
1155
|
-
"assistant",
|
1156
|
-
]:
|
1157
|
-
clean_messages.append(msg)
|
1158
|
-
elif hasattr(msg, "role") and msg.role not in [
|
1159
|
-
"tool",
|
1160
|
-
"assistant",
|
1161
|
-
]:
|
1162
|
-
clean_messages.append(msg.to_dict())
|
1163
|
-
|
1164
|
-
# Add final response content
|
1165
|
-
clean_messages.append(
|
1166
|
-
{"role": "assistant", "content": final_response.get_content()}
|
1167
|
-
)
|
1168
|
-
|
1169
|
-
final_response = working_model.run(
|
1170
|
-
messages=clean_messages,
|
1171
|
-
**final_model_kwargs,
|
1172
|
-
)
|
1173
|
-
else:
|
1174
|
-
# No steps taken, make a final call
|
1175
|
-
final_model_kwargs = kwargs.copy()
|
1176
|
-
if output_type and output_type != str:
|
1177
|
-
final_model_kwargs["type"] = output_type
|
1178
|
-
if self.instructor_mode:
|
1179
|
-
final_model_kwargs["instructor_mode"] = self.instructor_mode
|
1180
|
-
|
1181
|
-
final_response = working_model.run(
|
1182
|
-
messages=self._format_messages_with_context(
|
1183
|
-
messages=current_messages,
|
1184
|
-
context=context,
|
1185
|
-
),
|
1186
|
-
**final_model_kwargs,
|
1187
|
-
)
|
1188
|
-
|
1189
|
-
# Update context after processing if configured
|
1190
|
-
if context and self._should_update_context(
|
1191
|
-
context, "after", effective_context_settings["context_updates"]
|
1192
|
-
):
|
1193
|
-
context = self._perform_context_update(
|
1194
|
-
context=context,
|
1195
|
-
model=working_model,
|
1196
|
-
current_messages=current_messages,
|
1197
|
-
timing="after",
|
1198
|
-
effective_settings=effective_context_settings,
|
1199
|
-
)
|
1200
|
-
|
1201
|
-
return _create_agent_response_from_language_model_response(
|
1202
|
-
response=final_response, steps=steps, context=context
|
1203
|
-
)
|
1204
|
-
|
1205
|
-
finally:
|
1206
|
-
# Restore original logger level
|
1207
|
-
if debug is not None or verbose is not None:
|
1208
|
-
logger.level = original_level
|
1209
|
-
|
1210
|
-
async def async_run(
|
1211
|
-
self,
|
1212
|
-
messages: AgentMessages,
|
1213
|
-
model: Optional[Union[LanguageModel, LanguageModelName]] = None,
|
1214
|
-
max_steps: Optional[int] = None,
|
1215
|
-
context: Optional[AgentContext] = None,
|
1216
|
-
output_type: Optional[Type[T]] = None,
|
1217
|
-
context_updates: Optional[
|
1218
|
-
Union[List[Literal["before", "after"]], Literal["before", "after"]]
|
1219
|
-
] = None,
|
1220
|
-
context_confirm: Optional[bool] = None,
|
1221
|
-
context_strategy: Optional[Literal["selective", "all"]] = None,
|
1222
|
-
context_max_retries: Optional[int] = None,
|
1223
|
-
context_confirm_instructions: Optional[str] = None,
|
1224
|
-
context_selection_instructions: Optional[str] = None,
|
1225
|
-
context_update_instructions: Optional[str] = None,
|
1226
|
-
context_format: Optional[Literal["json", "python", "markdown"]] = None,
|
1227
|
-
verbose: Optional[bool] = None,
|
1228
|
-
debug: Optional[bool] = None,
|
1229
|
-
end_strategy: Optional[Literal["tool"]] = None,
|
1230
|
-
end_tool: Optional[Callable] = None,
|
1231
|
-
**kwargs: Any,
|
1232
|
-
) -> AgentResponse[T, AgentContext]:
|
1233
|
-
"""Runs this agent asynchronously and returns a final agent response.
|
1234
|
-
|
1235
|
-
You can override defaults assigned to this agent from this function directly.
|
1236
|
-
This is the async version of run() for non-blocking execution.
|
1237
|
-
|
1238
|
-
Args:
|
1239
|
-
messages: The messages to process. Can be:
|
1240
|
-
- A single string: "What's the weather like?"
|
1241
|
-
- A list of message dicts: [{"role": "user", "content": "Hello"}]
|
1242
|
-
- A list of strings: ["Hello", "How are you?"]
|
1243
|
-
model: The model to use for this run (overrides default).
|
1244
|
-
- Can be a LanguageModel instance or model name string like "gpt-4"
|
1245
|
-
max_steps: Maximum number of steps to execute (overrides default).
|
1246
|
-
- Useful for limiting tool usage or preventing infinite loops
|
1247
|
-
context: Context object for the agent (overrides default).
|
1248
|
-
- Any object that provides additional context for the conversation
|
1249
|
-
output_type: The expected output type (overrides default).
|
1250
|
-
- Use for structured outputs: output_type=MyPydanticModel
|
1251
|
-
- Defaults to str for unstructured text responses
|
1252
|
-
**kwargs: Additional keyword arguments passed to the language model.
|
1253
|
-
- Examples: temperature=0.7, top_p=0.9, presence_penalty=0.1
|
1254
|
-
|
1255
|
-
Returns:
|
1256
|
-
AgentResponse containing the final output, steps taken, and metadata.
|
1257
|
-
|
1258
|
-
Examples:
|
1259
|
-
Basic async usage:
|
1260
|
-
>>> import asyncio
|
1261
|
-
>>> agent = Agent()
|
1262
|
-
>>> async def main():
|
1263
|
-
... response = await agent.async_run("Hello, how are you?")
|
1264
|
-
... print(response.output)
|
1265
|
-
>>> asyncio.run(main())
|
1266
|
-
|
1267
|
-
Multiple concurrent requests:
|
1268
|
-
>>> async def process_multiple():
|
1269
|
-
... tasks = [
|
1270
|
-
... agent.async_run("What's 2+2?"),
|
1271
|
-
... agent.async_run("What's the capital of France?"),
|
1272
|
-
... agent.async_run("Explain photosynthesis")
|
1273
|
-
... ]
|
1274
|
-
... responses = await asyncio.gather(*tasks)
|
1275
|
-
... return responses
|
1276
|
-
|
1277
|
-
With structured output:
|
1278
|
-
>>> from pydantic import BaseModel
|
1279
|
-
>>> class Analysis(BaseModel):
|
1280
|
-
... sentiment: str
|
1281
|
-
... confidence: float
|
1282
|
-
>>> async def analyze_text():
|
1283
|
-
... response = await agent.async_run(
|
1284
|
-
... "Analyze the sentiment of: 'I love this product!'",
|
1285
|
-
... output_type=Analysis
|
1286
|
-
... )
|
1287
|
-
... return response.output
|
1288
|
-
|
1289
|
-
With custom model and context:
|
1290
|
-
>>> async def custom_run():
|
1291
|
-
... context = {"domain": "medical", "expertise_level": "expert"}
|
1292
|
-
... response = await agent.async_run(
|
1293
|
-
... "Explain diabetes",
|
1294
|
-
... model="gpt-4",
|
1295
|
-
... context=context,
|
1296
|
-
... temperature=0.2
|
1297
|
-
... )
|
1298
|
-
... return response.output
|
1299
|
-
"""
|
1300
|
-
# Set logger level for this request if specified
|
1301
|
-
original_level = logger.level
|
1302
|
-
if debug or (debug is None and self.debug):
|
1303
|
-
logger.level = "debug"
|
1304
|
-
elif verbose or (verbose is None and self.verbose):
|
1305
|
-
logger.level = "info"
|
1306
|
-
|
1307
|
-
try:
|
1308
|
-
# Use provided model or default
|
1309
|
-
if model is None:
|
1310
|
-
working_model = self.language_model
|
1311
|
-
elif isinstance(model, str):
|
1312
|
-
working_model = LanguageModel(
|
1313
|
-
model=model,
|
1314
|
-
verbose=verbose or self.verbose,
|
1315
|
-
debug=debug or self.debug,
|
1316
|
-
)
|
1317
|
-
else:
|
1318
|
-
working_model = model
|
1319
|
-
|
1320
|
-
# Use provided max_steps or default from instance
|
1321
|
-
if max_steps is None:
|
1322
|
-
max_steps = self.max_steps
|
1323
|
-
|
1324
|
-
# Use provided end_strategy or default from instance
|
1325
|
-
effective_end_strategy = (
|
1326
|
-
end_strategy if end_strategy is not None else self.end_strategy
|
1327
|
-
)
|
1328
|
-
effective_end_tool = end_tool if end_tool is not None else self.end_tool
|
1329
|
-
|
1330
|
-
# Create working tools list with end_tool if needed
|
1331
|
-
working_tools = self.tools.copy()
|
1332
|
-
if effective_end_strategy == "tool" and effective_end_tool is not None:
|
1333
|
-
end_tool_obj = define_tool(effective_end_tool)
|
1334
|
-
# Only add if not already present
|
1335
|
-
if not any(tool.name == end_tool_obj.name for tool in working_tools):
|
1336
|
-
working_tools.append(end_tool_obj)
|
1337
|
-
|
1338
|
-
# Get effective context settings
|
1339
|
-
effective_context_settings = self._get_effective_context_settings(
|
1340
|
-
context_updates=context_updates,
|
1341
|
-
context_confirm=context_confirm,
|
1342
|
-
context_strategy=context_strategy,
|
1343
|
-
context_max_retries=context_max_retries,
|
1344
|
-
context_confirm_instructions=context_confirm_instructions,
|
1345
|
-
context_selection_instructions=context_selection_instructions,
|
1346
|
-
context_update_instructions=context_update_instructions,
|
1347
|
-
context_format=context_format,
|
1348
|
-
)
|
1349
|
-
|
1350
|
-
# Parse initial messages
|
1351
|
-
parsed_messages = parse_messages(messages)
|
1352
|
-
current_messages = parsed_messages.copy()
|
1353
|
-
steps: List[LanguageModelResponse[str]] = []
|
1354
|
-
|
1355
|
-
# RUN MAIN AGENTIC LOOP
|
1356
|
-
for step in range(max_steps):
|
1357
|
-
# Update context before processing if configured
|
1358
|
-
if context and self._should_update_context(
|
1359
|
-
context, "before", effective_context_settings["context_updates"]
|
1360
|
-
):
|
1361
|
-
context = self._perform_context_update(
|
1362
|
-
context=context,
|
1363
|
-
model=working_model,
|
1364
|
-
current_messages=current_messages,
|
1365
|
-
timing="before",
|
1366
|
-
effective_settings=effective_context_settings,
|
1367
|
-
)
|
1368
|
-
|
1369
|
-
# Format messages with instructions and context for first step only
|
1370
|
-
if step == 0:
|
1371
|
-
formatted_messages = self._format_messages_with_context(
|
1372
|
-
messages=current_messages,
|
1373
|
-
context=context,
|
1374
|
-
)
|
1375
|
-
else:
|
1376
|
-
formatted_messages = current_messages
|
1377
|
-
|
1378
|
-
# Prepare kwargs for language model
|
1379
|
-
model_kwargs = kwargs.copy()
|
1380
|
-
# Don't add output_type for intermediate steps - only for final response
|
1381
|
-
if self.instructor_mode:
|
1382
|
-
model_kwargs["instructor_mode"] = self.instructor_mode
|
1383
|
-
|
1384
|
-
# Get language model response
|
1385
|
-
response = await working_model.async_run(
|
1386
|
-
messages=formatted_messages,
|
1387
|
-
tools=[tool.to_dict() for tool in working_tools]
|
1388
|
-
if working_tools
|
1389
|
-
else None,
|
1390
|
-
**model_kwargs,
|
1391
|
-
)
|
1392
|
-
|
1393
|
-
# Check if response has tool calls
|
1394
|
-
if response.has_tool_calls():
|
1395
|
-
# Add response to message history (with tool calls)
|
1396
|
-
current_messages.append(response.to_message())
|
1397
|
-
|
1398
|
-
# Execute tools and add their responses to messages
|
1399
|
-
tool_responses = execute_tools_from_language_model_response(
|
1400
|
-
tools=working_tools, response=response
|
1401
|
-
)
|
1402
|
-
# Add tool responses to message history
|
1403
|
-
for tool_resp in tool_responses:
|
1404
|
-
current_messages.append(tool_resp.to_dict())
|
1405
|
-
|
1406
|
-
# This is not the final step, add to steps
|
1407
|
-
steps.append(response)
|
1408
|
-
else:
|
1409
|
-
# No tool calls - check if this is actually the final step based on end_strategy
|
1410
|
-
if effective_end_strategy == "tool":
|
1411
|
-
# Check if the end_tool was called
|
1412
|
-
end_tool_called = (
|
1413
|
-
any(
|
1414
|
-
tool_call.function.name == effective_end_tool.__name__
|
1415
|
-
for tool_call in response.tool_calls
|
1416
|
-
)
|
1417
|
-
if response.tool_calls
|
1418
|
-
else False
|
1419
|
-
)
|
1420
|
-
|
1421
|
-
if not end_tool_called:
|
1422
|
-
# End tool not called, continue the conversation
|
1423
|
-
logger.debug(
|
1424
|
-
f"Agent '{self.name}' step {step + 1}: No end tool called, continuing..."
|
1425
|
-
)
|
1426
|
-
|
1427
|
-
# Add the response to history
|
1428
|
-
current_messages.append(response.to_message())
|
1429
|
-
|
1430
|
-
# Add system message instructing agent to call the end tool
|
1431
|
-
current_messages.append(
|
1432
|
-
{
|
1433
|
-
"role": "system",
|
1434
|
-
"content": f"You must call the {effective_end_tool.__name__} tool to complete your response. Do not provide a final answer until you have called this tool.",
|
1435
|
-
}
|
1436
|
-
)
|
1437
|
-
|
1438
|
-
# Add user message to continue
|
1439
|
-
current_messages.append(
|
1440
|
-
{"role": "user", "content": "continue"}
|
1441
|
-
)
|
1442
|
-
|
1443
|
-
# Remove the continue message and append assistant content
|
1444
|
-
current_messages.pop() # Remove "continue" message
|
1445
|
-
|
1446
|
-
# This is not the final step, add to steps and continue
|
1447
|
-
steps.append(response)
|
1448
|
-
continue
|
1449
|
-
|
1450
|
-
# This is the final step (either no end_strategy or end_tool was called)
|
1451
|
-
# Now we can make the final call with the output_type if specified
|
1452
|
-
# Only make structured output call for non-str types
|
1453
|
-
if output_type and output_type != str:
|
1454
|
-
# Make a final call with the structured output type
|
1455
|
-
final_model_kwargs = kwargs.copy()
|
1456
|
-
final_model_kwargs["type"] = output_type
|
1457
|
-
if self.instructor_mode:
|
1458
|
-
final_model_kwargs["instructor_mode"] = self.instructor_mode
|
1459
|
-
|
1460
|
-
# Create a clean conversation history for structured output
|
1461
|
-
# Include the original messages and the final response content
|
1462
|
-
clean_messages = []
|
1463
|
-
# Add original user messages (excluding tool calls/responses)
|
1464
|
-
for msg in formatted_messages:
|
1465
|
-
if isinstance(msg, dict) and msg.get("role") not in [
|
1466
|
-
"tool",
|
1467
|
-
"assistant",
|
1468
|
-
]:
|
1469
|
-
clean_messages.append(msg)
|
1470
|
-
elif hasattr(msg, "role") and msg.role not in [
|
1471
|
-
"tool",
|
1472
|
-
"assistant",
|
1473
|
-
]:
|
1474
|
-
clean_messages.append(msg.to_dict())
|
1475
|
-
|
1476
|
-
# Add the final assistant response content
|
1477
|
-
clean_messages.append(
|
1478
|
-
{"role": "assistant", "content": response.get_content()}
|
1479
|
-
)
|
1480
|
-
|
1481
|
-
# Use the clean conversation history to generate structured output
|
1482
|
-
final_response = await working_model.async_run(
|
1483
|
-
messages=clean_messages,
|
1484
|
-
**final_model_kwargs,
|
1485
|
-
)
|
1486
|
-
|
1487
|
-
# Update context after processing if configured
|
1488
|
-
if context and self._should_update_context(
|
1489
|
-
context,
|
1490
|
-
"after",
|
1491
|
-
effective_context_settings["context_updates"],
|
1492
|
-
):
|
1493
|
-
context = self._perform_context_update(
|
1494
|
-
context=context,
|
1495
|
-
model=working_model,
|
1496
|
-
current_messages=current_messages,
|
1497
|
-
timing="after",
|
1498
|
-
effective_settings=effective_context_settings,
|
1499
|
-
)
|
1500
|
-
return _create_agent_response_from_language_model_response(
|
1501
|
-
response=final_response, steps=steps, context=context
|
1502
|
-
)
|
1503
|
-
else:
|
1504
|
-
# Update context after processing if configured
|
1505
|
-
if context and self._should_update_context(
|
1506
|
-
context,
|
1507
|
-
"after",
|
1508
|
-
effective_context_settings["context_updates"],
|
1509
|
-
):
|
1510
|
-
context = self._perform_context_update(
|
1511
|
-
context=context,
|
1512
|
-
model=working_model,
|
1513
|
-
current_messages=current_messages,
|
1514
|
-
timing="after",
|
1515
|
-
effective_settings=effective_context_settings,
|
1516
|
-
)
|
1517
|
-
return _create_agent_response_from_language_model_response(
|
1518
|
-
response=response, steps=steps, context=context
|
1519
|
-
)
|
1520
|
-
|
1521
|
-
# Max steps reached - return last response
|
1522
|
-
if steps:
|
1523
|
-
final_response = steps[-1]
|
1524
|
-
# If we have an output_type, make a final structured call (but not for str)
|
1525
|
-
if output_type and output_type != str:
|
1526
|
-
final_model_kwargs = kwargs.copy()
|
1527
|
-
final_model_kwargs["type"] = output_type
|
1528
|
-
if self.instructor_mode:
|
1529
|
-
final_model_kwargs["instructor_mode"] = self.instructor_mode
|
1530
|
-
|
1531
|
-
# Create clean messages for structured output
|
1532
|
-
clean_messages = []
|
1533
|
-
formatted_messages = self._format_messages_with_context(
|
1534
|
-
messages=current_messages,
|
1535
|
-
context=context,
|
1536
|
-
)
|
1537
|
-
|
1538
|
-
# Add original user messages (excluding tool calls/responses)
|
1539
|
-
for msg in formatted_messages:
|
1540
|
-
if isinstance(msg, dict) and msg.get("role") not in [
|
1541
|
-
"tool",
|
1542
|
-
"assistant",
|
1543
|
-
]:
|
1544
|
-
clean_messages.append(msg)
|
1545
|
-
elif hasattr(msg, "role") and msg.role not in [
|
1546
|
-
"tool",
|
1547
|
-
"assistant",
|
1548
|
-
]:
|
1549
|
-
clean_messages.append(msg.to_dict())
|
1550
|
-
|
1551
|
-
# Add final response content
|
1552
|
-
clean_messages.append(
|
1553
|
-
{"role": "assistant", "content": final_response.get_content()}
|
1554
|
-
)
|
1555
|
-
|
1556
|
-
final_response = await working_model.async_run(
|
1557
|
-
messages=clean_messages,
|
1558
|
-
**final_model_kwargs,
|
1559
|
-
)
|
1560
|
-
else:
|
1561
|
-
# No steps taken, make a final call
|
1562
|
-
final_model_kwargs = kwargs.copy()
|
1563
|
-
if output_type and output_type != str:
|
1564
|
-
final_model_kwargs["type"] = output_type
|
1565
|
-
if self.instructor_mode:
|
1566
|
-
final_model_kwargs["instructor_mode"] = self.instructor_mode
|
1567
|
-
|
1568
|
-
final_response = await working_model.async_run(
|
1569
|
-
messages=self._format_messages_with_context(
|
1570
|
-
messages=current_messages,
|
1571
|
-
context=context,
|
1572
|
-
),
|
1573
|
-
**final_model_kwargs,
|
1574
|
-
)
|
1575
|
-
|
1576
|
-
# Update context after processing if configured
|
1577
|
-
if context and self._should_update_context(
|
1578
|
-
context, "after", effective_context_settings["context_updates"]
|
1579
|
-
):
|
1580
|
-
context = self._perform_context_update(
|
1581
|
-
context=context,
|
1582
|
-
model=working_model,
|
1583
|
-
current_messages=current_messages,
|
1584
|
-
timing="after",
|
1585
|
-
effective_settings=effective_context_settings,
|
1586
|
-
)
|
1587
|
-
|
1588
|
-
return _create_agent_response_from_language_model_response(
|
1589
|
-
response=final_response, steps=steps, context=context
|
1590
|
-
)
|
1591
|
-
|
1592
|
-
finally:
|
1593
|
-
# Restore original logger level
|
1594
|
-
if debug is not None or verbose is not None:
|
1595
|
-
logger.level = original_level
|
1596
|
-
|
1597
|
-
def stream(
|
1598
|
-
self,
|
1599
|
-
messages: AgentMessages,
|
1600
|
-
model: Optional[Union[LanguageModel, LanguageModelName]] = None,
|
1601
|
-
max_steps: Optional[int] = None,
|
1602
|
-
context: Optional[AgentContext] = None,
|
1603
|
-
output_type: Optional[Type[T]] = None,
|
1604
|
-
**kwargs: Any,
|
1605
|
-
) -> AgentStream[T, AgentContext]:
|
1606
|
-
"""Create a stream that yields agent steps.
|
1607
|
-
|
1608
|
-
Args:
|
1609
|
-
messages: The input messages to process
|
1610
|
-
model: Language model to use (overrides agent's default)
|
1611
|
-
max_steps: Maximum number of steps to take
|
1612
|
-
context: Context object to maintain state
|
1613
|
-
output_type: Type for structured output
|
1614
|
-
**kwargs: Additional parameters for the language model
|
1615
|
-
|
1616
|
-
Returns:
|
1617
|
-
An AgentStream that can be iterated over
|
1618
|
-
"""
|
1619
|
-
return AgentStream(
|
1620
|
-
agent=self,
|
1621
|
-
messages=messages,
|
1622
|
-
model=model,
|
1623
|
-
max_steps=max_steps,
|
1624
|
-
context=context,
|
1625
|
-
output_type=output_type,
|
1626
|
-
stream=True,
|
1627
|
-
**kwargs,
|
1628
|
-
)
|
1629
|
-
|
1630
|
-
def as_a2a(
|
1631
|
-
self,
|
1632
|
-
*,
|
1633
|
-
# Worker configuration
|
1634
|
-
context: Optional[AgentContext] = None,
|
1635
|
-
# Storage and broker configuration
|
1636
|
-
storage: Optional[Any] = None,
|
1637
|
-
broker: Optional[Any] = None,
|
1638
|
-
# Server configuration
|
1639
|
-
host: str = "0.0.0.0",
|
1640
|
-
port: int = 8000,
|
1641
|
-
reload: bool = False,
|
1642
|
-
workers: int = 1,
|
1643
|
-
log_level: str = "info",
|
1644
|
-
# A2A configuration
|
1645
|
-
name: Optional[str] = None,
|
1646
|
-
url: Optional[str] = None,
|
1647
|
-
version: str = "1.0.0",
|
1648
|
-
description: Optional[str] = None,
|
1649
|
-
# Advanced configuration
|
1650
|
-
lifespan_timeout: int = 30,
|
1651
|
-
**uvicorn_kwargs: Any,
|
1652
|
-
) -> "FastA2A": # type: ignore
|
1653
|
-
"""
|
1654
|
-
Convert this agent to an A2A server application.
|
1655
|
-
|
1656
|
-
This method creates a FastA2A server that can handle A2A requests
|
1657
|
-
for this agent instance. It sets up the necessary Worker, Storage,
|
1658
|
-
and Broker components automatically.
|
1659
|
-
|
1660
|
-
Args:
|
1661
|
-
context: Initial context for the agent
|
1662
|
-
storage: Custom storage backend (defaults to InMemoryStorage)
|
1663
|
-
broker: Custom broker backend (defaults to InMemoryBroker)
|
1664
|
-
host: Host to bind the server to
|
1665
|
-
port: Port to bind the server to
|
1666
|
-
reload: Enable auto-reload for development
|
1667
|
-
workers: Number of worker processes
|
1668
|
-
log_level: Logging level
|
1669
|
-
name: Agent name for the A2A server (defaults to agent's name)
|
1670
|
-
url: URL where the agent is hosted
|
1671
|
-
version: API version
|
1672
|
-
description: API description for the A2A server (defaults to agent's description)
|
1673
|
-
lifespan_timeout: Timeout for lifespan events
|
1674
|
-
**uvicorn_kwargs: Additional arguments passed to uvicorn
|
1675
|
-
|
1676
|
-
Returns:
|
1677
|
-
FastA2A application instance that can be run with uvicorn
|
1678
|
-
|
1679
|
-
Examples:
|
1680
|
-
Convert agent to A2A server:
|
1681
|
-
```python
|
1682
|
-
agent = Agent(
|
1683
|
-
name="assistant",
|
1684
|
-
instructions="You are a helpful assistant",
|
1685
|
-
model="openai/gpt-4o-mini"
|
1686
|
-
)
|
1687
|
-
|
1688
|
-
app = agent.as_a2a(port=8080)
|
1689
|
-
|
1690
|
-
# Run with uvicorn
|
1691
|
-
import uvicorn
|
1692
|
-
uvicorn.run(app, host="0.0.0.0", port=8080)
|
1693
|
-
```
|
1694
|
-
|
1695
|
-
Or use the CLI:
|
1696
|
-
```bash
|
1697
|
-
uvicorn mymodule:agent.as_a2a() --reload
|
1698
|
-
```
|
1699
|
-
|
1700
|
-
With custom configuration:
|
1701
|
-
```python
|
1702
|
-
app = agent.as_a2a(
|
1703
|
-
name="My Assistant API",
|
1704
|
-
description="A helpful AI assistant",
|
1705
|
-
host="localhost",
|
1706
|
-
port=3000
|
1707
|
-
)
|
1708
|
-
```
|
1709
|
-
"""
|
1710
|
-
from ..a2a import as_a2a_app
|
1711
|
-
|
1712
|
-
return as_a2a_app(
|
1713
|
-
self,
|
1714
|
-
context=context,
|
1715
|
-
storage=storage,
|
1716
|
-
broker=broker,
|
1717
|
-
host=host,
|
1718
|
-
port=port,
|
1719
|
-
reload=reload,
|
1720
|
-
workers=workers,
|
1721
|
-
log_level=log_level,
|
1722
|
-
name=name or self.name,
|
1723
|
-
url=url,
|
1724
|
-
version=version,
|
1725
|
-
description=description or self.description,
|
1726
|
-
lifespan_timeout=lifespan_timeout,
|
1727
|
-
**uvicorn_kwargs,
|
1728
|
-
)
|
1729
|
-
|
1730
|
-
def iter(
|
1731
|
-
self,
|
1732
|
-
messages: AgentMessages,
|
1733
|
-
model: Optional[Union[LanguageModel, LanguageModelName]] = None,
|
1734
|
-
max_steps: Optional[int] = None,
|
1735
|
-
context: Optional[AgentContext] = None,
|
1736
|
-
output_type: Optional[Type[T]] = None,
|
1737
|
-
context_updates: Optional[
|
1738
|
-
Union[List[Literal["before", "after"]], Literal["before", "after"]]
|
1739
|
-
] = None,
|
1740
|
-
context_confirm: Optional[bool] = None,
|
1741
|
-
context_strategy: Optional[Literal["selective", "all"]] = None,
|
1742
|
-
context_max_retries: Optional[int] = None,
|
1743
|
-
context_confirm_instructions: Optional[str] = None,
|
1744
|
-
context_selection_instructions: Optional[str] = None,
|
1745
|
-
context_update_instructions: Optional[str] = None,
|
1746
|
-
context_format: Optional[Literal["json", "python", "markdown"]] = None,
|
1747
|
-
end_strategy: Optional[Literal["tool"]] = None,
|
1748
|
-
end_tool: Optional[Callable] = None,
|
1749
|
-
**kwargs: Any,
|
1750
|
-
) -> AgentStream[T, AgentContext]:
|
1751
|
-
"""Iterate over agent steps, yielding each step response.
|
1752
|
-
|
1753
|
-
You can override defaults assigned to this agent from this function directly.
|
1754
|
-
Returns an AgentStream that yields intermediate steps and the final result.
|
1755
|
-
|
1756
|
-
Args:
|
1757
|
-
messages: The messages to process. Can be:
|
1758
|
-
- A single string: "What's the weather like?"
|
1759
|
-
- A list of message dicts: [{"role": "user", "content": "Hello"}]
|
1760
|
-
- A list of strings: ["Hello", "How are you?"]
|
1761
|
-
model: The model to use for this run (overrides default).
|
1762
|
-
- Can be a LanguageModel instance or model name string like "gpt-4"
|
1763
|
-
max_steps: Maximum number of steps to execute (overrides default).
|
1764
|
-
- Useful for limiting tool usage or preventing infinite loops
|
1765
|
-
context: Context object for the agent (overrides default).
|
1766
|
-
- Any object that provides additional context for the conversation
|
1767
|
-
output_type: The expected output type (overrides default).
|
1768
|
-
- Use for structured outputs: output_type=MyPydanticModel
|
1769
|
-
- Defaults to str for unstructured text responses
|
1770
|
-
**kwargs: Additional keyword arguments passed to the language model.
|
1771
|
-
- Examples: temperature=0.7, top_p=0.9, presence_penalty=0.1
|
1772
|
-
|
1773
|
-
Returns:
|
1774
|
-
AgentStream that can be iterated over to get each step response,
|
1775
|
-
including tool calls and intermediate reasoning steps.
|
1776
|
-
|
1777
|
-
Examples:
|
1778
|
-
Basic iteration over steps:
|
1779
|
-
>>> agent = Agent(tools=[calculator_tool])
|
1780
|
-
>>> stream = agent.iter("What's 25 * 47?")
|
1781
|
-
>>> for step in stream:
|
1782
|
-
... print(f"Step {step.step_number}: {step.output}")
|
1783
|
-
... if step.tool_calls:
|
1784
|
-
... print(f"Tool calls: {len(step.tool_calls)}")
|
1785
|
-
|
1786
|
-
Real-time processing with streaming:
|
1787
|
-
>>> stream = agent.iter("Write a poem about nature")
|
1788
|
-
>>> for chunk in stream:
|
1789
|
-
... if chunk.output:
|
1790
|
-
... print(chunk.output, end="", flush=True)
|
1791
|
-
... if chunk.is_final:
|
1792
|
-
... print("\n--- Final response ---")
|
1793
|
-
|
1794
|
-
With structured output iteration:
|
1795
|
-
>>> from pydantic import BaseModel
|
1796
|
-
>>> class StepAnalysis(BaseModel):
|
1797
|
-
... reasoning: str
|
1798
|
-
... confidence: float
|
1799
|
-
>>> stream = agent.iter(
|
1800
|
-
... "Analyze this step by step: Why is the sky blue?",
|
1801
|
-
... output_type=StepAnalysis
|
1802
|
-
... )
|
1803
|
-
>>> for step in stream:
|
1804
|
-
... if step.output:
|
1805
|
-
... print(f"Reasoning: {step.output.reasoning}")
|
1806
|
-
... print(f"Confidence: {step.output.confidence}")
|
1807
|
-
|
1808
|
-
Processing with custom model and context:
|
1809
|
-
>>> context = {"domain": "science", "depth": "detailed"}
|
1810
|
-
>>> stream = agent.iter(
|
1811
|
-
... "Explain quantum entanglement",
|
1812
|
-
... model="gpt-4",
|
1813
|
-
... context=context,
|
1814
|
-
... max_steps=3,
|
1815
|
-
... temperature=0.1
|
1816
|
-
... )
|
1817
|
-
>>> results = []
|
1818
|
-
>>> for step in stream:
|
1819
|
-
... results.append(step.output)
|
1820
|
-
... if step.is_final:
|
1821
|
-
... break
|
1822
|
-
|
1823
|
-
Error handling during iteration:
|
1824
|
-
>>> try:
|
1825
|
-
... stream = agent.iter("Complex calculation task")
|
1826
|
-
... for step in stream:
|
1827
|
-
... if step.error:
|
1828
|
-
... print(f"Error in step: {step.error}")
|
1829
|
-
... else:
|
1830
|
-
... print(f"Step result: {step.output}")
|
1831
|
-
... except Exception as e:
|
1832
|
-
... print(f"Stream error: {e}")
|
1833
|
-
"""
|
1834
|
-
return AgentStream(
|
1835
|
-
agent=self,
|
1836
|
-
messages=messages,
|
1837
|
-
model=model,
|
1838
|
-
max_steps=max_steps,
|
1839
|
-
context=context,
|
1840
|
-
output_type=output_type,
|
1841
|
-
stream=True,
|
1842
|
-
end_strategy=end_strategy,
|
1843
|
-
end_tool=end_tool,
|
1844
|
-
**kwargs,
|
1845
|
-
)
|
1846
|
-
|
1847
|
-
def async_iter(
|
1848
|
-
self,
|
1849
|
-
messages: AgentMessages,
|
1850
|
-
model: Optional[Union[LanguageModel, LanguageModelName]] = None,
|
1851
|
-
max_steps: Optional[int] = None,
|
1852
|
-
context: Optional[AgentContext] = None,
|
1853
|
-
output_type: Optional[Type[T]] = None,
|
1854
|
-
**kwargs: Any,
|
1855
|
-
) -> AgentStream[T, AgentContext]:
|
1856
|
-
"""Async iterate over agent steps, yielding each step response.
|
1857
|
-
|
1858
|
-
Args:
|
1859
|
-
messages: The input messages to process
|
1860
|
-
model: Language model to use (overrides agent's default)
|
1861
|
-
max_steps: Maximum number of steps to take
|
1862
|
-
context: Context object to maintain state
|
1863
|
-
output_type: Type for structured output
|
1864
|
-
**kwargs: Additional parameters for the language model
|
1865
|
-
|
1866
|
-
Returns:
|
1867
|
-
An AgentStream that can be iterated over asynchronously
|
1868
|
-
"""
|
1869
|
-
return AgentStream(
|
1870
|
-
agent=self,
|
1871
|
-
messages=messages,
|
1872
|
-
model=model,
|
1873
|
-
max_steps=max_steps,
|
1874
|
-
context=context,
|
1875
|
-
output_type=output_type,
|
1876
|
-
stream=True,
|
1877
|
-
**kwargs,
|
1878
|
-
)
|
1879
|
-
|
1880
|
-
|
1881
|
-
def create_agent(
|
1882
|
-
name: str = "agent",
|
1883
|
-
instructions: Optional[str] = None,
|
1884
|
-
model: Union[LanguageModel, LanguageModelName] = "openai/gpt-4o-mini",
|
1885
|
-
description: Optional[str] = None,
|
1886
|
-
tools: Union[List[Tool], Callable, None] = None,
|
1887
|
-
settings: Optional[AgentSettings] = None,
|
1888
|
-
instructor_mode: Optional[LanguageModelInstructorMode] = None,
|
1889
|
-
# Context management parameters
|
1890
|
-
context_updates: Optional[
|
1891
|
-
Union[List[Literal["before", "after"]], Literal["before", "after"]]
|
1892
|
-
] = None,
|
1893
|
-
context_confirm: bool = False,
|
1894
|
-
context_strategy: Literal["selective", "all"] = "all",
|
1895
|
-
context_max_retries: int = 3,
|
1896
|
-
context_confirm_instructions: Optional[str] = None,
|
1897
|
-
context_selection_instructions: Optional[str] = None,
|
1898
|
-
context_update_instructions: Optional[str] = None,
|
1899
|
-
context_format: Literal["json", "python", "markdown"] = "json",
|
1900
|
-
verbose: bool = False,
|
1901
|
-
debug: bool = False,
|
1902
|
-
**kwargs: Any,
|
1903
|
-
) -> Agent[T]:
|
1904
|
-
"""Create a new AI agent with specified capabilities and behavior.
|
1905
|
-
|
1906
|
-
An agent is an intelligent assistant that can use tools, follow instructions,
|
1907
|
-
and maintain context across conversations. It combines a language model with
|
1908
|
-
additional capabilities like tool execution and structured output generation.
|
1909
|
-
|
1910
|
-
Args:
|
1911
|
-
name: A human-readable name for the agent (default: "agent")
|
1912
|
-
instructions: System instructions that define the agent's behavior and personality
|
1913
|
-
model: The language model to use - either a LanguageModel instance or model name string
|
1914
|
-
description: Optional description of what the agent does
|
1915
|
-
tools: List of tools/functions the agent can call, or a single callable
|
1916
|
-
settings: AgentSettings object to customize default behavior
|
1917
|
-
instructor_mode: Mode for structured output generation
|
1918
|
-
context_updates: When to update context - "before", "after", or both
|
1919
|
-
context_confirm: Whether to confirm context updates with the user
|
1920
|
-
context_strategy: How to select context updates - "selective" or "all"
|
1921
|
-
context_max_retries: Maximum attempts for context update operations
|
1922
|
-
context_confirm_instructions: Custom instructions for context confirmation
|
1923
|
-
context_selection_instructions: Custom instructions for context selection
|
1924
|
-
context_update_instructions: Custom instructions for context updates
|
1925
|
-
context_format: Format for context display - "json", "python", or "markdown"
|
1926
|
-
verbose: If True, set logger to INFO level for detailed output
|
1927
|
-
debug: If True, set logger to DEBUG level for maximum verbosity
|
1928
|
-
**kwargs: Additional parameters passed to the underlying language model
|
1929
|
-
|
1930
|
-
Example:
|
1931
|
-
Basic agent:
|
1932
|
-
>>> agent = create_agent(name="assistant", instructions="You are helpful")
|
1933
|
-
|
1934
|
-
Agent with tools:
|
1935
|
-
>>> def calculator(x: int, y: int) -> int:
|
1936
|
-
... return x + y
|
1937
|
-
>>> agent = create_agent(tools=[calculator])
|
1938
|
-
|
1939
|
-
Agent with custom settings:
|
1940
|
-
>>> settings = AgentSettings(max_steps=5)
|
1941
|
-
>>> agent = create_agent(settings=settings, model="gpt-4")
|
1942
|
-
"""
|
1943
|
-
return Agent(
|
1944
|
-
name=name,
|
1945
|
-
instructions=instructions,
|
1946
|
-
model=model,
|
1947
|
-
description=description,
|
1948
|
-
tools=tools,
|
1949
|
-
settings=settings,
|
1950
|
-
instructor_mode=instructor_mode,
|
1951
|
-
context_updates=context_updates,
|
1952
|
-
context_confirm=context_confirm,
|
1953
|
-
context_strategy=context_strategy,
|
1954
|
-
context_max_retries=context_max_retries,
|
1955
|
-
context_confirm_instructions=context_confirm_instructions,
|
1956
|
-
context_selection_instructions=context_selection_instructions,
|
1957
|
-
context_update_instructions=context_update_instructions,
|
1958
|
-
context_format=context_format,
|
1959
|
-
verbose=verbose,
|
1960
|
-
debug=debug,
|
1961
|
-
**kwargs,
|
1962
|
-
)
|
1963
|
-
|
1964
|
-
|
1965
|
-
__all__ = [
|
1966
|
-
"Agent",
|
1967
|
-
"AgentSettings",
|
1968
|
-
"AgentModelSettings",
|
1969
|
-
"AgentEvent",
|
1970
|
-
"HookManager",
|
1971
|
-
"HookDecorator",
|
1972
|
-
"create_agent",
|
1973
|
-
]
|