agno 2.3.21__py3-none-any.whl → 2.3.23__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- agno/agent/agent.py +48 -2
- agno/agent/remote.py +234 -73
- 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/db/migrations/utils.py +19 -0
- agno/db/migrations/v1_to_v2.py +54 -16
- agno/db/migrations/versions/v2_3_0.py +92 -53
- agno/db/mysql/async_mysql.py +5 -7
- agno/db/mysql/mysql.py +5 -7
- agno/db/mysql/schemas.py +39 -21
- agno/db/postgres/async_postgres.py +172 -42
- agno/db/postgres/postgres.py +186 -38
- agno/db/postgres/schemas.py +39 -21
- agno/db/postgres/utils.py +6 -2
- agno/db/singlestore/schemas.py +41 -21
- agno/db/singlestore/singlestore.py +14 -3
- agno/db/sqlite/async_sqlite.py +7 -2
- agno/db/sqlite/schemas.py +36 -21
- agno/db/sqlite/sqlite.py +3 -7
- agno/knowledge/chunking/document.py +3 -2
- agno/knowledge/chunking/markdown.py +8 -3
- agno/knowledge/chunking/recursive.py +2 -2
- agno/models/base.py +4 -0
- agno/models/google/gemini.py +27 -4
- agno/models/openai/chat.py +1 -1
- agno/models/openai/responses.py +14 -7
- agno/os/middleware/jwt.py +66 -27
- agno/os/routers/agents/router.py +3 -3
- agno/os/routers/evals/evals.py +2 -2
- agno/os/routers/knowledge/knowledge.py +5 -5
- agno/os/routers/knowledge/schemas.py +1 -1
- agno/os/routers/memory/memory.py +4 -4
- agno/os/routers/session/session.py +2 -2
- agno/os/routers/teams/router.py +4 -4
- agno/os/routers/traces/traces.py +3 -3
- agno/os/routers/workflows/router.py +3 -3
- agno/os/schema.py +1 -1
- agno/reasoning/deepseek.py +11 -1
- agno/reasoning/gemini.py +6 -2
- agno/reasoning/groq.py +8 -3
- agno/reasoning/openai.py +2 -0
- agno/remote/base.py +106 -9
- agno/skills/__init__.py +17 -0
- agno/skills/agent_skills.py +370 -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/team/remote.py +220 -60
- agno/team/team.py +41 -3
- agno/tools/brandfetch.py +27 -18
- agno/tools/browserbase.py +150 -13
- agno/tools/function.py +6 -1
- agno/tools/mcp/mcp.py +300 -17
- agno/tools/mcp/multi_mcp.py +269 -14
- agno/tools/toolkit.py +89 -21
- agno/utils/mcp.py +49 -8
- agno/utils/string.py +43 -1
- agno/workflow/condition.py +4 -2
- agno/workflow/loop.py +20 -1
- agno/workflow/remote.py +173 -33
- agno/workflow/router.py +4 -1
- agno/workflow/steps.py +4 -0
- agno/workflow/workflow.py +14 -0
- {agno-2.3.21.dist-info → agno-2.3.23.dist-info}/METADATA +13 -14
- {agno-2.3.21.dist-info → agno-2.3.23.dist-info}/RECORD +74 -60
- {agno-2.3.21.dist-info → agno-2.3.23.dist-info}/WHEEL +0 -0
- {agno-2.3.21.dist-info → agno-2.3.23.dist-info}/licenses/LICENSE +0 -0
- {agno-2.3.21.dist-info → agno-2.3.23.dist-info}/top_level.txt +0 -0
agno/agent/agent.py
CHANGED
|
@@ -61,6 +61,9 @@ from agno.run.agent import (
|
|
|
61
61
|
RunOutput,
|
|
62
62
|
RunOutputEvent,
|
|
63
63
|
)
|
|
64
|
+
from agno.run.cancel import (
|
|
65
|
+
acancel_run as acancel_run_global,
|
|
66
|
+
)
|
|
64
67
|
from agno.run.cancel import (
|
|
65
68
|
acleanup_run,
|
|
66
69
|
araise_if_cancelled,
|
|
@@ -77,6 +80,7 @@ from agno.run.requirement import RunRequirement
|
|
|
77
80
|
from agno.run.team import TeamRunOutputEvent
|
|
78
81
|
from agno.session import AgentSession, SessionSummaryManager, TeamSession, WorkflowSession
|
|
79
82
|
from agno.session.summary import SessionSummary
|
|
83
|
+
from agno.skills import Skills
|
|
80
84
|
from agno.tools import Toolkit
|
|
81
85
|
from agno.tools.function import Function
|
|
82
86
|
from agno.utils.agent import (
|
|
@@ -264,6 +268,10 @@ class Agent:
|
|
|
264
268
|
knowledge_retriever: Optional[Callable[..., Optional[List[Union[Dict, str]]]]] = None
|
|
265
269
|
references_format: Literal["json", "yaml"] = "json"
|
|
266
270
|
|
|
271
|
+
# --- Skills ---
|
|
272
|
+
# Skills provide structured instructions, reference docs, and scripts for agents
|
|
273
|
+
skills: Optional[Skills] = None
|
|
274
|
+
|
|
267
275
|
# --- Agent Tools ---
|
|
268
276
|
# A list of tools provided to the Model.
|
|
269
277
|
# Tools are functions the model may generate JSON inputs for.
|
|
@@ -487,6 +495,7 @@ class Agent:
|
|
|
487
495
|
add_knowledge_to_context: bool = False,
|
|
488
496
|
knowledge_retriever: Optional[Callable[..., Optional[List[Union[Dict, str]]]]] = None,
|
|
489
497
|
references_format: Literal["json", "yaml"] = "json",
|
|
498
|
+
skills: Optional[Skills] = None,
|
|
490
499
|
metadata: Optional[Dict[str, Any]] = None,
|
|
491
500
|
tools: Optional[Sequence[Union[Toolkit, Callable, Function, Dict]]] = None,
|
|
492
501
|
tool_call_limit: Optional[int] = None,
|
|
@@ -610,6 +619,8 @@ class Agent:
|
|
|
610
619
|
self.knowledge_retriever = knowledge_retriever
|
|
611
620
|
self.references_format = references_format
|
|
612
621
|
|
|
622
|
+
self.skills = skills
|
|
623
|
+
|
|
613
624
|
self.metadata = metadata
|
|
614
625
|
|
|
615
626
|
self.tools = list(tools) if tools else []
|
|
@@ -2022,6 +2033,7 @@ class Agent:
|
|
|
2022
2033
|
run_response=run_response,
|
|
2023
2034
|
run_context=run_context,
|
|
2024
2035
|
session=agent_session,
|
|
2036
|
+
async_mode=True,
|
|
2025
2037
|
)
|
|
2026
2038
|
|
|
2027
2039
|
# 6. Prepare run messages
|
|
@@ -2369,6 +2381,7 @@ class Agent:
|
|
|
2369
2381
|
run_response=run_response,
|
|
2370
2382
|
run_context=run_context,
|
|
2371
2383
|
session=agent_session,
|
|
2384
|
+
async_mode=True,
|
|
2372
2385
|
)
|
|
2373
2386
|
|
|
2374
2387
|
# 6. Prepare run messages
|
|
@@ -3979,6 +3992,7 @@ class Agent:
|
|
|
3979
3992
|
run_response=run_response,
|
|
3980
3993
|
run_context=run_context,
|
|
3981
3994
|
session=agent_session,
|
|
3995
|
+
async_mode=True,
|
|
3982
3996
|
)
|
|
3983
3997
|
|
|
3984
3998
|
# 6. Prepare run messages
|
|
@@ -4285,6 +4299,7 @@ class Agent:
|
|
|
4285
4299
|
run_response=run_response,
|
|
4286
4300
|
run_context=run_context,
|
|
4287
4301
|
session=agent_session,
|
|
4302
|
+
async_mode=True,
|
|
4288
4303
|
)
|
|
4289
4304
|
|
|
4290
4305
|
# 6. Prepare run messages
|
|
@@ -6396,6 +6411,10 @@ class Agent:
|
|
|
6396
6411
|
if self.update_knowledge:
|
|
6397
6412
|
agent_tools.append(self.add_to_knowledge)
|
|
6398
6413
|
|
|
6414
|
+
# Add tools for accessing skills
|
|
6415
|
+
if self.skills is not None:
|
|
6416
|
+
agent_tools.extend(self.skills.get_tools())
|
|
6417
|
+
|
|
6399
6418
|
return agent_tools
|
|
6400
6419
|
|
|
6401
6420
|
async def aget_tools(
|
|
@@ -6489,6 +6508,10 @@ class Agent:
|
|
|
6489
6508
|
if self.update_knowledge:
|
|
6490
6509
|
agent_tools.append(self.add_to_knowledge)
|
|
6491
6510
|
|
|
6511
|
+
# Add tools for accessing skills
|
|
6512
|
+
if self.skills is not None:
|
|
6513
|
+
agent_tools.extend(self.skills.get_tools())
|
|
6514
|
+
|
|
6492
6515
|
return agent_tools
|
|
6493
6516
|
|
|
6494
6517
|
def _determine_tools_for_model(
|
|
@@ -6498,6 +6521,7 @@ class Agent:
|
|
|
6498
6521
|
run_response: RunOutput,
|
|
6499
6522
|
run_context: RunContext,
|
|
6500
6523
|
session: AgentSession,
|
|
6524
|
+
async_mode: bool = False,
|
|
6501
6525
|
) -> List[Union[Function, dict]]:
|
|
6502
6526
|
_function_names = []
|
|
6503
6527
|
_functions: List[Union[Function, dict]] = []
|
|
@@ -6528,7 +6552,8 @@ class Agent:
|
|
|
6528
6552
|
|
|
6529
6553
|
elif isinstance(tool, Toolkit):
|
|
6530
6554
|
# For each function in the toolkit and process entrypoint
|
|
6531
|
-
|
|
6555
|
+
toolkit_functions = tool.get_async_functions() if async_mode else tool.get_functions()
|
|
6556
|
+
for name, _func in toolkit_functions.items():
|
|
6532
6557
|
if name in _function_names:
|
|
6533
6558
|
continue
|
|
6534
6559
|
_function_names.append(name)
|
|
@@ -6758,6 +6783,18 @@ class Agent:
|
|
|
6758
6783
|
"""
|
|
6759
6784
|
return cancel_run_global(run_id)
|
|
6760
6785
|
|
|
6786
|
+
@staticmethod
|
|
6787
|
+
async def acancel_run(run_id: str) -> bool:
|
|
6788
|
+
"""Cancel a running agent execution (async version).
|
|
6789
|
+
|
|
6790
|
+
Args:
|
|
6791
|
+
run_id (str): The run_id to cancel.
|
|
6792
|
+
|
|
6793
|
+
Returns:
|
|
6794
|
+
bool: True if the run was found and marked for cancellation, False otherwise.
|
|
6795
|
+
"""
|
|
6796
|
+
return await acancel_run_global(run_id)
|
|
6797
|
+
|
|
6761
6798
|
# -*- Session Database Functions
|
|
6762
6799
|
def _read_session(
|
|
6763
6800
|
self, session_id: str, session_type: SessionType = SessionType.AGENT
|
|
@@ -7171,7 +7208,6 @@ class Agent:
|
|
|
7171
7208
|
"""
|
|
7172
7209
|
if self._has_async_db():
|
|
7173
7210
|
raise ValueError("Async database not supported for save_session")
|
|
7174
|
-
|
|
7175
7211
|
# If the agent is a member of a team, do not save the session to the database
|
|
7176
7212
|
if (
|
|
7177
7213
|
self.db is not None
|
|
@@ -7937,6 +7973,11 @@ class Agent:
|
|
|
7937
7973
|
# 3.3.8 Then add additional context
|
|
7938
7974
|
if self.additional_context is not None:
|
|
7939
7975
|
system_message_content += f"{self.additional_context}\n"
|
|
7976
|
+
# 3.3.8.1 Then add skills to the system prompt
|
|
7977
|
+
if self.skills is not None:
|
|
7978
|
+
skills_snippet = self.skills.get_system_prompt_snippet()
|
|
7979
|
+
if skills_snippet:
|
|
7980
|
+
system_message_content += f"\n{skills_snippet}\n"
|
|
7940
7981
|
# 3.3.9 Then add memories to the system prompt
|
|
7941
7982
|
if self.add_memories_to_context:
|
|
7942
7983
|
_memory_manager_not_set = False
|
|
@@ -8281,6 +8322,11 @@ class Agent:
|
|
|
8281
8322
|
# 3.3.8 Then add additional context
|
|
8282
8323
|
if self.additional_context is not None:
|
|
8283
8324
|
system_message_content += f"{self.additional_context}\n"
|
|
8325
|
+
# 3.3.8.1 Then add skills to the system prompt
|
|
8326
|
+
if self.skills is not None:
|
|
8327
|
+
skills_snippet = self.skills.get_system_prompt_snippet()
|
|
8328
|
+
if skills_snippet:
|
|
8329
|
+
system_message_content += f"\n{skills_snippet}\n"
|
|
8284
8330
|
# 3.3.9 Then add memories to the system prompt
|
|
8285
8331
|
if self.add_memories_to_context:
|
|
8286
8332
|
_memory_manager_not_set = False
|
agno/agent/remote.py
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import json
|
|
2
|
-
import time
|
|
3
2
|
from dataclasses import dataclass, field
|
|
4
3
|
from typing import TYPE_CHECKING, Any, AsyncIterator, Dict, List, Literal, Optional, Sequence, Tuple, Union, overload
|
|
5
4
|
|
|
@@ -29,19 +28,25 @@ class RemoteAgent(BaseRemote):
|
|
|
29
28
|
base_url: str,
|
|
30
29
|
agent_id: str,
|
|
31
30
|
timeout: float = 60.0,
|
|
31
|
+
protocol: Literal["agentos", "a2a"] = "agentos",
|
|
32
|
+
a2a_protocol: Literal["json-rpc", "rest"] = "rest",
|
|
32
33
|
config_ttl: float = 300.0,
|
|
33
34
|
):
|
|
34
|
-
"""Initialize
|
|
35
|
+
"""Initialize RemoteAgent for remote execution.
|
|
35
36
|
|
|
36
|
-
|
|
37
|
+
Supports two protocols:
|
|
38
|
+
- "agentos": Agno's proprietary AgentOS REST API (default)
|
|
39
|
+
- "a2a": A2A (Agent-to-Agent) protocol for cross-framework communication
|
|
37
40
|
|
|
38
41
|
Args:
|
|
39
|
-
base_url: Base URL for remote
|
|
40
|
-
agent_id: ID of remote agent
|
|
42
|
+
base_url: Base URL for remote instance (e.g., "http://localhost:7777")
|
|
43
|
+
agent_id: ID of remote agent on the remote server
|
|
41
44
|
timeout: Request timeout in seconds (default: 60)
|
|
45
|
+
protocol: Communication protocol - "agentos" (default) or "a2a"
|
|
46
|
+
a2a_protocol: For A2A protocol only - Whether to use JSON-RPC or REST protocol.
|
|
42
47
|
config_ttl: Time-to-live for cached config in seconds (default: 300)
|
|
43
48
|
"""
|
|
44
|
-
super().__init__(base_url, timeout, config_ttl)
|
|
49
|
+
super().__init__(base_url, timeout, protocol, a2a_protocol, config_ttl)
|
|
45
50
|
self.agent_id = agent_id
|
|
46
51
|
self._cached_agent_config = None
|
|
47
52
|
|
|
@@ -50,14 +55,48 @@ class RemoteAgent(BaseRemote):
|
|
|
50
55
|
return self.agent_id
|
|
51
56
|
|
|
52
57
|
async def get_agent_config(self) -> "AgentResponse":
|
|
53
|
-
"""
|
|
54
|
-
|
|
58
|
+
"""
|
|
59
|
+
Get the agent config from remote.
|
|
60
|
+
|
|
61
|
+
For A2A protocol, returns a minimal AgentResponse since A2A servers
|
|
62
|
+
don't expose the same config endpoints as AgentOS. For AgentOS, always fetches fresh config.
|
|
63
|
+
"""
|
|
64
|
+
from agno.os.routers.agents.schema import AgentResponse
|
|
65
|
+
|
|
66
|
+
if self.a2a_client:
|
|
67
|
+
from agno.client.a2a.schemas import AgentCard
|
|
68
|
+
|
|
69
|
+
agent_card: Optional[AgentCard] = await self.a2a_client.aget_agent_card()
|
|
70
|
+
|
|
71
|
+
return AgentResponse(
|
|
72
|
+
id=self.agent_id,
|
|
73
|
+
name=agent_card.name if agent_card else self.agent_id,
|
|
74
|
+
description=agent_card.description if agent_card else f"A2A agent: {self.agent_id}",
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
return await self.agentos_client.aget_agent(self.agent_id) # type: ignore
|
|
55
78
|
|
|
56
79
|
@property
|
|
57
|
-
def _agent_config(self) -> "AgentResponse":
|
|
58
|
-
"""
|
|
80
|
+
def _agent_config(self) -> Optional["AgentResponse"]:
|
|
81
|
+
"""
|
|
82
|
+
Get the agent config from remote, cached with TTL.
|
|
83
|
+
Returns None for A2A protocol since A2A servers don't expose agent config endpoints.
|
|
84
|
+
"""
|
|
85
|
+
import time
|
|
86
|
+
|
|
59
87
|
from agno.os.routers.agents.schema import AgentResponse
|
|
60
88
|
|
|
89
|
+
if self.a2a_client:
|
|
90
|
+
from agno.client.a2a.schemas import AgentCard
|
|
91
|
+
|
|
92
|
+
agent_card: Optional[AgentCard] = self.a2a_client.get_agent_card()
|
|
93
|
+
|
|
94
|
+
return AgentResponse(
|
|
95
|
+
id=self.agent_id,
|
|
96
|
+
name=agent_card.name if agent_card else self.agent_id,
|
|
97
|
+
description=agent_card.description if agent_card else f"A2A agent: {self.agent_id}",
|
|
98
|
+
)
|
|
99
|
+
|
|
61
100
|
current_time = time.time()
|
|
62
101
|
|
|
63
102
|
# Check if cache is valid
|
|
@@ -67,15 +106,24 @@ class RemoteAgent(BaseRemote):
|
|
|
67
106
|
return config
|
|
68
107
|
|
|
69
108
|
# Fetch fresh config
|
|
70
|
-
config: AgentResponse = self.
|
|
109
|
+
config: AgentResponse = self.agentos_client.get_agent(self.agent_id) # type: ignore
|
|
71
110
|
self._cached_agent_config = (config, current_time)
|
|
72
111
|
return config
|
|
73
112
|
|
|
74
|
-
def refresh_config(self) -> "AgentResponse":
|
|
75
|
-
"""
|
|
113
|
+
async def refresh_config(self) -> Optional["AgentResponse"]:
|
|
114
|
+
"""
|
|
115
|
+
Force refresh the cached agent config.
|
|
116
|
+
Returns None for A2A protocol.
|
|
117
|
+
"""
|
|
118
|
+
import time
|
|
119
|
+
|
|
76
120
|
from agno.os.routers.agents.schema import AgentResponse
|
|
77
121
|
|
|
78
|
-
|
|
122
|
+
if self.a2a_client:
|
|
123
|
+
self._cached_agent_config = None
|
|
124
|
+
return None
|
|
125
|
+
|
|
126
|
+
config: AgentResponse = await self.agentos_client.aget_agent(self.agent_id) # type: ignore
|
|
79
127
|
self._cached_agent_config = (config, time.time())
|
|
80
128
|
return config
|
|
81
129
|
|
|
@@ -91,7 +139,6 @@ class RemoteAgent(BaseRemote):
|
|
|
91
139
|
return self._agent_config.description
|
|
92
140
|
return ""
|
|
93
141
|
|
|
94
|
-
@property
|
|
95
142
|
def role(self) -> Optional[str]:
|
|
96
143
|
if self._agent_config is not None:
|
|
97
144
|
return self._agent_config.role
|
|
@@ -109,23 +156,27 @@ class RemoteAgent(BaseRemote):
|
|
|
109
156
|
|
|
110
157
|
@property
|
|
111
158
|
def db(self) -> Optional[RemoteDb]:
|
|
112
|
-
if
|
|
159
|
+
if (
|
|
160
|
+
self.agentos_client
|
|
161
|
+
and self._config
|
|
162
|
+
and self._agent_config is not None
|
|
163
|
+
and self._agent_config.db_id is not None
|
|
164
|
+
):
|
|
113
165
|
return RemoteDb.from_config(
|
|
114
166
|
db_id=self._agent_config.db_id,
|
|
115
|
-
client=self.
|
|
167
|
+
client=self.agentos_client,
|
|
116
168
|
config=self._config,
|
|
117
169
|
)
|
|
118
170
|
return None
|
|
119
171
|
|
|
120
172
|
@property
|
|
121
173
|
def knowledge(self) -> Optional[RemoteKnowledge]:
|
|
122
|
-
|
|
123
|
-
if self._agent_config is not None and self._agent_config.knowledge is not None:
|
|
174
|
+
if self.agentos_client and self._agent_config is not None and self._agent_config.knowledge is not None:
|
|
124
175
|
return RemoteKnowledge(
|
|
125
|
-
client=self.
|
|
176
|
+
client=self.agentos_client,
|
|
126
177
|
contents_db=RemoteDb(
|
|
127
178
|
id=self._agent_config.knowledge.get("db_id"), # type: ignore
|
|
128
|
-
client=self.
|
|
179
|
+
client=self.agentos_client,
|
|
129
180
|
knowledge_table_name=self._agent_config.knowledge.get("knowledge_table"),
|
|
130
181
|
)
|
|
131
182
|
if self._agent_config.knowledge.get("db_id") is not None
|
|
@@ -139,7 +190,7 @@ class RemoteAgent(BaseRemote):
|
|
|
139
190
|
return None
|
|
140
191
|
|
|
141
192
|
async def aget_tools(self, **kwargs: Any) -> List[Dict]:
|
|
142
|
-
if self._agent_config.tools is not None:
|
|
193
|
+
if self._agent_config is not None and self._agent_config.tools is not None:
|
|
143
194
|
return json.loads(self._agent_config.tools["tools"])
|
|
144
195
|
return []
|
|
145
196
|
|
|
@@ -222,52 +273,156 @@ class RemoteAgent(BaseRemote):
|
|
|
222
273
|
serialized_input = serialize_input(validated_input)
|
|
223
274
|
headers = self._get_auth_headers(auth_token)
|
|
224
275
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
return self.
|
|
228
|
-
agent_id=self.agent_id,
|
|
276
|
+
# A2A protocol path
|
|
277
|
+
if self.a2a_client:
|
|
278
|
+
return self._arun_a2a( # type: ignore[return-value]
|
|
229
279
|
message=serialized_input,
|
|
230
|
-
|
|
280
|
+
stream=stream or False,
|
|
231
281
|
user_id=user_id,
|
|
282
|
+
context_id=session_id, # Map session_id → context_id for A2A
|
|
232
283
|
audio=audio,
|
|
233
284
|
images=images,
|
|
234
285
|
videos=videos,
|
|
235
286
|
files=files,
|
|
236
|
-
session_state=session_state,
|
|
237
|
-
stream_events=stream_events,
|
|
238
|
-
retries=retries,
|
|
239
|
-
knowledge_filters=knowledge_filters,
|
|
240
|
-
add_history_to_context=add_history_to_context,
|
|
241
|
-
add_dependencies_to_context=add_dependencies_to_context,
|
|
242
|
-
add_session_state_to_context=add_session_state_to_context,
|
|
243
|
-
dependencies=dependencies,
|
|
244
|
-
metadata=metadata,
|
|
245
287
|
headers=headers,
|
|
246
|
-
**kwargs,
|
|
247
288
|
)
|
|
289
|
+
|
|
290
|
+
# AgentOS protocol path (default)
|
|
291
|
+
if self.agentos_client:
|
|
292
|
+
if stream:
|
|
293
|
+
# Handle streaming response
|
|
294
|
+
return self.agentos_client.run_agent_stream(
|
|
295
|
+
agent_id=self.agent_id,
|
|
296
|
+
message=serialized_input,
|
|
297
|
+
session_id=session_id,
|
|
298
|
+
user_id=user_id,
|
|
299
|
+
audio=audio,
|
|
300
|
+
images=images,
|
|
301
|
+
videos=videos,
|
|
302
|
+
files=files,
|
|
303
|
+
session_state=session_state,
|
|
304
|
+
stream_events=stream_events,
|
|
305
|
+
retries=retries,
|
|
306
|
+
knowledge_filters=knowledge_filters,
|
|
307
|
+
add_history_to_context=add_history_to_context,
|
|
308
|
+
add_dependencies_to_context=add_dependencies_to_context,
|
|
309
|
+
add_session_state_to_context=add_session_state_to_context,
|
|
310
|
+
dependencies=dependencies,
|
|
311
|
+
metadata=metadata,
|
|
312
|
+
headers=headers,
|
|
313
|
+
**kwargs,
|
|
314
|
+
)
|
|
315
|
+
else:
|
|
316
|
+
return self.agentos_client.run_agent( # type: ignore
|
|
317
|
+
agent_id=self.agent_id,
|
|
318
|
+
message=serialized_input,
|
|
319
|
+
session_id=session_id,
|
|
320
|
+
user_id=user_id,
|
|
321
|
+
audio=audio,
|
|
322
|
+
images=images,
|
|
323
|
+
videos=videos,
|
|
324
|
+
files=files,
|
|
325
|
+
session_state=session_state,
|
|
326
|
+
stream_events=stream_events,
|
|
327
|
+
retries=retries,
|
|
328
|
+
knowledge_filters=knowledge_filters,
|
|
329
|
+
add_history_to_context=add_history_to_context,
|
|
330
|
+
add_dependencies_to_context=add_dependencies_to_context,
|
|
331
|
+
add_session_state_to_context=add_session_state_to_context,
|
|
332
|
+
dependencies=dependencies,
|
|
333
|
+
metadata=metadata,
|
|
334
|
+
headers=headers,
|
|
335
|
+
**kwargs,
|
|
336
|
+
)
|
|
248
337
|
else:
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
338
|
+
raise ValueError("No client available")
|
|
339
|
+
|
|
340
|
+
def _arun_a2a(
|
|
341
|
+
self,
|
|
342
|
+
message: str,
|
|
343
|
+
stream: bool,
|
|
344
|
+
user_id: Optional[str],
|
|
345
|
+
context_id: Optional[str],
|
|
346
|
+
audio: Optional[Sequence[Audio]],
|
|
347
|
+
images: Optional[Sequence[Image]],
|
|
348
|
+
videos: Optional[Sequence[Video]],
|
|
349
|
+
files: Optional[Sequence[File]],
|
|
350
|
+
headers: Optional[Dict[str, str]],
|
|
351
|
+
) -> Union[RunOutput, AsyncIterator[RunOutputEvent]]:
|
|
352
|
+
"""Execute via A2A protocol.
|
|
353
|
+
|
|
354
|
+
Args:
|
|
355
|
+
message: Serialized message string
|
|
356
|
+
stream: Whether to stream the response
|
|
357
|
+
user_id: User identifier
|
|
358
|
+
context_id: Session/context ID (maps to session_id)
|
|
359
|
+
audio: Audio files to include
|
|
360
|
+
images: Images to include
|
|
361
|
+
videos: Videos to include
|
|
362
|
+
files: Files to include
|
|
363
|
+
headers: HTTP headers to include in the request (optional)
|
|
364
|
+
|
|
365
|
+
Returns:
|
|
366
|
+
RunOutput for non-streaming, AsyncIterator[RunOutputEvent] for streaming
|
|
367
|
+
"""
|
|
368
|
+
if not self.a2a_client:
|
|
369
|
+
raise ValueError("A2A client not available")
|
|
370
|
+
from agno.client.a2a.utils import map_stream_events_to_run_events
|
|
371
|
+
|
|
372
|
+
if stream:
|
|
373
|
+
# Return async generator for streaming
|
|
374
|
+
event_stream = self.a2a_client.stream_message(
|
|
375
|
+
message=message,
|
|
376
|
+
context_id=context_id,
|
|
253
377
|
user_id=user_id,
|
|
378
|
+
images=list(images) if images else None,
|
|
379
|
+
audio=list(audio) if audio else None,
|
|
380
|
+
videos=list(videos) if videos else None,
|
|
381
|
+
files=list(files) if files else None,
|
|
382
|
+
headers=headers,
|
|
383
|
+
)
|
|
384
|
+
return map_stream_events_to_run_events(event_stream, agent_id=self.agent_id)
|
|
385
|
+
else:
|
|
386
|
+
# Return coroutine for non-streaming
|
|
387
|
+
return self._arun_a2a_send( # type: ignore[return-value]
|
|
388
|
+
message=message,
|
|
389
|
+
user_id=user_id,
|
|
390
|
+
context_id=context_id,
|
|
254
391
|
audio=audio,
|
|
255
392
|
images=images,
|
|
256
393
|
videos=videos,
|
|
257
394
|
files=files,
|
|
258
|
-
session_state=session_state,
|
|
259
|
-
stream_events=stream_events,
|
|
260
|
-
retries=retries,
|
|
261
|
-
knowledge_filters=knowledge_filters,
|
|
262
|
-
add_history_to_context=add_history_to_context,
|
|
263
|
-
add_dependencies_to_context=add_dependencies_to_context,
|
|
264
|
-
add_session_state_to_context=add_session_state_to_context,
|
|
265
|
-
dependencies=dependencies,
|
|
266
|
-
metadata=metadata,
|
|
267
395
|
headers=headers,
|
|
268
|
-
**kwargs,
|
|
269
396
|
)
|
|
270
397
|
|
|
398
|
+
async def _arun_a2a_send(
|
|
399
|
+
self,
|
|
400
|
+
message: str,
|
|
401
|
+
user_id: Optional[str],
|
|
402
|
+
context_id: Optional[str],
|
|
403
|
+
audio: Optional[Sequence[Audio]],
|
|
404
|
+
images: Optional[Sequence[Image]],
|
|
405
|
+
videos: Optional[Sequence[Video]],
|
|
406
|
+
files: Optional[Sequence[File]],
|
|
407
|
+
headers: Optional[Dict[str, str]],
|
|
408
|
+
) -> RunOutput:
|
|
409
|
+
"""Send a non-streaming A2A message and convert response to RunOutput."""
|
|
410
|
+
if not self.a2a_client:
|
|
411
|
+
raise ValueError("A2A client not available")
|
|
412
|
+
from agno.client.a2a.utils import map_task_result_to_run_output
|
|
413
|
+
|
|
414
|
+
task_result = await self.a2a_client.send_message(
|
|
415
|
+
message=message,
|
|
416
|
+
context_id=context_id,
|
|
417
|
+
user_id=user_id,
|
|
418
|
+
images=list(images) if images else None,
|
|
419
|
+
audio=list(audio) if audio else None,
|
|
420
|
+
videos=list(videos) if videos else None,
|
|
421
|
+
files=list(files) if files else None,
|
|
422
|
+
headers=headers,
|
|
423
|
+
)
|
|
424
|
+
return map_task_result_to_run_output(task_result, agent_id=self.agent_id, user_id=user_id)
|
|
425
|
+
|
|
271
426
|
@overload
|
|
272
427
|
async def acontinue_run(
|
|
273
428
|
self,
|
|
@@ -307,29 +462,33 @@ class RemoteAgent(BaseRemote):
|
|
|
307
462
|
]:
|
|
308
463
|
headers = self._get_auth_headers(auth_token)
|
|
309
464
|
|
|
310
|
-
if
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
465
|
+
if self.agentos_client:
|
|
466
|
+
if stream:
|
|
467
|
+
# Handle streaming response
|
|
468
|
+
return self.agentos_client.continue_agent_run_stream( # type: ignore
|
|
469
|
+
agent_id=self.agent_id,
|
|
470
|
+
run_id=run_id,
|
|
471
|
+
user_id=user_id,
|
|
472
|
+
session_id=session_id,
|
|
473
|
+
tools=updated_tools,
|
|
474
|
+
headers=headers,
|
|
475
|
+
**kwargs,
|
|
476
|
+
)
|
|
477
|
+
else:
|
|
478
|
+
return self.agentos_client.continue_agent_run( # type: ignore
|
|
479
|
+
agent_id=self.agent_id,
|
|
480
|
+
run_id=run_id,
|
|
481
|
+
tools=updated_tools,
|
|
482
|
+
user_id=user_id,
|
|
483
|
+
session_id=session_id,
|
|
484
|
+
headers=headers,
|
|
485
|
+
**kwargs,
|
|
486
|
+
)
|
|
487
|
+
|
|
321
488
|
else:
|
|
322
|
-
|
|
323
|
-
agent_id=self.agent_id,
|
|
324
|
-
run_id=run_id,
|
|
325
|
-
tools=updated_tools,
|
|
326
|
-
user_id=user_id,
|
|
327
|
-
session_id=session_id,
|
|
328
|
-
headers=headers,
|
|
329
|
-
**kwargs,
|
|
330
|
-
)
|
|
489
|
+
raise ValueError("No client available")
|
|
331
490
|
|
|
332
|
-
async def
|
|
491
|
+
async def acancel_run(self, run_id: str, auth_token: Optional[str] = None) -> bool:
|
|
333
492
|
"""Cancel a running agent execution.
|
|
334
493
|
|
|
335
494
|
Args:
|
|
@@ -340,8 +499,10 @@ class RemoteAgent(BaseRemote):
|
|
|
340
499
|
bool: True if the run was successfully cancelled, False otherwise.
|
|
341
500
|
"""
|
|
342
501
|
headers = self._get_auth_headers(auth_token)
|
|
502
|
+
if not self.agentos_client:
|
|
503
|
+
raise ValueError("AgentOS client not available")
|
|
343
504
|
try:
|
|
344
|
-
await self.
|
|
505
|
+
await self.agentos_client.cancel_agent_run(
|
|
345
506
|
agent_id=self.agent_id,
|
|
346
507
|
run_id=run_id,
|
|
347
508
|
headers=headers,
|