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/os/interfaces/a2a/utils.py
CHANGED
|
@@ -95,7 +95,6 @@ async def map_a2a_request_to_run_input(request_body: dict, stream: bool = True)
|
|
|
95
95
|
```json
|
|
96
96
|
{
|
|
97
97
|
"jsonrpc": "2.0",
|
|
98
|
-
"method": "message/send",
|
|
99
98
|
"id": "id",
|
|
100
99
|
"params": {
|
|
101
100
|
"message": {
|
|
@@ -110,7 +109,7 @@ async def map_a2a_request_to_run_input(request_body: dict, stream: bool = True)
|
|
|
110
109
|
|
|
111
110
|
Returns:
|
|
112
111
|
RunInput: The Agno RunInput
|
|
113
|
-
stream:
|
|
112
|
+
stream: Whether we are in stream mode
|
|
114
113
|
"""
|
|
115
114
|
|
|
116
115
|
# 1. Validate the request
|
|
@@ -325,7 +324,7 @@ async def stream_a2a_response(
|
|
|
325
324
|
final=False,
|
|
326
325
|
)
|
|
327
326
|
response = SendStreamingMessageSuccessResponse(id=request_id, result=status_event)
|
|
328
|
-
yield json.dumps(response.model_dump(exclude_none=True))
|
|
327
|
+
yield f"event: TaskStatusUpdateEvent\ndata: {json.dumps(response.model_dump(exclude_none=True))}\n\n"
|
|
329
328
|
|
|
330
329
|
# 2. Send all content and secondary events
|
|
331
330
|
|
|
@@ -341,7 +340,7 @@ async def stream_a2a_response(
|
|
|
341
340
|
metadata={"agno_content_category": "content"},
|
|
342
341
|
)
|
|
343
342
|
response = SendStreamingMessageSuccessResponse(id=request_id, result=message)
|
|
344
|
-
yield json.dumps(response.model_dump(exclude_none=True))
|
|
343
|
+
yield f"event: Message\ndata: {json.dumps(response.model_dump(exclude_none=True))}\n\n"
|
|
345
344
|
|
|
346
345
|
# Send tool call events
|
|
347
346
|
elif isinstance(event, (ToolCallStartedEvent, TeamToolCallStartedEvent)):
|
|
@@ -361,7 +360,7 @@ async def stream_a2a_response(
|
|
|
361
360
|
metadata=metadata,
|
|
362
361
|
)
|
|
363
362
|
response = SendStreamingMessageSuccessResponse(id=request_id, result=status_event)
|
|
364
|
-
yield json.dumps(response.model_dump(exclude_none=True))
|
|
363
|
+
yield f"event: TaskStatusUpdateEvent\ndata: {json.dumps(response.model_dump(exclude_none=True))}\n\n"
|
|
365
364
|
|
|
366
365
|
elif isinstance(event, (ToolCallCompletedEvent, TeamToolCallCompletedEvent)):
|
|
367
366
|
metadata = {"agno_event_type": "tool_call_completed"}
|
|
@@ -380,7 +379,7 @@ async def stream_a2a_response(
|
|
|
380
379
|
metadata=metadata,
|
|
381
380
|
)
|
|
382
381
|
response = SendStreamingMessageSuccessResponse(id=request_id, result=status_event)
|
|
383
|
-
yield json.dumps(response.model_dump(exclude_none=True))
|
|
382
|
+
yield f"event: TaskStatusUpdateEvent\ndata: {json.dumps(response.model_dump(exclude_none=True))}\n\n"
|
|
384
383
|
|
|
385
384
|
# Send reasoning events
|
|
386
385
|
elif isinstance(event, (ReasoningStartedEvent, TeamReasoningStartedEvent)):
|
|
@@ -392,7 +391,7 @@ async def stream_a2a_response(
|
|
|
392
391
|
metadata={"agno_event_type": "reasoning_started"},
|
|
393
392
|
)
|
|
394
393
|
response = SendStreamingMessageSuccessResponse(id=request_id, result=status_event)
|
|
395
|
-
yield json.dumps(response.model_dump(exclude_none=True))
|
|
394
|
+
yield f"event: TaskStatusUpdateEvent\ndata: {json.dumps(response.model_dump(exclude_none=True))}\n\n"
|
|
396
395
|
|
|
397
396
|
elif isinstance(event, (ReasoningStepEvent, TeamReasoningStepEvent)):
|
|
398
397
|
if event.reasoning_content:
|
|
@@ -415,7 +414,7 @@ async def stream_a2a_response(
|
|
|
415
414
|
metadata={"agno_content_category": "reasoning", "agno_event_type": "reasoning_step"},
|
|
416
415
|
)
|
|
417
416
|
response = SendStreamingMessageSuccessResponse(id=request_id, result=reasoning_message)
|
|
418
|
-
yield json.dumps(response.model_dump(exclude_none=True))
|
|
417
|
+
yield f"event: Message\ndata: {json.dumps(response.model_dump(exclude_none=True))}\n\n"
|
|
419
418
|
|
|
420
419
|
elif isinstance(event, (ReasoningCompletedEvent, TeamReasoningCompletedEvent)):
|
|
421
420
|
status_event = TaskStatusUpdateEvent(
|
|
@@ -426,7 +425,7 @@ async def stream_a2a_response(
|
|
|
426
425
|
metadata={"agno_event_type": "reasoning_completed"},
|
|
427
426
|
)
|
|
428
427
|
response = SendStreamingMessageSuccessResponse(id=request_id, result=status_event)
|
|
429
|
-
yield json.dumps(response.model_dump(exclude_none=True))
|
|
428
|
+
yield f"event: TaskStatusUpdateEvent\ndata: {json.dumps(response.model_dump(exclude_none=True))}\n\n"
|
|
430
429
|
|
|
431
430
|
# Send memory update events
|
|
432
431
|
elif isinstance(event, (MemoryUpdateStartedEvent, TeamMemoryUpdateStartedEvent)):
|
|
@@ -438,7 +437,7 @@ async def stream_a2a_response(
|
|
|
438
437
|
metadata={"agno_event_type": "memory_update_started"},
|
|
439
438
|
)
|
|
440
439
|
response = SendStreamingMessageSuccessResponse(id=request_id, result=status_event)
|
|
441
|
-
yield json.dumps(response.model_dump(exclude_none=True))
|
|
440
|
+
yield f"event: TaskStatusUpdateEvent\ndata: {json.dumps(response.model_dump(exclude_none=True))}\n\n"
|
|
442
441
|
|
|
443
442
|
elif isinstance(event, (MemoryUpdateCompletedEvent, TeamMemoryUpdateCompletedEvent)):
|
|
444
443
|
status_event = TaskStatusUpdateEvent(
|
|
@@ -449,7 +448,7 @@ async def stream_a2a_response(
|
|
|
449
448
|
metadata={"agno_event_type": "memory_update_completed"},
|
|
450
449
|
)
|
|
451
450
|
response = SendStreamingMessageSuccessResponse(id=request_id, result=status_event)
|
|
452
|
-
yield json.dumps(response.model_dump(exclude_none=True))
|
|
451
|
+
yield f"event: TaskStatusUpdateEvent\ndata: {json.dumps(response.model_dump(exclude_none=True))}\n\n"
|
|
453
452
|
|
|
454
453
|
# Send workflow events
|
|
455
454
|
elif isinstance(event, WorkflowStepStartedEvent):
|
|
@@ -465,7 +464,7 @@ async def stream_a2a_response(
|
|
|
465
464
|
metadata=metadata,
|
|
466
465
|
)
|
|
467
466
|
response = SendStreamingMessageSuccessResponse(id=request_id, result=status_event)
|
|
468
|
-
yield json.dumps(response.model_dump(exclude_none=True))
|
|
467
|
+
yield f"event: TaskStatusUpdateEvent\ndata: {json.dumps(response.model_dump(exclude_none=True))}\n\n"
|
|
469
468
|
|
|
470
469
|
elif isinstance(event, WorkflowStepCompletedEvent):
|
|
471
470
|
metadata = {"agno_event_type": "workflow_step_completed"}
|
|
@@ -480,7 +479,7 @@ async def stream_a2a_response(
|
|
|
480
479
|
metadata=metadata,
|
|
481
480
|
)
|
|
482
481
|
response = SendStreamingMessageSuccessResponse(id=request_id, result=status_event)
|
|
483
|
-
yield json.dumps(response.model_dump(exclude_none=True))
|
|
482
|
+
yield f"event: TaskStatusUpdateEvent\ndata: {json.dumps(response.model_dump(exclude_none=True))}\n\n"
|
|
484
483
|
|
|
485
484
|
elif isinstance(event, WorkflowStepErrorEvent):
|
|
486
485
|
metadata = {"agno_event_type": "workflow_step_error"}
|
|
@@ -497,7 +496,7 @@ async def stream_a2a_response(
|
|
|
497
496
|
metadata=metadata,
|
|
498
497
|
)
|
|
499
498
|
response = SendStreamingMessageSuccessResponse(id=request_id, result=status_event)
|
|
500
|
-
yield json.dumps(response.model_dump(exclude_none=True))
|
|
499
|
+
yield f"event: TaskStatusUpdateEvent\ndata: {json.dumps(response.model_dump(exclude_none=True))}\n\n"
|
|
501
500
|
|
|
502
501
|
# Send loop events
|
|
503
502
|
elif isinstance(event, LoopExecutionStartedEvent):
|
|
@@ -515,7 +514,7 @@ async def stream_a2a_response(
|
|
|
515
514
|
metadata=metadata,
|
|
516
515
|
)
|
|
517
516
|
response = SendStreamingMessageSuccessResponse(id=request_id, result=status_event)
|
|
518
|
-
yield json.dumps(response.model_dump(exclude_none=True))
|
|
517
|
+
yield f"event: TaskStatusUpdateEvent\ndata: {json.dumps(response.model_dump(exclude_none=True))}\n\n"
|
|
519
518
|
|
|
520
519
|
elif isinstance(event, LoopIterationStartedEvent):
|
|
521
520
|
metadata = {"agno_event_type": "loop_iteration_started"}
|
|
@@ -534,7 +533,7 @@ async def stream_a2a_response(
|
|
|
534
533
|
metadata=metadata,
|
|
535
534
|
)
|
|
536
535
|
response = SendStreamingMessageSuccessResponse(id=request_id, result=status_event)
|
|
537
|
-
yield json.dumps(response.model_dump(exclude_none=True))
|
|
536
|
+
yield f"event: TaskStatusUpdateEvent\ndata: {json.dumps(response.model_dump(exclude_none=True))}\n\n"
|
|
538
537
|
|
|
539
538
|
elif isinstance(event, LoopIterationCompletedEvent):
|
|
540
539
|
metadata = {"agno_event_type": "loop_iteration_completed"}
|
|
@@ -553,7 +552,7 @@ async def stream_a2a_response(
|
|
|
553
552
|
metadata=metadata,
|
|
554
553
|
)
|
|
555
554
|
response = SendStreamingMessageSuccessResponse(id=request_id, result=status_event)
|
|
556
|
-
yield json.dumps(response.model_dump(exclude_none=True))
|
|
555
|
+
yield f"event: TaskStatusUpdateEvent\ndata: {json.dumps(response.model_dump(exclude_none=True))}\n\n"
|
|
557
556
|
|
|
558
557
|
elif isinstance(event, LoopExecutionCompletedEvent):
|
|
559
558
|
metadata = {"agno_event_type": "loop_execution_completed"}
|
|
@@ -570,7 +569,7 @@ async def stream_a2a_response(
|
|
|
570
569
|
metadata=metadata,
|
|
571
570
|
)
|
|
572
571
|
response = SendStreamingMessageSuccessResponse(id=request_id, result=status_event)
|
|
573
|
-
yield json.dumps(response.model_dump(exclude_none=True))
|
|
572
|
+
yield f"event: TaskStatusUpdateEvent\ndata: {json.dumps(response.model_dump(exclude_none=True))}\n\n"
|
|
574
573
|
|
|
575
574
|
# Send parallel events
|
|
576
575
|
elif isinstance(event, ParallelExecutionStartedEvent):
|
|
@@ -588,7 +587,7 @@ async def stream_a2a_response(
|
|
|
588
587
|
metadata=metadata,
|
|
589
588
|
)
|
|
590
589
|
response = SendStreamingMessageSuccessResponse(id=request_id, result=status_event)
|
|
591
|
-
yield json.dumps(response.model_dump(exclude_none=True))
|
|
590
|
+
yield f"event: TaskStatusUpdateEvent\ndata: {json.dumps(response.model_dump(exclude_none=True))}\n\n"
|
|
592
591
|
|
|
593
592
|
elif isinstance(event, ParallelExecutionCompletedEvent):
|
|
594
593
|
metadata = {"agno_event_type": "parallel_execution_completed"}
|
|
@@ -605,7 +604,7 @@ async def stream_a2a_response(
|
|
|
605
604
|
metadata=metadata,
|
|
606
605
|
)
|
|
607
606
|
response = SendStreamingMessageSuccessResponse(id=request_id, result=status_event)
|
|
608
|
-
yield json.dumps(response.model_dump(exclude_none=True))
|
|
607
|
+
yield f"event: TaskStatusUpdateEvent\ndata: {json.dumps(response.model_dump(exclude_none=True))}\n\n"
|
|
609
608
|
|
|
610
609
|
# Send condition events
|
|
611
610
|
elif isinstance(event, ConditionExecutionStartedEvent):
|
|
@@ -623,7 +622,7 @@ async def stream_a2a_response(
|
|
|
623
622
|
metadata=metadata,
|
|
624
623
|
)
|
|
625
624
|
response = SendStreamingMessageSuccessResponse(id=request_id, result=status_event)
|
|
626
|
-
yield json.dumps(response.model_dump(exclude_none=True))
|
|
625
|
+
yield f"event: TaskStatusUpdateEvent\ndata: {json.dumps(response.model_dump(exclude_none=True))}\n\n"
|
|
627
626
|
|
|
628
627
|
elif isinstance(event, ConditionExecutionCompletedEvent):
|
|
629
628
|
metadata = {"agno_event_type": "condition_execution_completed"}
|
|
@@ -642,7 +641,7 @@ async def stream_a2a_response(
|
|
|
642
641
|
metadata=metadata,
|
|
643
642
|
)
|
|
644
643
|
response = SendStreamingMessageSuccessResponse(id=request_id, result=status_event)
|
|
645
|
-
yield json.dumps(response.model_dump(exclude_none=True))
|
|
644
|
+
yield f"event: TaskStatusUpdateEvent\ndata: {json.dumps(response.model_dump(exclude_none=True))}\n\n"
|
|
646
645
|
|
|
647
646
|
# Send router events
|
|
648
647
|
elif isinstance(event, RouterExecutionStartedEvent):
|
|
@@ -660,7 +659,7 @@ async def stream_a2a_response(
|
|
|
660
659
|
metadata=metadata,
|
|
661
660
|
)
|
|
662
661
|
response = SendStreamingMessageSuccessResponse(id=request_id, result=status_event)
|
|
663
|
-
yield json.dumps(response.model_dump(exclude_none=True))
|
|
662
|
+
yield f"event: TaskStatusUpdateEvent\ndata: {json.dumps(response.model_dump(exclude_none=True))}\n\n"
|
|
664
663
|
|
|
665
664
|
elif isinstance(event, RouterExecutionCompletedEvent):
|
|
666
665
|
metadata = {"agno_event_type": "router_execution_completed"}
|
|
@@ -679,7 +678,7 @@ async def stream_a2a_response(
|
|
|
679
678
|
metadata=metadata,
|
|
680
679
|
)
|
|
681
680
|
response = SendStreamingMessageSuccessResponse(id=request_id, result=status_event)
|
|
682
|
-
yield json.dumps(response.model_dump(exclude_none=True))
|
|
681
|
+
yield f"event: TaskStatusUpdateEvent\ndata: {json.dumps(response.model_dump(exclude_none=True))}\n\n"
|
|
683
682
|
|
|
684
683
|
# Send steps events
|
|
685
684
|
elif isinstance(event, StepsExecutionStartedEvent):
|
|
@@ -697,7 +696,7 @@ async def stream_a2a_response(
|
|
|
697
696
|
metadata=metadata,
|
|
698
697
|
)
|
|
699
698
|
response = SendStreamingMessageSuccessResponse(id=request_id, result=status_event)
|
|
700
|
-
yield json.dumps(response.model_dump(exclude_none=True))
|
|
699
|
+
yield f"event: TaskStatusUpdateEvent\ndata: {json.dumps(response.model_dump(exclude_none=True))}\n\n"
|
|
701
700
|
|
|
702
701
|
elif isinstance(event, StepsExecutionCompletedEvent):
|
|
703
702
|
metadata = {"agno_event_type": "steps_execution_completed"}
|
|
@@ -716,7 +715,7 @@ async def stream_a2a_response(
|
|
|
716
715
|
metadata=metadata,
|
|
717
716
|
)
|
|
718
717
|
response = SendStreamingMessageSuccessResponse(id=request_id, result=status_event)
|
|
719
|
-
yield json.dumps(response.model_dump(exclude_none=True))
|
|
718
|
+
yield f"event: TaskStatusUpdateEvent\ndata: {json.dumps(response.model_dump(exclude_none=True))}\n\n"
|
|
720
719
|
|
|
721
720
|
# Capture completion event for final task construction
|
|
722
721
|
elif isinstance(event, (RunCompletedEvent, TeamRunCompletedEvent, WorkflowCompletedEvent)):
|
|
@@ -748,7 +747,7 @@ async def stream_a2a_response(
|
|
|
748
747
|
final=True,
|
|
749
748
|
)
|
|
750
749
|
response = SendStreamingMessageSuccessResponse(id=request_id, result=final_status_event)
|
|
751
|
-
yield json.dumps(response.model_dump(exclude_none=True))
|
|
750
|
+
yield f"event: TaskStatusUpdateEvent\ndata: {json.dumps(response.model_dump(exclude_none=True))}\n\n"
|
|
752
751
|
|
|
753
752
|
# 4. Send final task
|
|
754
753
|
# Handle cancelled case
|
|
@@ -778,7 +777,7 @@ async def stream_a2a_response(
|
|
|
778
777
|
history=[final_message],
|
|
779
778
|
)
|
|
780
779
|
response = SendStreamingMessageSuccessResponse(id=request_id, result=task)
|
|
781
|
-
yield json.dumps(response.model_dump(exclude_none=True))
|
|
780
|
+
yield f"event: Task\ndata: {json.dumps(response.model_dump(exclude_none=True))}\n\n"
|
|
782
781
|
return
|
|
783
782
|
|
|
784
783
|
# Build from completion_event if available, otherwise use accumulated content
|
|
@@ -846,8 +845,8 @@ async def stream_a2a_response(
|
|
|
846
845
|
|
|
847
846
|
# Handle all other data as Message metadata
|
|
848
847
|
final_metadata: Dict[str, Any] = {}
|
|
849
|
-
if hasattr(completion_event, "metrics") and completion_event.metrics:
|
|
850
|
-
final_metadata["metrics"] = completion_event.metrics.
|
|
848
|
+
if hasattr(completion_event, "metrics") and completion_event.metrics: # type: ignore
|
|
849
|
+
final_metadata["metrics"] = completion_event.metrics.to_dict() # type: ignore
|
|
851
850
|
if hasattr(completion_event, "metadata") and completion_event.metadata:
|
|
852
851
|
final_metadata.update(completion_event.metadata)
|
|
853
852
|
|
|
@@ -880,7 +879,7 @@ async def stream_a2a_response(
|
|
|
880
879
|
artifacts=artifacts if artifacts else None,
|
|
881
880
|
)
|
|
882
881
|
response = SendStreamingMessageSuccessResponse(id=request_id, result=task)
|
|
883
|
-
yield json.dumps(response.model_dump(exclude_none=True))
|
|
882
|
+
yield f"event: Task\ndata: {json.dumps(response.model_dump(exclude_none=True))}\n\n"
|
|
884
883
|
|
|
885
884
|
|
|
886
885
|
async def stream_a2a_response_with_error_handling(
|
|
@@ -904,7 +903,7 @@ async def stream_a2a_response_with_error_handling(
|
|
|
904
903
|
final=True,
|
|
905
904
|
)
|
|
906
905
|
response = SendStreamingMessageSuccessResponse(id=request_id, result=failed_status_event)
|
|
907
|
-
yield json.dumps(response.model_dump(exclude_none=True))
|
|
906
|
+
yield f"event: TaskStatusUpdateEvent\ndata: {json.dumps(response.model_dump(exclude_none=True))}\n\n"
|
|
908
907
|
|
|
909
908
|
# Send failed Task
|
|
910
909
|
error_message = A2AMessage(
|
|
@@ -921,4 +920,4 @@ async def stream_a2a_response_with_error_handling(
|
|
|
921
920
|
)
|
|
922
921
|
|
|
923
922
|
response = SendStreamingMessageSuccessResponse(id=request_id, result=failed_task)
|
|
924
|
-
yield json.dumps(response.model_dump(exclude_none=True))
|
|
923
|
+
yield f"event: Task\ndata: {json.dumps(response.model_dump(exclude_none=True))}\n\n"
|
agno/os/interfaces/agui/agui.py
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
"""Main class for the AG-UI app, used to expose an Agno Agent or Team in an AG-UI compatible format."""
|
|
2
2
|
|
|
3
|
-
from typing import List, Optional
|
|
3
|
+
from typing import List, Optional, Union
|
|
4
4
|
|
|
5
5
|
from fastapi.routing import APIRouter
|
|
6
6
|
|
|
7
7
|
from agno.agent import Agent
|
|
8
|
+
from agno.agent.remote import RemoteAgent
|
|
8
9
|
from agno.os.interfaces.agui.router import attach_routes
|
|
9
10
|
from agno.os.interfaces.base import BaseInterface
|
|
10
11
|
from agno.team import Team
|
|
12
|
+
from agno.team.remote import RemoteTeam
|
|
11
13
|
|
|
12
14
|
|
|
13
15
|
class AGUI(BaseInterface):
|
|
@@ -17,8 +19,8 @@ class AGUI(BaseInterface):
|
|
|
17
19
|
|
|
18
20
|
def __init__(
|
|
19
21
|
self,
|
|
20
|
-
agent: Optional[Agent] = None,
|
|
21
|
-
team: Optional[Team] = None,
|
|
22
|
+
agent: Optional[Union[Agent, RemoteAgent]] = None,
|
|
23
|
+
team: Optional[Union[Team, RemoteTeam]] = None,
|
|
22
24
|
prefix: str = "",
|
|
23
25
|
tags: Optional[List[str]] = None,
|
|
24
26
|
):
|
|
@@ -2,37 +2,43 @@
|
|
|
2
2
|
|
|
3
3
|
import logging
|
|
4
4
|
import uuid
|
|
5
|
-
from typing import AsyncIterator, Optional
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
5
|
+
from typing import AsyncIterator, Optional, Union
|
|
6
|
+
|
|
7
|
+
try:
|
|
8
|
+
from ag_ui.core import (
|
|
9
|
+
BaseEvent,
|
|
10
|
+
EventType,
|
|
11
|
+
RunAgentInput,
|
|
12
|
+
RunErrorEvent,
|
|
13
|
+
RunStartedEvent,
|
|
14
|
+
)
|
|
15
|
+
from ag_ui.encoder import EventEncoder
|
|
16
|
+
except ImportError as e:
|
|
17
|
+
raise ImportError("`ag_ui` not installed. Please install it with `pip install -U ag-ui`") from e
|
|
18
|
+
|
|
15
19
|
from fastapi import APIRouter
|
|
16
20
|
from fastapi.responses import StreamingResponse
|
|
17
21
|
|
|
18
|
-
from agno.agent
|
|
22
|
+
from agno.agent import Agent, RemoteAgent
|
|
19
23
|
from agno.os.interfaces.agui.utils import (
|
|
20
24
|
async_stream_agno_response_as_agui_events,
|
|
21
25
|
convert_agui_messages_to_agno_messages,
|
|
22
26
|
validate_agui_state,
|
|
23
27
|
)
|
|
28
|
+
from agno.team.remote import RemoteTeam
|
|
24
29
|
from agno.team.team import Team
|
|
25
30
|
|
|
26
31
|
logger = logging.getLogger(__name__)
|
|
27
32
|
|
|
28
33
|
|
|
29
|
-
async def run_agent(agent: Agent, run_input: RunAgentInput) -> AsyncIterator[BaseEvent]:
|
|
34
|
+
async def run_agent(agent: Union[Agent, RemoteAgent], run_input: RunAgentInput) -> AsyncIterator[BaseEvent]:
|
|
30
35
|
"""Run the contextual Agent, mapping AG-UI input messages to Agno format, and streaming the response in AG-UI format."""
|
|
31
36
|
run_id = run_input.run_id or str(uuid.uuid4())
|
|
32
37
|
|
|
33
38
|
try:
|
|
34
39
|
# Preparing the input for the Agent and emitting the run started event
|
|
35
40
|
messages = convert_agui_messages_to_agno_messages(run_input.messages or [])
|
|
41
|
+
|
|
36
42
|
yield RunStartedEvent(type=EventType.RUN_STARTED, thread_id=run_input.thread_id, run_id=run_id)
|
|
37
43
|
|
|
38
44
|
# Look for user_id in run_input.forwarded_props
|
|
@@ -44,13 +50,14 @@ async def run_agent(agent: Agent, run_input: RunAgentInput) -> AsyncIterator[Bas
|
|
|
44
50
|
session_state = validate_agui_state(run_input.state, run_input.thread_id)
|
|
45
51
|
|
|
46
52
|
# Request streaming response from agent
|
|
47
|
-
response_stream = agent.arun(
|
|
53
|
+
response_stream = agent.arun( # type: ignore
|
|
48
54
|
input=messages,
|
|
49
55
|
session_id=run_input.thread_id,
|
|
50
56
|
stream=True,
|
|
51
57
|
stream_events=True,
|
|
52
58
|
user_id=user_id,
|
|
53
59
|
session_state=session_state,
|
|
60
|
+
run_id=run_id,
|
|
54
61
|
)
|
|
55
62
|
|
|
56
63
|
# Stream the response content in AG-UI format
|
|
@@ -67,7 +74,7 @@ async def run_agent(agent: Agent, run_input: RunAgentInput) -> AsyncIterator[Bas
|
|
|
67
74
|
yield RunErrorEvent(type=EventType.RUN_ERROR, message=str(e))
|
|
68
75
|
|
|
69
76
|
|
|
70
|
-
async def run_team(team: Team, input: RunAgentInput) -> AsyncIterator[BaseEvent]:
|
|
77
|
+
async def run_team(team: Union[Team, RemoteTeam], input: RunAgentInput) -> AsyncIterator[BaseEvent]:
|
|
71
78
|
"""Run the contextual Team, mapping AG-UI input messages to Agno format, and streaming the response in AG-UI format."""
|
|
72
79
|
run_id = input.run_id or str(uuid.uuid4())
|
|
73
80
|
try:
|
|
@@ -84,13 +91,14 @@ async def run_team(team: Team, input: RunAgentInput) -> AsyncIterator[BaseEvent]
|
|
|
84
91
|
session_state = validate_agui_state(input.state, input.thread_id)
|
|
85
92
|
|
|
86
93
|
# Request streaming response from team
|
|
87
|
-
response_stream = team.arun(
|
|
94
|
+
response_stream = team.arun( # type: ignore
|
|
88
95
|
input=messages,
|
|
89
96
|
session_id=input.thread_id,
|
|
90
97
|
stream=True,
|
|
91
98
|
stream_steps=True,
|
|
92
99
|
user_id=user_id,
|
|
93
100
|
session_state=session_state,
|
|
101
|
+
run_id=run_id,
|
|
94
102
|
)
|
|
95
103
|
|
|
96
104
|
# Stream the response content in AG-UI format
|
|
@@ -104,7 +112,9 @@ async def run_team(team: Team, input: RunAgentInput) -> AsyncIterator[BaseEvent]
|
|
|
104
112
|
yield RunErrorEvent(type=EventType.RUN_ERROR, message=str(e))
|
|
105
113
|
|
|
106
114
|
|
|
107
|
-
def attach_routes(
|
|
115
|
+
def attach_routes(
|
|
116
|
+
router: APIRouter, agent: Optional[Union[Agent, RemoteAgent]] = None, team: Optional[Union[Team, RemoteTeam]] = None
|
|
117
|
+
) -> APIRouter:
|
|
108
118
|
if agent is None and team is None:
|
|
109
119
|
raise ValueError("Either agent or team must be provided.")
|
|
110
120
|
|
agno/os/interfaces/agui/utils.py
CHANGED
|
@@ -28,7 +28,7 @@ from agno.models.message import Message
|
|
|
28
28
|
from agno.run.agent import RunContentEvent, RunEvent, RunOutputEvent, RunPausedEvent
|
|
29
29
|
from agno.run.team import RunContentEvent as TeamRunContentEvent
|
|
30
30
|
from agno.run.team import TeamRunEvent, TeamRunOutputEvent
|
|
31
|
-
from agno.utils.log import log_warning
|
|
31
|
+
from agno.utils.log import log_debug, log_warning
|
|
32
32
|
from agno.utils.message import get_text_from_message
|
|
33
33
|
|
|
34
34
|
|
|
@@ -116,23 +116,43 @@ class EventBuffer:
|
|
|
116
116
|
|
|
117
117
|
def convert_agui_messages_to_agno_messages(messages: List[AGUIMessage]) -> List[Message]:
|
|
118
118
|
"""Convert AG-UI messages to Agno messages."""
|
|
119
|
-
|
|
119
|
+
# First pass: collect all tool_call_ids that have results
|
|
120
|
+
tool_call_ids_with_results: Set[str] = set()
|
|
121
|
+
for msg in messages:
|
|
122
|
+
if msg.role == "tool" and msg.tool_call_id:
|
|
123
|
+
tool_call_ids_with_results.add(msg.tool_call_id)
|
|
124
|
+
|
|
125
|
+
# Second pass: convert messages
|
|
126
|
+
result: List[Message] = []
|
|
127
|
+
seen_tool_call_ids: Set[str] = set()
|
|
128
|
+
|
|
120
129
|
for msg in messages:
|
|
121
130
|
if msg.role == "tool":
|
|
131
|
+
# Deduplicate tool results - keep only first occurrence
|
|
132
|
+
if msg.tool_call_id in seen_tool_call_ids:
|
|
133
|
+
log_debug(f"Skipping duplicate AGUI tool result: {msg.tool_call_id}")
|
|
134
|
+
continue
|
|
135
|
+
seen_tool_call_ids.add(msg.tool_call_id)
|
|
122
136
|
result.append(Message(role="tool", tool_call_id=msg.tool_call_id, content=msg.content))
|
|
137
|
+
|
|
123
138
|
elif msg.role == "assistant":
|
|
124
139
|
tool_calls = None
|
|
125
140
|
if msg.tool_calls:
|
|
126
|
-
tool_calls
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
)
|
|
133
|
-
)
|
|
141
|
+
# Filter tool_calls to only those with results in this message sequence
|
|
142
|
+
filtered_calls = [call for call in msg.tool_calls if call.id in tool_call_ids_with_results]
|
|
143
|
+
if filtered_calls:
|
|
144
|
+
tool_calls = [call.model_dump() for call in filtered_calls]
|
|
145
|
+
result.append(Message(role="assistant", content=msg.content, tool_calls=tool_calls))
|
|
146
|
+
|
|
134
147
|
elif msg.role == "user":
|
|
135
148
|
result.append(Message(role="user", content=msg.content))
|
|
149
|
+
|
|
150
|
+
elif msg.role == "system":
|
|
151
|
+
pass # Skip - agent builds its own system message from configuration
|
|
152
|
+
|
|
153
|
+
else:
|
|
154
|
+
log_warning(f"Unknown AGUI message role: {msg.role}")
|
|
155
|
+
|
|
136
156
|
return result
|
|
137
157
|
|
|
138
158
|
|
|
@@ -250,7 +270,25 @@ def _create_events_from_chunk(
|
|
|
250
270
|
parent_message_id = event_buffer.get_parent_message_id_for_tool_call()
|
|
251
271
|
|
|
252
272
|
if not parent_message_id:
|
|
253
|
-
|
|
273
|
+
# Create parent message for tool calls without preceding assistant message
|
|
274
|
+
parent_message_id = str(uuid.uuid4())
|
|
275
|
+
|
|
276
|
+
# Emit a text message to serve as the parent
|
|
277
|
+
text_start = TextMessageStartEvent(
|
|
278
|
+
type=EventType.TEXT_MESSAGE_START,
|
|
279
|
+
message_id=parent_message_id,
|
|
280
|
+
role="assistant",
|
|
281
|
+
)
|
|
282
|
+
events_to_emit.append(text_start)
|
|
283
|
+
|
|
284
|
+
text_end = TextMessageEndEvent(
|
|
285
|
+
type=EventType.TEXT_MESSAGE_END,
|
|
286
|
+
message_id=parent_message_id,
|
|
287
|
+
)
|
|
288
|
+
events_to_emit.append(text_end)
|
|
289
|
+
|
|
290
|
+
# Set this as the pending parent for subsequent tool calls in this batch
|
|
291
|
+
event_buffer.set_pending_tool_calls_parent_id(parent_message_id)
|
|
254
292
|
|
|
255
293
|
start_event = ToolCallStartEvent(
|
|
256
294
|
type=EventType.TOOL_CALL_START,
|
|
@@ -341,58 +379,60 @@ def _create_completion_events(
|
|
|
341
379
|
end_message_event = TextMessageEndEvent(type=EventType.TEXT_MESSAGE_END, message_id=message_id)
|
|
342
380
|
events_to_emit.append(end_message_event)
|
|
343
381
|
|
|
344
|
-
#
|
|
345
|
-
if isinstance(chunk, RunPausedEvent)
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
)
|
|
353
|
-
events_to_emit.append(assistant_start_event)
|
|
354
|
-
|
|
355
|
-
# Add any text content if present for the assistant message
|
|
356
|
-
if chunk.content:
|
|
357
|
-
content_event = TextMessageContentEvent(
|
|
358
|
-
type=EventType.TEXT_MESSAGE_CONTENT,
|
|
382
|
+
# Emit external execution tools
|
|
383
|
+
if isinstance(chunk, RunPausedEvent):
|
|
384
|
+
external_tools = chunk.tools_awaiting_external_execution
|
|
385
|
+
if external_tools:
|
|
386
|
+
# First, emit an assistant message for external tool calls
|
|
387
|
+
assistant_message_id = str(uuid.uuid4())
|
|
388
|
+
assistant_start_event = TextMessageStartEvent(
|
|
389
|
+
type=EventType.TEXT_MESSAGE_START,
|
|
359
390
|
message_id=assistant_message_id,
|
|
360
|
-
|
|
391
|
+
role="assistant",
|
|
361
392
|
)
|
|
362
|
-
events_to_emit.append(
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
for tool in chunk.tools:
|
|
373
|
-
if tool.tool_call_id is None or tool.tool_name is None:
|
|
374
|
-
continue
|
|
393
|
+
events_to_emit.append(assistant_start_event)
|
|
394
|
+
|
|
395
|
+
# Add any text content if present for the assistant message
|
|
396
|
+
if chunk.content:
|
|
397
|
+
content_event = TextMessageContentEvent(
|
|
398
|
+
type=EventType.TEXT_MESSAGE_CONTENT,
|
|
399
|
+
message_id=assistant_message_id,
|
|
400
|
+
delta=str(chunk.content),
|
|
401
|
+
)
|
|
402
|
+
events_to_emit.append(content_event)
|
|
375
403
|
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
parent_message_id=assistant_message_id, # Use the assistant message as parent
|
|
404
|
+
# End the assistant message
|
|
405
|
+
assistant_end_event = TextMessageEndEvent(
|
|
406
|
+
type=EventType.TEXT_MESSAGE_END,
|
|
407
|
+
message_id=assistant_message_id,
|
|
381
408
|
)
|
|
382
|
-
events_to_emit.append(
|
|
409
|
+
events_to_emit.append(assistant_end_event)
|
|
410
|
+
|
|
411
|
+
# Emit tool call events for external execution
|
|
412
|
+
for tool in external_tools:
|
|
413
|
+
if tool.tool_call_id is None or tool.tool_name is None:
|
|
414
|
+
continue
|
|
415
|
+
|
|
416
|
+
start_event = ToolCallStartEvent(
|
|
417
|
+
type=EventType.TOOL_CALL_START,
|
|
418
|
+
tool_call_id=tool.tool_call_id,
|
|
419
|
+
tool_call_name=tool.tool_name,
|
|
420
|
+
parent_message_id=assistant_message_id, # Use the assistant message as parent
|
|
421
|
+
)
|
|
422
|
+
events_to_emit.append(start_event)
|
|
383
423
|
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
424
|
+
args_event = ToolCallArgsEvent(
|
|
425
|
+
type=EventType.TOOL_CALL_ARGS,
|
|
426
|
+
tool_call_id=tool.tool_call_id,
|
|
427
|
+
delta=json.dumps(tool.tool_args),
|
|
428
|
+
)
|
|
429
|
+
events_to_emit.append(args_event)
|
|
390
430
|
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
431
|
+
end_event = ToolCallEndEvent(
|
|
432
|
+
type=EventType.TOOL_CALL_END,
|
|
433
|
+
tool_call_id=tool.tool_call_id,
|
|
434
|
+
)
|
|
435
|
+
events_to_emit.append(end_event)
|
|
396
436
|
|
|
397
437
|
run_finished_event = RunFinishedEvent(type=EventType.RUN_FINISHED, thread_id=thread_id, run_id=run_id)
|
|
398
438
|
events_to_emit.append(run_finished_event)
|
agno/os/interfaces/base.py
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
from abc import ABC, abstractmethod
|
|
2
|
-
from typing import List, Optional
|
|
2
|
+
from typing import List, Optional, Union
|
|
3
3
|
|
|
4
4
|
from fastapi import APIRouter
|
|
5
5
|
|
|
6
|
-
from agno.agent import Agent
|
|
7
|
-
from agno.team import Team
|
|
8
|
-
from agno.workflow
|
|
6
|
+
from agno.agent import Agent, RemoteAgent
|
|
7
|
+
from agno.team import RemoteTeam, Team
|
|
8
|
+
from agno.workflow import RemoteWorkflow, Workflow
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
class BaseInterface(ABC):
|
|
12
12
|
type: str
|
|
13
13
|
version: str = "1.0"
|
|
14
|
-
agent: Optional[Agent] = None
|
|
15
|
-
team: Optional[Team] = None
|
|
16
|
-
workflow: Optional[Workflow] = None
|
|
14
|
+
agent: Optional[Union[Agent, RemoteAgent]] = None
|
|
15
|
+
team: Optional[Union[Team, RemoteTeam]] = None
|
|
16
|
+
workflow: Optional[Union[Workflow, RemoteWorkflow]] = None
|
|
17
17
|
|
|
18
18
|
prefix: str
|
|
19
19
|
tags: List[str]
|