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/workflow/remote.py
ADDED
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
import time
|
|
2
|
+
from typing import TYPE_CHECKING, Any, AsyncIterator, Dict, List, Literal, Optional, Tuple, Union, overload
|
|
3
|
+
|
|
4
|
+
from fastapi import WebSocket
|
|
5
|
+
from pydantic import BaseModel
|
|
6
|
+
|
|
7
|
+
from agno.media import Audio, File, Image, Video
|
|
8
|
+
from agno.models.message import Message
|
|
9
|
+
from agno.remote.base import BaseRemote, RemoteDb
|
|
10
|
+
from agno.run.workflow import WorkflowRunOutput, WorkflowRunOutputEvent
|
|
11
|
+
from agno.utils.agent import validate_input
|
|
12
|
+
from agno.utils.remote import serialize_input
|
|
13
|
+
|
|
14
|
+
if TYPE_CHECKING:
|
|
15
|
+
from agno.os.routers.workflows.schema import WorkflowResponse
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class RemoteWorkflow(BaseRemote):
|
|
19
|
+
# Private cache for workflow config with TTL: (config, timestamp)
|
|
20
|
+
_cached_workflow_config: Optional[Tuple["WorkflowResponse", float]] = None
|
|
21
|
+
|
|
22
|
+
def __init__(
|
|
23
|
+
self,
|
|
24
|
+
base_url: str,
|
|
25
|
+
workflow_id: str,
|
|
26
|
+
timeout: float = 300.0,
|
|
27
|
+
protocol: Literal["agentos", "a2a"] = "agentos",
|
|
28
|
+
a2a_protocol: Literal["json-rpc", "rest"] = "rest",
|
|
29
|
+
config_ttl: float = 300.0,
|
|
30
|
+
):
|
|
31
|
+
"""Initialize RemoteWorkflow for remote execution.
|
|
32
|
+
|
|
33
|
+
Supports two protocols:
|
|
34
|
+
- "agentos": Agno's proprietary AgentOS REST API (default)
|
|
35
|
+
- "a2a": A2A (Agent-to-Agent) protocol for cross-framework communication
|
|
36
|
+
|
|
37
|
+
Args:
|
|
38
|
+
base_url: Base URL for remote instance (e.g., "http://localhost:7777")
|
|
39
|
+
workflow_id: ID of remote workflow on the remote server
|
|
40
|
+
timeout: Request timeout in seconds (default: 300)
|
|
41
|
+
protocol: Communication protocol - "agentos" (default) or "a2a"
|
|
42
|
+
a2a_protocol: For A2A protocol only - Whether to use JSON-RPC or REST protocol.
|
|
43
|
+
config_ttl: Time-to-live for cached config in seconds (default: 300)
|
|
44
|
+
"""
|
|
45
|
+
super().__init__(base_url, timeout, protocol, a2a_protocol, config_ttl)
|
|
46
|
+
self.workflow_id = workflow_id
|
|
47
|
+
self._cached_workflow_config = None
|
|
48
|
+
self._config_ttl = config_ttl
|
|
49
|
+
|
|
50
|
+
@property
|
|
51
|
+
def id(self) -> str:
|
|
52
|
+
return self.workflow_id
|
|
53
|
+
|
|
54
|
+
async def get_workflow_config(self) -> "WorkflowResponse":
|
|
55
|
+
"""Get the workflow config from remote (always fetches fresh)."""
|
|
56
|
+
from agno.os.routers.workflows.schema import WorkflowResponse
|
|
57
|
+
|
|
58
|
+
if self.protocol == "a2a":
|
|
59
|
+
from agno.client.a2a.schemas import AgentCard
|
|
60
|
+
|
|
61
|
+
agent_card: Optional[AgentCard] = await self.a2a_client.aget_agent_card() # type: ignore
|
|
62
|
+
|
|
63
|
+
return WorkflowResponse(
|
|
64
|
+
id=self.workflow_id,
|
|
65
|
+
name=agent_card.name if agent_card else self.workflow_id,
|
|
66
|
+
description=agent_card.description if agent_card else f"A2A workflow: {self.workflow_id}",
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
# AgentOS protocol: fetch fresh config from remote
|
|
70
|
+
return await self.agentos_client.aget_workflow(self.workflow_id) # type: ignore
|
|
71
|
+
|
|
72
|
+
@property
|
|
73
|
+
def _workflow_config(self) -> "WorkflowResponse":
|
|
74
|
+
"""Get the workflow config from remote, cached with TTL."""
|
|
75
|
+
from agno.os.routers.workflows.schema import WorkflowResponse
|
|
76
|
+
|
|
77
|
+
if self.protocol == "a2a":
|
|
78
|
+
from agno.client.a2a.schemas import AgentCard
|
|
79
|
+
|
|
80
|
+
agent_card: Optional[AgentCard] = self.a2a_client.get_agent_card() # type: ignore
|
|
81
|
+
|
|
82
|
+
return WorkflowResponse(
|
|
83
|
+
id=self.workflow_id,
|
|
84
|
+
name=agent_card.name if agent_card else self.workflow_id,
|
|
85
|
+
description=agent_card.description if agent_card else f"A2A workflow: {self.workflow_id}",
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
current_time = time.time()
|
|
89
|
+
|
|
90
|
+
# Check if cache is valid
|
|
91
|
+
if self._cached_workflow_config is not None:
|
|
92
|
+
config, cached_at = self._cached_workflow_config
|
|
93
|
+
if current_time - cached_at < self.config_ttl:
|
|
94
|
+
return config
|
|
95
|
+
|
|
96
|
+
# Fetch fresh config
|
|
97
|
+
config: WorkflowResponse = self.agentos_client.get_workflow(self.workflow_id) # type: ignore
|
|
98
|
+
self._cached_workflow_config = (config, current_time)
|
|
99
|
+
return config
|
|
100
|
+
|
|
101
|
+
async def refresh_config(self) -> "WorkflowResponse":
|
|
102
|
+
"""Force refresh the cached workflow config."""
|
|
103
|
+
from agno.os.routers.workflows.schema import WorkflowResponse
|
|
104
|
+
|
|
105
|
+
config: WorkflowResponse = await self.agentos_client.aget_workflow(self.workflow_id) # type: ignore
|
|
106
|
+
self._cached_workflow_config = (config, time.time())
|
|
107
|
+
return config
|
|
108
|
+
|
|
109
|
+
@property
|
|
110
|
+
def name(self) -> Optional[str]:
|
|
111
|
+
if self._workflow_config is not None:
|
|
112
|
+
return self._workflow_config.name
|
|
113
|
+
return None
|
|
114
|
+
|
|
115
|
+
@property
|
|
116
|
+
def description(self) -> Optional[str]:
|
|
117
|
+
if self._workflow_config is not None:
|
|
118
|
+
return self._workflow_config.description
|
|
119
|
+
return None
|
|
120
|
+
|
|
121
|
+
@property
|
|
122
|
+
def db(self) -> Optional[RemoteDb]:
|
|
123
|
+
if (
|
|
124
|
+
self.agentos_client
|
|
125
|
+
and self._config
|
|
126
|
+
and self._workflow_config is not None
|
|
127
|
+
and self._workflow_config.db_id is not None
|
|
128
|
+
):
|
|
129
|
+
return RemoteDb.from_config(
|
|
130
|
+
db_id=self._workflow_config.db_id,
|
|
131
|
+
client=self.agentos_client,
|
|
132
|
+
config=self._config,
|
|
133
|
+
)
|
|
134
|
+
return None
|
|
135
|
+
|
|
136
|
+
@overload
|
|
137
|
+
async def arun(
|
|
138
|
+
self,
|
|
139
|
+
input: Optional[Union[str, Dict[str, Any], List[Any], BaseModel, List[Message]]] = None,
|
|
140
|
+
additional_data: Optional[Dict[str, Any]] = None,
|
|
141
|
+
user_id: Optional[str] = None,
|
|
142
|
+
run_id: Optional[str] = None,
|
|
143
|
+
session_id: Optional[str] = None,
|
|
144
|
+
session_state: Optional[Dict[str, Any]] = None,
|
|
145
|
+
audio: Optional[List[Audio]] = None,
|
|
146
|
+
images: Optional[List[Image]] = None,
|
|
147
|
+
videos: Optional[List[Video]] = None,
|
|
148
|
+
files: Optional[List[File]] = None,
|
|
149
|
+
stream: Literal[False] = False,
|
|
150
|
+
stream_events: Optional[bool] = None,
|
|
151
|
+
stream_intermediate_steps: Optional[bool] = None,
|
|
152
|
+
background: Optional[bool] = False,
|
|
153
|
+
websocket: Optional[WebSocket] = None,
|
|
154
|
+
background_tasks: Optional[Any] = None,
|
|
155
|
+
auth_token: Optional[str] = None,
|
|
156
|
+
) -> WorkflowRunOutput: ...
|
|
157
|
+
|
|
158
|
+
@overload
|
|
159
|
+
def arun(
|
|
160
|
+
self,
|
|
161
|
+
input: Optional[Union[str, Dict[str, Any], List[Any], BaseModel, List[Message]]] = None,
|
|
162
|
+
additional_data: Optional[Dict[str, Any]] = None,
|
|
163
|
+
user_id: Optional[str] = None,
|
|
164
|
+
run_id: Optional[str] = None,
|
|
165
|
+
session_id: Optional[str] = None,
|
|
166
|
+
session_state: Optional[Dict[str, Any]] = None,
|
|
167
|
+
audio: Optional[List[Audio]] = None,
|
|
168
|
+
images: Optional[List[Image]] = None,
|
|
169
|
+
videos: Optional[List[Video]] = None,
|
|
170
|
+
files: Optional[List[File]] = None,
|
|
171
|
+
stream: Literal[True] = True,
|
|
172
|
+
stream_events: Optional[bool] = None,
|
|
173
|
+
stream_intermediate_steps: Optional[bool] = None,
|
|
174
|
+
background: Optional[bool] = False,
|
|
175
|
+
websocket: Optional[WebSocket] = None,
|
|
176
|
+
background_tasks: Optional[Any] = None,
|
|
177
|
+
auth_token: Optional[str] = None,
|
|
178
|
+
) -> AsyncIterator[WorkflowRunOutputEvent]: ...
|
|
179
|
+
|
|
180
|
+
def arun( # type: ignore
|
|
181
|
+
self,
|
|
182
|
+
input: Union[str, Dict[str, Any], List[Any], BaseModel, List[Message]],
|
|
183
|
+
additional_data: Optional[Dict[str, Any]] = None,
|
|
184
|
+
user_id: Optional[str] = None,
|
|
185
|
+
run_id: Optional[str] = None,
|
|
186
|
+
session_id: Optional[str] = None,
|
|
187
|
+
session_state: Optional[Dict[str, Any]] = None,
|
|
188
|
+
audio: Optional[List[Audio]] = None,
|
|
189
|
+
images: Optional[List[Image]] = None,
|
|
190
|
+
videos: Optional[List[Video]] = None,
|
|
191
|
+
files: Optional[List[File]] = None,
|
|
192
|
+
stream: bool = False,
|
|
193
|
+
stream_events: Optional[bool] = None,
|
|
194
|
+
background: Optional[bool] = False,
|
|
195
|
+
websocket: Optional[WebSocket] = None,
|
|
196
|
+
background_tasks: Optional[Any] = None,
|
|
197
|
+
auth_token: Optional[str] = None,
|
|
198
|
+
**kwargs: Any,
|
|
199
|
+
) -> Union[WorkflowRunOutput, AsyncIterator[WorkflowRunOutputEvent]]:
|
|
200
|
+
# TODO: Deal with background
|
|
201
|
+
validated_input = validate_input(input)
|
|
202
|
+
serialized_input = serialize_input(validated_input)
|
|
203
|
+
headers = self._get_auth_headers(auth_token)
|
|
204
|
+
|
|
205
|
+
# A2A protocol path
|
|
206
|
+
if self.a2a_client:
|
|
207
|
+
return self._arun_a2a( # type: ignore[return-value]
|
|
208
|
+
message=serialized_input,
|
|
209
|
+
stream=stream or False,
|
|
210
|
+
user_id=user_id,
|
|
211
|
+
context_id=session_id, # Map session_id → context_id for A2A
|
|
212
|
+
images=images,
|
|
213
|
+
videos=videos,
|
|
214
|
+
audio=audio,
|
|
215
|
+
files=files,
|
|
216
|
+
headers=headers,
|
|
217
|
+
)
|
|
218
|
+
|
|
219
|
+
# AgentOS protocol path (default)
|
|
220
|
+
if self.agentos_client:
|
|
221
|
+
if stream:
|
|
222
|
+
# Handle streaming response
|
|
223
|
+
return self.agentos_client.run_workflow_stream(
|
|
224
|
+
workflow_id=self.workflow_id,
|
|
225
|
+
message=serialized_input,
|
|
226
|
+
additional_data=additional_data,
|
|
227
|
+
run_id=run_id,
|
|
228
|
+
session_id=session_id,
|
|
229
|
+
user_id=user_id,
|
|
230
|
+
audio=audio,
|
|
231
|
+
images=images,
|
|
232
|
+
videos=videos,
|
|
233
|
+
files=files,
|
|
234
|
+
session_state=session_state,
|
|
235
|
+
stream_events=stream_events,
|
|
236
|
+
headers=headers,
|
|
237
|
+
**kwargs,
|
|
238
|
+
)
|
|
239
|
+
else:
|
|
240
|
+
return self.agentos_client.run_workflow( # type: ignore
|
|
241
|
+
workflow_id=self.workflow_id,
|
|
242
|
+
message=serialized_input,
|
|
243
|
+
additional_data=additional_data,
|
|
244
|
+
run_id=run_id,
|
|
245
|
+
session_id=session_id,
|
|
246
|
+
user_id=user_id,
|
|
247
|
+
audio=audio,
|
|
248
|
+
images=images,
|
|
249
|
+
videos=videos,
|
|
250
|
+
files=files,
|
|
251
|
+
session_state=session_state,
|
|
252
|
+
headers=headers,
|
|
253
|
+
**kwargs,
|
|
254
|
+
)
|
|
255
|
+
else:
|
|
256
|
+
raise ValueError("No client available")
|
|
257
|
+
|
|
258
|
+
def _arun_a2a(
|
|
259
|
+
self,
|
|
260
|
+
message: str,
|
|
261
|
+
stream: bool,
|
|
262
|
+
user_id: Optional[str],
|
|
263
|
+
context_id: Optional[str],
|
|
264
|
+
images: Optional[List[Image]],
|
|
265
|
+
videos: Optional[List[Video]],
|
|
266
|
+
audio: Optional[List[Audio]],
|
|
267
|
+
files: Optional[List[File]],
|
|
268
|
+
headers: Optional[Dict[str, str]],
|
|
269
|
+
) -> Union[WorkflowRunOutput, AsyncIterator[WorkflowRunOutputEvent]]:
|
|
270
|
+
"""Execute via A2A protocol.
|
|
271
|
+
|
|
272
|
+
Args:
|
|
273
|
+
message: Serialized message string
|
|
274
|
+
stream: Whether to stream the response
|
|
275
|
+
user_id: User identifier
|
|
276
|
+
context_id: Session/context ID (maps to session_id)
|
|
277
|
+
images: Images to include
|
|
278
|
+
videos: Videos to include
|
|
279
|
+
audio: Audio files to include
|
|
280
|
+
files: Files to include
|
|
281
|
+
headers: HTTP headers to include in the request (optional)
|
|
282
|
+
Returns:
|
|
283
|
+
WorkflowRunOutput for non-streaming, AsyncIterator[WorkflowRunOutputEvent] for streaming
|
|
284
|
+
"""
|
|
285
|
+
if not self.a2a_client:
|
|
286
|
+
raise ValueError("A2A client not available")
|
|
287
|
+
from agno.client.a2a.utils import map_stream_events_to_workflow_run_events
|
|
288
|
+
|
|
289
|
+
if stream:
|
|
290
|
+
# Return async generator for streaming
|
|
291
|
+
event_stream = self.a2a_client.stream_message(
|
|
292
|
+
message=message,
|
|
293
|
+
context_id=context_id,
|
|
294
|
+
user_id=user_id,
|
|
295
|
+
images=list(images) if images else None,
|
|
296
|
+
audio=list(audio) if audio else None,
|
|
297
|
+
videos=list(videos) if videos else None,
|
|
298
|
+
files=list(files) if files else None,
|
|
299
|
+
headers=headers,
|
|
300
|
+
)
|
|
301
|
+
return map_stream_events_to_workflow_run_events(event_stream, workflow_id=self.workflow_id) # type: ignore
|
|
302
|
+
else:
|
|
303
|
+
# Return coroutine for non-streaming
|
|
304
|
+
return self._arun_a2a_send( # type: ignore[return-value]
|
|
305
|
+
message=message,
|
|
306
|
+
user_id=user_id,
|
|
307
|
+
context_id=context_id,
|
|
308
|
+
images=images,
|
|
309
|
+
audio=audio,
|
|
310
|
+
videos=videos,
|
|
311
|
+
files=files,
|
|
312
|
+
headers=headers,
|
|
313
|
+
)
|
|
314
|
+
|
|
315
|
+
async def _arun_a2a_send(
|
|
316
|
+
self,
|
|
317
|
+
message: str,
|
|
318
|
+
user_id: Optional[str],
|
|
319
|
+
context_id: Optional[str],
|
|
320
|
+
images: Optional[List[Image]],
|
|
321
|
+
videos: Optional[List[Video]],
|
|
322
|
+
audio: Optional[List[Audio]],
|
|
323
|
+
files: Optional[List[File]],
|
|
324
|
+
headers: Optional[Dict[str, str]],
|
|
325
|
+
) -> WorkflowRunOutput:
|
|
326
|
+
"""Send a non-streaming A2A message and convert response to WorkflowRunOutput."""
|
|
327
|
+
if not self.a2a_client:
|
|
328
|
+
raise ValueError("A2A client not available")
|
|
329
|
+
from agno.client.a2a.utils import map_task_result_to_workflow_run_output
|
|
330
|
+
|
|
331
|
+
task_result = await self.a2a_client.send_message(
|
|
332
|
+
message=message,
|
|
333
|
+
context_id=context_id,
|
|
334
|
+
user_id=user_id,
|
|
335
|
+
images=list(images) if images else None,
|
|
336
|
+
audio=list(audio) if audio else None,
|
|
337
|
+
videos=list(videos) if videos else None,
|
|
338
|
+
files=list(files) if files else None,
|
|
339
|
+
headers=headers,
|
|
340
|
+
)
|
|
341
|
+
return map_task_result_to_workflow_run_output(task_result, workflow_id=self.workflow_id, user_id=user_id)
|
|
342
|
+
|
|
343
|
+
async def acancel_run(self, run_id: str, auth_token: Optional[str] = None) -> bool:
|
|
344
|
+
"""Cancel a running workflow execution.
|
|
345
|
+
|
|
346
|
+
Args:
|
|
347
|
+
run_id (str): The run_id to cancel.
|
|
348
|
+
auth_token: Optional JWT token for authentication.
|
|
349
|
+
|
|
350
|
+
Returns:
|
|
351
|
+
bool: True if the run was found and marked for cancellation, False otherwise.
|
|
352
|
+
"""
|
|
353
|
+
headers = self._get_auth_headers(auth_token)
|
|
354
|
+
try:
|
|
355
|
+
await self.get_os_client().cancel_workflow_run(
|
|
356
|
+
workflow_id=self.workflow_id,
|
|
357
|
+
run_id=run_id,
|
|
358
|
+
headers=headers,
|
|
359
|
+
)
|
|
360
|
+
return True
|
|
361
|
+
except Exception:
|
|
362
|
+
return False
|
agno/workflow/router.py
CHANGED
|
@@ -178,6 +178,7 @@ class Router:
|
|
|
178
178
|
workflow_session: Optional[WorkflowSession] = None,
|
|
179
179
|
add_workflow_history_to_steps: Optional[bool] = False,
|
|
180
180
|
num_history_runs: int = 3,
|
|
181
|
+
background_tasks: Optional[Any] = None,
|
|
181
182
|
) -> StepOutput:
|
|
182
183
|
"""Execute the router and its selected steps with sequential chaining"""
|
|
183
184
|
log_debug(f"Router Start: {self.name}", center=True, symbol="-")
|
|
@@ -219,6 +220,7 @@ class Router:
|
|
|
219
220
|
workflow_session=workflow_session,
|
|
220
221
|
add_workflow_history_to_steps=add_workflow_history_to_steps,
|
|
221
222
|
num_history_runs=num_history_runs,
|
|
223
|
+
background_tasks=background_tasks,
|
|
222
224
|
)
|
|
223
225
|
|
|
224
226
|
# Handle both single StepOutput and List[StepOutput]
|
|
@@ -264,6 +266,7 @@ class Router:
|
|
|
264
266
|
step_type=StepType.ROUTER,
|
|
265
267
|
content=f"Router {self.name} completed with {len(all_results)} results",
|
|
266
268
|
success=all(result.success for result in all_results) if all_results else True,
|
|
269
|
+
stop=any(result.stop for result in all_results) if all_results else False,
|
|
267
270
|
steps=all_results,
|
|
268
271
|
)
|
|
269
272
|
|
|
@@ -275,7 +278,6 @@ class Router:
|
|
|
275
278
|
run_context: Optional[RunContext] = None,
|
|
276
279
|
session_state: Optional[Dict[str, Any]] = None,
|
|
277
280
|
stream_events: bool = False,
|
|
278
|
-
stream_intermediate_steps: bool = False,
|
|
279
281
|
stream_executor_events: bool = True,
|
|
280
282
|
workflow_run_response: Optional[WorkflowRunOutput] = None,
|
|
281
283
|
step_index: Optional[Union[int, tuple]] = None,
|
|
@@ -284,6 +286,7 @@ class Router:
|
|
|
284
286
|
workflow_session: Optional[WorkflowSession] = None,
|
|
285
287
|
add_workflow_history_to_steps: Optional[bool] = False,
|
|
286
288
|
num_history_runs: int = 3,
|
|
289
|
+
background_tasks: Optional[Any] = None,
|
|
287
290
|
) -> Iterator[Union[WorkflowRunOutputEvent, StepOutput]]:
|
|
288
291
|
"""Execute the router with streaming support"""
|
|
289
292
|
log_debug(f"Router Start: {self.name}", center=True, symbol="-")
|
|
@@ -299,9 +302,6 @@ class Router:
|
|
|
299
302
|
steps_to_execute = self._route_steps(step_input, session_state=session_state)
|
|
300
303
|
log_debug(f"Router {self.name}: Selected {len(steps_to_execute)} steps to execute")
|
|
301
304
|
|
|
302
|
-
# Considering both stream_events and stream_intermediate_steps (deprecated)
|
|
303
|
-
stream_events = stream_events or stream_intermediate_steps
|
|
304
|
-
|
|
305
305
|
if stream_events and workflow_run_response:
|
|
306
306
|
# Yield router started event
|
|
307
307
|
yield RouterExecutionStartedEvent(
|
|
@@ -357,6 +357,7 @@ class Router:
|
|
|
357
357
|
workflow_session=workflow_session,
|
|
358
358
|
add_workflow_history_to_steps=add_workflow_history_to_steps,
|
|
359
359
|
num_history_runs=num_history_runs,
|
|
360
|
+
background_tasks=background_tasks,
|
|
360
361
|
):
|
|
361
362
|
if isinstance(event, StepOutput):
|
|
362
363
|
step_outputs_for_step.append(event)
|
|
@@ -427,6 +428,7 @@ class Router:
|
|
|
427
428
|
step_type=StepType.ROUTER,
|
|
428
429
|
content=f"Router {self.name} completed with {len(all_results)} results",
|
|
429
430
|
success=all(result.success for result in all_results) if all_results else True,
|
|
431
|
+
stop=any(result.stop for result in all_results) if all_results else False,
|
|
430
432
|
steps=all_results,
|
|
431
433
|
)
|
|
432
434
|
|
|
@@ -442,6 +444,7 @@ class Router:
|
|
|
442
444
|
workflow_session: Optional[WorkflowSession] = None,
|
|
443
445
|
add_workflow_history_to_steps: Optional[bool] = False,
|
|
444
446
|
num_history_runs: int = 3,
|
|
447
|
+
background_tasks: Optional[Any] = None,
|
|
445
448
|
) -> StepOutput:
|
|
446
449
|
"""Async execute the router and its selected steps with sequential chaining"""
|
|
447
450
|
log_debug(f"Router Start: {self.name}", center=True, symbol="-")
|
|
@@ -484,6 +487,7 @@ class Router:
|
|
|
484
487
|
workflow_session=workflow_session,
|
|
485
488
|
add_workflow_history_to_steps=add_workflow_history_to_steps,
|
|
486
489
|
num_history_runs=num_history_runs,
|
|
490
|
+
background_tasks=background_tasks,
|
|
487
491
|
)
|
|
488
492
|
# Handle both single StepOutput and List[StepOutput]
|
|
489
493
|
if isinstance(step_output, list):
|
|
@@ -531,6 +535,7 @@ class Router:
|
|
|
531
535
|
step_type=StepType.ROUTER,
|
|
532
536
|
content=f"Router {self.name} completed with {len(all_results)} results",
|
|
533
537
|
success=all(result.success for result in all_results) if all_results else True,
|
|
538
|
+
stop=any(result.stop for result in all_results) if all_results else False,
|
|
534
539
|
steps=all_results,
|
|
535
540
|
)
|
|
536
541
|
|
|
@@ -542,7 +547,6 @@ class Router:
|
|
|
542
547
|
run_context: Optional[RunContext] = None,
|
|
543
548
|
session_state: Optional[Dict[str, Any]] = None,
|
|
544
549
|
stream_events: bool = False,
|
|
545
|
-
stream_intermediate_steps: bool = False,
|
|
546
550
|
stream_executor_events: bool = True,
|
|
547
551
|
workflow_run_response: Optional[WorkflowRunOutput] = None,
|
|
548
552
|
step_index: Optional[Union[int, tuple]] = None,
|
|
@@ -551,6 +555,7 @@ class Router:
|
|
|
551
555
|
workflow_session: Optional[WorkflowSession] = None,
|
|
552
556
|
add_workflow_history_to_steps: Optional[bool] = False,
|
|
553
557
|
num_history_runs: int = 3,
|
|
558
|
+
background_tasks: Optional[Any] = None,
|
|
554
559
|
) -> AsyncIterator[Union[WorkflowRunOutputEvent, TeamRunOutputEvent, RunOutputEvent, StepOutput]]:
|
|
555
560
|
"""Async execute the router with streaming support"""
|
|
556
561
|
log_debug(f"Router Start: {self.name}", center=True, symbol="-")
|
|
@@ -566,9 +571,6 @@ class Router:
|
|
|
566
571
|
steps_to_execute = await self._aroute_steps(step_input, session_state=session_state)
|
|
567
572
|
log_debug(f"Router {self.name} selected: {len(steps_to_execute)} steps to execute")
|
|
568
573
|
|
|
569
|
-
# Considering both stream_events and stream_intermediate_steps (deprecated)
|
|
570
|
-
stream_events = stream_events or stream_intermediate_steps
|
|
571
|
-
|
|
572
574
|
if stream_events and workflow_run_response:
|
|
573
575
|
# Yield router started event
|
|
574
576
|
yield RouterExecutionStartedEvent(
|
|
@@ -626,6 +628,7 @@ class Router:
|
|
|
626
628
|
workflow_session=workflow_session,
|
|
627
629
|
add_workflow_history_to_steps=add_workflow_history_to_steps,
|
|
628
630
|
num_history_runs=num_history_runs,
|
|
631
|
+
background_tasks=background_tasks,
|
|
629
632
|
):
|
|
630
633
|
if isinstance(event, StepOutput):
|
|
631
634
|
step_outputs_for_step.append(event)
|
|
@@ -697,6 +700,6 @@ class Router:
|
|
|
697
700
|
content=f"Router {self.name} completed with {len(all_results)} results",
|
|
698
701
|
success=all(result.success for result in all_results) if all_results else True,
|
|
699
702
|
error=None,
|
|
700
|
-
stop=False,
|
|
703
|
+
stop=any(result.stop for result in all_results) if all_results else False,
|
|
701
704
|
steps=all_results,
|
|
702
705
|
)
|