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/utils/string.py
CHANGED
|
@@ -2,13 +2,15 @@ import hashlib
|
|
|
2
2
|
import json
|
|
3
3
|
import re
|
|
4
4
|
import uuid
|
|
5
|
-
from typing import Optional, Type
|
|
5
|
+
from typing import Any, Optional, Type, Union
|
|
6
6
|
from uuid import uuid4
|
|
7
7
|
|
|
8
8
|
from pydantic import BaseModel, ValidationError
|
|
9
9
|
|
|
10
10
|
from agno.utils.log import logger
|
|
11
11
|
|
|
12
|
+
POSTGRES_INVALID_CHARS_REGEX = re.compile(r"[\x00-\x08\x0b\x0c\x0e-\x1f\ufffe\uffff]")
|
|
13
|
+
|
|
12
14
|
|
|
13
15
|
def is_valid_uuid(uuid_str: str) -> bool:
|
|
14
16
|
"""
|
|
@@ -87,7 +89,8 @@ def _clean_json_content(content: str) -> str:
|
|
|
87
89
|
if "```json" in content:
|
|
88
90
|
content = content.split("```json")[-1].strip()
|
|
89
91
|
parts = content.split("```")
|
|
90
|
-
parts
|
|
92
|
+
if len(parts) > 1:
|
|
93
|
+
parts.pop(-1)
|
|
91
94
|
content = "".join(parts)
|
|
92
95
|
elif "```" in content:
|
|
93
96
|
content = content.split("```")[1].strip()
|
|
@@ -201,6 +204,52 @@ def parse_response_model_str(content: str, output_schema: Type[BaseModel]) -> Op
|
|
|
201
204
|
return structured_output
|
|
202
205
|
|
|
203
206
|
|
|
207
|
+
def parse_response_dict_str(content: str) -> Optional[dict]:
|
|
208
|
+
"""Parse dict from string content, extracting JSON if needed"""
|
|
209
|
+
from agno.utils.reasoning import extract_thinking_content
|
|
210
|
+
|
|
211
|
+
# Handle thinking content b/w <think> tags
|
|
212
|
+
if "</think>" in content:
|
|
213
|
+
reasoning_content, output_content = extract_thinking_content(content)
|
|
214
|
+
if reasoning_content:
|
|
215
|
+
content = output_content
|
|
216
|
+
|
|
217
|
+
# Clean content first to simplify all parsing attempts
|
|
218
|
+
cleaned_content = _clean_json_content(content)
|
|
219
|
+
|
|
220
|
+
try:
|
|
221
|
+
# First attempt: direct JSON parsing on cleaned content
|
|
222
|
+
return json.loads(cleaned_content)
|
|
223
|
+
except json.JSONDecodeError as e:
|
|
224
|
+
logger.warning(f"Failed to parse cleaned JSON: {e}")
|
|
225
|
+
|
|
226
|
+
# Second attempt: Extract individual JSON objects
|
|
227
|
+
candidate_jsons = _extract_json_objects(cleaned_content)
|
|
228
|
+
|
|
229
|
+
if len(candidate_jsons) == 1:
|
|
230
|
+
# Single JSON object - try to parse it directly
|
|
231
|
+
try:
|
|
232
|
+
return json.loads(candidate_jsons[0])
|
|
233
|
+
except json.JSONDecodeError:
|
|
234
|
+
pass
|
|
235
|
+
|
|
236
|
+
if len(candidate_jsons) > 1:
|
|
237
|
+
# Final attempt: Merge multiple JSON objects
|
|
238
|
+
merged_data: dict = {}
|
|
239
|
+
for candidate in candidate_jsons:
|
|
240
|
+
try:
|
|
241
|
+
obj = json.loads(candidate)
|
|
242
|
+
if isinstance(obj, dict):
|
|
243
|
+
merged_data.update(obj)
|
|
244
|
+
except json.JSONDecodeError:
|
|
245
|
+
continue
|
|
246
|
+
if merged_data:
|
|
247
|
+
return merged_data
|
|
248
|
+
|
|
249
|
+
logger.warning("All parsing attempts failed.")
|
|
250
|
+
return None
|
|
251
|
+
|
|
252
|
+
|
|
204
253
|
def generate_id(seed: Optional[str] = None) -> str:
|
|
205
254
|
"""
|
|
206
255
|
Generate a deterministic UUID5 based on a seed string.
|
|
@@ -229,3 +278,43 @@ def generate_id_from_name(name: Optional[str] = None) -> str:
|
|
|
229
278
|
return name.lower().replace(" ", "-").replace("_", "-")
|
|
230
279
|
else:
|
|
231
280
|
return str(uuid4())
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
def sanitize_postgres_string(value: Optional[str]) -> Optional[str]:
|
|
284
|
+
"""Remove illegal chars from string values to prevent PostgreSQL encoding errors.
|
|
285
|
+
|
|
286
|
+
This function all chars illegal in Postgres UTF-8 text fields.
|
|
287
|
+
Useful to prevent CharacterNotInRepertoireError when storing strings.
|
|
288
|
+
|
|
289
|
+
Args:
|
|
290
|
+
value: The string value to sanitize.
|
|
291
|
+
|
|
292
|
+
Returns:
|
|
293
|
+
The sanitized string with illegal chars removed, or None if input was None.
|
|
294
|
+
"""
|
|
295
|
+
if value is None:
|
|
296
|
+
return None
|
|
297
|
+
if isinstance(value, str):
|
|
298
|
+
return POSTGRES_INVALID_CHARS_REGEX.sub("", value)
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
def sanitize_postgres_strings(data: Union[dict, list, str, Any]) -> Union[dict, list, str, Any]:
|
|
302
|
+
"""Recursively sanitize all string values in a dictionary or JSON structure.
|
|
303
|
+
|
|
304
|
+
This function traverses dictionaries, lists, and nested structures to find
|
|
305
|
+
and sanitize all string values, removing null bytes that PostgreSQL cannot handle.
|
|
306
|
+
|
|
307
|
+
Args:
|
|
308
|
+
data: The data structure to sanitize (dict, list, str or any other type).
|
|
309
|
+
|
|
310
|
+
Returns:
|
|
311
|
+
The sanitized data structure with all strings cleaned of null bytes.
|
|
312
|
+
"""
|
|
313
|
+
if isinstance(data, dict):
|
|
314
|
+
return {key: sanitize_postgres_strings(value) for key, value in data.items()}
|
|
315
|
+
elif isinstance(data, list):
|
|
316
|
+
return [sanitize_postgres_strings(item) for item in data]
|
|
317
|
+
elif isinstance(data, str):
|
|
318
|
+
return sanitize_postgres_string(data)
|
|
319
|
+
else:
|
|
320
|
+
return data
|
agno/utils/team.py
CHANGED
|
@@ -59,7 +59,7 @@ def add_interaction_to_team_run_context(
|
|
|
59
59
|
team_run_context: Dict[str, Any],
|
|
60
60
|
member_name: str,
|
|
61
61
|
task: str,
|
|
62
|
-
run_response: Union[RunOutput, TeamRunOutput],
|
|
62
|
+
run_response: Optional[Union[RunOutput, TeamRunOutput]],
|
|
63
63
|
) -> None:
|
|
64
64
|
if "member_responses" not in team_run_context:
|
|
65
65
|
team_run_context["member_responses"] = []
|
|
@@ -73,14 +73,40 @@ def add_interaction_to_team_run_context(
|
|
|
73
73
|
log_debug(f"Updated team run context with member name: {member_name}")
|
|
74
74
|
|
|
75
75
|
|
|
76
|
-
def get_team_member_interactions_str(
|
|
76
|
+
def get_team_member_interactions_str(
|
|
77
|
+
team_run_context: Dict[str, Any],
|
|
78
|
+
max_interactions: Optional[int] = None,
|
|
79
|
+
) -> str:
|
|
80
|
+
"""
|
|
81
|
+
Build a string representation of member interactions from the team run context.
|
|
82
|
+
|
|
83
|
+
Args:
|
|
84
|
+
team_run_context: The context containing member responses
|
|
85
|
+
max_interactions: Maximum number of recent interactions to include.
|
|
86
|
+
None means include all interactions.
|
|
87
|
+
If set, only the most recent N interactions are included.
|
|
88
|
+
|
|
89
|
+
Returns:
|
|
90
|
+
A formatted string with member interactions
|
|
91
|
+
"""
|
|
77
92
|
if not team_run_context:
|
|
78
93
|
return ""
|
|
79
94
|
team_member_interactions_str = ""
|
|
80
95
|
if "member_responses" in team_run_context:
|
|
81
|
-
|
|
96
|
+
member_responses = team_run_context["member_responses"]
|
|
97
|
+
|
|
98
|
+
# If max_interactions is set, only include the most recent N interactions
|
|
99
|
+
if max_interactions is not None and len(member_responses) > max_interactions:
|
|
100
|
+
member_responses = member_responses[-max_interactions:]
|
|
82
101
|
|
|
83
|
-
|
|
102
|
+
if not member_responses:
|
|
103
|
+
return ""
|
|
104
|
+
|
|
105
|
+
team_member_interactions_str += (
|
|
106
|
+
"<member_interaction_context>\nSee below interactions with other team members.\n"
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
for interaction in member_responses:
|
|
84
110
|
response_dict = interaction["run_response"].to_dict()
|
|
85
111
|
response_content = (
|
|
86
112
|
response_dict.get("content")
|
|
@@ -95,45 +121,69 @@ def get_team_member_interactions_str(team_run_context: Dict[str, Any]) -> str:
|
|
|
95
121
|
return team_member_interactions_str
|
|
96
122
|
|
|
97
123
|
|
|
98
|
-
def get_team_run_context_images(
|
|
124
|
+
def get_team_run_context_images(
|
|
125
|
+
team_run_context: Dict[str, Any],
|
|
126
|
+
max_interactions: Optional[int] = None,
|
|
127
|
+
) -> List[Image]:
|
|
99
128
|
if not team_run_context:
|
|
100
129
|
return []
|
|
101
130
|
images = []
|
|
102
131
|
if "member_responses" in team_run_context:
|
|
103
|
-
|
|
132
|
+
member_responses = team_run_context["member_responses"]
|
|
133
|
+
if max_interactions is not None and len(member_responses) > max_interactions:
|
|
134
|
+
member_responses = member_responses[-max_interactions:]
|
|
135
|
+
for interaction in member_responses:
|
|
104
136
|
if interaction["run_response"].images:
|
|
105
137
|
images.extend(interaction["run_response"].images)
|
|
106
138
|
return images
|
|
107
139
|
|
|
108
140
|
|
|
109
|
-
def get_team_run_context_videos(
|
|
141
|
+
def get_team_run_context_videos(
|
|
142
|
+
team_run_context: Dict[str, Any],
|
|
143
|
+
max_interactions: Optional[int] = None,
|
|
144
|
+
) -> List[Video]:
|
|
110
145
|
if not team_run_context:
|
|
111
146
|
return []
|
|
112
147
|
videos = []
|
|
113
148
|
if "member_responses" in team_run_context:
|
|
114
|
-
|
|
149
|
+
member_responses = team_run_context["member_responses"]
|
|
150
|
+
if max_interactions is not None and len(member_responses) > max_interactions:
|
|
151
|
+
member_responses = member_responses[-max_interactions:]
|
|
152
|
+
for interaction in member_responses:
|
|
115
153
|
if interaction["run_response"].videos:
|
|
116
154
|
videos.extend(interaction["run_response"].videos)
|
|
117
155
|
return videos
|
|
118
156
|
|
|
119
157
|
|
|
120
|
-
def get_team_run_context_audio(
|
|
158
|
+
def get_team_run_context_audio(
|
|
159
|
+
team_run_context: Dict[str, Any],
|
|
160
|
+
max_interactions: Optional[int] = None,
|
|
161
|
+
) -> List[Audio]:
|
|
121
162
|
if not team_run_context:
|
|
122
163
|
return []
|
|
123
164
|
audio = []
|
|
124
165
|
if "member_responses" in team_run_context:
|
|
125
|
-
|
|
166
|
+
member_responses = team_run_context["member_responses"]
|
|
167
|
+
if max_interactions is not None and len(member_responses) > max_interactions:
|
|
168
|
+
member_responses = member_responses[-max_interactions:]
|
|
169
|
+
for interaction in member_responses:
|
|
126
170
|
if interaction["run_response"].audio:
|
|
127
171
|
audio.extend(interaction["run_response"].audio)
|
|
128
172
|
return audio
|
|
129
173
|
|
|
130
174
|
|
|
131
|
-
def get_team_run_context_files(
|
|
175
|
+
def get_team_run_context_files(
|
|
176
|
+
team_run_context: Dict[str, Any],
|
|
177
|
+
max_interactions: Optional[int] = None,
|
|
178
|
+
) -> List[File]:
|
|
132
179
|
if not team_run_context:
|
|
133
180
|
return []
|
|
134
181
|
files = []
|
|
135
182
|
if "member_responses" in team_run_context:
|
|
136
|
-
|
|
183
|
+
member_responses = team_run_context["member_responses"]
|
|
184
|
+
if max_interactions is not None and len(member_responses) > max_interactions:
|
|
185
|
+
member_responses = member_responses[-max_interactions:]
|
|
186
|
+
for interaction in member_responses:
|
|
137
187
|
if interaction["run_response"].files:
|
|
138
188
|
files.extend(interaction["run_response"].files)
|
|
139
189
|
return files
|