agno 2.1.2__py3-none-any.whl → 2.3.13__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.
- agno/agent/agent.py +5540 -2273
- agno/api/api.py +2 -0
- agno/api/os.py +1 -1
- agno/compression/__init__.py +3 -0
- agno/compression/manager.py +247 -0
- agno/culture/__init__.py +3 -0
- agno/culture/manager.py +956 -0
- agno/db/async_postgres/__init__.py +3 -0
- agno/db/base.py +689 -6
- agno/db/dynamo/dynamo.py +933 -37
- agno/db/dynamo/schemas.py +174 -10
- agno/db/dynamo/utils.py +63 -4
- agno/db/firestore/firestore.py +831 -9
- agno/db/firestore/schemas.py +51 -0
- agno/db/firestore/utils.py +102 -4
- agno/db/gcs_json/gcs_json_db.py +660 -12
- agno/db/gcs_json/utils.py +60 -26
- agno/db/in_memory/in_memory_db.py +287 -14
- agno/db/in_memory/utils.py +60 -2
- agno/db/json/json_db.py +590 -14
- agno/db/json/utils.py +60 -26
- agno/db/migrations/manager.py +199 -0
- agno/db/migrations/v1_to_v2.py +43 -13
- agno/db/migrations/versions/__init__.py +0 -0
- agno/db/migrations/versions/v2_3_0.py +938 -0
- agno/db/mongo/__init__.py +15 -1
- agno/db/mongo/async_mongo.py +2760 -0
- agno/db/mongo/mongo.py +879 -11
- agno/db/mongo/schemas.py +42 -0
- agno/db/mongo/utils.py +80 -8
- agno/db/mysql/__init__.py +2 -1
- agno/db/mysql/async_mysql.py +2912 -0
- agno/db/mysql/mysql.py +946 -68
- agno/db/mysql/schemas.py +72 -10
- agno/db/mysql/utils.py +198 -7
- agno/db/postgres/__init__.py +2 -1
- agno/db/postgres/async_postgres.py +2579 -0
- agno/db/postgres/postgres.py +942 -57
- agno/db/postgres/schemas.py +81 -18
- agno/db/postgres/utils.py +164 -2
- agno/db/redis/redis.py +671 -7
- agno/db/redis/schemas.py +50 -0
- agno/db/redis/utils.py +65 -7
- agno/db/schemas/__init__.py +2 -1
- agno/db/schemas/culture.py +120 -0
- agno/db/schemas/evals.py +1 -0
- agno/db/schemas/memory.py +17 -2
- agno/db/singlestore/schemas.py +63 -0
- agno/db/singlestore/singlestore.py +949 -83
- agno/db/singlestore/utils.py +60 -2
- agno/db/sqlite/__init__.py +2 -1
- agno/db/sqlite/async_sqlite.py +2911 -0
- agno/db/sqlite/schemas.py +62 -0
- agno/db/sqlite/sqlite.py +965 -46
- agno/db/sqlite/utils.py +169 -8
- agno/db/surrealdb/__init__.py +3 -0
- agno/db/surrealdb/metrics.py +292 -0
- agno/db/surrealdb/models.py +334 -0
- agno/db/surrealdb/queries.py +71 -0
- agno/db/surrealdb/surrealdb.py +1908 -0
- agno/db/surrealdb/utils.py +147 -0
- agno/db/utils.py +2 -0
- agno/eval/__init__.py +10 -0
- agno/eval/accuracy.py +75 -55
- agno/eval/agent_as_judge.py +861 -0
- agno/eval/base.py +29 -0
- agno/eval/performance.py +16 -7
- agno/eval/reliability.py +28 -16
- agno/eval/utils.py +35 -17
- agno/exceptions.py +27 -2
- agno/filters.py +354 -0
- agno/guardrails/prompt_injection.py +1 -0
- agno/hooks/__init__.py +3 -0
- agno/hooks/decorator.py +164 -0
- agno/integrations/discord/client.py +1 -1
- agno/knowledge/chunking/agentic.py +13 -10
- agno/knowledge/chunking/fixed.py +4 -1
- agno/knowledge/chunking/semantic.py +9 -4
- agno/knowledge/chunking/strategy.py +59 -15
- agno/knowledge/embedder/fastembed.py +1 -1
- agno/knowledge/embedder/nebius.py +1 -1
- agno/knowledge/embedder/ollama.py +8 -0
- agno/knowledge/embedder/openai.py +8 -8
- agno/knowledge/embedder/sentence_transformer.py +6 -2
- agno/knowledge/embedder/vllm.py +262 -0
- agno/knowledge/knowledge.py +1618 -318
- agno/knowledge/reader/base.py +6 -2
- agno/knowledge/reader/csv_reader.py +8 -10
- agno/knowledge/reader/docx_reader.py +5 -6
- agno/knowledge/reader/field_labeled_csv_reader.py +16 -20
- agno/knowledge/reader/json_reader.py +5 -4
- agno/knowledge/reader/markdown_reader.py +8 -8
- agno/knowledge/reader/pdf_reader.py +17 -19
- agno/knowledge/reader/pptx_reader.py +101 -0
- agno/knowledge/reader/reader_factory.py +32 -3
- agno/knowledge/reader/s3_reader.py +3 -3
- agno/knowledge/reader/tavily_reader.py +193 -0
- agno/knowledge/reader/text_reader.py +22 -10
- agno/knowledge/reader/web_search_reader.py +1 -48
- agno/knowledge/reader/website_reader.py +10 -10
- agno/knowledge/reader/wikipedia_reader.py +33 -1
- agno/knowledge/types.py +1 -0
- agno/knowledge/utils.py +72 -7
- agno/media.py +22 -6
- agno/memory/__init__.py +14 -1
- agno/memory/manager.py +544 -83
- agno/memory/strategies/__init__.py +15 -0
- agno/memory/strategies/base.py +66 -0
- agno/memory/strategies/summarize.py +196 -0
- agno/memory/strategies/types.py +37 -0
- agno/models/aimlapi/aimlapi.py +17 -0
- agno/models/anthropic/claude.py +515 -40
- agno/models/aws/bedrock.py +102 -21
- agno/models/aws/claude.py +131 -274
- agno/models/azure/ai_foundry.py +41 -19
- agno/models/azure/openai_chat.py +39 -8
- agno/models/base.py +1249 -525
- agno/models/cerebras/cerebras.py +91 -21
- agno/models/cerebras/cerebras_openai.py +21 -2
- agno/models/cohere/chat.py +40 -6
- agno/models/cometapi/cometapi.py +18 -1
- agno/models/dashscope/dashscope.py +2 -3
- agno/models/deepinfra/deepinfra.py +18 -1
- agno/models/deepseek/deepseek.py +69 -3
- agno/models/fireworks/fireworks.py +18 -1
- agno/models/google/gemini.py +877 -80
- agno/models/google/utils.py +22 -0
- agno/models/groq/groq.py +51 -18
- agno/models/huggingface/huggingface.py +17 -6
- agno/models/ibm/watsonx.py +16 -6
- agno/models/internlm/internlm.py +18 -1
- agno/models/langdb/langdb.py +13 -1
- agno/models/litellm/chat.py +44 -9
- agno/models/litellm/litellm_openai.py +18 -1
- agno/models/message.py +28 -5
- agno/models/meta/llama.py +47 -14
- agno/models/meta/llama_openai.py +22 -17
- agno/models/mistral/mistral.py +8 -4
- agno/models/nebius/nebius.py +6 -7
- agno/models/nvidia/nvidia.py +20 -3
- agno/models/ollama/chat.py +24 -8
- agno/models/openai/chat.py +104 -29
- agno/models/openai/responses.py +101 -81
- agno/models/openrouter/openrouter.py +60 -3
- agno/models/perplexity/perplexity.py +17 -1
- agno/models/portkey/portkey.py +7 -6
- agno/models/requesty/requesty.py +24 -4
- agno/models/response.py +73 -2
- agno/models/sambanova/sambanova.py +20 -3
- agno/models/siliconflow/siliconflow.py +19 -2
- agno/models/together/together.py +20 -3
- agno/models/utils.py +254 -8
- agno/models/vercel/v0.py +20 -3
- agno/models/vertexai/__init__.py +0 -0
- agno/models/vertexai/claude.py +190 -0
- agno/models/vllm/vllm.py +19 -14
- agno/models/xai/xai.py +19 -2
- agno/os/app.py +549 -152
- agno/os/auth.py +190 -3
- agno/os/config.py +23 -0
- agno/os/interfaces/a2a/router.py +8 -11
- agno/os/interfaces/a2a/utils.py +1 -1
- agno/os/interfaces/agui/router.py +18 -3
- agno/os/interfaces/agui/utils.py +152 -39
- agno/os/interfaces/slack/router.py +55 -37
- agno/os/interfaces/slack/slack.py +9 -1
- agno/os/interfaces/whatsapp/router.py +0 -1
- agno/os/interfaces/whatsapp/security.py +3 -1
- agno/os/mcp.py +110 -52
- agno/os/middleware/__init__.py +2 -0
- agno/os/middleware/jwt.py +676 -112
- agno/os/router.py +40 -1478
- agno/os/routers/agents/__init__.py +3 -0
- agno/os/routers/agents/router.py +599 -0
- agno/os/routers/agents/schema.py +261 -0
- agno/os/routers/evals/evals.py +96 -39
- agno/os/routers/evals/schemas.py +65 -33
- agno/os/routers/evals/utils.py +80 -10
- agno/os/routers/health.py +10 -4
- agno/os/routers/knowledge/knowledge.py +196 -38
- agno/os/routers/knowledge/schemas.py +82 -22
- agno/os/routers/memory/memory.py +279 -52
- agno/os/routers/memory/schemas.py +46 -17
- agno/os/routers/metrics/metrics.py +20 -8
- agno/os/routers/metrics/schemas.py +16 -16
- agno/os/routers/session/session.py +462 -34
- agno/os/routers/teams/__init__.py +3 -0
- agno/os/routers/teams/router.py +512 -0
- agno/os/routers/teams/schema.py +257 -0
- agno/os/routers/traces/__init__.py +3 -0
- agno/os/routers/traces/schemas.py +414 -0
- agno/os/routers/traces/traces.py +499 -0
- agno/os/routers/workflows/__init__.py +3 -0
- agno/os/routers/workflows/router.py +624 -0
- agno/os/routers/workflows/schema.py +75 -0
- agno/os/schema.py +256 -693
- agno/os/scopes.py +469 -0
- agno/os/utils.py +514 -36
- agno/reasoning/anthropic.py +80 -0
- agno/reasoning/gemini.py +73 -0
- agno/reasoning/openai.py +5 -0
- agno/reasoning/vertexai.py +76 -0
- agno/run/__init__.py +6 -0
- agno/run/agent.py +155 -32
- agno/run/base.py +55 -3
- agno/run/requirement.py +181 -0
- agno/run/team.py +125 -38
- agno/run/workflow.py +72 -18
- agno/session/agent.py +102 -89
- agno/session/summary.py +56 -15
- agno/session/team.py +164 -90
- agno/session/workflow.py +405 -40
- agno/table.py +10 -0
- agno/team/team.py +3974 -1903
- agno/tools/dalle.py +2 -4
- agno/tools/eleven_labs.py +23 -25
- agno/tools/exa.py +21 -16
- agno/tools/file.py +153 -23
- agno/tools/file_generation.py +16 -10
- agno/tools/firecrawl.py +15 -7
- agno/tools/function.py +193 -38
- agno/tools/gmail.py +238 -14
- agno/tools/google_drive.py +271 -0
- agno/tools/googlecalendar.py +36 -8
- agno/tools/googlesheets.py +20 -5
- agno/tools/jira.py +20 -0
- agno/tools/mcp/__init__.py +10 -0
- agno/tools/mcp/mcp.py +331 -0
- agno/tools/mcp/multi_mcp.py +347 -0
- agno/tools/mcp/params.py +24 -0
- agno/tools/mcp_toolbox.py +3 -3
- agno/tools/models/nebius.py +5 -5
- agno/tools/models_labs.py +20 -10
- agno/tools/nano_banana.py +151 -0
- agno/tools/notion.py +204 -0
- agno/tools/parallel.py +314 -0
- agno/tools/postgres.py +76 -36
- agno/tools/redshift.py +406 -0
- agno/tools/scrapegraph.py +1 -1
- agno/tools/shopify.py +1519 -0
- agno/tools/slack.py +18 -3
- agno/tools/spotify.py +919 -0
- agno/tools/tavily.py +146 -0
- agno/tools/toolkit.py +25 -0
- agno/tools/workflow.py +8 -1
- agno/tools/yfinance.py +12 -11
- agno/tracing/__init__.py +12 -0
- agno/tracing/exporter.py +157 -0
- agno/tracing/schemas.py +276 -0
- agno/tracing/setup.py +111 -0
- agno/utils/agent.py +938 -0
- agno/utils/cryptography.py +22 -0
- agno/utils/dttm.py +33 -0
- agno/utils/events.py +151 -3
- agno/utils/gemini.py +15 -5
- agno/utils/hooks.py +118 -4
- agno/utils/http.py +113 -2
- agno/utils/knowledge.py +12 -5
- agno/utils/log.py +1 -0
- agno/utils/mcp.py +92 -2
- agno/utils/media.py +187 -1
- agno/utils/merge_dict.py +3 -3
- agno/utils/message.py +60 -0
- agno/utils/models/ai_foundry.py +9 -2
- agno/utils/models/claude.py +49 -14
- agno/utils/models/cohere.py +9 -2
- agno/utils/models/llama.py +9 -2
- agno/utils/models/mistral.py +4 -2
- agno/utils/print_response/agent.py +109 -16
- agno/utils/print_response/team.py +223 -30
- agno/utils/print_response/workflow.py +251 -34
- agno/utils/streamlit.py +1 -1
- agno/utils/team.py +98 -9
- agno/utils/tokens.py +657 -0
- agno/vectordb/base.py +39 -7
- agno/vectordb/cassandra/cassandra.py +21 -5
- agno/vectordb/chroma/chromadb.py +43 -12
- agno/vectordb/clickhouse/clickhousedb.py +21 -5
- agno/vectordb/couchbase/couchbase.py +29 -5
- agno/vectordb/lancedb/lance_db.py +92 -181
- agno/vectordb/langchaindb/langchaindb.py +24 -4
- agno/vectordb/lightrag/lightrag.py +17 -3
- agno/vectordb/llamaindex/llamaindexdb.py +25 -5
- agno/vectordb/milvus/milvus.py +50 -37
- agno/vectordb/mongodb/__init__.py +7 -1
- agno/vectordb/mongodb/mongodb.py +36 -30
- agno/vectordb/pgvector/pgvector.py +201 -77
- agno/vectordb/pineconedb/pineconedb.py +41 -23
- agno/vectordb/qdrant/qdrant.py +67 -54
- agno/vectordb/redis/__init__.py +9 -0
- agno/vectordb/redis/redisdb.py +682 -0
- agno/vectordb/singlestore/singlestore.py +50 -29
- agno/vectordb/surrealdb/surrealdb.py +31 -41
- agno/vectordb/upstashdb/upstashdb.py +34 -6
- agno/vectordb/weaviate/weaviate.py +53 -14
- agno/workflow/__init__.py +2 -0
- agno/workflow/agent.py +299 -0
- agno/workflow/condition.py +120 -18
- agno/workflow/loop.py +77 -10
- agno/workflow/parallel.py +231 -143
- agno/workflow/router.py +118 -17
- agno/workflow/step.py +609 -170
- agno/workflow/steps.py +73 -6
- agno/workflow/types.py +96 -21
- agno/workflow/workflow.py +2039 -262
- {agno-2.1.2.dist-info → agno-2.3.13.dist-info}/METADATA +201 -66
- agno-2.3.13.dist-info/RECORD +613 -0
- agno/tools/googlesearch.py +0 -98
- agno/tools/mcp.py +0 -679
- agno/tools/memori.py +0 -339
- agno-2.1.2.dist-info/RECORD +0 -543
- {agno-2.1.2.dist-info → agno-2.3.13.dist-info}/WHEEL +0 -0
- {agno-2.1.2.dist-info → agno-2.3.13.dist-info}/licenses/LICENSE +0 -0
- {agno-2.1.2.dist-info → agno-2.3.13.dist-info}/top_level.txt +0 -0
agno/tools/function.py
CHANGED
|
@@ -9,6 +9,7 @@ from pydantic import BaseModel, Field, validate_call
|
|
|
9
9
|
|
|
10
10
|
from agno.exceptions import AgentRunException
|
|
11
11
|
from agno.media import Audio, File, Image, Video
|
|
12
|
+
from agno.run import RunContext
|
|
12
13
|
from agno.utils.log import log_debug, log_error, log_exception, log_warning
|
|
13
14
|
|
|
14
15
|
T = TypeVar("T")
|
|
@@ -122,6 +123,8 @@ class Function(BaseModel):
|
|
|
122
123
|
_agent: Optional[Any] = None
|
|
123
124
|
# The team that the function is associated with
|
|
124
125
|
_team: Optional[Any] = None
|
|
126
|
+
# The run context that the function is associated with
|
|
127
|
+
_run_context: Optional[RunContext] = None
|
|
125
128
|
# The session state that the function is associated with
|
|
126
129
|
_session_state: Optional[Dict[str, Any]] = None
|
|
127
130
|
# The dependencies that the function is associated with
|
|
@@ -139,6 +142,46 @@ class Function(BaseModel):
|
|
|
139
142
|
include={"name", "description", "parameters", "strict", "requires_confirmation", "external_execution"},
|
|
140
143
|
)
|
|
141
144
|
|
|
145
|
+
def model_copy(self, *, deep: bool = False) -> "Function":
|
|
146
|
+
"""
|
|
147
|
+
Override model_copy to handle callable fields that can't be deep copied (pickled).
|
|
148
|
+
Callables should always be shallow copied (referenced), not deep copied.
|
|
149
|
+
"""
|
|
150
|
+
# For deep copy, we need to handle callable fields specially
|
|
151
|
+
if deep:
|
|
152
|
+
# Fields that should NOT be deep copied (callables and complex objects)
|
|
153
|
+
shallow_fields = {
|
|
154
|
+
"entrypoint",
|
|
155
|
+
"pre_hook",
|
|
156
|
+
"post_hook",
|
|
157
|
+
"tool_hooks",
|
|
158
|
+
"_agent",
|
|
159
|
+
"_team",
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
# Create a copy with shallow references to callable fields
|
|
163
|
+
copied_data = {}
|
|
164
|
+
for field_name, field_value in self.__dict__.items():
|
|
165
|
+
if field_name in shallow_fields:
|
|
166
|
+
# Shallow copy - just reference the same object
|
|
167
|
+
copied_data[field_name] = field_value
|
|
168
|
+
elif field_name == "parameters":
|
|
169
|
+
# Deep copy the parameters dict
|
|
170
|
+
from copy import deepcopy
|
|
171
|
+
|
|
172
|
+
copied_data[field_name] = deepcopy(field_value)
|
|
173
|
+
else:
|
|
174
|
+
# For simple types, just copy the value
|
|
175
|
+
copied_data[field_name] = field_value
|
|
176
|
+
|
|
177
|
+
# Create new instance with copied data
|
|
178
|
+
new_instance = self.__class__.model_construct(**copied_data)
|
|
179
|
+
|
|
180
|
+
return new_instance
|
|
181
|
+
else:
|
|
182
|
+
# For shallow copy, use the default Pydantic behavior
|
|
183
|
+
return super().model_copy(deep=False)
|
|
184
|
+
|
|
142
185
|
@classmethod
|
|
143
186
|
def from_callable(cls, c: Callable, name: Optional[str] = None, strict: bool = False) -> "Function":
|
|
144
187
|
from inspect import getdoc, signature
|
|
@@ -156,8 +199,13 @@ class Function(BaseModel):
|
|
|
156
199
|
del type_hints["agent"]
|
|
157
200
|
if "team" in sig.parameters and "team" in type_hints:
|
|
158
201
|
del type_hints["team"]
|
|
202
|
+
if "run_context" in sig.parameters and "run_context" in type_hints:
|
|
203
|
+
del type_hints["run_context"]
|
|
159
204
|
if "session_state" in sig.parameters and "session_state" in type_hints:
|
|
160
205
|
del type_hints["session_state"]
|
|
206
|
+
if "dependencies" in sig.parameters and "dependencies" in type_hints:
|
|
207
|
+
del type_hints["dependencies"]
|
|
208
|
+
|
|
161
209
|
# Remove media parameters from type hints as they are injected automatically
|
|
162
210
|
if "images" in sig.parameters and "images" in type_hints:
|
|
163
211
|
del type_hints["images"]
|
|
@@ -167,8 +215,6 @@ class Function(BaseModel):
|
|
|
167
215
|
del type_hints["audios"]
|
|
168
216
|
if "files" in sig.parameters and "files" in type_hints:
|
|
169
217
|
del type_hints["files"]
|
|
170
|
-
if "dependencies" in sig.parameters and "dependencies" in type_hints:
|
|
171
|
-
del type_hints["dependencies"]
|
|
172
218
|
# log_info(f"Type hints for {function_name}: {type_hints}")
|
|
173
219
|
|
|
174
220
|
# Filter out return type and only process parameters
|
|
@@ -177,7 +223,18 @@ class Function(BaseModel):
|
|
|
177
223
|
for name in sig.parameters
|
|
178
224
|
if name != "return"
|
|
179
225
|
and name
|
|
180
|
-
not in [
|
|
226
|
+
not in [
|
|
227
|
+
"agent",
|
|
228
|
+
"team",
|
|
229
|
+
"run_context",
|
|
230
|
+
"session_state",
|
|
231
|
+
"dependencies",
|
|
232
|
+
"self",
|
|
233
|
+
"images",
|
|
234
|
+
"videos",
|
|
235
|
+
"audios",
|
|
236
|
+
"files",
|
|
237
|
+
]
|
|
181
238
|
}
|
|
182
239
|
|
|
183
240
|
# Parse docstring for parameters
|
|
@@ -210,13 +267,14 @@ class Function(BaseModel):
|
|
|
210
267
|
not in [
|
|
211
268
|
"agent",
|
|
212
269
|
"team",
|
|
270
|
+
"run_context",
|
|
213
271
|
"session_state",
|
|
272
|
+
"dependencies",
|
|
214
273
|
"self",
|
|
215
274
|
"images",
|
|
216
275
|
"videos",
|
|
217
276
|
"audios",
|
|
218
277
|
"files",
|
|
219
|
-
"dependencies",
|
|
220
278
|
]
|
|
221
279
|
]
|
|
222
280
|
else:
|
|
@@ -229,13 +287,14 @@ class Function(BaseModel):
|
|
|
229
287
|
not in [
|
|
230
288
|
"agent",
|
|
231
289
|
"team",
|
|
290
|
+
"run_context",
|
|
232
291
|
"session_state",
|
|
292
|
+
"dependencies",
|
|
233
293
|
"self",
|
|
234
294
|
"images",
|
|
235
295
|
"videos",
|
|
236
296
|
"audios",
|
|
237
297
|
"files",
|
|
238
|
-
"dependencies",
|
|
239
298
|
]
|
|
240
299
|
]
|
|
241
300
|
|
|
@@ -285,8 +344,12 @@ class Function(BaseModel):
|
|
|
285
344
|
del type_hints["agent"]
|
|
286
345
|
if "team" in sig.parameters and "team" in type_hints:
|
|
287
346
|
del type_hints["team"]
|
|
347
|
+
if "run_context" in sig.parameters and "run_context" in type_hints:
|
|
348
|
+
del type_hints["run_context"]
|
|
288
349
|
if "session_state" in sig.parameters and "session_state" in type_hints:
|
|
289
350
|
del type_hints["session_state"]
|
|
351
|
+
if "dependencies" in sig.parameters and "dependencies" in type_hints:
|
|
352
|
+
del type_hints["dependencies"]
|
|
290
353
|
if "images" in sig.parameters and "images" in type_hints:
|
|
291
354
|
del type_hints["images"]
|
|
292
355
|
if "videos" in sig.parameters and "videos" in type_hints:
|
|
@@ -295,8 +358,6 @@ class Function(BaseModel):
|
|
|
295
358
|
del type_hints["audios"]
|
|
296
359
|
if "files" in sig.parameters and "files" in type_hints:
|
|
297
360
|
del type_hints["files"]
|
|
298
|
-
if "dependencies" in sig.parameters and "dependencies" in type_hints:
|
|
299
|
-
del type_hints["dependencies"]
|
|
300
361
|
# log_info(f"Type hints for {self.name}: {type_hints}")
|
|
301
362
|
|
|
302
363
|
# Filter out return type and only process parameters
|
|
@@ -304,13 +365,14 @@ class Function(BaseModel):
|
|
|
304
365
|
"return",
|
|
305
366
|
"agent",
|
|
306
367
|
"team",
|
|
368
|
+
"run_context",
|
|
307
369
|
"session_state",
|
|
370
|
+
"dependencies",
|
|
308
371
|
"self",
|
|
309
372
|
"images",
|
|
310
373
|
"videos",
|
|
311
374
|
"audios",
|
|
312
375
|
"files",
|
|
313
|
-
"dependencies",
|
|
314
376
|
]
|
|
315
377
|
if self.requires_user_input and self.user_input_fields:
|
|
316
378
|
if len(self.user_input_fields) == 0:
|
|
@@ -400,7 +462,7 @@ class Function(BaseModel):
|
|
|
400
462
|
@staticmethod
|
|
401
463
|
def _wrap_callable(func: Callable) -> Callable:
|
|
402
464
|
"""Wrap a callable with Pydantic's validate_call decorator, if relevant"""
|
|
403
|
-
from inspect import isasyncgenfunction, iscoroutinefunction
|
|
465
|
+
from inspect import isasyncgenfunction, iscoroutinefunction, signature
|
|
404
466
|
|
|
405
467
|
pydantic_version = Version(version("pydantic"))
|
|
406
468
|
|
|
@@ -418,6 +480,10 @@ class Function(BaseModel):
|
|
|
418
480
|
# Don't wrap callables that are already wrapped with validate_call
|
|
419
481
|
elif getattr(func, "_wrapped_for_validation", False):
|
|
420
482
|
return func
|
|
483
|
+
# Don't wrap functions with session_state parameter
|
|
484
|
+
# session_state needs to be passed by reference, not copied by pydantic's validation
|
|
485
|
+
elif "session_state" in signature(func).parameters:
|
|
486
|
+
return func
|
|
421
487
|
# Wrap the callable with validate_call
|
|
422
488
|
else:
|
|
423
489
|
wrapped = validate_call(func, config=dict(arbitrary_types_allowed=True)) # type: ignore
|
|
@@ -468,11 +534,23 @@ class Function(BaseModel):
|
|
|
468
534
|
name
|
|
469
535
|
for name in self.parameters["properties"]
|
|
470
536
|
if name
|
|
471
|
-
not in [
|
|
537
|
+
not in [
|
|
538
|
+
"agent",
|
|
539
|
+
"team",
|
|
540
|
+
"run_context",
|
|
541
|
+
"session_state",
|
|
542
|
+
"dependencies",
|
|
543
|
+
"images",
|
|
544
|
+
"videos",
|
|
545
|
+
"audios",
|
|
546
|
+
"files",
|
|
547
|
+
"self",
|
|
548
|
+
]
|
|
472
549
|
]
|
|
473
550
|
|
|
474
551
|
def _get_cache_key(self, entrypoint_args: Dict[str, Any], call_args: Optional[Dict[str, Any]] = None) -> str:
|
|
475
552
|
"""Generate a cache key based on function name and arguments."""
|
|
553
|
+
import json
|
|
476
554
|
from hashlib import md5
|
|
477
555
|
|
|
478
556
|
copy_entrypoint_args = entrypoint_args.copy()
|
|
@@ -481,8 +559,12 @@ class Function(BaseModel):
|
|
|
481
559
|
del copy_entrypoint_args["agent"]
|
|
482
560
|
if "team" in copy_entrypoint_args:
|
|
483
561
|
del copy_entrypoint_args["team"]
|
|
562
|
+
if "run_context" in copy_entrypoint_args:
|
|
563
|
+
del copy_entrypoint_args["run_context"]
|
|
484
564
|
if "session_state" in copy_entrypoint_args:
|
|
485
565
|
del copy_entrypoint_args["session_state"]
|
|
566
|
+
if "dependencies" in copy_entrypoint_args:
|
|
567
|
+
del copy_entrypoint_args["dependencies"]
|
|
486
568
|
if "images" in copy_entrypoint_args:
|
|
487
569
|
del copy_entrypoint_args["images"]
|
|
488
570
|
if "videos" in copy_entrypoint_args:
|
|
@@ -491,9 +573,8 @@ class Function(BaseModel):
|
|
|
491
573
|
del copy_entrypoint_args["audios"]
|
|
492
574
|
if "files" in copy_entrypoint_args:
|
|
493
575
|
del copy_entrypoint_args["files"]
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
args_str = str(copy_entrypoint_args)
|
|
576
|
+
# Use json.dumps with sort_keys=True to ensure consistent ordering regardless of dict key order
|
|
577
|
+
args_str = json.dumps(copy_entrypoint_args, sort_keys=True, default=str)
|
|
497
578
|
|
|
498
579
|
kwargs_str = str(sorted((call_args or {}).items()))
|
|
499
580
|
key_str = f"{self.name}:{args_str}:{kwargs_str}"
|
|
@@ -617,8 +698,14 @@ class FunctionCall(BaseModel):
|
|
|
617
698
|
if "team" in signature(self.function.pre_hook).parameters:
|
|
618
699
|
pre_hook_args["team"] = self.function._team
|
|
619
700
|
# Check if the pre-hook has an session_state argument
|
|
701
|
+
if "run_context" in signature(self.function.pre_hook).parameters:
|
|
702
|
+
pre_hook_args["run_context"] = self.function._run_context
|
|
703
|
+
# Check if the pre-hook has an session_state argument
|
|
620
704
|
if "session_state" in signature(self.function.pre_hook).parameters:
|
|
621
705
|
pre_hook_args["session_state"] = self.function._session_state
|
|
706
|
+
# Check if the pre-hook has an dependencies argument
|
|
707
|
+
if "dependencies" in signature(self.function.pre_hook).parameters:
|
|
708
|
+
pre_hook_args["dependencies"] = self.function._dependencies
|
|
622
709
|
# Check if the pre-hook has an fc argument
|
|
623
710
|
if "fc" in signature(self.function.pre_hook).parameters:
|
|
624
711
|
pre_hook_args["fc"] = self
|
|
@@ -645,8 +732,14 @@ class FunctionCall(BaseModel):
|
|
|
645
732
|
if "team" in signature(self.function.post_hook).parameters:
|
|
646
733
|
post_hook_args["team"] = self.function._team
|
|
647
734
|
# Check if the post-hook has an session_state argument
|
|
735
|
+
if "run_context" in signature(self.function.post_hook).parameters:
|
|
736
|
+
post_hook_args["run_context"] = self.function._run_context
|
|
737
|
+
# Check if the post-hook has an session_state argument
|
|
648
738
|
if "session_state" in signature(self.function.post_hook).parameters:
|
|
649
739
|
post_hook_args["session_state"] = self.function._session_state
|
|
740
|
+
# Check if the post-hook has an dependencies argument
|
|
741
|
+
if "dependencies" in signature(self.function.post_hook).parameters:
|
|
742
|
+
post_hook_args["dependencies"] = self.function._dependencies
|
|
650
743
|
# Check if the post-hook has an fc argument
|
|
651
744
|
if "fc" in signature(self.function.post_hook).parameters:
|
|
652
745
|
post_hook_args["fc"] = self
|
|
@@ -670,6 +763,9 @@ class FunctionCall(BaseModel):
|
|
|
670
763
|
# Check if the entrypoint has an team argument
|
|
671
764
|
if "team" in signature(self.function.entrypoint).parameters: # type: ignore
|
|
672
765
|
entrypoint_args["team"] = self.function._team
|
|
766
|
+
# Check if the entrypoint has an run_context argument
|
|
767
|
+
if "run_context" in signature(self.function.entrypoint).parameters: # type: ignore
|
|
768
|
+
entrypoint_args["run_context"] = self.function._run_context
|
|
673
769
|
# Check if the entrypoint has an session_state argument
|
|
674
770
|
if "session_state" in signature(self.function.entrypoint).parameters: # type: ignore
|
|
675
771
|
entrypoint_args["session_state"] = self.function._session_state
|
|
@@ -702,13 +798,15 @@ class FunctionCall(BaseModel):
|
|
|
702
798
|
# Check if the hook has an team argument
|
|
703
799
|
if "team" in signature(hook).parameters:
|
|
704
800
|
hook_args["team"] = self.function._team
|
|
801
|
+
# Check if the hook has an run_context argument
|
|
802
|
+
if "run_context" in signature(hook).parameters:
|
|
803
|
+
hook_args["run_context"] = self.function._run_context
|
|
705
804
|
# Check if the hook has an session_state argument
|
|
706
805
|
if "session_state" in signature(hook).parameters:
|
|
707
806
|
hook_args["session_state"] = self.function._session_state
|
|
708
807
|
# Check if the hook has an dependencies argument
|
|
709
808
|
if "dependencies" in signature(hook).parameters:
|
|
710
809
|
hook_args["dependencies"] = self.function._dependencies
|
|
711
|
-
|
|
712
810
|
if "name" in signature(hook).parameters:
|
|
713
811
|
hook_args["name"] = name
|
|
714
812
|
if "function_name" in signature(hook).parameters:
|
|
@@ -799,6 +897,9 @@ class FunctionCall(BaseModel):
|
|
|
799
897
|
return FunctionExecutionResult(status="success", result=cached_result)
|
|
800
898
|
|
|
801
899
|
# Execute function
|
|
900
|
+
execution_result: FunctionExecutionResult
|
|
901
|
+
exception_to_raise = None
|
|
902
|
+
|
|
802
903
|
try:
|
|
803
904
|
# Build and execute the nested chain of hooks
|
|
804
905
|
if self.function.tool_hooks is not None:
|
|
@@ -807,13 +908,16 @@ class FunctionCall(BaseModel):
|
|
|
807
908
|
else:
|
|
808
909
|
result = self.function.entrypoint(**entrypoint_args, **self.arguments) # type: ignore
|
|
809
910
|
|
|
810
|
-
updated_session_state = None
|
|
811
|
-
if entrypoint_args.get("session_state") is not None:
|
|
812
|
-
updated_session_state = entrypoint_args.get("session_state")
|
|
813
|
-
|
|
814
911
|
# Handle generator case
|
|
815
912
|
if isgenerator(result):
|
|
816
913
|
self.result = result # Store generator directly, can't cache
|
|
914
|
+
# For generators, don't capture updated_session_state yet -
|
|
915
|
+
# session_state is passed by reference, so mutations made during
|
|
916
|
+
# generator iteration are already reflected in the original dict.
|
|
917
|
+
# Returning None prevents stale state from being merged later.
|
|
918
|
+
execution_result = FunctionExecutionResult(
|
|
919
|
+
status="success", result=self.result, updated_session_state=None
|
|
920
|
+
)
|
|
817
921
|
else:
|
|
818
922
|
self.result = result
|
|
819
923
|
# Only cache non-generator results
|
|
@@ -822,22 +926,40 @@ class FunctionCall(BaseModel):
|
|
|
822
926
|
cache_file = self.function._get_cache_file_path(cache_key)
|
|
823
927
|
self.function._save_to_cache(cache_file, self.result)
|
|
824
928
|
|
|
929
|
+
updated_session_state = None
|
|
930
|
+
if entrypoint_args.get("run_context") is not None:
|
|
931
|
+
run_context = entrypoint_args.get("run_context")
|
|
932
|
+
updated_session_state = (
|
|
933
|
+
run_context.session_state
|
|
934
|
+
if run_context is not None and run_context.session_state is not None
|
|
935
|
+
else None
|
|
936
|
+
)
|
|
937
|
+
else:
|
|
938
|
+
if self.function._session_state is not None:
|
|
939
|
+
updated_session_state = self.function._session_state
|
|
940
|
+
|
|
941
|
+
execution_result = FunctionExecutionResult(
|
|
942
|
+
status="success", result=self.result, updated_session_state=updated_session_state
|
|
943
|
+
)
|
|
944
|
+
|
|
825
945
|
except AgentRunException as e:
|
|
826
946
|
log_debug(f"{e.__class__.__name__}: {e}")
|
|
827
947
|
self.error = str(e)
|
|
828
|
-
|
|
948
|
+
exception_to_raise = e
|
|
949
|
+
execution_result = FunctionExecutionResult(status="failure", error=str(e))
|
|
829
950
|
except Exception as e:
|
|
830
951
|
log_warning(f"Could not run function {self.get_call_str()}")
|
|
831
952
|
log_exception(e)
|
|
832
953
|
self.error = str(e)
|
|
833
|
-
|
|
954
|
+
execution_result = FunctionExecutionResult(status="failure", error=str(e))
|
|
955
|
+
|
|
956
|
+
finally:
|
|
957
|
+
self._handle_post_hook()
|
|
834
958
|
|
|
835
|
-
|
|
836
|
-
|
|
959
|
+
if exception_to_raise is not None:
|
|
960
|
+
raise exception_to_raise
|
|
837
961
|
|
|
838
|
-
return
|
|
839
|
-
status="success", result=self.result, updated_session_state=updated_session_state
|
|
840
|
-
)
|
|
962
|
+
return execution_result
|
|
841
963
|
|
|
842
964
|
async def _handle_pre_hook_async(self):
|
|
843
965
|
"""Handles the async pre-hook for the function call."""
|
|
@@ -852,9 +974,15 @@ class FunctionCall(BaseModel):
|
|
|
852
974
|
# Check if the pre-hook has an team argument
|
|
853
975
|
if "team" in signature(self.function.pre_hook).parameters:
|
|
854
976
|
pre_hook_args["team"] = self.function._team
|
|
977
|
+
# Check if the pre-hook has an run_context argument
|
|
978
|
+
if "run_context" in signature(self.function.pre_hook).parameters:
|
|
979
|
+
pre_hook_args["run_context"] = self.function._run_context
|
|
855
980
|
# Check if the pre-hook has an session_state argument
|
|
856
981
|
if "session_state" in signature(self.function.pre_hook).parameters:
|
|
857
982
|
pre_hook_args["session_state"] = self.function._session_state
|
|
983
|
+
# Check if the pre-hook has an dependencies argument
|
|
984
|
+
if "dependencies" in signature(self.function.pre_hook).parameters:
|
|
985
|
+
pre_hook_args["dependencies"] = self.function._dependencies
|
|
858
986
|
# Check if the pre-hook has an fc argument
|
|
859
987
|
if "fc" in signature(self.function.pre_hook).parameters:
|
|
860
988
|
pre_hook_args["fc"] = self
|
|
@@ -881,9 +1009,15 @@ class FunctionCall(BaseModel):
|
|
|
881
1009
|
# Check if the post-hook has an team argument
|
|
882
1010
|
if "team" in signature(self.function.post_hook).parameters:
|
|
883
1011
|
post_hook_args["team"] = self.function._team
|
|
1012
|
+
# Check if the post-hook has an run_context argument
|
|
1013
|
+
if "run_context" in signature(self.function.post_hook).parameters:
|
|
1014
|
+
post_hook_args["run_context"] = self.function._run_context
|
|
884
1015
|
# Check if the post-hook has an session_state argument
|
|
885
1016
|
if "session_state" in signature(self.function.post_hook).parameters:
|
|
886
1017
|
post_hook_args["session_state"] = self.function._session_state
|
|
1018
|
+
# Check if the post-hook has an dependencies argument
|
|
1019
|
+
if "dependencies" in signature(self.function.post_hook).parameters:
|
|
1020
|
+
post_hook_args["dependencies"] = self.function._dependencies
|
|
887
1021
|
|
|
888
1022
|
# Check if the post-hook has an fc argument
|
|
889
1023
|
if "fc" in signature(self.function.post_hook).parameters:
|
|
@@ -991,6 +1125,9 @@ class FunctionCall(BaseModel):
|
|
|
991
1125
|
return FunctionExecutionResult(status="success", result=cached_result)
|
|
992
1126
|
|
|
993
1127
|
# Execute function
|
|
1128
|
+
execution_result: FunctionExecutionResult
|
|
1129
|
+
exception_to_raise = None
|
|
1130
|
+
|
|
994
1131
|
try:
|
|
995
1132
|
# Build and execute the nested chain of hooks
|
|
996
1133
|
if self.function.tool_hooks is not None:
|
|
@@ -1013,29 +1150,47 @@ class FunctionCall(BaseModel):
|
|
|
1013
1150
|
cache_file = self.function._get_cache_file_path(cache_key)
|
|
1014
1151
|
self.function._save_to_cache(cache_file, self.result)
|
|
1015
1152
|
|
|
1016
|
-
updated_session_state
|
|
1017
|
-
|
|
1018
|
-
|
|
1153
|
+
# For generators, don't capture updated_session_state -
|
|
1154
|
+
# session_state is passed by reference, so mutations made during
|
|
1155
|
+
# generator iteration are already reflected in the original dict.
|
|
1156
|
+
# Returning None prevents stale state from being merged later.
|
|
1157
|
+
if isgenerator(self.result) or isasyncgen(self.result):
|
|
1158
|
+
updated_session_state = None
|
|
1159
|
+
else:
|
|
1160
|
+
updated_session_state = None
|
|
1161
|
+
if entrypoint_args.get("run_context") is not None:
|
|
1162
|
+
run_context = entrypoint_args.get("run_context")
|
|
1163
|
+
updated_session_state = (
|
|
1164
|
+
run_context.session_state
|
|
1165
|
+
if run_context is not None and run_context.session_state is not None
|
|
1166
|
+
else None
|
|
1167
|
+
)
|
|
1168
|
+
|
|
1169
|
+
execution_result = FunctionExecutionResult(
|
|
1170
|
+
status="success", result=self.result, updated_session_state=updated_session_state
|
|
1171
|
+
)
|
|
1019
1172
|
|
|
1020
1173
|
except AgentRunException as e:
|
|
1021
1174
|
log_debug(f"{e.__class__.__name__}: {e}")
|
|
1022
1175
|
self.error = str(e)
|
|
1023
|
-
|
|
1176
|
+
exception_to_raise = e
|
|
1177
|
+
execution_result = FunctionExecutionResult(status="failure", error=str(e))
|
|
1024
1178
|
except Exception as e:
|
|
1025
1179
|
log_warning(f"Could not run function {self.get_call_str()}")
|
|
1026
1180
|
log_exception(e)
|
|
1027
1181
|
self.error = str(e)
|
|
1028
|
-
|
|
1182
|
+
execution_result = FunctionExecutionResult(status="failure", error=str(e))
|
|
1029
1183
|
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1184
|
+
finally:
|
|
1185
|
+
if iscoroutinefunction(self.function.post_hook):
|
|
1186
|
+
await self._handle_post_hook_async()
|
|
1187
|
+
else:
|
|
1188
|
+
self._handle_post_hook()
|
|
1035
1189
|
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1190
|
+
if exception_to_raise is not None:
|
|
1191
|
+
raise exception_to_raise
|
|
1192
|
+
|
|
1193
|
+
return execution_result
|
|
1039
1194
|
|
|
1040
1195
|
|
|
1041
1196
|
class ToolResult(BaseModel):
|