agno 2.2.13__py3-none-any.whl → 2.4.3__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/__init__.py +6 -0
- agno/agent/agent.py +5252 -3145
- agno/agent/remote.py +525 -0
- agno/api/api.py +2 -0
- agno/client/__init__.py +3 -0
- agno/client/a2a/__init__.py +10 -0
- agno/client/a2a/client.py +554 -0
- agno/client/a2a/schemas.py +112 -0
- agno/client/a2a/utils.py +369 -0
- agno/client/os.py +2669 -0
- agno/compression/__init__.py +3 -0
- agno/compression/manager.py +247 -0
- agno/culture/manager.py +2 -2
- agno/db/base.py +927 -6
- agno/db/dynamo/dynamo.py +788 -2
- agno/db/dynamo/schemas.py +128 -0
- agno/db/dynamo/utils.py +26 -3
- agno/db/firestore/firestore.py +674 -50
- agno/db/firestore/schemas.py +41 -0
- agno/db/firestore/utils.py +25 -10
- agno/db/gcs_json/gcs_json_db.py +506 -3
- agno/db/gcs_json/utils.py +14 -2
- agno/db/in_memory/in_memory_db.py +203 -4
- agno/db/in_memory/utils.py +14 -2
- agno/db/json/json_db.py +498 -2
- agno/db/json/utils.py +14 -2
- agno/db/migrations/manager.py +199 -0
- agno/db/migrations/utils.py +19 -0
- agno/db/migrations/v1_to_v2.py +54 -16
- agno/db/migrations/versions/__init__.py +0 -0
- agno/db/migrations/versions/v2_3_0.py +977 -0
- agno/db/mongo/async_mongo.py +1013 -39
- agno/db/mongo/mongo.py +684 -4
- agno/db/mongo/schemas.py +48 -0
- agno/db/mongo/utils.py +17 -0
- agno/db/mysql/__init__.py +2 -1
- agno/db/mysql/async_mysql.py +2958 -0
- agno/db/mysql/mysql.py +722 -53
- agno/db/mysql/schemas.py +77 -11
- agno/db/mysql/utils.py +151 -8
- agno/db/postgres/async_postgres.py +1254 -137
- agno/db/postgres/postgres.py +2316 -93
- agno/db/postgres/schemas.py +153 -21
- agno/db/postgres/utils.py +22 -7
- agno/db/redis/redis.py +531 -3
- agno/db/redis/schemas.py +36 -0
- agno/db/redis/utils.py +31 -15
- agno/db/schemas/evals.py +1 -0
- agno/db/schemas/memory.py +20 -9
- agno/db/singlestore/schemas.py +70 -1
- agno/db/singlestore/singlestore.py +737 -74
- agno/db/singlestore/utils.py +13 -3
- agno/db/sqlite/async_sqlite.py +1069 -89
- agno/db/sqlite/schemas.py +133 -1
- agno/db/sqlite/sqlite.py +2203 -165
- agno/db/sqlite/utils.py +21 -11
- agno/db/surrealdb/models.py +25 -0
- agno/db/surrealdb/surrealdb.py +603 -1
- agno/db/utils.py +60 -0
- agno/eval/__init__.py +26 -3
- agno/eval/accuracy.py +25 -12
- agno/eval/agent_as_judge.py +871 -0
- agno/eval/base.py +29 -0
- agno/eval/performance.py +10 -4
- agno/eval/reliability.py +22 -13
- agno/eval/utils.py +2 -1
- agno/exceptions.py +42 -0
- agno/hooks/__init__.py +3 -0
- agno/hooks/decorator.py +164 -0
- agno/integrations/discord/client.py +13 -2
- agno/knowledge/__init__.py +4 -0
- agno/knowledge/chunking/code.py +90 -0
- agno/knowledge/chunking/document.py +65 -4
- agno/knowledge/chunking/fixed.py +4 -1
- agno/knowledge/chunking/markdown.py +102 -11
- agno/knowledge/chunking/recursive.py +2 -2
- agno/knowledge/chunking/semantic.py +130 -48
- agno/knowledge/chunking/strategy.py +18 -0
- agno/knowledge/embedder/azure_openai.py +0 -1
- agno/knowledge/embedder/google.py +1 -1
- agno/knowledge/embedder/mistral.py +1 -1
- agno/knowledge/embedder/nebius.py +1 -1
- agno/knowledge/embedder/openai.py +16 -12
- agno/knowledge/filesystem.py +412 -0
- agno/knowledge/knowledge.py +4261 -1199
- agno/knowledge/protocol.py +134 -0
- agno/knowledge/reader/arxiv_reader.py +3 -2
- agno/knowledge/reader/base.py +9 -7
- agno/knowledge/reader/csv_reader.py +91 -42
- agno/knowledge/reader/docx_reader.py +9 -10
- agno/knowledge/reader/excel_reader.py +225 -0
- agno/knowledge/reader/field_labeled_csv_reader.py +38 -48
- agno/knowledge/reader/firecrawl_reader.py +3 -2
- agno/knowledge/reader/json_reader.py +16 -22
- agno/knowledge/reader/markdown_reader.py +15 -14
- agno/knowledge/reader/pdf_reader.py +33 -28
- agno/knowledge/reader/pptx_reader.py +9 -10
- agno/knowledge/reader/reader_factory.py +135 -1
- agno/knowledge/reader/s3_reader.py +8 -16
- agno/knowledge/reader/tavily_reader.py +3 -3
- agno/knowledge/reader/text_reader.py +15 -14
- agno/knowledge/reader/utils/__init__.py +17 -0
- agno/knowledge/reader/utils/spreadsheet.py +114 -0
- agno/knowledge/reader/web_search_reader.py +8 -65
- agno/knowledge/reader/website_reader.py +16 -13
- agno/knowledge/reader/wikipedia_reader.py +36 -3
- agno/knowledge/reader/youtube_reader.py +3 -2
- agno/knowledge/remote_content/__init__.py +33 -0
- agno/knowledge/remote_content/config.py +266 -0
- agno/knowledge/remote_content/remote_content.py +105 -17
- agno/knowledge/utils.py +76 -22
- agno/learn/__init__.py +71 -0
- agno/learn/config.py +463 -0
- agno/learn/curate.py +185 -0
- agno/learn/machine.py +725 -0
- agno/learn/schemas.py +1114 -0
- agno/learn/stores/__init__.py +38 -0
- agno/learn/stores/decision_log.py +1156 -0
- agno/learn/stores/entity_memory.py +3275 -0
- agno/learn/stores/learned_knowledge.py +1583 -0
- agno/learn/stores/protocol.py +117 -0
- agno/learn/stores/session_context.py +1217 -0
- agno/learn/stores/user_memory.py +1495 -0
- agno/learn/stores/user_profile.py +1220 -0
- agno/learn/utils.py +209 -0
- agno/media.py +22 -6
- agno/memory/__init__.py +14 -1
- agno/memory/manager.py +223 -8
- 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 +434 -59
- agno/models/aws/bedrock.py +121 -20
- agno/models/aws/claude.py +131 -274
- agno/models/azure/ai_foundry.py +10 -6
- agno/models/azure/openai_chat.py +33 -10
- agno/models/base.py +1162 -561
- agno/models/cerebras/cerebras.py +120 -24
- agno/models/cerebras/cerebras_openai.py +21 -2
- agno/models/cohere/chat.py +65 -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 +959 -89
- agno/models/google/utils.py +22 -0
- agno/models/groq/groq.py +48 -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 +88 -9
- agno/models/litellm/litellm_openai.py +18 -1
- agno/models/message.py +24 -5
- agno/models/meta/llama.py +40 -13
- agno/models/meta/llama_openai.py +22 -21
- agno/models/metrics.py +12 -0
- agno/models/mistral/mistral.py +8 -4
- agno/models/n1n/__init__.py +3 -0
- agno/models/n1n/n1n.py +57 -0
- agno/models/nebius/nebius.py +6 -7
- agno/models/nvidia/nvidia.py +20 -3
- agno/models/ollama/__init__.py +2 -0
- agno/models/ollama/chat.py +17 -6
- agno/models/ollama/responses.py +100 -0
- agno/models/openai/__init__.py +2 -0
- agno/models/openai/chat.py +117 -26
- agno/models/openai/open_responses.py +46 -0
- agno/models/openai/responses.py +110 -32
- agno/models/openrouter/__init__.py +2 -0
- agno/models/openrouter/openrouter.py +67 -2
- agno/models/openrouter/responses.py +146 -0
- agno/models/perplexity/perplexity.py +19 -1
- agno/models/portkey/portkey.py +7 -6
- agno/models/requesty/requesty.py +19 -2
- agno/models/response.py +20 -2
- agno/models/sambanova/sambanova.py +20 -3
- agno/models/siliconflow/siliconflow.py +19 -2
- agno/models/together/together.py +20 -3
- agno/models/vercel/v0.py +20 -3
- agno/models/vertexai/claude.py +124 -4
- agno/models/vllm/vllm.py +19 -14
- agno/models/xai/xai.py +19 -2
- agno/os/app.py +467 -137
- agno/os/auth.py +253 -5
- agno/os/config.py +22 -0
- agno/os/interfaces/a2a/a2a.py +7 -6
- agno/os/interfaces/a2a/router.py +635 -26
- agno/os/interfaces/a2a/utils.py +32 -33
- agno/os/interfaces/agui/agui.py +5 -3
- agno/os/interfaces/agui/router.py +26 -16
- agno/os/interfaces/agui/utils.py +97 -57
- agno/os/interfaces/base.py +7 -7
- agno/os/interfaces/slack/router.py +16 -7
- agno/os/interfaces/slack/slack.py +7 -7
- agno/os/interfaces/whatsapp/router.py +35 -7
- agno/os/interfaces/whatsapp/security.py +3 -1
- agno/os/interfaces/whatsapp/whatsapp.py +11 -8
- agno/os/managers.py +326 -0
- agno/os/mcp.py +652 -79
- agno/os/middleware/__init__.py +4 -0
- agno/os/middleware/jwt.py +718 -115
- agno/os/middleware/trailing_slash.py +27 -0
- agno/os/router.py +105 -1558
- agno/os/routers/agents/__init__.py +3 -0
- agno/os/routers/agents/router.py +655 -0
- agno/os/routers/agents/schema.py +288 -0
- agno/os/routers/components/__init__.py +3 -0
- agno/os/routers/components/components.py +475 -0
- agno/os/routers/database.py +155 -0
- agno/os/routers/evals/evals.py +111 -18
- agno/os/routers/evals/schemas.py +38 -5
- agno/os/routers/evals/utils.py +80 -11
- agno/os/routers/health.py +3 -3
- agno/os/routers/knowledge/knowledge.py +284 -35
- agno/os/routers/knowledge/schemas.py +14 -2
- agno/os/routers/memory/memory.py +274 -11
- agno/os/routers/memory/schemas.py +44 -3
- agno/os/routers/metrics/metrics.py +30 -15
- agno/os/routers/metrics/schemas.py +10 -6
- agno/os/routers/registry/__init__.py +3 -0
- agno/os/routers/registry/registry.py +337 -0
- agno/os/routers/session/session.py +143 -14
- agno/os/routers/teams/__init__.py +3 -0
- agno/os/routers/teams/router.py +550 -0
- agno/os/routers/teams/schema.py +280 -0
- agno/os/routers/traces/__init__.py +3 -0
- agno/os/routers/traces/schemas.py +414 -0
- agno/os/routers/traces/traces.py +549 -0
- agno/os/routers/workflows/__init__.py +3 -0
- agno/os/routers/workflows/router.py +757 -0
- agno/os/routers/workflows/schema.py +139 -0
- agno/os/schema.py +157 -584
- agno/os/scopes.py +469 -0
- agno/os/settings.py +3 -0
- agno/os/utils.py +574 -185
- agno/reasoning/anthropic.py +85 -1
- agno/reasoning/azure_ai_foundry.py +93 -1
- agno/reasoning/deepseek.py +102 -2
- agno/reasoning/default.py +6 -7
- agno/reasoning/gemini.py +87 -3
- agno/reasoning/groq.py +109 -2
- agno/reasoning/helpers.py +6 -7
- agno/reasoning/manager.py +1238 -0
- agno/reasoning/ollama.py +93 -1
- agno/reasoning/openai.py +115 -1
- agno/reasoning/vertexai.py +85 -1
- agno/registry/__init__.py +3 -0
- agno/registry/registry.py +68 -0
- agno/remote/__init__.py +3 -0
- agno/remote/base.py +581 -0
- agno/run/__init__.py +2 -4
- agno/run/agent.py +134 -19
- agno/run/base.py +49 -1
- agno/run/cancel.py +65 -52
- agno/run/cancellation_management/__init__.py +9 -0
- agno/run/cancellation_management/base.py +78 -0
- agno/run/cancellation_management/in_memory_cancellation_manager.py +100 -0
- agno/run/cancellation_management/redis_cancellation_manager.py +236 -0
- agno/run/requirement.py +181 -0
- agno/run/team.py +111 -19
- agno/run/workflow.py +2 -1
- agno/session/agent.py +57 -92
- agno/session/summary.py +1 -1
- agno/session/team.py +62 -115
- agno/session/workflow.py +353 -57
- agno/skills/__init__.py +17 -0
- agno/skills/agent_skills.py +377 -0
- agno/skills/errors.py +32 -0
- agno/skills/loaders/__init__.py +4 -0
- agno/skills/loaders/base.py +27 -0
- agno/skills/loaders/local.py +216 -0
- agno/skills/skill.py +65 -0
- agno/skills/utils.py +107 -0
- agno/skills/validator.py +277 -0
- agno/table.py +10 -0
- agno/team/__init__.py +5 -1
- agno/team/remote.py +447 -0
- agno/team/team.py +3769 -2202
- agno/tools/brandfetch.py +27 -18
- agno/tools/browserbase.py +225 -16
- agno/tools/crawl4ai.py +3 -0
- agno/tools/duckduckgo.py +25 -71
- agno/tools/exa.py +0 -21
- agno/tools/file.py +14 -13
- agno/tools/file_generation.py +12 -6
- agno/tools/firecrawl.py +15 -7
- agno/tools/function.py +94 -113
- agno/tools/google_bigquery.py +11 -2
- agno/tools/google_drive.py +4 -3
- agno/tools/knowledge.py +9 -4
- agno/tools/mcp/mcp.py +301 -18
- agno/tools/mcp/multi_mcp.py +269 -14
- agno/tools/mem0.py +11 -10
- agno/tools/memory.py +47 -46
- agno/tools/mlx_transcribe.py +10 -7
- agno/tools/models/nebius.py +5 -5
- agno/tools/models_labs.py +20 -10
- agno/tools/nano_banana.py +151 -0
- agno/tools/parallel.py +0 -7
- agno/tools/postgres.py +76 -36
- agno/tools/python.py +14 -6
- agno/tools/reasoning.py +30 -23
- agno/tools/redshift.py +406 -0
- agno/tools/shopify.py +1519 -0
- agno/tools/spotify.py +919 -0
- agno/tools/tavily.py +4 -1
- agno/tools/toolkit.py +253 -18
- agno/tools/websearch.py +93 -0
- agno/tools/website.py +1 -1
- agno/tools/wikipedia.py +1 -1
- agno/tools/workflow.py +56 -48
- agno/tools/yfinance.py +12 -11
- agno/tracing/__init__.py +12 -0
- agno/tracing/exporter.py +161 -0
- agno/tracing/schemas.py +276 -0
- agno/tracing/setup.py +112 -0
- agno/utils/agent.py +251 -10
- agno/utils/cryptography.py +22 -0
- agno/utils/dttm.py +33 -0
- agno/utils/events.py +264 -7
- agno/utils/hooks.py +111 -3
- agno/utils/http.py +161 -2
- agno/utils/mcp.py +49 -8
- agno/utils/media.py +22 -1
- agno/utils/models/ai_foundry.py +9 -2
- agno/utils/models/claude.py +20 -5
- agno/utils/models/cohere.py +9 -2
- agno/utils/models/llama.py +9 -2
- agno/utils/models/mistral.py +4 -2
- agno/utils/os.py +0 -0
- agno/utils/print_response/agent.py +99 -16
- agno/utils/print_response/team.py +223 -24
- agno/utils/print_response/workflow.py +0 -2
- agno/utils/prompts.py +8 -6
- agno/utils/remote.py +23 -0
- agno/utils/response.py +1 -13
- agno/utils/string.py +91 -2
- agno/utils/team.py +62 -12
- agno/utils/tokens.py +657 -0
- agno/vectordb/base.py +15 -2
- agno/vectordb/cassandra/cassandra.py +1 -1
- agno/vectordb/chroma/__init__.py +2 -1
- agno/vectordb/chroma/chromadb.py +468 -23
- agno/vectordb/clickhouse/clickhousedb.py +1 -1
- agno/vectordb/couchbase/couchbase.py +6 -2
- agno/vectordb/lancedb/lance_db.py +7 -38
- agno/vectordb/lightrag/lightrag.py +7 -6
- agno/vectordb/milvus/milvus.py +118 -84
- agno/vectordb/mongodb/__init__.py +2 -1
- agno/vectordb/mongodb/mongodb.py +14 -31
- agno/vectordb/pgvector/pgvector.py +120 -66
- agno/vectordb/pineconedb/pineconedb.py +2 -19
- agno/vectordb/qdrant/__init__.py +2 -1
- agno/vectordb/qdrant/qdrant.py +33 -56
- agno/vectordb/redis/__init__.py +2 -1
- agno/vectordb/redis/redisdb.py +19 -31
- agno/vectordb/singlestore/singlestore.py +17 -9
- agno/vectordb/surrealdb/surrealdb.py +2 -38
- agno/vectordb/weaviate/__init__.py +2 -1
- agno/vectordb/weaviate/weaviate.py +7 -3
- agno/workflow/__init__.py +5 -1
- agno/workflow/agent.py +2 -2
- agno/workflow/condition.py +12 -10
- agno/workflow/loop.py +28 -9
- agno/workflow/parallel.py +21 -13
- agno/workflow/remote.py +362 -0
- agno/workflow/router.py +12 -9
- agno/workflow/step.py +261 -36
- agno/workflow/steps.py +12 -8
- agno/workflow/types.py +40 -77
- agno/workflow/workflow.py +939 -213
- {agno-2.2.13.dist-info → agno-2.4.3.dist-info}/METADATA +134 -181
- agno-2.4.3.dist-info/RECORD +677 -0
- {agno-2.2.13.dist-info → agno-2.4.3.dist-info}/WHEEL +1 -1
- agno/tools/googlesearch.py +0 -98
- agno/tools/memori.py +0 -339
- agno-2.2.13.dist-info/RECORD +0 -575
- {agno-2.2.13.dist-info → agno-2.4.3.dist-info}/licenses/LICENSE +0 -0
- {agno-2.2.13.dist-info → agno-2.4.3.dist-info}/top_level.txt +0 -0
agno/tools/file_generation.py
CHANGED
|
@@ -108,14 +108,16 @@ class FileGenerationTools(Toolkit):
|
|
|
108
108
|
# Save file to disk (if output_directory is set)
|
|
109
109
|
file_path = self._save_file_to_disk(json_content, filename)
|
|
110
110
|
|
|
111
|
+
content_bytes = json_content.encode("utf-8")
|
|
112
|
+
|
|
111
113
|
# Create FileArtifact
|
|
112
114
|
file_artifact = File(
|
|
113
115
|
id=str(uuid4()),
|
|
114
|
-
content=
|
|
116
|
+
content=content_bytes,
|
|
115
117
|
mime_type="application/json",
|
|
116
118
|
file_type="json",
|
|
117
119
|
filename=filename,
|
|
118
|
-
size=len(
|
|
120
|
+
size=len(content_bytes),
|
|
119
121
|
filepath=file_path if file_path else None,
|
|
120
122
|
)
|
|
121
123
|
|
|
@@ -195,14 +197,16 @@ class FileGenerationTools(Toolkit):
|
|
|
195
197
|
# Save file to disk (if output_directory is set)
|
|
196
198
|
file_path = self._save_file_to_disk(csv_content, filename)
|
|
197
199
|
|
|
200
|
+
content_bytes = csv_content.encode("utf-8")
|
|
201
|
+
|
|
198
202
|
# Create FileArtifact
|
|
199
203
|
file_artifact = File(
|
|
200
204
|
id=str(uuid4()),
|
|
201
|
-
content=
|
|
205
|
+
content=content_bytes,
|
|
202
206
|
mime_type="text/csv",
|
|
203
207
|
file_type="csv",
|
|
204
208
|
filename=filename,
|
|
205
|
-
size=len(
|
|
209
|
+
size=len(content_bytes),
|
|
206
210
|
filepath=file_path if file_path else None,
|
|
207
211
|
)
|
|
208
212
|
|
|
@@ -325,14 +329,16 @@ class FileGenerationTools(Toolkit):
|
|
|
325
329
|
# Save file to disk (if output_directory is set)
|
|
326
330
|
file_path = self._save_file_to_disk(content, filename)
|
|
327
331
|
|
|
332
|
+
content_bytes = content.encode("utf-8")
|
|
333
|
+
|
|
328
334
|
# Create FileArtifact
|
|
329
335
|
file_artifact = File(
|
|
330
336
|
id=str(uuid4()),
|
|
331
|
-
content=
|
|
337
|
+
content=content_bytes,
|
|
332
338
|
mime_type="text/plain",
|
|
333
339
|
file_type="txt",
|
|
334
340
|
filename=filename,
|
|
335
|
-
size=len(
|
|
341
|
+
size=len(content_bytes),
|
|
336
342
|
filepath=file_path if file_path else None,
|
|
337
343
|
)
|
|
338
344
|
|
agno/tools/firecrawl.py
CHANGED
|
@@ -101,8 +101,10 @@ class FirecrawlTools(Toolkit):
|
|
|
101
101
|
The results of the crawling.
|
|
102
102
|
"""
|
|
103
103
|
params: Dict[str, Any] = {}
|
|
104
|
-
if self.limit
|
|
105
|
-
params["limit"] = self.limit
|
|
104
|
+
if self.limit is not None:
|
|
105
|
+
params["limit"] = self.limit
|
|
106
|
+
elif limit is not None:
|
|
107
|
+
params["limit"] = limit
|
|
106
108
|
if self.formats:
|
|
107
109
|
params["scrape_options"] = ScrapeOptions(formats=self.formats) # type: ignore
|
|
108
110
|
|
|
@@ -129,15 +131,21 @@ class FirecrawlTools(Toolkit):
|
|
|
129
131
|
limit (int): The maximum number of results to return.
|
|
130
132
|
"""
|
|
131
133
|
params: Dict[str, Any] = {}
|
|
132
|
-
if self.limit
|
|
133
|
-
params["limit"] = self.limit
|
|
134
|
+
if self.limit is not None:
|
|
135
|
+
params["limit"] = self.limit
|
|
136
|
+
elif limit is not None:
|
|
137
|
+
params["limit"] = limit
|
|
134
138
|
if self.formats:
|
|
135
139
|
params["scrape_options"] = ScrapeOptions(formats=self.formats) # type: ignore
|
|
136
140
|
if self.search_params:
|
|
137
141
|
params.update(self.search_params)
|
|
138
142
|
|
|
139
143
|
search_result = self.app.search(query, **params)
|
|
140
|
-
|
|
141
|
-
|
|
144
|
+
|
|
145
|
+
if hasattr(search_result, "success"):
|
|
146
|
+
if search_result.success:
|
|
147
|
+
return json.dumps(search_result.data, cls=CustomJSONEncoder)
|
|
148
|
+
else:
|
|
149
|
+
return f"Error searching with the Firecrawl tool: {search_result.error}"
|
|
142
150
|
else:
|
|
143
|
-
return
|
|
151
|
+
return json.dumps(search_result.model_dump(), cls=CustomJSONEncoder)
|
agno/tools/function.py
CHANGED
|
@@ -54,9 +54,17 @@ class UserInputField:
|
|
|
54
54
|
|
|
55
55
|
@classmethod
|
|
56
56
|
def from_dict(cls, data: Dict[str, Any]) -> "UserInputField":
|
|
57
|
+
type_mapping = {"str": str, "int": int, "float": float, "bool": bool, "list": list, "dict": dict}
|
|
58
|
+
field_type_raw = data["field_type"]
|
|
59
|
+
if isinstance(field_type_raw, str):
|
|
60
|
+
field_type = type_mapping.get(field_type_raw, str)
|
|
61
|
+
elif isinstance(field_type_raw, type):
|
|
62
|
+
field_type = field_type_raw
|
|
63
|
+
else:
|
|
64
|
+
field_type = str
|
|
57
65
|
return cls(
|
|
58
66
|
name=data["name"],
|
|
59
|
-
field_type=
|
|
67
|
+
field_type=field_type,
|
|
60
68
|
description=data["description"],
|
|
61
69
|
value=data["value"],
|
|
62
70
|
)
|
|
@@ -125,10 +133,6 @@ class Function(BaseModel):
|
|
|
125
133
|
_team: Optional[Any] = None
|
|
126
134
|
# The run context that the function is associated with
|
|
127
135
|
_run_context: Optional[RunContext] = None
|
|
128
|
-
# The session state that the function is associated with
|
|
129
|
-
_session_state: Optional[Dict[str, Any]] = None
|
|
130
|
-
# The dependencies that the function is associated with
|
|
131
|
-
_dependencies: Optional[Dict[str, Any]] = None
|
|
132
136
|
|
|
133
137
|
# Media context that the function is associated with
|
|
134
138
|
_images: Optional[Sequence[Image]] = None
|
|
@@ -142,6 +146,19 @@ class Function(BaseModel):
|
|
|
142
146
|
include={"name", "description", "parameters", "strict", "requires_confirmation", "external_execution"},
|
|
143
147
|
)
|
|
144
148
|
|
|
149
|
+
@classmethod
|
|
150
|
+
def from_dict(cls, data: Dict[str, Any]) -> "Function":
|
|
151
|
+
"""Reconstruct a Function from a dictionary."""
|
|
152
|
+
|
|
153
|
+
return cls(
|
|
154
|
+
name=data.get("name"),
|
|
155
|
+
description=data.get("description"),
|
|
156
|
+
parameters=data.get("parameters"),
|
|
157
|
+
strict=data.get("strict"),
|
|
158
|
+
requires_confirmation=data.get("requires_confirmation", False),
|
|
159
|
+
external_execution=data.get("external_execution", False),
|
|
160
|
+
)
|
|
161
|
+
|
|
145
162
|
def model_copy(self, *, deep: bool = False) -> "Function":
|
|
146
163
|
"""
|
|
147
164
|
Override model_copy to handle callable fields that can't be deep copied (pickled).
|
|
@@ -201,10 +218,6 @@ class Function(BaseModel):
|
|
|
201
218
|
del type_hints["team"]
|
|
202
219
|
if "run_context" in sig.parameters and "run_context" in type_hints:
|
|
203
220
|
del type_hints["run_context"]
|
|
204
|
-
if "session_state" in sig.parameters and "session_state" in type_hints:
|
|
205
|
-
del type_hints["session_state"]
|
|
206
|
-
if "dependencies" in sig.parameters and "dependencies" in type_hints:
|
|
207
|
-
del type_hints["dependencies"]
|
|
208
221
|
|
|
209
222
|
# Remove media parameters from type hints as they are injected automatically
|
|
210
223
|
if "images" in sig.parameters and "images" in type_hints:
|
|
@@ -227,8 +240,6 @@ class Function(BaseModel):
|
|
|
227
240
|
"agent",
|
|
228
241
|
"team",
|
|
229
242
|
"run_context",
|
|
230
|
-
"session_state",
|
|
231
|
-
"dependencies",
|
|
232
243
|
"self",
|
|
233
244
|
"images",
|
|
234
245
|
"videos",
|
|
@@ -268,8 +279,6 @@ class Function(BaseModel):
|
|
|
268
279
|
"agent",
|
|
269
280
|
"team",
|
|
270
281
|
"run_context",
|
|
271
|
-
"session_state",
|
|
272
|
-
"dependencies",
|
|
273
282
|
"self",
|
|
274
283
|
"images",
|
|
275
284
|
"videos",
|
|
@@ -288,8 +297,6 @@ class Function(BaseModel):
|
|
|
288
297
|
"agent",
|
|
289
298
|
"team",
|
|
290
299
|
"run_context",
|
|
291
|
-
"session_state",
|
|
292
|
-
"dependencies",
|
|
293
300
|
"self",
|
|
294
301
|
"images",
|
|
295
302
|
"videos",
|
|
@@ -346,10 +353,6 @@ class Function(BaseModel):
|
|
|
346
353
|
del type_hints["team"]
|
|
347
354
|
if "run_context" in sig.parameters and "run_context" in type_hints:
|
|
348
355
|
del type_hints["run_context"]
|
|
349
|
-
if "session_state" in sig.parameters and "session_state" in type_hints:
|
|
350
|
-
del type_hints["session_state"]
|
|
351
|
-
if "dependencies" in sig.parameters and "dependencies" in type_hints:
|
|
352
|
-
del type_hints["dependencies"]
|
|
353
356
|
if "images" in sig.parameters and "images" in type_hints:
|
|
354
357
|
del type_hints["images"]
|
|
355
358
|
if "videos" in sig.parameters and "videos" in type_hints:
|
|
@@ -366,8 +369,6 @@ class Function(BaseModel):
|
|
|
366
369
|
"agent",
|
|
367
370
|
"team",
|
|
368
371
|
"run_context",
|
|
369
|
-
"session_state",
|
|
370
|
-
"dependencies",
|
|
371
372
|
"self",
|
|
372
373
|
"images",
|
|
373
374
|
"videos",
|
|
@@ -480,10 +481,15 @@ class Function(BaseModel):
|
|
|
480
481
|
# Don't wrap callables that are already wrapped with validate_call
|
|
481
482
|
elif getattr(func, "_wrapped_for_validation", False):
|
|
482
483
|
return func
|
|
483
|
-
|
|
484
|
-
#
|
|
485
|
-
|
|
484
|
+
|
|
485
|
+
# Don't wrap functions with framework-injected parameters
|
|
486
|
+
# These parameters (agent, team) are
|
|
487
|
+
# injected by the framework at runtime and shouldn't be validated by Pydantic
|
|
488
|
+
sig = signature(func)
|
|
489
|
+
framework_params = {"agent", "team"}
|
|
490
|
+
if framework_params & set(sig.parameters.keys()):
|
|
486
491
|
return func
|
|
492
|
+
|
|
487
493
|
# Wrap the callable with validate_call
|
|
488
494
|
else:
|
|
489
495
|
wrapped = validate_call(func, config=dict(arbitrary_types_allowed=True)) # type: ignore
|
|
@@ -538,8 +544,6 @@ class Function(BaseModel):
|
|
|
538
544
|
"agent",
|
|
539
545
|
"team",
|
|
540
546
|
"run_context",
|
|
541
|
-
"session_state",
|
|
542
|
-
"dependencies",
|
|
543
547
|
"images",
|
|
544
548
|
"videos",
|
|
545
549
|
"audios",
|
|
@@ -561,10 +565,6 @@ class Function(BaseModel):
|
|
|
561
565
|
del copy_entrypoint_args["team"]
|
|
562
566
|
if "run_context" in copy_entrypoint_args:
|
|
563
567
|
del copy_entrypoint_args["run_context"]
|
|
564
|
-
if "session_state" in copy_entrypoint_args:
|
|
565
|
-
del copy_entrypoint_args["session_state"]
|
|
566
|
-
if "dependencies" in copy_entrypoint_args:
|
|
567
|
-
del copy_entrypoint_args["dependencies"]
|
|
568
568
|
if "images" in copy_entrypoint_args:
|
|
569
569
|
del copy_entrypoint_args["images"]
|
|
570
570
|
if "videos" in copy_entrypoint_args:
|
|
@@ -691,21 +691,15 @@ class FunctionCall(BaseModel):
|
|
|
691
691
|
from inspect import signature
|
|
692
692
|
|
|
693
693
|
pre_hook_args = {}
|
|
694
|
-
# Check if the pre-hook has
|
|
694
|
+
# Check if the pre-hook has an agent argument
|
|
695
695
|
if "agent" in signature(self.function.pre_hook).parameters:
|
|
696
696
|
pre_hook_args["agent"] = self.function._agent
|
|
697
|
-
# Check if the pre-hook has
|
|
697
|
+
# Check if the pre-hook has a team argument
|
|
698
698
|
if "team" in signature(self.function.pre_hook).parameters:
|
|
699
699
|
pre_hook_args["team"] = self.function._team
|
|
700
|
-
# Check if the pre-hook has
|
|
700
|
+
# Check if the pre-hook has a run_context argument
|
|
701
701
|
if "run_context" in signature(self.function.pre_hook).parameters:
|
|
702
702
|
pre_hook_args["run_context"] = self.function._run_context
|
|
703
|
-
# Check if the pre-hook has an session_state argument
|
|
704
|
-
if "session_state" in signature(self.function.pre_hook).parameters:
|
|
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
|
|
709
703
|
# Check if the pre-hook has an fc argument
|
|
710
704
|
if "fc" in signature(self.function.pre_hook).parameters:
|
|
711
705
|
pre_hook_args["fc"] = self
|
|
@@ -725,21 +719,15 @@ class FunctionCall(BaseModel):
|
|
|
725
719
|
from inspect import signature
|
|
726
720
|
|
|
727
721
|
post_hook_args = {}
|
|
728
|
-
# Check if the post-hook has
|
|
722
|
+
# Check if the post-hook has an agent argument
|
|
729
723
|
if "agent" in signature(self.function.post_hook).parameters:
|
|
730
724
|
post_hook_args["agent"] = self.function._agent
|
|
731
|
-
# Check if the post-hook has
|
|
725
|
+
# Check if the post-hook has a team argument
|
|
732
726
|
if "team" in signature(self.function.post_hook).parameters:
|
|
733
727
|
post_hook_args["team"] = self.function._team
|
|
734
|
-
# Check if the post-hook has
|
|
728
|
+
# Check if the post-hook has a run_context argument
|
|
735
729
|
if "run_context" in signature(self.function.post_hook).parameters:
|
|
736
730
|
post_hook_args["run_context"] = self.function._run_context
|
|
737
|
-
# Check if the post-hook has an session_state argument
|
|
738
|
-
if "session_state" in signature(self.function.post_hook).parameters:
|
|
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
|
|
743
731
|
# Check if the post-hook has an fc argument
|
|
744
732
|
if "fc" in signature(self.function.post_hook).parameters:
|
|
745
733
|
post_hook_args["fc"] = self
|
|
@@ -760,18 +748,12 @@ class FunctionCall(BaseModel):
|
|
|
760
748
|
# Check if the entrypoint has an agent argument
|
|
761
749
|
if "agent" in signature(self.function.entrypoint).parameters: # type: ignore
|
|
762
750
|
entrypoint_args["agent"] = self.function._agent
|
|
763
|
-
# Check if the entrypoint has
|
|
751
|
+
# Check if the entrypoint has a team argument
|
|
764
752
|
if "team" in signature(self.function.entrypoint).parameters: # type: ignore
|
|
765
753
|
entrypoint_args["team"] = self.function._team
|
|
766
|
-
# Check if the entrypoint has
|
|
754
|
+
# Check if the entrypoint has a run_context argument
|
|
767
755
|
if "run_context" in signature(self.function.entrypoint).parameters: # type: ignore
|
|
768
756
|
entrypoint_args["run_context"] = self.function._run_context
|
|
769
|
-
# Check if the entrypoint has an session_state argument
|
|
770
|
-
if "session_state" in signature(self.function.entrypoint).parameters: # type: ignore
|
|
771
|
-
entrypoint_args["session_state"] = self.function._session_state
|
|
772
|
-
# Check if the entrypoint has an dependencies argument
|
|
773
|
-
if "dependencies" in signature(self.function.entrypoint).parameters: # type: ignore
|
|
774
|
-
entrypoint_args["dependencies"] = self.function._dependencies
|
|
775
757
|
# Check if the entrypoint has an fc argument
|
|
776
758
|
if "fc" in signature(self.function.entrypoint).parameters: # type: ignore
|
|
777
759
|
entrypoint_args["fc"] = self
|
|
@@ -795,18 +777,12 @@ class FunctionCall(BaseModel):
|
|
|
795
777
|
# Check if the hook has an agent argument
|
|
796
778
|
if "agent" in signature(hook).parameters:
|
|
797
779
|
hook_args["agent"] = self.function._agent
|
|
798
|
-
# Check if the hook has
|
|
780
|
+
# Check if the hook has a team argument
|
|
799
781
|
if "team" in signature(hook).parameters:
|
|
800
782
|
hook_args["team"] = self.function._team
|
|
801
|
-
# Check if the hook has
|
|
783
|
+
# Check if the hook has a run_context argument
|
|
802
784
|
if "run_context" in signature(hook).parameters:
|
|
803
785
|
hook_args["run_context"] = self.function._run_context
|
|
804
|
-
# Check if the hook has an session_state argument
|
|
805
|
-
if "session_state" in signature(hook).parameters:
|
|
806
|
-
hook_args["session_state"] = self.function._session_state
|
|
807
|
-
# Check if the hook has an dependencies argument
|
|
808
|
-
if "dependencies" in signature(hook).parameters:
|
|
809
|
-
hook_args["dependencies"] = self.function._dependencies
|
|
810
786
|
if "name" in signature(hook).parameters:
|
|
811
787
|
hook_args["name"] = name
|
|
812
788
|
if "function_name" in signature(hook).parameters:
|
|
@@ -897,7 +873,7 @@ class FunctionCall(BaseModel):
|
|
|
897
873
|
return FunctionExecutionResult(status="success", result=cached_result)
|
|
898
874
|
|
|
899
875
|
# Execute function
|
|
900
|
-
execution_result
|
|
876
|
+
execution_result: FunctionExecutionResult
|
|
901
877
|
exception_to_raise = None
|
|
902
878
|
|
|
903
879
|
try:
|
|
@@ -908,21 +884,16 @@ class FunctionCall(BaseModel):
|
|
|
908
884
|
else:
|
|
909
885
|
result = self.function.entrypoint(**entrypoint_args, **self.arguments) # type: ignore
|
|
910
886
|
|
|
911
|
-
updated_session_state = None
|
|
912
|
-
if entrypoint_args.get("run_context") is not None:
|
|
913
|
-
run_context = entrypoint_args.get("run_context")
|
|
914
|
-
updated_session_state = (
|
|
915
|
-
run_context.session_state
|
|
916
|
-
if run_context is not None and run_context.session_state is not None
|
|
917
|
-
else None
|
|
918
|
-
)
|
|
919
|
-
else:
|
|
920
|
-
if self.function._session_state is not None:
|
|
921
|
-
updated_session_state = self.function._session_state
|
|
922
|
-
|
|
923
887
|
# Handle generator case
|
|
924
888
|
if isgenerator(result):
|
|
925
889
|
self.result = result # Store generator directly, can't cache
|
|
890
|
+
# For generators, don't capture updated_session_state yet -
|
|
891
|
+
# session_state is passed by reference, so mutations made during
|
|
892
|
+
# generator iteration are already reflected in the original dict.
|
|
893
|
+
# Returning None prevents stale state from being merged later.
|
|
894
|
+
execution_result = FunctionExecutionResult(
|
|
895
|
+
status="success", result=self.result, updated_session_state=None
|
|
896
|
+
)
|
|
926
897
|
else:
|
|
927
898
|
self.result = result
|
|
928
899
|
# Only cache non-generator results
|
|
@@ -931,14 +902,24 @@ class FunctionCall(BaseModel):
|
|
|
931
902
|
cache_file = self.function._get_cache_file_path(cache_key)
|
|
932
903
|
self.function._save_to_cache(cache_file, self.result)
|
|
933
904
|
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
905
|
+
updated_session_state = None
|
|
906
|
+
if entrypoint_args.get("run_context") is not None:
|
|
907
|
+
run_context = entrypoint_args.get("run_context")
|
|
908
|
+
updated_session_state = (
|
|
909
|
+
run_context.session_state
|
|
910
|
+
if run_context is not None and run_context.session_state is not None
|
|
911
|
+
else None
|
|
912
|
+
)
|
|
913
|
+
|
|
914
|
+
execution_result = FunctionExecutionResult(
|
|
915
|
+
status="success", result=self.result, updated_session_state=updated_session_state
|
|
916
|
+
)
|
|
937
917
|
|
|
938
918
|
except AgentRunException as e:
|
|
939
919
|
log_debug(f"{e.__class__.__name__}: {e}")
|
|
940
920
|
self.error = str(e)
|
|
941
921
|
exception_to_raise = e
|
|
922
|
+
execution_result = FunctionExecutionResult(status="failure", error=str(e))
|
|
942
923
|
except Exception as e:
|
|
943
924
|
log_warning(f"Could not run function {self.get_call_str()}")
|
|
944
925
|
log_exception(e)
|
|
@@ -948,10 +929,10 @@ class FunctionCall(BaseModel):
|
|
|
948
929
|
finally:
|
|
949
930
|
self._handle_post_hook()
|
|
950
931
|
|
|
951
|
-
|
|
952
|
-
|
|
932
|
+
if exception_to_raise is not None:
|
|
933
|
+
raise exception_to_raise
|
|
953
934
|
|
|
954
|
-
|
|
935
|
+
return execution_result
|
|
955
936
|
|
|
956
937
|
async def _handle_pre_hook_async(self):
|
|
957
938
|
"""Handles the async pre-hook for the function call."""
|
|
@@ -963,18 +944,12 @@ class FunctionCall(BaseModel):
|
|
|
963
944
|
# Check if the pre-hook has an agent argument
|
|
964
945
|
if "agent" in signature(self.function.pre_hook).parameters:
|
|
965
946
|
pre_hook_args["agent"] = self.function._agent
|
|
966
|
-
# Check if the pre-hook has
|
|
947
|
+
# Check if the pre-hook has a team argument
|
|
967
948
|
if "team" in signature(self.function.pre_hook).parameters:
|
|
968
949
|
pre_hook_args["team"] = self.function._team
|
|
969
|
-
# Check if the pre-hook has
|
|
950
|
+
# Check if the pre-hook has a run_context argument
|
|
970
951
|
if "run_context" in signature(self.function.pre_hook).parameters:
|
|
971
952
|
pre_hook_args["run_context"] = self.function._run_context
|
|
972
|
-
# Check if the pre-hook has an session_state argument
|
|
973
|
-
if "session_state" in signature(self.function.pre_hook).parameters:
|
|
974
|
-
pre_hook_args["session_state"] = self.function._session_state
|
|
975
|
-
# Check if the pre-hook has an dependencies argument
|
|
976
|
-
if "dependencies" in signature(self.function.pre_hook).parameters:
|
|
977
|
-
pre_hook_args["dependencies"] = self.function._dependencies
|
|
978
953
|
# Check if the pre-hook has an fc argument
|
|
979
954
|
if "fc" in signature(self.function.pre_hook).parameters:
|
|
980
955
|
pre_hook_args["fc"] = self
|
|
@@ -998,19 +973,12 @@ class FunctionCall(BaseModel):
|
|
|
998
973
|
# Check if the post-hook has an agent argument
|
|
999
974
|
if "agent" in signature(self.function.post_hook).parameters:
|
|
1000
975
|
post_hook_args["agent"] = self.function._agent
|
|
1001
|
-
# Check if the post-hook has
|
|
976
|
+
# Check if the post-hook has a team argument
|
|
1002
977
|
if "team" in signature(self.function.post_hook).parameters:
|
|
1003
978
|
post_hook_args["team"] = self.function._team
|
|
1004
|
-
# Check if the post-hook has
|
|
979
|
+
# Check if the post-hook has a run_context argument
|
|
1005
980
|
if "run_context" in signature(self.function.post_hook).parameters:
|
|
1006
981
|
post_hook_args["run_context"] = self.function._run_context
|
|
1007
|
-
# Check if the post-hook has an session_state argument
|
|
1008
|
-
if "session_state" in signature(self.function.post_hook).parameters:
|
|
1009
|
-
post_hook_args["session_state"] = self.function._session_state
|
|
1010
|
-
# Check if the post-hook has an dependencies argument
|
|
1011
|
-
if "dependencies" in signature(self.function.post_hook).parameters:
|
|
1012
|
-
post_hook_args["dependencies"] = self.function._dependencies
|
|
1013
|
-
|
|
1014
982
|
# Check if the post-hook has an fc argument
|
|
1015
983
|
if "fc" in signature(self.function.post_hook).parameters:
|
|
1016
984
|
post_hook_args["fc"] = self
|
|
@@ -1117,7 +1085,7 @@ class FunctionCall(BaseModel):
|
|
|
1117
1085
|
return FunctionExecutionResult(status="success", result=cached_result)
|
|
1118
1086
|
|
|
1119
1087
|
# Execute function
|
|
1120
|
-
execution_result
|
|
1088
|
+
execution_result: FunctionExecutionResult
|
|
1121
1089
|
exception_to_raise = None
|
|
1122
1090
|
|
|
1123
1091
|
try:
|
|
@@ -1131,10 +1099,15 @@ class FunctionCall(BaseModel):
|
|
|
1131
1099
|
else:
|
|
1132
1100
|
result = self.function.entrypoint(**entrypoint_args, **self.arguments)
|
|
1133
1101
|
|
|
1102
|
+
# Handle both sync and async entrypoints
|
|
1134
1103
|
if isasyncgenfunction(self.function.entrypoint):
|
|
1135
1104
|
self.result = result # Store async generator directly
|
|
1105
|
+
elif iscoroutinefunction(self.function.entrypoint):
|
|
1106
|
+
self.result = await result # Await coroutine result
|
|
1107
|
+
elif isgeneratorfunction(self.function.entrypoint):
|
|
1108
|
+
self.result = result # Store sync generator directly
|
|
1136
1109
|
else:
|
|
1137
|
-
self.result =
|
|
1110
|
+
self.result = result # Sync function, result is already computed
|
|
1138
1111
|
|
|
1139
1112
|
# Only cache if not a generator
|
|
1140
1113
|
if self.function.cache_results and not (isgenerator(self.result) or isasyncgen(self.result)):
|
|
@@ -1142,14 +1115,21 @@ class FunctionCall(BaseModel):
|
|
|
1142
1115
|
cache_file = self.function._get_cache_file_path(cache_key)
|
|
1143
1116
|
self.function._save_to_cache(cache_file, self.result)
|
|
1144
1117
|
|
|
1145
|
-
updated_session_state
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1118
|
+
# For generators, don't capture updated_session_state -
|
|
1119
|
+
# session_state is passed by reference, so mutations made during
|
|
1120
|
+
# generator iteration are already reflected in the original dict.
|
|
1121
|
+
# Returning None prevents stale state from being merged later.
|
|
1122
|
+
if isgenerator(self.result) or isasyncgen(self.result):
|
|
1123
|
+
updated_session_state = None
|
|
1124
|
+
else:
|
|
1125
|
+
updated_session_state = None
|
|
1126
|
+
if entrypoint_args.get("run_context") is not None:
|
|
1127
|
+
run_context = entrypoint_args.get("run_context")
|
|
1128
|
+
updated_session_state = (
|
|
1129
|
+
run_context.session_state
|
|
1130
|
+
if run_context is not None and run_context.session_state is not None
|
|
1131
|
+
else None
|
|
1132
|
+
)
|
|
1153
1133
|
|
|
1154
1134
|
execution_result = FunctionExecutionResult(
|
|
1155
1135
|
status="success", result=self.result, updated_session_state=updated_session_state
|
|
@@ -1159,6 +1139,7 @@ class FunctionCall(BaseModel):
|
|
|
1159
1139
|
log_debug(f"{e.__class__.__name__}: {e}")
|
|
1160
1140
|
self.error = str(e)
|
|
1161
1141
|
exception_to_raise = e
|
|
1142
|
+
execution_result = FunctionExecutionResult(status="failure", error=str(e))
|
|
1162
1143
|
except Exception as e:
|
|
1163
1144
|
log_warning(f"Could not run function {self.get_call_str()}")
|
|
1164
1145
|
log_exception(e)
|
|
@@ -1171,10 +1152,10 @@ class FunctionCall(BaseModel):
|
|
|
1171
1152
|
else:
|
|
1172
1153
|
self._handle_post_hook()
|
|
1173
1154
|
|
|
1174
|
-
|
|
1175
|
-
|
|
1155
|
+
if exception_to_raise is not None:
|
|
1156
|
+
raise exception_to_raise
|
|
1176
1157
|
|
|
1177
|
-
|
|
1158
|
+
return execution_result
|
|
1178
1159
|
|
|
1179
1160
|
|
|
1180
1161
|
class ToolResult(BaseModel):
|
agno/tools/google_bigquery.py
CHANGED
|
@@ -11,6 +11,15 @@ except ImportError:
|
|
|
11
11
|
raise ImportError("`bigquery` not installed. Please install using `pip install google-cloud-bigquery`")
|
|
12
12
|
|
|
13
13
|
|
|
14
|
+
def _clean_sql(sql: str) -> str:
|
|
15
|
+
"""Clean SQL query by normalizing whitespace while preserving token boundaries.
|
|
16
|
+
|
|
17
|
+
Replaces newlines with spaces (not empty strings) to prevent line comments
|
|
18
|
+
from swallowing subsequent SQL statements.
|
|
19
|
+
"""
|
|
20
|
+
return sql.replace("\\n", " ").replace("\n", " ")
|
|
21
|
+
|
|
22
|
+
|
|
14
23
|
class GoogleBigQueryTools(Toolkit):
|
|
15
24
|
def __init__(
|
|
16
25
|
self,
|
|
@@ -106,12 +115,12 @@ class GoogleBigQueryTools(Toolkit):
|
|
|
106
115
|
"""
|
|
107
116
|
try:
|
|
108
117
|
log_debug(f"Running Google SQL |\n{sql}")
|
|
109
|
-
cleaned_query = sql
|
|
118
|
+
cleaned_query = _clean_sql(sql)
|
|
110
119
|
job_config = bigquery.QueryJobConfig(default_dataset=f"{self.project}.{self.dataset}")
|
|
111
120
|
query_job = self.client.query(cleaned_query, job_config)
|
|
112
121
|
results = query_job.result()
|
|
113
122
|
results_str = str([dict(row) for row in results])
|
|
114
|
-
return results_str.replace("
|
|
123
|
+
return results_str.replace("\n", " ")
|
|
115
124
|
except Exception as e:
|
|
116
125
|
logger.error(f"Error while executing SQL: {e}")
|
|
117
126
|
return ""
|
agno/tools/google_drive.py
CHANGED
|
@@ -69,6 +69,7 @@ from pathlib import Path
|
|
|
69
69
|
from typing import Any, List, Optional, Union
|
|
70
70
|
|
|
71
71
|
from agno.tools import Toolkit
|
|
72
|
+
from agno.utils.log import log_error
|
|
72
73
|
|
|
73
74
|
try:
|
|
74
75
|
from google.auth.transport.requests import Request
|
|
@@ -202,7 +203,7 @@ class GoogleDriveTools(Toolkit):
|
|
|
202
203
|
items = results.get("files", [])
|
|
203
204
|
return items
|
|
204
205
|
except Exception as error:
|
|
205
|
-
|
|
206
|
+
log_error(f"Could not list files: {error}")
|
|
206
207
|
return []
|
|
207
208
|
|
|
208
209
|
@authenticate
|
|
@@ -238,7 +239,7 @@ class GoogleDriveTools(Toolkit):
|
|
|
238
239
|
)
|
|
239
240
|
return uploaded_file
|
|
240
241
|
except Exception as error:
|
|
241
|
-
|
|
242
|
+
log_error(f"Could not upload file '{file_path}': {error}")
|
|
242
243
|
return None
|
|
243
244
|
|
|
244
245
|
@authenticate
|
|
@@ -266,5 +267,5 @@ class GoogleDriveTools(Toolkit):
|
|
|
266
267
|
print(f"Download progress: {int(status.progress() * 100)}%.")
|
|
267
268
|
return dest_path
|
|
268
269
|
except Exception as error:
|
|
269
|
-
|
|
270
|
+
log_error(f"Could not download file '{file_id}': {error}")
|
|
270
271
|
return None
|
agno/tools/knowledge.py
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import json
|
|
2
2
|
from textwrap import dedent
|
|
3
|
-
from typing import Any,
|
|
3
|
+
from typing import Any, List, Optional
|
|
4
4
|
|
|
5
5
|
from agno.knowledge.document import Document
|
|
6
6
|
from agno.knowledge.knowledge import Knowledge
|
|
7
|
+
from agno.run import RunContext
|
|
7
8
|
from agno.tools import Toolkit
|
|
8
9
|
from agno.utils.log import log_debug, log_error
|
|
9
10
|
|
|
@@ -55,7 +56,7 @@ class KnowledgeTools(Toolkit):
|
|
|
55
56
|
**kwargs,
|
|
56
57
|
)
|
|
57
58
|
|
|
58
|
-
def think(self,
|
|
59
|
+
def think(self, run_context: RunContext, thought: str) -> str:
|
|
59
60
|
"""Use this tool as a scratchpad to reason about the question, refine your approach, brainstorm search terms, or revise your plan.
|
|
60
61
|
|
|
61
62
|
Call `Think` whenever you need to figure out what to do next, analyze the user's question, or plan your approach.
|
|
@@ -71,8 +72,10 @@ class KnowledgeTools(Toolkit):
|
|
|
71
72
|
log_debug(f"Thought: {thought}")
|
|
72
73
|
|
|
73
74
|
# Add the thought to the Agent state
|
|
75
|
+
session_state = run_context.session_state
|
|
74
76
|
if session_state is None:
|
|
75
77
|
session_state = {}
|
|
78
|
+
run_context.session_state = session_state
|
|
76
79
|
if "thoughts" not in session_state:
|
|
77
80
|
session_state["thoughts"] = []
|
|
78
81
|
session_state["thoughts"].append(thought)
|
|
@@ -89,7 +92,7 @@ class KnowledgeTools(Toolkit):
|
|
|
89
92
|
log_error(f"Error recording thought: {e}")
|
|
90
93
|
return f"Error recording thought: {e}"
|
|
91
94
|
|
|
92
|
-
def search_knowledge(self,
|
|
95
|
+
def search_knowledge(self, run_context: RunContext, query: str) -> str:
|
|
93
96
|
"""Use this tool to search the knowledge base for relevant information.
|
|
94
97
|
After thinking through the question, use this tool as many times as needed to search for relevant information.
|
|
95
98
|
|
|
@@ -111,7 +114,7 @@ class KnowledgeTools(Toolkit):
|
|
|
111
114
|
log_error(f"Error searching knowledge base: {e}")
|
|
112
115
|
return f"Error searching knowledge base: {e}"
|
|
113
116
|
|
|
114
|
-
def analyze(self,
|
|
117
|
+
def analyze(self, run_context: RunContext, analysis: str) -> str:
|
|
115
118
|
"""Use this tool to evaluate whether the returned documents are correct and sufficient.
|
|
116
119
|
If not, go back to "Think" or "Search" with refined queries.
|
|
117
120
|
|
|
@@ -125,8 +128,10 @@ class KnowledgeTools(Toolkit):
|
|
|
125
128
|
log_debug(f"Analysis: {analysis}")
|
|
126
129
|
|
|
127
130
|
# Add the thought to the Agent state
|
|
131
|
+
session_state = run_context.session_state
|
|
128
132
|
if session_state is None:
|
|
129
133
|
session_state = {}
|
|
134
|
+
run_context.session_state = session_state
|
|
130
135
|
if "analysis" not in session_state:
|
|
131
136
|
session_state["analysis"] = []
|
|
132
137
|
session_state["analysis"].append(analysis)
|