agno 2.2.0__py3-none-any.whl → 2.2.2__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 +751 -575
- agno/culture/manager.py +22 -24
- agno/db/async_postgres/__init__.py +1 -1
- agno/db/dynamo/dynamo.py +0 -2
- agno/db/firestore/firestore.py +0 -2
- agno/db/gcs_json/gcs_json_db.py +0 -4
- agno/db/gcs_json/utils.py +0 -24
- agno/db/in_memory/in_memory_db.py +0 -3
- agno/db/json/json_db.py +4 -10
- agno/db/json/utils.py +0 -24
- agno/db/mongo/mongo.py +0 -2
- agno/db/mysql/mysql.py +0 -3
- agno/db/postgres/__init__.py +1 -1
- agno/db/{async_postgres → postgres}/async_postgres.py +19 -22
- agno/db/postgres/postgres.py +7 -10
- agno/db/postgres/utils.py +106 -2
- agno/db/redis/redis.py +0 -2
- agno/db/singlestore/singlestore.py +0 -3
- agno/db/sqlite/__init__.py +2 -1
- agno/db/sqlite/async_sqlite.py +2269 -0
- agno/db/sqlite/sqlite.py +0 -2
- agno/db/sqlite/utils.py +96 -0
- agno/db/surrealdb/surrealdb.py +0 -6
- agno/knowledge/knowledge.py +14 -3
- agno/knowledge/reader/pptx_reader.py +101 -0
- agno/knowledge/reader/reader_factory.py +30 -0
- agno/knowledge/reader/tavily_reader.py +194 -0
- agno/knowledge/types.py +1 -0
- agno/memory/manager.py +28 -25
- agno/models/anthropic/claude.py +63 -6
- agno/models/base.py +255 -36
- agno/models/response.py +69 -0
- agno/os/router.py +7 -5
- agno/os/routers/memory/memory.py +2 -1
- agno/os/routers/memory/schemas.py +5 -2
- agno/os/schema.py +26 -20
- agno/os/utils.py +9 -2
- agno/run/agent.py +28 -30
- agno/run/base.py +17 -1
- agno/run/team.py +28 -29
- agno/run/workflow.py +32 -17
- agno/session/agent.py +3 -0
- agno/session/summary.py +4 -1
- agno/session/team.py +1 -1
- agno/team/team.py +620 -374
- agno/tools/dalle.py +2 -4
- agno/tools/eleven_labs.py +23 -25
- agno/tools/function.py +40 -0
- agno/tools/mcp/__init__.py +10 -0
- agno/tools/mcp/mcp.py +324 -0
- agno/tools/mcp/multi_mcp.py +347 -0
- agno/tools/mcp/params.py +24 -0
- agno/tools/slack.py +18 -3
- agno/tools/tavily.py +146 -0
- agno/utils/agent.py +366 -1
- agno/utils/mcp.py +92 -2
- agno/utils/media.py +166 -1
- agno/utils/message.py +60 -0
- agno/utils/print_response/workflow.py +17 -1
- agno/utils/team.py +89 -1
- agno/workflow/step.py +0 -1
- agno/workflow/types.py +10 -15
- agno/workflow/workflow.py +86 -1
- {agno-2.2.0.dist-info → agno-2.2.2.dist-info}/METADATA +31 -25
- {agno-2.2.0.dist-info → agno-2.2.2.dist-info}/RECORD +68 -64
- agno/db/async_postgres/schemas.py +0 -139
- agno/db/async_postgres/utils.py +0 -347
- agno/tools/mcp.py +0 -679
- {agno-2.2.0.dist-info → agno-2.2.2.dist-info}/WHEEL +0 -0
- {agno-2.2.0.dist-info → agno-2.2.2.dist-info}/licenses/LICENSE +0 -0
- {agno-2.2.0.dist-info → agno-2.2.2.dist-info}/top_level.txt +0 -0
agno/agent/agent.py
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
import
|
|
3
|
+
from asyncio import CancelledError, create_task
|
|
4
4
|
from collections import ChainMap, deque
|
|
5
5
|
from dataclasses import dataclass
|
|
6
|
+
from inspect import iscoroutinefunction
|
|
6
7
|
from os import getenv
|
|
7
8
|
from textwrap import dedent
|
|
8
9
|
from typing import (
|
|
@@ -65,18 +66,35 @@ from agno.run.cancel import (
|
|
|
65
66
|
from agno.run.messages import RunMessages
|
|
66
67
|
from agno.run.team import TeamRunOutputEvent
|
|
67
68
|
from agno.session import AgentSession, SessionSummaryManager, TeamSession, WorkflowSession
|
|
69
|
+
from agno.session.summary import SessionSummary
|
|
68
70
|
from agno.tools import Toolkit
|
|
69
71
|
from agno.tools.function import Function
|
|
70
72
|
from agno.utils.agent import (
|
|
73
|
+
aget_chat_history_util,
|
|
74
|
+
aget_last_run_output_util,
|
|
75
|
+
aget_run_output_util,
|
|
76
|
+
aget_session_metrics_util,
|
|
77
|
+
aget_session_name_util,
|
|
78
|
+
aget_session_state_util,
|
|
79
|
+
aset_session_name_util,
|
|
80
|
+
aupdate_session_state_util,
|
|
71
81
|
await_for_background_tasks,
|
|
72
82
|
await_for_background_tasks_stream,
|
|
73
83
|
collect_joint_audios,
|
|
74
84
|
collect_joint_files,
|
|
75
85
|
collect_joint_images,
|
|
76
86
|
collect_joint_videos,
|
|
87
|
+
get_chat_history_util,
|
|
88
|
+
get_last_run_output_util,
|
|
89
|
+
get_run_output_util,
|
|
90
|
+
get_session_metrics_util,
|
|
91
|
+
get_session_name_util,
|
|
92
|
+
get_session_state_util,
|
|
77
93
|
scrub_history_messages_from_run_output,
|
|
78
94
|
scrub_media_from_run_output,
|
|
79
95
|
scrub_tool_results_from_run_output,
|
|
96
|
+
set_session_name_util,
|
|
97
|
+
update_session_state_util,
|
|
80
98
|
wait_for_background_tasks,
|
|
81
99
|
wait_for_background_tasks_stream,
|
|
82
100
|
)
|
|
@@ -117,7 +135,7 @@ from agno.utils.log import (
|
|
|
117
135
|
set_log_level_to_info,
|
|
118
136
|
)
|
|
119
137
|
from agno.utils.merge_dict import merge_dictionaries
|
|
120
|
-
from agno.utils.message import get_text_from_message
|
|
138
|
+
from agno.utils.message import filter_tool_calls, get_text_from_message
|
|
121
139
|
from agno.utils.print_response.agent import (
|
|
122
140
|
aprint_response,
|
|
123
141
|
aprint_response_stream,
|
|
@@ -203,6 +221,8 @@ class Agent:
|
|
|
203
221
|
add_history_to_context: bool = False
|
|
204
222
|
# Number of historical runs to include in the messages
|
|
205
223
|
num_history_runs: int = 3
|
|
224
|
+
# Maximum number of tool calls to include from history (None = no limit)
|
|
225
|
+
max_tool_calls_from_history: Optional[int] = None
|
|
206
226
|
|
|
207
227
|
# --- Knowledge ---
|
|
208
228
|
knowledge: Optional[Knowledge] = None
|
|
@@ -419,6 +439,7 @@ class Agent:
|
|
|
419
439
|
session_summary_manager: Optional[SessionSummaryManager] = None,
|
|
420
440
|
add_history_to_context: bool = False,
|
|
421
441
|
num_history_runs: int = 3,
|
|
442
|
+
max_tool_calls_from_history: Optional[int] = None,
|
|
422
443
|
store_media: bool = True,
|
|
423
444
|
store_tool_messages: bool = True,
|
|
424
445
|
store_history_messages: bool = True,
|
|
@@ -520,6 +541,7 @@ class Agent:
|
|
|
520
541
|
|
|
521
542
|
self.add_history_to_context = add_history_to_context
|
|
522
543
|
self.num_history_runs = num_history_runs
|
|
544
|
+
self.max_tool_calls_from_history = max_tool_calls_from_history
|
|
523
545
|
|
|
524
546
|
self.store_media = store_media
|
|
525
547
|
self.store_tool_messages = store_tool_messages
|
|
@@ -611,17 +633,16 @@ class Agent:
|
|
|
611
633
|
self.telemetry = telemetry
|
|
612
634
|
|
|
613
635
|
# If we are caching the agent session
|
|
614
|
-
self.
|
|
636
|
+
self._cached_session: Optional[AgentSession] = None
|
|
615
637
|
|
|
616
638
|
self._tool_instructions: Optional[List[str]] = None
|
|
617
|
-
self._tools_for_model: Optional[List[Dict[str, Any]]] = None
|
|
618
|
-
self._functions_for_model: Optional[Dict[str, Function]] = None
|
|
619
|
-
self._rebuild_tools: bool = True
|
|
620
639
|
|
|
621
640
|
self._formatter: Optional[SafeFormatter] = None
|
|
622
641
|
|
|
623
642
|
self._hooks_normalised = False
|
|
624
643
|
|
|
644
|
+
self._mcp_tools_initialized_on_run: List[Any] = []
|
|
645
|
+
|
|
625
646
|
# Lazy-initialized shared thread pool executor for background tasks (memory, cultural knowledge, etc.)
|
|
626
647
|
self._background_executor: Optional[Any] = None
|
|
627
648
|
|
|
@@ -638,6 +659,14 @@ class Agent:
|
|
|
638
659
|
self._background_executor = ThreadPoolExecutor(max_workers=3, thread_name_prefix="agno-bg")
|
|
639
660
|
return self._background_executor
|
|
640
661
|
|
|
662
|
+
@property
|
|
663
|
+
def should_parse_structured_output(self) -> bool:
|
|
664
|
+
return self.output_schema is not None and self.parse_response and self.parser_model is None
|
|
665
|
+
|
|
666
|
+
@property
|
|
667
|
+
def cached_session(self) -> Optional[AgentSession]:
|
|
668
|
+
return self._cached_session
|
|
669
|
+
|
|
641
670
|
def set_id(self) -> None:
|
|
642
671
|
if self.id is None:
|
|
643
672
|
self.id = generate_id_from_name(self.name)
|
|
@@ -794,19 +823,28 @@ class Agent:
|
|
|
794
823
|
if self._formatter is None:
|
|
795
824
|
self._formatter = SafeFormatter()
|
|
796
825
|
|
|
797
|
-
@property
|
|
798
|
-
def should_parse_structured_output(self) -> bool:
|
|
799
|
-
return self.output_schema is not None and self.parse_response and self.parser_model is None
|
|
800
|
-
|
|
801
826
|
def add_tool(self, tool: Union[Toolkit, Callable, Function, Dict]):
|
|
802
827
|
if not self.tools:
|
|
803
828
|
self.tools = []
|
|
804
829
|
self.tools.append(tool)
|
|
805
|
-
self._rebuild_tools = True
|
|
806
830
|
|
|
807
831
|
def set_tools(self, tools: Sequence[Union[Toolkit, Callable, Function, Dict]]):
|
|
808
832
|
self.tools = list(tools) if tools else []
|
|
809
|
-
|
|
833
|
+
|
|
834
|
+
async def _connect_mcp_tools(self) -> None:
|
|
835
|
+
"""Connect the MCP tools to the agent."""
|
|
836
|
+
if self.tools:
|
|
837
|
+
for tool in self.tools:
|
|
838
|
+
if tool.__class__.__name__ in ["MCPTools", "MultiMCPTools"] and not tool.initialized: # type: ignore
|
|
839
|
+
# Connect the MCP server
|
|
840
|
+
await tool.connect() # type: ignore
|
|
841
|
+
self._mcp_tools_initialized_on_run.append(tool)
|
|
842
|
+
|
|
843
|
+
async def _disconnect_mcp_tools(self) -> None:
|
|
844
|
+
"""Disconnect the MCP tools from the agent."""
|
|
845
|
+
for tool in self._mcp_tools_initialized_on_run:
|
|
846
|
+
await tool.close()
|
|
847
|
+
self._mcp_tools_initialized_on_run = []
|
|
810
848
|
|
|
811
849
|
def _initialize_session(
|
|
812
850
|
self,
|
|
@@ -905,15 +943,19 @@ class Agent:
|
|
|
905
943
|
deque(pre_hook_iterator, maxlen=0)
|
|
906
944
|
|
|
907
945
|
# 2. Determine tools for model
|
|
908
|
-
self.
|
|
946
|
+
processed_tools = self.get_tools(
|
|
947
|
+
run_response=run_response,
|
|
948
|
+
session=session,
|
|
949
|
+
user_id=user_id,
|
|
950
|
+
knowledge_filters=knowledge_filters,
|
|
951
|
+
)
|
|
952
|
+
_tools = self._determine_tools_for_model(
|
|
909
953
|
model=self.model,
|
|
954
|
+
processed_tools=processed_tools,
|
|
910
955
|
run_response=run_response,
|
|
911
956
|
session=session,
|
|
912
957
|
session_state=session_state,
|
|
913
958
|
dependencies=dependencies,
|
|
914
|
-
user_id=user_id,
|
|
915
|
-
async_mode=False,
|
|
916
|
-
knowledge_filters=knowledge_filters,
|
|
917
959
|
)
|
|
918
960
|
|
|
919
961
|
# 3. Prepare run messages
|
|
@@ -933,6 +975,7 @@ class Agent:
|
|
|
933
975
|
add_dependencies_to_context=add_dependencies_to_context,
|
|
934
976
|
add_session_state_to_context=add_session_state_to_context,
|
|
935
977
|
metadata=metadata,
|
|
978
|
+
tools=_tools,
|
|
936
979
|
**kwargs,
|
|
937
980
|
)
|
|
938
981
|
if len(run_messages.messages) == 0:
|
|
@@ -974,8 +1017,7 @@ class Agent:
|
|
|
974
1017
|
self.model = cast(Model, self.model)
|
|
975
1018
|
model_response: ModelResponse = self.model.response(
|
|
976
1019
|
messages=run_messages.messages,
|
|
977
|
-
tools=
|
|
978
|
-
functions=self._functions_for_model,
|
|
1020
|
+
tools=_tools,
|
|
979
1021
|
tool_choice=self.tool_choice,
|
|
980
1022
|
tool_call_limit=self.tool_call_limit,
|
|
981
1023
|
response_format=response_format,
|
|
@@ -1124,15 +1166,19 @@ class Agent:
|
|
|
1124
1166
|
yield event
|
|
1125
1167
|
|
|
1126
1168
|
# 2. Determine tools for model
|
|
1127
|
-
self.
|
|
1169
|
+
processed_tools = self.get_tools(
|
|
1170
|
+
run_response=run_response,
|
|
1171
|
+
session=session,
|
|
1172
|
+
user_id=user_id,
|
|
1173
|
+
knowledge_filters=knowledge_filters,
|
|
1174
|
+
)
|
|
1175
|
+
_tools = self._determine_tools_for_model(
|
|
1128
1176
|
model=self.model,
|
|
1177
|
+
processed_tools=processed_tools,
|
|
1129
1178
|
run_response=run_response,
|
|
1130
1179
|
session=session,
|
|
1131
1180
|
session_state=session_state,
|
|
1132
1181
|
dependencies=dependencies,
|
|
1133
|
-
user_id=user_id,
|
|
1134
|
-
async_mode=False,
|
|
1135
|
-
knowledge_filters=knowledge_filters,
|
|
1136
1182
|
)
|
|
1137
1183
|
|
|
1138
1184
|
# 3. Prepare run messages
|
|
@@ -1152,6 +1198,7 @@ class Agent:
|
|
|
1152
1198
|
add_dependencies_to_context=add_dependencies_to_context,
|
|
1153
1199
|
add_session_state_to_context=add_session_state_to_context,
|
|
1154
1200
|
metadata=metadata,
|
|
1201
|
+
tools=_tools,
|
|
1155
1202
|
**kwargs,
|
|
1156
1203
|
)
|
|
1157
1204
|
if len(run_messages.messages) == 0:
|
|
@@ -1206,6 +1253,7 @@ class Agent:
|
|
|
1206
1253
|
session=session,
|
|
1207
1254
|
run_response=run_response,
|
|
1208
1255
|
run_messages=run_messages,
|
|
1256
|
+
tools=_tools,
|
|
1209
1257
|
response_format=response_format,
|
|
1210
1258
|
stream_events=stream_events,
|
|
1211
1259
|
):
|
|
@@ -1221,6 +1269,7 @@ class Agent:
|
|
|
1221
1269
|
session=session,
|
|
1222
1270
|
run_response=run_response,
|
|
1223
1271
|
run_messages=run_messages,
|
|
1272
|
+
tools=_tools,
|
|
1224
1273
|
response_format=response_format,
|
|
1225
1274
|
stream_events=stream_events,
|
|
1226
1275
|
):
|
|
@@ -1355,7 +1404,10 @@ class Agent:
|
|
|
1355
1404
|
# Handle run cancellation during streaming
|
|
1356
1405
|
log_info(f"Run {run_response.run_id} was cancelled during streaming")
|
|
1357
1406
|
run_response.status = RunStatus.cancelled
|
|
1358
|
-
|
|
1407
|
+
# Don't overwrite content - preserve any partial content that was streamed
|
|
1408
|
+
# Only set content if it's empty
|
|
1409
|
+
if not run_response.content:
|
|
1410
|
+
run_response.content = str(e)
|
|
1359
1411
|
|
|
1360
1412
|
# Yield the cancellation event
|
|
1361
1413
|
yield handle_event( # type: ignore
|
|
@@ -1741,15 +1793,19 @@ class Agent:
|
|
|
1741
1793
|
|
|
1742
1794
|
# 5. Determine tools for model
|
|
1743
1795
|
self.model = cast(Model, self.model)
|
|
1744
|
-
await self.
|
|
1796
|
+
processed_tools = await self.aget_tools(
|
|
1797
|
+
run_response=run_response,
|
|
1798
|
+
session=agent_session,
|
|
1799
|
+
user_id=user_id,
|
|
1800
|
+
knowledge_filters=knowledge_filters,
|
|
1801
|
+
)
|
|
1802
|
+
_tools = self._determine_tools_for_model(
|
|
1745
1803
|
model=self.model,
|
|
1804
|
+
processed_tools=processed_tools,
|
|
1746
1805
|
run_response=run_response,
|
|
1747
1806
|
session=agent_session,
|
|
1748
1807
|
session_state=session_state,
|
|
1749
1808
|
dependencies=dependencies,
|
|
1750
|
-
user_id=user_id,
|
|
1751
|
-
async_mode=True,
|
|
1752
|
-
knowledge_filters=knowledge_filters,
|
|
1753
1809
|
)
|
|
1754
1810
|
|
|
1755
1811
|
# 6. Prepare run messages
|
|
@@ -1769,6 +1825,7 @@ class Agent:
|
|
|
1769
1825
|
add_dependencies_to_context=add_dependencies_to_context,
|
|
1770
1826
|
add_session_state_to_context=add_session_state_to_context,
|
|
1771
1827
|
metadata=metadata,
|
|
1828
|
+
tools=_tools,
|
|
1772
1829
|
**kwargs,
|
|
1773
1830
|
)
|
|
1774
1831
|
if len(run_messages.messages) == 0:
|
|
@@ -1777,10 +1834,8 @@ class Agent:
|
|
|
1777
1834
|
# 7. Start memory creation as a background task (runs concurrently with the main execution)
|
|
1778
1835
|
memory_task = None
|
|
1779
1836
|
if run_messages.user_message is not None and self.memory_manager is not None and not self.enable_agentic_memory:
|
|
1780
|
-
import asyncio
|
|
1781
|
-
|
|
1782
1837
|
log_debug("Starting memory creation in background task.")
|
|
1783
|
-
memory_task =
|
|
1838
|
+
memory_task = create_task(self._amake_memories(run_messages=run_messages, user_id=user_id))
|
|
1784
1839
|
|
|
1785
1840
|
# Start cultural knowledge creation on a separate thread (runs concurrently with the main execution loop)
|
|
1786
1841
|
cultural_knowledge_task = None
|
|
@@ -1789,10 +1844,8 @@ class Agent:
|
|
|
1789
1844
|
and self.culture_manager is not None
|
|
1790
1845
|
and self.update_cultural_knowledge
|
|
1791
1846
|
):
|
|
1792
|
-
import asyncio
|
|
1793
|
-
|
|
1794
1847
|
log_debug("Starting cultural knowledge creation in background thread.")
|
|
1795
|
-
cultural_knowledge_task =
|
|
1848
|
+
cultural_knowledge_task = create_task(self._acreate_cultural_knowledge(run_messages=run_messages))
|
|
1796
1849
|
|
|
1797
1850
|
try:
|
|
1798
1851
|
# Check for cancellation before model call
|
|
@@ -1807,8 +1860,7 @@ class Agent:
|
|
|
1807
1860
|
# 9. Generate a response from the Model (includes running function calls)
|
|
1808
1861
|
model_response: ModelResponse = await self.model.aresponse(
|
|
1809
1862
|
messages=run_messages.messages,
|
|
1810
|
-
tools=
|
|
1811
|
-
functions=self._functions_for_model,
|
|
1863
|
+
tools=_tools,
|
|
1812
1864
|
tool_choice=self.tool_choice,
|
|
1813
1865
|
tool_call_limit=self.tool_call_limit,
|
|
1814
1866
|
response_format=response_format,
|
|
@@ -1901,23 +1953,22 @@ class Agent:
|
|
|
1901
1953
|
return run_response
|
|
1902
1954
|
|
|
1903
1955
|
finally:
|
|
1956
|
+
# Always disconnect MCP tools
|
|
1957
|
+
await self._disconnect_mcp_tools()
|
|
1958
|
+
|
|
1904
1959
|
# Cancel the memory task if it's still running
|
|
1905
1960
|
if memory_task is not None and not memory_task.done():
|
|
1906
|
-
import asyncio
|
|
1907
|
-
|
|
1908
1961
|
memory_task.cancel()
|
|
1909
1962
|
try:
|
|
1910
1963
|
await memory_task
|
|
1911
|
-
except
|
|
1964
|
+
except CancelledError:
|
|
1912
1965
|
pass
|
|
1913
1966
|
# Cancel the cultural knowledge task if it's still running
|
|
1914
1967
|
if cultural_knowledge_task is not None and not cultural_knowledge_task.done():
|
|
1915
|
-
import asyncio
|
|
1916
|
-
|
|
1917
1968
|
cultural_knowledge_task.cancel()
|
|
1918
1969
|
try:
|
|
1919
1970
|
await cultural_knowledge_task
|
|
1920
|
-
except
|
|
1971
|
+
except CancelledError:
|
|
1921
1972
|
pass
|
|
1922
1973
|
# Always clean up the run tracking
|
|
1923
1974
|
cleanup_run(run_response.run_id) # type: ignore
|
|
@@ -1995,6 +2046,9 @@ class Agent:
|
|
|
1995
2046
|
run_response=run_response,
|
|
1996
2047
|
run_input=run_input,
|
|
1997
2048
|
session=agent_session,
|
|
2049
|
+
session_state=session_state,
|
|
2050
|
+
dependencies=dependencies,
|
|
2051
|
+
metadata=metadata,
|
|
1998
2052
|
user_id=user_id,
|
|
1999
2053
|
debug_mode=debug_mode,
|
|
2000
2054
|
**kwargs,
|
|
@@ -2004,14 +2058,18 @@ class Agent:
|
|
|
2004
2058
|
|
|
2005
2059
|
# 5. Determine tools for model
|
|
2006
2060
|
self.model = cast(Model, self.model)
|
|
2007
|
-
self.
|
|
2008
|
-
model=self.model,
|
|
2061
|
+
processed_tools = await self.aget_tools(
|
|
2009
2062
|
run_response=run_response,
|
|
2010
2063
|
session=agent_session,
|
|
2011
|
-
session_state=session_state,
|
|
2012
2064
|
user_id=user_id,
|
|
2013
|
-
async_mode=True,
|
|
2014
2065
|
knowledge_filters=knowledge_filters,
|
|
2066
|
+
)
|
|
2067
|
+
_tools = self._determine_tools_for_model(
|
|
2068
|
+
model=self.model,
|
|
2069
|
+
processed_tools=processed_tools,
|
|
2070
|
+
run_response=run_response,
|
|
2071
|
+
session=agent_session,
|
|
2072
|
+
session_state=session_state,
|
|
2015
2073
|
dependencies=dependencies,
|
|
2016
2074
|
)
|
|
2017
2075
|
|
|
@@ -2032,6 +2090,7 @@ class Agent:
|
|
|
2032
2090
|
add_dependencies_to_context=add_dependencies_to_context,
|
|
2033
2091
|
add_session_state_to_context=add_session_state_to_context,
|
|
2034
2092
|
metadata=metadata,
|
|
2093
|
+
tools=_tools,
|
|
2035
2094
|
**kwargs,
|
|
2036
2095
|
)
|
|
2037
2096
|
if len(run_messages.messages) == 0:
|
|
@@ -2040,10 +2099,8 @@ class Agent:
|
|
|
2040
2099
|
# 7. Start memory creation as a background task (runs concurrently with the main execution)
|
|
2041
2100
|
memory_task = None
|
|
2042
2101
|
if run_messages.user_message is not None and self.memory_manager is not None and not self.enable_agentic_memory:
|
|
2043
|
-
import asyncio
|
|
2044
|
-
|
|
2045
2102
|
log_debug("Starting memory creation in background task.")
|
|
2046
|
-
memory_task =
|
|
2103
|
+
memory_task = create_task(self._amake_memories(run_messages=run_messages, user_id=user_id))
|
|
2047
2104
|
|
|
2048
2105
|
# Start cultural knowledge creation on a separate thread (runs concurrently with the main execution loop)
|
|
2049
2106
|
cultural_knowledge_task = None
|
|
@@ -2052,10 +2109,8 @@ class Agent:
|
|
|
2052
2109
|
and self.culture_manager is not None
|
|
2053
2110
|
and self.update_cultural_knowledge
|
|
2054
2111
|
):
|
|
2055
|
-
import asyncio
|
|
2056
|
-
|
|
2057
2112
|
log_debug("Starting cultural knowledge creation in background task.")
|
|
2058
|
-
cultural_knowledge_task =
|
|
2113
|
+
cultural_knowledge_task = create_task(self._acreate_cultural_knowledge(run_messages=run_messages))
|
|
2059
2114
|
|
|
2060
2115
|
# Register run for cancellation tracking
|
|
2061
2116
|
register_run(run_response.run_id) # type: ignore
|
|
@@ -2078,6 +2133,7 @@ class Agent:
|
|
|
2078
2133
|
session=agent_session,
|
|
2079
2134
|
run_response=run_response,
|
|
2080
2135
|
run_messages=run_messages,
|
|
2136
|
+
tools=_tools,
|
|
2081
2137
|
response_format=response_format,
|
|
2082
2138
|
stream_events=stream_events,
|
|
2083
2139
|
):
|
|
@@ -2093,6 +2149,7 @@ class Agent:
|
|
|
2093
2149
|
session=agent_session,
|
|
2094
2150
|
run_response=run_response,
|
|
2095
2151
|
run_messages=run_messages,
|
|
2152
|
+
tools=_tools,
|
|
2096
2153
|
response_format=response_format,
|
|
2097
2154
|
stream_events=stream_events,
|
|
2098
2155
|
):
|
|
@@ -2230,7 +2287,10 @@ class Agent:
|
|
|
2230
2287
|
# Handle run cancellation during async streaming
|
|
2231
2288
|
log_info(f"Run {run_response.run_id} was cancelled during async streaming")
|
|
2232
2289
|
run_response.status = RunStatus.cancelled
|
|
2233
|
-
|
|
2290
|
+
# Don't overwrite content - preserve any partial content that was streamed
|
|
2291
|
+
# Only set content if it's empty
|
|
2292
|
+
if not run_response.content:
|
|
2293
|
+
run_response.content = str(e)
|
|
2234
2294
|
|
|
2235
2295
|
# Yield the cancellation event
|
|
2236
2296
|
yield handle_event( # type: ignore
|
|
@@ -2243,19 +2303,22 @@ class Agent:
|
|
|
2243
2303
|
# Cleanup and store the run response and session
|
|
2244
2304
|
await self._acleanup_and_store(run_response=run_response, session=agent_session, user_id=user_id)
|
|
2245
2305
|
finally:
|
|
2306
|
+
# Always disconnect MCP tools
|
|
2307
|
+
await self._disconnect_mcp_tools()
|
|
2308
|
+
|
|
2246
2309
|
# Cancel the memory task if it's still running
|
|
2247
2310
|
if memory_task is not None and not memory_task.done():
|
|
2248
2311
|
memory_task.cancel()
|
|
2249
2312
|
try:
|
|
2250
2313
|
await memory_task
|
|
2251
|
-
except
|
|
2314
|
+
except CancelledError:
|
|
2252
2315
|
pass
|
|
2253
2316
|
|
|
2254
2317
|
if cultural_knowledge_task is not None and not cultural_knowledge_task.done():
|
|
2255
2318
|
cultural_knowledge_task.cancel()
|
|
2256
2319
|
try:
|
|
2257
2320
|
await cultural_knowledge_task
|
|
2258
|
-
except
|
|
2321
|
+
except CancelledError:
|
|
2259
2322
|
pass
|
|
2260
2323
|
|
|
2261
2324
|
# Always clean up the run tracking
|
|
@@ -2697,15 +2760,19 @@ class Agent:
|
|
|
2697
2760
|
response_format = self._get_response_format()
|
|
2698
2761
|
self.model = cast(Model, self.model)
|
|
2699
2762
|
|
|
2700
|
-
self.
|
|
2763
|
+
processed_tools = self.get_tools(
|
|
2764
|
+
run_response=run_response,
|
|
2765
|
+
session=agent_session,
|
|
2766
|
+
user_id=user_id,
|
|
2767
|
+
knowledge_filters=effective_filters,
|
|
2768
|
+
)
|
|
2769
|
+
_tools = self._determine_tools_for_model(
|
|
2701
2770
|
model=self.model,
|
|
2771
|
+
processed_tools=processed_tools,
|
|
2702
2772
|
run_response=run_response,
|
|
2703
2773
|
session=agent_session,
|
|
2704
2774
|
session_state=session_state,
|
|
2705
2775
|
dependencies=run_dependencies,
|
|
2706
|
-
user_id=user_id,
|
|
2707
|
-
async_mode=False,
|
|
2708
|
-
knowledge_filters=effective_filters,
|
|
2709
2776
|
)
|
|
2710
2777
|
|
|
2711
2778
|
last_exception = None
|
|
@@ -2728,6 +2795,7 @@ class Agent:
|
|
|
2728
2795
|
response_iterator = self._continue_run_stream(
|
|
2729
2796
|
run_response=run_response,
|
|
2730
2797
|
run_messages=run_messages,
|
|
2798
|
+
tools=_tools,
|
|
2731
2799
|
user_id=user_id,
|
|
2732
2800
|
session=agent_session,
|
|
2733
2801
|
session_state=session_state,
|
|
@@ -2743,6 +2811,7 @@ class Agent:
|
|
|
2743
2811
|
response = self._continue_run(
|
|
2744
2812
|
run_response=run_response,
|
|
2745
2813
|
run_messages=run_messages,
|
|
2814
|
+
tools=_tools,
|
|
2746
2815
|
user_id=user_id,
|
|
2747
2816
|
session=agent_session,
|
|
2748
2817
|
session_state=session_state,
|
|
@@ -2795,6 +2864,7 @@ class Agent:
|
|
|
2795
2864
|
run_response: RunOutput,
|
|
2796
2865
|
run_messages: RunMessages,
|
|
2797
2866
|
session: AgentSession,
|
|
2867
|
+
tools: List[Union[Function, dict]],
|
|
2798
2868
|
session_state: Optional[Dict[str, Any]] = None,
|
|
2799
2869
|
dependencies: Optional[Dict[str, Any]] = None,
|
|
2800
2870
|
metadata: Optional[Dict[str, Any]] = None,
|
|
@@ -2821,7 +2891,7 @@ class Agent:
|
|
|
2821
2891
|
self.model = cast(Model, self.model)
|
|
2822
2892
|
|
|
2823
2893
|
# 1. Handle the updated tools
|
|
2824
|
-
self._handle_tool_call_updates(run_response=run_response, run_messages=run_messages)
|
|
2894
|
+
self._handle_tool_call_updates(run_response=run_response, run_messages=run_messages, tools=tools)
|
|
2825
2895
|
|
|
2826
2896
|
try:
|
|
2827
2897
|
# Check for cancellation before model call
|
|
@@ -2832,8 +2902,7 @@ class Agent:
|
|
|
2832
2902
|
model_response: ModelResponse = self.model.response(
|
|
2833
2903
|
messages=run_messages.messages,
|
|
2834
2904
|
response_format=response_format,
|
|
2835
|
-
tools=
|
|
2836
|
-
functions=self._functions_for_model,
|
|
2905
|
+
tools=tools,
|
|
2837
2906
|
tool_choice=self.tool_choice,
|
|
2838
2907
|
tool_call_limit=self.tool_call_limit,
|
|
2839
2908
|
)
|
|
@@ -2913,6 +2982,7 @@ class Agent:
|
|
|
2913
2982
|
run_response: RunOutput,
|
|
2914
2983
|
run_messages: RunMessages,
|
|
2915
2984
|
session: AgentSession,
|
|
2985
|
+
tools: List[Union[Function, dict]],
|
|
2916
2986
|
session_state: Optional[Dict[str, Any]] = None,
|
|
2917
2987
|
metadata: Optional[Dict[str, Any]] = None,
|
|
2918
2988
|
user_id: Optional[str] = None,
|
|
@@ -2948,7 +3018,7 @@ class Agent:
|
|
|
2948
3018
|
|
|
2949
3019
|
# 2. Handle the updated tools
|
|
2950
3020
|
yield from self._handle_tool_call_updates_stream(
|
|
2951
|
-
run_response=run_response, run_messages=run_messages, stream_events=stream_events
|
|
3021
|
+
run_response=run_response, run_messages=run_messages, tools=tools, stream_events=stream_events
|
|
2952
3022
|
)
|
|
2953
3023
|
|
|
2954
3024
|
try:
|
|
@@ -2957,6 +3027,7 @@ class Agent:
|
|
|
2957
3027
|
session=session,
|
|
2958
3028
|
run_response=run_response,
|
|
2959
3029
|
run_messages=run_messages,
|
|
3030
|
+
tools=tools,
|
|
2960
3031
|
response_format=response_format,
|
|
2961
3032
|
stream_events=stream_events,
|
|
2962
3033
|
):
|
|
@@ -3345,15 +3416,19 @@ class Agent:
|
|
|
3345
3416
|
|
|
3346
3417
|
# 5. Determine tools for model
|
|
3347
3418
|
self.model = cast(Model, self.model)
|
|
3348
|
-
await self.
|
|
3419
|
+
processed_tools = await self.aget_tools(
|
|
3420
|
+
run_response=run_response,
|
|
3421
|
+
session=agent_session,
|
|
3422
|
+
user_id=user_id,
|
|
3423
|
+
knowledge_filters=knowledge_filters,
|
|
3424
|
+
)
|
|
3425
|
+
_tools = self._determine_tools_for_model(
|
|
3349
3426
|
model=self.model,
|
|
3427
|
+
processed_tools=processed_tools,
|
|
3350
3428
|
run_response=run_response,
|
|
3351
3429
|
session=agent_session,
|
|
3352
3430
|
session_state=session_state,
|
|
3353
3431
|
dependencies=dependencies,
|
|
3354
|
-
user_id=user_id,
|
|
3355
|
-
async_mode=True,
|
|
3356
|
-
knowledge_filters=knowledge_filters,
|
|
3357
3432
|
)
|
|
3358
3433
|
|
|
3359
3434
|
# 6. Prepare run messages
|
|
@@ -3366,14 +3441,13 @@ class Agent:
|
|
|
3366
3441
|
|
|
3367
3442
|
try:
|
|
3368
3443
|
# 7. Handle the updated tools
|
|
3369
|
-
await self._ahandle_tool_call_updates(run_response=run_response, run_messages=run_messages)
|
|
3444
|
+
await self._ahandle_tool_call_updates(run_response=run_response, run_messages=run_messages, tools=_tools)
|
|
3370
3445
|
|
|
3371
3446
|
# 8. Get model response
|
|
3372
3447
|
model_response: ModelResponse = await self.model.aresponse(
|
|
3373
3448
|
messages=run_messages.messages,
|
|
3374
3449
|
response_format=response_format,
|
|
3375
|
-
tools=
|
|
3376
|
-
functions=self._functions_for_model,
|
|
3450
|
+
tools=_tools,
|
|
3377
3451
|
tool_choice=self.tool_choice,
|
|
3378
3452
|
tool_call_limit=self.tool_call_limit,
|
|
3379
3453
|
)
|
|
@@ -3460,6 +3534,9 @@ class Agent:
|
|
|
3460
3534
|
|
|
3461
3535
|
return run_response
|
|
3462
3536
|
finally:
|
|
3537
|
+
# Always disconnect MCP tools
|
|
3538
|
+
await self._disconnect_mcp_tools()
|
|
3539
|
+
|
|
3463
3540
|
# Always clean up the run tracking
|
|
3464
3541
|
cleanup_run(run_response.run_id) # type: ignore
|
|
3465
3542
|
|
|
@@ -3501,7 +3578,7 @@ class Agent:
|
|
|
3501
3578
|
await self._aresolve_run_dependencies(dependencies=dependencies)
|
|
3502
3579
|
|
|
3503
3580
|
# 2. Read existing session from db
|
|
3504
|
-
agent_session = self.
|
|
3581
|
+
agent_session = await self._aread_or_create_session(session_id=session_id, user_id=user_id)
|
|
3505
3582
|
|
|
3506
3583
|
# 3. Update session state and metadata
|
|
3507
3584
|
self._update_metadata(session=agent_session)
|
|
@@ -3536,15 +3613,19 @@ class Agent:
|
|
|
3536
3613
|
|
|
3537
3614
|
# 5. Determine tools for model
|
|
3538
3615
|
self.model = cast(Model, self.model)
|
|
3539
|
-
await self.
|
|
3616
|
+
processed_tools = await self.aget_tools(
|
|
3617
|
+
run_response=run_response,
|
|
3618
|
+
session=agent_session,
|
|
3619
|
+
user_id=user_id,
|
|
3620
|
+
knowledge_filters=knowledge_filters,
|
|
3621
|
+
)
|
|
3622
|
+
_tools = self._determine_tools_for_model(
|
|
3540
3623
|
model=self.model,
|
|
3624
|
+
processed_tools=processed_tools,
|
|
3541
3625
|
run_response=run_response,
|
|
3542
3626
|
session=agent_session,
|
|
3543
3627
|
session_state=session_state,
|
|
3544
3628
|
dependencies=dependencies,
|
|
3545
|
-
user_id=user_id,
|
|
3546
|
-
async_mode=True,
|
|
3547
|
-
knowledge_filters=knowledge_filters,
|
|
3548
3629
|
)
|
|
3549
3630
|
|
|
3550
3631
|
# 6. Prepare run messages
|
|
@@ -3567,7 +3648,7 @@ class Agent:
|
|
|
3567
3648
|
|
|
3568
3649
|
# 7. Handle the updated tools
|
|
3569
3650
|
async for event in self._ahandle_tool_call_updates_stream(
|
|
3570
|
-
run_response=run_response, run_messages=run_messages
|
|
3651
|
+
run_response=run_response, run_messages=run_messages, tools=_tools, stream_events=stream_events
|
|
3571
3652
|
):
|
|
3572
3653
|
raise_if_cancelled(run_response.run_id) # type: ignore
|
|
3573
3654
|
yield event
|
|
@@ -3578,6 +3659,7 @@ class Agent:
|
|
|
3578
3659
|
session=agent_session,
|
|
3579
3660
|
run_response=run_response,
|
|
3580
3661
|
run_messages=run_messages,
|
|
3662
|
+
tools=_tools,
|
|
3581
3663
|
response_format=response_format,
|
|
3582
3664
|
stream_events=stream_events,
|
|
3583
3665
|
):
|
|
@@ -3593,6 +3675,7 @@ class Agent:
|
|
|
3593
3675
|
session=agent_session,
|
|
3594
3676
|
run_response=run_response,
|
|
3595
3677
|
run_messages=run_messages,
|
|
3678
|
+
tools=_tools,
|
|
3596
3679
|
response_format=response_format,
|
|
3597
3680
|
stream_events=stream_events,
|
|
3598
3681
|
):
|
|
@@ -3726,6 +3809,9 @@ class Agent:
|
|
|
3726
3809
|
# Cleanup and store the run response and session
|
|
3727
3810
|
await self._acleanup_and_store(run_response=run_response, session=agent_session, user_id=user_id)
|
|
3728
3811
|
finally:
|
|
3812
|
+
# Always disconnect MCP tools
|
|
3813
|
+
await self._disconnect_mcp_tools()
|
|
3814
|
+
|
|
3729
3815
|
# Always clean up the run tracking
|
|
3730
3816
|
cleanup_run(run_response.run_id) # type: ignore
|
|
3731
3817
|
|
|
@@ -3844,7 +3930,7 @@ class Agent:
|
|
|
3844
3930
|
# Filter arguments to only include those that the hook accepts
|
|
3845
3931
|
filtered_args = filter_hook_args(hook, all_args)
|
|
3846
3932
|
|
|
3847
|
-
if
|
|
3933
|
+
if iscoroutinefunction(hook):
|
|
3848
3934
|
await hook(**filtered_args)
|
|
3849
3935
|
else:
|
|
3850
3936
|
# Synchronous function
|
|
@@ -3978,8 +4064,9 @@ class Agent:
|
|
|
3978
4064
|
try:
|
|
3979
4065
|
# Filter arguments to only include those that the hook accepts
|
|
3980
4066
|
filtered_args = filter_hook_args(hook, all_args)
|
|
4067
|
+
from inspect import iscoroutinefunction
|
|
3981
4068
|
|
|
3982
|
-
if
|
|
4069
|
+
if iscoroutinefunction(hook):
|
|
3983
4070
|
await hook(**filtered_args)
|
|
3984
4071
|
else:
|
|
3985
4072
|
hook(**filtered_args)
|
|
@@ -4176,11 +4263,12 @@ class Agent:
|
|
|
4176
4263
|
run_response: RunOutput,
|
|
4177
4264
|
run_messages: RunMessages,
|
|
4178
4265
|
tool: ToolExecution,
|
|
4266
|
+
functions: Optional[Dict[str, Function]] = None,
|
|
4179
4267
|
stream_events: bool = False,
|
|
4180
4268
|
) -> Iterator[RunOutputEvent]:
|
|
4181
4269
|
self.model = cast(Model, self.model)
|
|
4182
4270
|
# Execute the tool
|
|
4183
|
-
function_call = self.model.get_function_call_to_run_from_tool_execution(tool,
|
|
4271
|
+
function_call = self.model.get_function_call_to_run_from_tool_execution(tool, functions)
|
|
4184
4272
|
function_call_results: List[Message] = []
|
|
4185
4273
|
|
|
4186
4274
|
for call_result in self.model.run_function_call(
|
|
@@ -4214,9 +4302,11 @@ class Agent:
|
|
|
4214
4302
|
if len(function_call_results) > 0:
|
|
4215
4303
|
run_messages.messages.extend(function_call_results)
|
|
4216
4304
|
|
|
4217
|
-
def _reject_tool_call(
|
|
4305
|
+
def _reject_tool_call(
|
|
4306
|
+
self, run_messages: RunMessages, tool: ToolExecution, functions: Optional[Dict[str, Function]] = None
|
|
4307
|
+
):
|
|
4218
4308
|
self.model = cast(Model, self.model)
|
|
4219
|
-
function_call = self.model.get_function_call_to_run_from_tool_execution(tool,
|
|
4309
|
+
function_call = self.model.get_function_call_to_run_from_tool_execution(tool, functions)
|
|
4220
4310
|
function_call.error = tool.confirmation_note or "Function call was rejected by the user"
|
|
4221
4311
|
function_call_result = self.model.create_function_call_result(
|
|
4222
4312
|
function_call=function_call,
|
|
@@ -4229,12 +4319,13 @@ class Agent:
|
|
|
4229
4319
|
run_response: RunOutput,
|
|
4230
4320
|
run_messages: RunMessages,
|
|
4231
4321
|
tool: ToolExecution,
|
|
4322
|
+
functions: Optional[Dict[str, Function]] = None,
|
|
4232
4323
|
stream_events: bool = False,
|
|
4233
4324
|
) -> AsyncIterator[RunOutputEvent]:
|
|
4234
4325
|
self.model = cast(Model, self.model)
|
|
4235
4326
|
|
|
4236
4327
|
# Execute the tool
|
|
4237
|
-
function_call = self.model.get_function_call_to_run_from_tool_execution(tool,
|
|
4328
|
+
function_call = self.model.get_function_call_to_run_from_tool_execution(tool, functions)
|
|
4238
4329
|
function_call_results: List[Message] = []
|
|
4239
4330
|
|
|
4240
4331
|
async for call_result in self.model.arun_function_calls(
|
|
@@ -4267,17 +4358,21 @@ class Agent:
|
|
|
4267
4358
|
if len(function_call_results) > 0:
|
|
4268
4359
|
run_messages.messages.extend(function_call_results)
|
|
4269
4360
|
|
|
4270
|
-
def _handle_tool_call_updates(
|
|
4361
|
+
def _handle_tool_call_updates(
|
|
4362
|
+
self, run_response: RunOutput, run_messages: RunMessages, tools: List[Union[Function, dict]]
|
|
4363
|
+
):
|
|
4271
4364
|
self.model = cast(Model, self.model)
|
|
4365
|
+
_functions = {tool.name: tool for tool in tools if isinstance(tool, Function)}
|
|
4366
|
+
|
|
4272
4367
|
for _t in run_response.tools or []:
|
|
4273
4368
|
# Case 1: Handle confirmed tools and execute them
|
|
4274
|
-
if _t.requires_confirmation is not None and _t.requires_confirmation is True and
|
|
4369
|
+
if _t.requires_confirmation is not None and _t.requires_confirmation is True and _functions:
|
|
4275
4370
|
# Tool is confirmed and hasn't been run before
|
|
4276
4371
|
if _t.confirmed is not None and _t.confirmed is True and _t.result is None:
|
|
4277
4372
|
# Consume the generator without yielding
|
|
4278
|
-
deque(self._run_tool(run_response, run_messages, _t), maxlen=0)
|
|
4373
|
+
deque(self._run_tool(run_response, run_messages, _t, functions=_functions), maxlen=0)
|
|
4279
4374
|
else:
|
|
4280
|
-
self._reject_tool_call(run_messages, _t)
|
|
4375
|
+
self._reject_tool_call(run_messages, _t, functions=_functions)
|
|
4281
4376
|
_t.confirmed = False
|
|
4282
4377
|
_t.confirmation_note = _t.confirmation_note or "Tool call was rejected"
|
|
4283
4378
|
_t.tool_call_error = True
|
|
@@ -4302,20 +4397,28 @@ class Agent:
|
|
|
4302
4397
|
_t.requires_user_input = False
|
|
4303
4398
|
_t.answered = True
|
|
4304
4399
|
# Consume the generator without yielding
|
|
4305
|
-
deque(self._run_tool(run_response, run_messages, _t), maxlen=0)
|
|
4400
|
+
deque(self._run_tool(run_response, run_messages, _t, functions=_functions), maxlen=0)
|
|
4306
4401
|
|
|
4307
4402
|
def _handle_tool_call_updates_stream(
|
|
4308
|
-
self,
|
|
4403
|
+
self,
|
|
4404
|
+
run_response: RunOutput,
|
|
4405
|
+
run_messages: RunMessages,
|
|
4406
|
+
tools: List[Union[Function, dict]],
|
|
4407
|
+
stream_events: bool = False,
|
|
4309
4408
|
) -> Iterator[RunOutputEvent]:
|
|
4310
4409
|
self.model = cast(Model, self.model)
|
|
4410
|
+
_functions = {tool.name: tool for tool in tools if isinstance(tool, Function)}
|
|
4411
|
+
|
|
4311
4412
|
for _t in run_response.tools or []:
|
|
4312
4413
|
# Case 1: Handle confirmed tools and execute them
|
|
4313
|
-
if _t.requires_confirmation is not None and _t.requires_confirmation is True and
|
|
4414
|
+
if _t.requires_confirmation is not None and _t.requires_confirmation is True and _functions:
|
|
4314
4415
|
# Tool is confirmed and hasn't been run before
|
|
4315
4416
|
if _t.confirmed is not None and _t.confirmed is True and _t.result is None:
|
|
4316
|
-
yield from self._run_tool(
|
|
4417
|
+
yield from self._run_tool(
|
|
4418
|
+
run_response, run_messages, _t, functions=_functions, stream_events=stream_events
|
|
4419
|
+
)
|
|
4317
4420
|
else:
|
|
4318
|
-
self._reject_tool_call(run_messages, _t)
|
|
4421
|
+
self._reject_tool_call(run_messages, _t, functions=_functions)
|
|
4319
4422
|
_t.confirmed = False
|
|
4320
4423
|
_t.confirmation_note = _t.confirmation_note or "Tool call was rejected"
|
|
4321
4424
|
_t.tool_call_error = True
|
|
@@ -4338,21 +4441,27 @@ class Agent:
|
|
|
4338
4441
|
# Case 4: Handle user input required tools
|
|
4339
4442
|
elif _t.requires_user_input is not None and _t.requires_user_input is True:
|
|
4340
4443
|
self._handle_user_input_update(tool=_t)
|
|
4341
|
-
yield from self._run_tool(
|
|
4444
|
+
yield from self._run_tool(
|
|
4445
|
+
run_response, run_messages, _t, functions=_functions, stream_events=stream_events
|
|
4446
|
+
)
|
|
4342
4447
|
_t.requires_user_input = False
|
|
4343
4448
|
_t.answered = True
|
|
4344
4449
|
|
|
4345
|
-
async def _ahandle_tool_call_updates(
|
|
4450
|
+
async def _ahandle_tool_call_updates(
|
|
4451
|
+
self, run_response: RunOutput, run_messages: RunMessages, tools: List[Union[Function, dict]]
|
|
4452
|
+
):
|
|
4346
4453
|
self.model = cast(Model, self.model)
|
|
4454
|
+
_functions = {tool.name: tool for tool in tools if isinstance(tool, Function)}
|
|
4455
|
+
|
|
4347
4456
|
for _t in run_response.tools or []:
|
|
4348
4457
|
# Case 1: Handle confirmed tools and execute them
|
|
4349
|
-
if _t.requires_confirmation is not None and _t.requires_confirmation is True and
|
|
4458
|
+
if _t.requires_confirmation is not None and _t.requires_confirmation is True and _functions:
|
|
4350
4459
|
# Tool is confirmed and hasn't been run before
|
|
4351
4460
|
if _t.confirmed is not None and _t.confirmed is True and _t.result is None:
|
|
4352
|
-
async for _ in self._arun_tool(run_response, run_messages, _t):
|
|
4461
|
+
async for _ in self._arun_tool(run_response, run_messages, _t, functions=_functions):
|
|
4353
4462
|
pass
|
|
4354
4463
|
else:
|
|
4355
|
-
self._reject_tool_call(run_messages, _t)
|
|
4464
|
+
self._reject_tool_call(run_messages, _t, functions=_functions)
|
|
4356
4465
|
_t.confirmed = False
|
|
4357
4466
|
_t.confirmation_note = _t.confirmation_note or "Tool call was rejected"
|
|
4358
4467
|
_t.tool_call_error = True
|
|
@@ -4373,24 +4482,32 @@ class Agent:
|
|
|
4373
4482
|
# Case 4: Handle user input required tools
|
|
4374
4483
|
elif _t.requires_user_input is not None and _t.requires_user_input is True:
|
|
4375
4484
|
self._handle_user_input_update(tool=_t)
|
|
4376
|
-
async for _ in self._arun_tool(run_response, run_messages, _t):
|
|
4485
|
+
async for _ in self._arun_tool(run_response, run_messages, _t, functions=_functions):
|
|
4377
4486
|
pass
|
|
4378
4487
|
_t.requires_user_input = False
|
|
4379
4488
|
_t.answered = True
|
|
4380
4489
|
|
|
4381
4490
|
async def _ahandle_tool_call_updates_stream(
|
|
4382
|
-
self,
|
|
4491
|
+
self,
|
|
4492
|
+
run_response: RunOutput,
|
|
4493
|
+
run_messages: RunMessages,
|
|
4494
|
+
tools: List[Union[Function, dict]],
|
|
4495
|
+
stream_events: bool = False,
|
|
4383
4496
|
) -> AsyncIterator[RunOutputEvent]:
|
|
4384
4497
|
self.model = cast(Model, self.model)
|
|
4498
|
+
_functions = {tool.name: tool for tool in tools if isinstance(tool, Function)}
|
|
4499
|
+
|
|
4385
4500
|
for _t in run_response.tools or []:
|
|
4386
4501
|
# Case 1: Handle confirmed tools and execute them
|
|
4387
|
-
if _t.requires_confirmation is not None and _t.requires_confirmation is True and
|
|
4502
|
+
if _t.requires_confirmation is not None and _t.requires_confirmation is True and _functions:
|
|
4388
4503
|
# Tool is confirmed and hasn't been run before
|
|
4389
4504
|
if _t.confirmed is not None and _t.confirmed is True and _t.result is None:
|
|
4390
|
-
async for event in self._arun_tool(
|
|
4505
|
+
async for event in self._arun_tool(
|
|
4506
|
+
run_response, run_messages, _t, functions=_functions, stream_events=stream_events
|
|
4507
|
+
):
|
|
4391
4508
|
yield event
|
|
4392
4509
|
else:
|
|
4393
|
-
self._reject_tool_call(run_messages, _t)
|
|
4510
|
+
self._reject_tool_call(run_messages, _t, functions=_functions)
|
|
4394
4511
|
_t.confirmed = False
|
|
4395
4512
|
_t.confirmation_note = _t.confirmation_note or "Tool call was rejected"
|
|
4396
4513
|
_t.tool_call_error = True
|
|
@@ -4411,7 +4528,9 @@ class Agent:
|
|
|
4411
4528
|
# # Case 4: Handle user input required tools
|
|
4412
4529
|
elif _t.requires_user_input is not None and _t.requires_user_input is True:
|
|
4413
4530
|
self._handle_user_input_update(tool=_t)
|
|
4414
|
-
async for event in self._arun_tool(
|
|
4531
|
+
async for event in self._arun_tool(
|
|
4532
|
+
run_response, run_messages, _t, functions=_functions, stream_events=stream_events
|
|
4533
|
+
):
|
|
4415
4534
|
yield event
|
|
4416
4535
|
_t.requires_user_input = False
|
|
4417
4536
|
_t.answered = True
|
|
@@ -4517,6 +4636,7 @@ class Agent:
|
|
|
4517
4636
|
session: AgentSession,
|
|
4518
4637
|
run_response: RunOutput,
|
|
4519
4638
|
run_messages: RunMessages,
|
|
4639
|
+
tools: Optional[List[Union[Function, dict]]] = None,
|
|
4520
4640
|
response_format: Optional[Union[Dict, Type[BaseModel]]] = None,
|
|
4521
4641
|
stream_events: bool = False,
|
|
4522
4642
|
) -> Iterator[RunOutputEvent]:
|
|
@@ -4536,8 +4656,7 @@ class Agent:
|
|
|
4536
4656
|
for model_response_event in self.model.response_stream(
|
|
4537
4657
|
messages=run_messages.messages,
|
|
4538
4658
|
response_format=response_format,
|
|
4539
|
-
tools=
|
|
4540
|
-
functions=self._functions_for_model,
|
|
4659
|
+
tools=tools,
|
|
4541
4660
|
tool_choice=self.tool_choice,
|
|
4542
4661
|
tool_call_limit=self.tool_call_limit,
|
|
4543
4662
|
stream_model_response=stream_model_response,
|
|
@@ -4595,6 +4714,7 @@ class Agent:
|
|
|
4595
4714
|
session: AgentSession,
|
|
4596
4715
|
run_response: RunOutput,
|
|
4597
4716
|
run_messages: RunMessages,
|
|
4717
|
+
tools: Optional[List[Union[Function, dict]]] = None,
|
|
4598
4718
|
response_format: Optional[Union[Dict, Type[BaseModel]]] = None,
|
|
4599
4719
|
stream_events: bool = False,
|
|
4600
4720
|
) -> AsyncIterator[RunOutputEvent]:
|
|
@@ -4614,8 +4734,7 @@ class Agent:
|
|
|
4614
4734
|
model_response_stream = self.model.aresponse_stream(
|
|
4615
4735
|
messages=run_messages.messages,
|
|
4616
4736
|
response_format=response_format,
|
|
4617
|
-
tools=
|
|
4618
|
-
functions=self._functions_for_model,
|
|
4737
|
+
tools=tools,
|
|
4619
4738
|
tool_choice=self.tool_choice,
|
|
4620
4739
|
tool_call_limit=self.tool_call_limit,
|
|
4621
4740
|
stream_model_response=stream_model_response,
|
|
@@ -5121,69 +5240,34 @@ class Agent:
|
|
|
5121
5240
|
self,
|
|
5122
5241
|
run_response: RunOutput,
|
|
5123
5242
|
session: AgentSession,
|
|
5124
|
-
async_mode: bool = False,
|
|
5125
5243
|
user_id: Optional[str] = None,
|
|
5126
5244
|
knowledge_filters: Optional[Dict[str, Any]] = None,
|
|
5127
|
-
) ->
|
|
5245
|
+
) -> List[Union[Toolkit, Callable, Function, Dict]]:
|
|
5128
5246
|
agent_tools: List[Union[Toolkit, Callable, Function, Dict]] = []
|
|
5129
5247
|
|
|
5130
5248
|
# Add provided tools
|
|
5131
5249
|
if self.tools is not None:
|
|
5132
5250
|
# If not running in async mode, raise if any tool is async
|
|
5133
|
-
|
|
5134
|
-
self._raise_if_async_tools()
|
|
5251
|
+
self._raise_if_async_tools()
|
|
5135
5252
|
agent_tools.extend(self.tools)
|
|
5136
5253
|
|
|
5137
|
-
# If any of the tools has "agent" as parameter, set _rebuild_tools to True
|
|
5138
|
-
for tool in agent_tools:
|
|
5139
|
-
param_names = {
|
|
5140
|
-
"agent",
|
|
5141
|
-
"session_state",
|
|
5142
|
-
"team",
|
|
5143
|
-
"images",
|
|
5144
|
-
"videos",
|
|
5145
|
-
"audios",
|
|
5146
|
-
"files",
|
|
5147
|
-
}
|
|
5148
|
-
|
|
5149
|
-
if isinstance(tool, Function):
|
|
5150
|
-
if param_names & set(tool.parameters):
|
|
5151
|
-
self._rebuild_tools = True
|
|
5152
|
-
break
|
|
5153
|
-
elif isinstance(tool, Toolkit):
|
|
5154
|
-
for func in tool.functions.values():
|
|
5155
|
-
if param_names & set(func.parameters):
|
|
5156
|
-
self._rebuild_tools = True
|
|
5157
|
-
break
|
|
5158
|
-
elif callable(tool):
|
|
5159
|
-
from inspect import signature
|
|
5160
|
-
|
|
5161
|
-
if param_names & set(signature(tool).parameters):
|
|
5162
|
-
self._rebuild_tools = True
|
|
5163
|
-
break
|
|
5164
|
-
|
|
5165
5254
|
# Add tools for accessing memory
|
|
5166
5255
|
if self.read_chat_history:
|
|
5167
5256
|
agent_tools.append(self._get_chat_history_function(session=session))
|
|
5168
|
-
self._rebuild_tools = True
|
|
5169
5257
|
if self.read_tool_call_history:
|
|
5170
5258
|
agent_tools.append(self._get_tool_call_history_function(session=session))
|
|
5171
|
-
self._rebuild_tools = True
|
|
5172
5259
|
if self.search_session_history:
|
|
5173
5260
|
agent_tools.append(
|
|
5174
5261
|
self._get_previous_sessions_messages_function(
|
|
5175
5262
|
num_history_sessions=self.num_history_sessions, user_id=user_id
|
|
5176
5263
|
)
|
|
5177
5264
|
)
|
|
5178
|
-
self._rebuild_tools = True
|
|
5179
5265
|
|
|
5180
5266
|
if self.enable_agentic_memory:
|
|
5181
|
-
agent_tools.append(self._get_update_user_memory_function(user_id=user_id, async_mode=
|
|
5182
|
-
self._rebuild_tools = True
|
|
5267
|
+
agent_tools.append(self._get_update_user_memory_function(user_id=user_id, async_mode=False))
|
|
5183
5268
|
|
|
5184
5269
|
if self.enable_agentic_culture:
|
|
5185
|
-
agent_tools.append(self._get_update_cultural_knowledge_function(async_mode=
|
|
5186
|
-
self._rebuild_tools = True
|
|
5270
|
+
agent_tools.append(self._get_update_cultural_knowledge_function(async_mode=False))
|
|
5187
5271
|
|
|
5188
5272
|
if self.enable_agentic_state:
|
|
5189
5273
|
agent_tools.append(Function(name="update_session_state", entrypoint=self._update_session_state_tool))
|
|
@@ -5193,7 +5277,7 @@ class Agent:
|
|
|
5193
5277
|
# Check if knowledge retriever is an async function but used in sync mode
|
|
5194
5278
|
from inspect import iscoroutinefunction
|
|
5195
5279
|
|
|
5196
|
-
if
|
|
5280
|
+
if self.knowledge_retriever and iscoroutinefunction(self.knowledge_retriever):
|
|
5197
5281
|
log_warning(
|
|
5198
5282
|
"Async knowledge retriever function is being used with synchronous agent.run() or agent.print_response(). "
|
|
5199
5283
|
"It is recommended to use agent.arun() or agent.aprint_response() instead."
|
|
@@ -5205,7 +5289,7 @@ class Agent:
|
|
|
5205
5289
|
agent_tools.append(
|
|
5206
5290
|
self._search_knowledge_base_with_agentic_filters_function(
|
|
5207
5291
|
run_response=run_response,
|
|
5208
|
-
async_mode=
|
|
5292
|
+
async_mode=False,
|
|
5209
5293
|
knowledge_filters=knowledge_filters,
|
|
5210
5294
|
)
|
|
5211
5295
|
)
|
|
@@ -5213,11 +5297,10 @@ class Agent:
|
|
|
5213
5297
|
agent_tools.append(
|
|
5214
5298
|
self._get_search_knowledge_base_function(
|
|
5215
5299
|
run_response=run_response,
|
|
5216
|
-
async_mode=
|
|
5300
|
+
async_mode=False,
|
|
5217
5301
|
knowledge_filters=knowledge_filters,
|
|
5218
5302
|
)
|
|
5219
5303
|
)
|
|
5220
|
-
self._rebuild_tools = True
|
|
5221
5304
|
|
|
5222
5305
|
if self.update_knowledge:
|
|
5223
5306
|
agent_tools.append(self.add_to_knowledge)
|
|
@@ -5228,82 +5311,69 @@ class Agent:
|
|
|
5228
5311
|
self,
|
|
5229
5312
|
run_response: RunOutput,
|
|
5230
5313
|
session: AgentSession,
|
|
5231
|
-
async_mode: bool = False,
|
|
5232
5314
|
user_id: Optional[str] = None,
|
|
5233
5315
|
knowledge_filters: Optional[Dict[str, Any]] = None,
|
|
5234
|
-
|
|
5316
|
+
check_mcp_tools: bool = True,
|
|
5317
|
+
) -> List[Union[Toolkit, Callable, Function, Dict]]:
|
|
5235
5318
|
agent_tools: List[Union[Toolkit, Callable, Function, Dict]] = []
|
|
5236
5319
|
|
|
5320
|
+
# Connect MCP tools
|
|
5321
|
+
await self._connect_mcp_tools()
|
|
5322
|
+
|
|
5237
5323
|
# Add provided tools
|
|
5238
5324
|
if self.tools is not None:
|
|
5239
|
-
|
|
5325
|
+
for tool in self.tools:
|
|
5326
|
+
if tool.__class__.__name__ in ["MCPTools", "MultiMCPTools"]:
|
|
5327
|
+
if tool.refresh_connection: # type: ignore
|
|
5328
|
+
try:
|
|
5329
|
+
is_alive = await tool.is_alive() # type: ignore
|
|
5330
|
+
if not is_alive:
|
|
5331
|
+
await tool.connect(force=True) # type: ignore
|
|
5332
|
+
except (RuntimeError, BaseException) as e:
|
|
5333
|
+
log_warning(f"Failed to check if MCP tool is alive or to connect to it: {e}")
|
|
5334
|
+
continue
|
|
5240
5335
|
|
|
5241
|
-
|
|
5242
|
-
|
|
5243
|
-
|
|
5244
|
-
|
|
5245
|
-
|
|
5246
|
-
|
|
5247
|
-
if
|
|
5248
|
-
|
|
5249
|
-
|
|
5250
|
-
|
|
5251
|
-
|
|
5252
|
-
|
|
5253
|
-
|
|
5254
|
-
break
|
|
5255
|
-
if "team" in func.parameters:
|
|
5256
|
-
self._rebuild_tools = True
|
|
5257
|
-
break
|
|
5258
|
-
if callable(tool):
|
|
5259
|
-
from inspect import signature
|
|
5260
|
-
|
|
5261
|
-
sig = signature(tool)
|
|
5262
|
-
if "agent" in sig.parameters:
|
|
5263
|
-
self._rebuild_tools = True
|
|
5264
|
-
break
|
|
5265
|
-
if "team" in sig.parameters:
|
|
5266
|
-
self._rebuild_tools = True
|
|
5267
|
-
break
|
|
5336
|
+
try:
|
|
5337
|
+
await tool.build_tools() # type: ignore
|
|
5338
|
+
except (RuntimeError, BaseException) as e:
|
|
5339
|
+
log_warning(f"Failed to build tools for {str(tool)}: {e}")
|
|
5340
|
+
continue
|
|
5341
|
+
|
|
5342
|
+
# Only add the tool if it successfully connected and built its tools
|
|
5343
|
+
if check_mcp_tools and not tool.initialized: # type: ignore
|
|
5344
|
+
continue
|
|
5345
|
+
|
|
5346
|
+
agent_tools.append(tool)
|
|
5347
|
+
else:
|
|
5348
|
+
agent_tools.append(tool)
|
|
5268
5349
|
|
|
5269
5350
|
# Add tools for accessing memory
|
|
5270
5351
|
if self.read_chat_history:
|
|
5271
5352
|
agent_tools.append(self._get_chat_history_function(session=session))
|
|
5272
|
-
self._rebuild_tools = True
|
|
5273
5353
|
if self.read_tool_call_history:
|
|
5274
5354
|
agent_tools.append(self._get_tool_call_history_function(session=session))
|
|
5275
|
-
self._rebuild_tools = True
|
|
5276
5355
|
if self.search_session_history:
|
|
5277
5356
|
agent_tools.append(
|
|
5278
|
-
await self._aget_previous_sessions_messages_function(
|
|
5357
|
+
await self._aget_previous_sessions_messages_function(
|
|
5358
|
+
num_history_sessions=self.num_history_sessions, user_id=user_id
|
|
5359
|
+
)
|
|
5279
5360
|
)
|
|
5280
|
-
self._rebuild_tools = True
|
|
5281
5361
|
|
|
5282
5362
|
if self.enable_agentic_memory:
|
|
5283
|
-
agent_tools.append(self._get_update_user_memory_function(user_id=user_id, async_mode=
|
|
5284
|
-
self._rebuild_tools = True
|
|
5363
|
+
agent_tools.append(self._get_update_user_memory_function(user_id=user_id, async_mode=True))
|
|
5285
5364
|
|
|
5286
5365
|
if self.enable_agentic_state:
|
|
5287
5366
|
agent_tools.append(Function(name="update_session_state", entrypoint=self._update_session_state_tool))
|
|
5288
5367
|
|
|
5289
5368
|
# Add tools for accessing knowledge
|
|
5290
5369
|
if self.knowledge is not None or self.knowledge_retriever is not None:
|
|
5291
|
-
# Check if knowledge retriever is an async function but used in sync mode
|
|
5292
|
-
from inspect import iscoroutinefunction
|
|
5293
|
-
|
|
5294
|
-
if not async_mode and self.knowledge_retriever and iscoroutinefunction(self.knowledge_retriever):
|
|
5295
|
-
log_warning(
|
|
5296
|
-
"Async knowledge retriever function is being used with synchronous agent.run() or agent.print_response(). "
|
|
5297
|
-
"It is recommended to use agent.arun() or agent.aprint_response() instead."
|
|
5298
|
-
)
|
|
5299
|
-
|
|
5300
5370
|
if self.search_knowledge:
|
|
5301
5371
|
# Use async or sync search based on async_mode
|
|
5302
5372
|
if self.enable_agentic_knowledge_filters:
|
|
5303
5373
|
agent_tools.append(
|
|
5304
5374
|
self._search_knowledge_base_with_agentic_filters_function(
|
|
5305
5375
|
run_response=run_response,
|
|
5306
|
-
async_mode=
|
|
5376
|
+
async_mode=True,
|
|
5307
5377
|
knowledge_filters=knowledge_filters,
|
|
5308
5378
|
)
|
|
5309
5379
|
)
|
|
@@ -5311,11 +5381,10 @@ class Agent:
|
|
|
5311
5381
|
agent_tools.append(
|
|
5312
5382
|
self._get_search_knowledge_base_function(
|
|
5313
5383
|
run_response=run_response,
|
|
5314
|
-
async_mode=
|
|
5384
|
+
async_mode=True,
|
|
5315
5385
|
knowledge_filters=knowledge_filters,
|
|
5316
5386
|
)
|
|
5317
5387
|
)
|
|
5318
|
-
self._rebuild_tools = True
|
|
5319
5388
|
|
|
5320
5389
|
if self.update_knowledge:
|
|
5321
5390
|
agent_tools.append(self.add_to_knowledge)
|
|
@@ -5325,231 +5394,105 @@ class Agent:
|
|
|
5325
5394
|
def _determine_tools_for_model(
|
|
5326
5395
|
self,
|
|
5327
5396
|
model: Model,
|
|
5397
|
+
processed_tools: List[Union[Toolkit, Callable, Function, Dict]],
|
|
5328
5398
|
run_response: RunOutput,
|
|
5329
5399
|
session: AgentSession,
|
|
5330
5400
|
session_state: Optional[Dict[str, Any]] = None,
|
|
5331
5401
|
dependencies: Optional[Dict[str, Any]] = None,
|
|
5332
|
-
|
|
5333
|
-
|
|
5334
|
-
|
|
5335
|
-
|
|
5336
|
-
if self._rebuild_tools:
|
|
5337
|
-
self._rebuild_tools = False
|
|
5402
|
+
) -> List[Union[Function, dict]]:
|
|
5403
|
+
_function_names = []
|
|
5404
|
+
_functions: List[Union[Function, dict]] = []
|
|
5405
|
+
self._tool_instructions = []
|
|
5338
5406
|
|
|
5339
|
-
|
|
5340
|
-
|
|
5341
|
-
|
|
5342
|
-
async_mode=async_mode,
|
|
5343
|
-
user_id=user_id,
|
|
5344
|
-
knowledge_filters=knowledge_filters,
|
|
5345
|
-
)
|
|
5346
|
-
|
|
5347
|
-
self._tools_for_model = []
|
|
5348
|
-
self._functions_for_model = {}
|
|
5349
|
-
self._tool_instructions = []
|
|
5350
|
-
|
|
5351
|
-
# Get Agent tools
|
|
5352
|
-
if agent_tools is not None and len(agent_tools) > 0:
|
|
5353
|
-
log_debug("Processing tools for model")
|
|
5407
|
+
# Get Agent tools
|
|
5408
|
+
if processed_tools is not None and len(processed_tools) > 0:
|
|
5409
|
+
log_debug("Processing tools for model")
|
|
5354
5410
|
|
|
5355
|
-
|
|
5356
|
-
|
|
5357
|
-
|
|
5358
|
-
|
|
5359
|
-
|
|
5360
|
-
|
|
5361
|
-
|
|
5362
|
-
|
|
5363
|
-
|
|
5364
|
-
for tool in agent_tools:
|
|
5365
|
-
if isinstance(tool, Dict):
|
|
5366
|
-
# If a dict is passed, it is a builtin tool
|
|
5367
|
-
# that is run by the model provider and not the Agent
|
|
5368
|
-
self._tools_for_model.append(tool)
|
|
5369
|
-
log_debug(f"Included builtin tool {tool}")
|
|
5370
|
-
|
|
5371
|
-
elif isinstance(tool, Toolkit):
|
|
5372
|
-
# For each function in the toolkit and process entrypoint
|
|
5373
|
-
for name, func in tool.functions.items():
|
|
5374
|
-
# If the function does not exist in self.functions
|
|
5375
|
-
if name not in self._functions_for_model:
|
|
5376
|
-
func._agent = self
|
|
5377
|
-
func.process_entrypoint(strict=strict)
|
|
5378
|
-
if strict and func.strict is None:
|
|
5379
|
-
func.strict = True
|
|
5380
|
-
if self.tool_hooks is not None:
|
|
5381
|
-
func.tool_hooks = self.tool_hooks
|
|
5382
|
-
self._functions_for_model[name] = func
|
|
5383
|
-
self._tools_for_model.append({"type": "function", "function": func.to_dict()})
|
|
5384
|
-
log_debug(f"Added tool {name} from {tool.name}")
|
|
5385
|
-
|
|
5386
|
-
# Add instructions from the toolkit
|
|
5387
|
-
if tool.add_instructions and tool.instructions is not None:
|
|
5388
|
-
self._tool_instructions.append(tool.instructions)
|
|
5389
|
-
|
|
5390
|
-
elif isinstance(tool, Function):
|
|
5391
|
-
if tool.name not in self._functions_for_model:
|
|
5392
|
-
tool._agent = self
|
|
5393
|
-
tool.process_entrypoint(strict=strict)
|
|
5394
|
-
if strict and tool.strict is None:
|
|
5395
|
-
tool.strict = True
|
|
5396
|
-
if self.tool_hooks is not None:
|
|
5397
|
-
tool.tool_hooks = self.tool_hooks
|
|
5398
|
-
self._functions_for_model[tool.name] = tool
|
|
5399
|
-
self._tools_for_model.append({"type": "function", "function": tool.to_dict()})
|
|
5400
|
-
log_debug(f"Added tool {tool.name}")
|
|
5401
|
-
|
|
5402
|
-
# Add instructions from the Function
|
|
5403
|
-
if tool.add_instructions and tool.instructions is not None:
|
|
5404
|
-
self._tool_instructions.append(tool.instructions)
|
|
5405
|
-
|
|
5406
|
-
elif callable(tool):
|
|
5407
|
-
try:
|
|
5408
|
-
function_name = tool.__name__
|
|
5409
|
-
if function_name not in self._functions_for_model:
|
|
5410
|
-
func = Function.from_callable(tool, strict=strict)
|
|
5411
|
-
func._agent = self
|
|
5412
|
-
if strict:
|
|
5413
|
-
func.strict = True
|
|
5414
|
-
if self.tool_hooks is not None:
|
|
5415
|
-
func.tool_hooks = self.tool_hooks
|
|
5416
|
-
self._functions_for_model[func.name] = func
|
|
5417
|
-
self._tools_for_model.append({"type": "function", "function": func.to_dict()})
|
|
5418
|
-
log_debug(f"Added tool {func.name}")
|
|
5419
|
-
except Exception as e:
|
|
5420
|
-
log_warning(f"Could not add tool {tool}: {e}")
|
|
5421
|
-
|
|
5422
|
-
# Update the session state for the functions
|
|
5423
|
-
if self._functions_for_model:
|
|
5424
|
-
from inspect import signature
|
|
5425
|
-
|
|
5426
|
-
# Check if any functions need media before collecting
|
|
5427
|
-
needs_media = any(
|
|
5428
|
-
any(param in signature(func.entrypoint).parameters for param in ["images", "videos", "audios", "files"])
|
|
5429
|
-
for func in self._functions_for_model.values()
|
|
5430
|
-
if func.entrypoint is not None
|
|
5431
|
-
)
|
|
5432
|
-
|
|
5433
|
-
# Only collect media if functions actually need them
|
|
5434
|
-
joint_images = collect_joint_images(run_response.input, session) if needs_media else None
|
|
5435
|
-
joint_files = collect_joint_files(run_response.input) if needs_media else None
|
|
5436
|
-
joint_audios = collect_joint_audios(run_response.input, session) if needs_media else None
|
|
5437
|
-
joint_videos = collect_joint_videos(run_response.input, session) if needs_media else None
|
|
5438
|
-
|
|
5439
|
-
for func in self._functions_for_model.values():
|
|
5440
|
-
func._session_state = session_state
|
|
5441
|
-
func._dependencies = dependencies
|
|
5442
|
-
func._images = joint_images
|
|
5443
|
-
func._files = joint_files
|
|
5444
|
-
func._audios = joint_audios
|
|
5445
|
-
func._videos = joint_videos
|
|
5446
|
-
|
|
5447
|
-
async def _adetermine_tools_for_model(
|
|
5448
|
-
self,
|
|
5449
|
-
model: Model,
|
|
5450
|
-
run_response: RunOutput,
|
|
5451
|
-
session: AgentSession,
|
|
5452
|
-
session_state: Optional[Dict[str, Any]] = None,
|
|
5453
|
-
dependencies: Optional[Dict[str, Any]] = None,
|
|
5454
|
-
user_id: Optional[str] = None,
|
|
5455
|
-
async_mode: bool = False,
|
|
5456
|
-
knowledge_filters: Optional[Dict[str, Any]] = None,
|
|
5457
|
-
) -> None:
|
|
5458
|
-
if self._rebuild_tools:
|
|
5459
|
-
self._rebuild_tools = False
|
|
5460
|
-
|
|
5461
|
-
agent_tools = await self.aget_tools(
|
|
5462
|
-
run_response=run_response,
|
|
5463
|
-
session=session,
|
|
5464
|
-
async_mode=async_mode,
|
|
5465
|
-
user_id=user_id,
|
|
5466
|
-
knowledge_filters=knowledge_filters,
|
|
5467
|
-
)
|
|
5411
|
+
# Check if we need strict mode for the functions for the model
|
|
5412
|
+
strict = False
|
|
5413
|
+
if (
|
|
5414
|
+
self.output_schema is not None
|
|
5415
|
+
and (self.structured_outputs or (not self.use_json_mode))
|
|
5416
|
+
and model.supports_native_structured_outputs
|
|
5417
|
+
):
|
|
5418
|
+
strict = True
|
|
5468
5419
|
|
|
5469
|
-
|
|
5470
|
-
|
|
5471
|
-
|
|
5420
|
+
for tool in processed_tools:
|
|
5421
|
+
if isinstance(tool, Dict):
|
|
5422
|
+
# If a dict is passed, it is a builtin tool
|
|
5423
|
+
# that is run by the model provider and not the Agent
|
|
5424
|
+
_functions.append(tool)
|
|
5425
|
+
log_debug(f"Included builtin tool {tool}")
|
|
5472
5426
|
|
|
5473
|
-
|
|
5474
|
-
|
|
5475
|
-
|
|
5427
|
+
elif isinstance(tool, Toolkit):
|
|
5428
|
+
# For each function in the toolkit and process entrypoint
|
|
5429
|
+
for name, _func in tool.functions.items():
|
|
5430
|
+
if name in _function_names:
|
|
5431
|
+
continue
|
|
5432
|
+
_function_names.append(name)
|
|
5433
|
+
_func = _func.model_copy(deep=True)
|
|
5434
|
+
_func._agent = self
|
|
5435
|
+
_func.process_entrypoint(strict=strict)
|
|
5436
|
+
if strict and _func.strict is None:
|
|
5437
|
+
_func.strict = True
|
|
5438
|
+
if self.tool_hooks is not None:
|
|
5439
|
+
_func.tool_hooks = self.tool_hooks
|
|
5440
|
+
_functions.append(_func)
|
|
5441
|
+
log_debug(f"Added tool {name} from {tool.name}")
|
|
5442
|
+
|
|
5443
|
+
# Add instructions from the toolkit
|
|
5444
|
+
if tool.add_instructions and tool.instructions is not None:
|
|
5445
|
+
self._tool_instructions.append(tool.instructions)
|
|
5446
|
+
|
|
5447
|
+
elif isinstance(tool, Function):
|
|
5448
|
+
if tool.name in _function_names:
|
|
5449
|
+
continue
|
|
5450
|
+
_function_names.append(tool.name)
|
|
5451
|
+
|
|
5452
|
+
tool.process_entrypoint(strict=strict)
|
|
5453
|
+
tool = tool.model_copy(deep=True)
|
|
5454
|
+
|
|
5455
|
+
tool._agent = self
|
|
5456
|
+
if strict and tool.strict is None:
|
|
5457
|
+
tool.strict = True
|
|
5458
|
+
if self.tool_hooks is not None:
|
|
5459
|
+
tool.tool_hooks = self.tool_hooks
|
|
5460
|
+
_functions.append(tool)
|
|
5461
|
+
log_debug(f"Added tool {tool.name}")
|
|
5462
|
+
|
|
5463
|
+
# Add instructions from the Function
|
|
5464
|
+
if tool.add_instructions and tool.instructions is not None:
|
|
5465
|
+
self._tool_instructions.append(tool.instructions)
|
|
5476
5466
|
|
|
5477
|
-
|
|
5478
|
-
|
|
5479
|
-
|
|
5480
|
-
|
|
5481
|
-
|
|
5482
|
-
|
|
5483
|
-
|
|
5484
|
-
|
|
5485
|
-
|
|
5486
|
-
|
|
5487
|
-
|
|
5488
|
-
|
|
5489
|
-
|
|
5490
|
-
self.
|
|
5491
|
-
|
|
5492
|
-
|
|
5493
|
-
|
|
5494
|
-
|
|
5495
|
-
|
|
5496
|
-
# If the function does not exist in self.functions
|
|
5497
|
-
if name not in self._functions_for_model:
|
|
5498
|
-
func._agent = self
|
|
5499
|
-
func.process_entrypoint(strict=strict)
|
|
5500
|
-
if strict and func.strict is None:
|
|
5501
|
-
func.strict = True
|
|
5502
|
-
if self.tool_hooks is not None:
|
|
5503
|
-
func.tool_hooks = self.tool_hooks
|
|
5504
|
-
self._functions_for_model[name] = func
|
|
5505
|
-
self._tools_for_model.append({"type": "function", "function": func.to_dict()})
|
|
5506
|
-
log_debug(f"Added tool {name} from {tool.name}")
|
|
5507
|
-
|
|
5508
|
-
# Add instructions from the toolkit
|
|
5509
|
-
if tool.add_instructions and tool.instructions is not None:
|
|
5510
|
-
self._tool_instructions.append(tool.instructions)
|
|
5511
|
-
|
|
5512
|
-
elif isinstance(tool, Function):
|
|
5513
|
-
if tool.name not in self._functions_for_model:
|
|
5514
|
-
tool._agent = self
|
|
5515
|
-
tool.process_entrypoint(strict=strict)
|
|
5516
|
-
if strict and tool.strict is None:
|
|
5517
|
-
tool.strict = True
|
|
5518
|
-
if self.tool_hooks is not None:
|
|
5519
|
-
tool.tool_hooks = self.tool_hooks
|
|
5520
|
-
self._functions_for_model[tool.name] = tool
|
|
5521
|
-
self._tools_for_model.append({"type": "function", "function": tool.to_dict()})
|
|
5522
|
-
log_debug(f"Added tool {tool.name}")
|
|
5523
|
-
|
|
5524
|
-
# Add instructions from the Function
|
|
5525
|
-
if tool.add_instructions and tool.instructions is not None:
|
|
5526
|
-
self._tool_instructions.append(tool.instructions)
|
|
5527
|
-
|
|
5528
|
-
elif callable(tool):
|
|
5529
|
-
try:
|
|
5530
|
-
function_name = tool.__name__
|
|
5531
|
-
if function_name not in self._functions_for_model:
|
|
5532
|
-
func = Function.from_callable(tool, strict=strict)
|
|
5533
|
-
func._agent = self
|
|
5534
|
-
if strict:
|
|
5535
|
-
func.strict = True
|
|
5536
|
-
if self.tool_hooks is not None:
|
|
5537
|
-
func.tool_hooks = self.tool_hooks
|
|
5538
|
-
self._functions_for_model[func.name] = func
|
|
5539
|
-
self._tools_for_model.append({"type": "function", "function": func.to_dict()})
|
|
5540
|
-
log_debug(f"Added tool {func.name}")
|
|
5541
|
-
except Exception as e:
|
|
5542
|
-
log_warning(f"Could not add tool {tool}: {e}")
|
|
5467
|
+
elif callable(tool):
|
|
5468
|
+
try:
|
|
5469
|
+
function_name = tool.__name__
|
|
5470
|
+
|
|
5471
|
+
if function_name in _function_names:
|
|
5472
|
+
continue
|
|
5473
|
+
_function_names.append(function_name)
|
|
5474
|
+
|
|
5475
|
+
_func = Function.from_callable(tool, strict=strict)
|
|
5476
|
+
_func = _func.model_copy(deep=True)
|
|
5477
|
+
_func._agent = self
|
|
5478
|
+
if strict:
|
|
5479
|
+
_func.strict = True
|
|
5480
|
+
if self.tool_hooks is not None:
|
|
5481
|
+
_func.tool_hooks = self.tool_hooks
|
|
5482
|
+
_functions.append(_func)
|
|
5483
|
+
log_debug(f"Added tool {_func.name}")
|
|
5484
|
+
except Exception as e:
|
|
5485
|
+
log_warning(f"Could not add tool {tool}: {e}")
|
|
5543
5486
|
|
|
5544
5487
|
# Update the session state for the functions
|
|
5545
|
-
if
|
|
5488
|
+
if _functions:
|
|
5546
5489
|
from inspect import signature
|
|
5547
5490
|
|
|
5548
5491
|
# Check if any functions need media before collecting
|
|
5549
5492
|
needs_media = any(
|
|
5550
5493
|
any(param in signature(func.entrypoint).parameters for param in ["images", "videos", "audios", "files"])
|
|
5551
|
-
for func in
|
|
5552
|
-
if func.entrypoint is not None
|
|
5494
|
+
for func in _functions
|
|
5495
|
+
if isinstance(func, Function) and func.entrypoint is not None
|
|
5553
5496
|
)
|
|
5554
5497
|
|
|
5555
5498
|
# Only collect media if functions actually need them
|
|
@@ -5558,13 +5501,16 @@ class Agent:
|
|
|
5558
5501
|
joint_audios = collect_joint_audios(run_response.input, session) if needs_media else None
|
|
5559
5502
|
joint_videos = collect_joint_videos(run_response.input, session) if needs_media else None
|
|
5560
5503
|
|
|
5561
|
-
for func in
|
|
5562
|
-
func
|
|
5563
|
-
|
|
5564
|
-
|
|
5565
|
-
|
|
5566
|
-
|
|
5567
|
-
|
|
5504
|
+
for func in _functions: # type: ignore
|
|
5505
|
+
if isinstance(func, Function):
|
|
5506
|
+
func._session_state = session_state
|
|
5507
|
+
func._dependencies = dependencies
|
|
5508
|
+
func._images = joint_images
|
|
5509
|
+
func._files = joint_files
|
|
5510
|
+
func._audios = joint_audios
|
|
5511
|
+
func._videos = joint_videos
|
|
5512
|
+
|
|
5513
|
+
return _functions
|
|
5568
5514
|
|
|
5569
5515
|
def _model_should_return_structured_output(self):
|
|
5570
5516
|
self.model = cast(Model, self.model)
|
|
@@ -5664,6 +5610,18 @@ class Agent:
|
|
|
5664
5610
|
agent_data["model"] = self.model.to_dict()
|
|
5665
5611
|
return agent_data
|
|
5666
5612
|
|
|
5613
|
+
@staticmethod
|
|
5614
|
+
def cancel_run(run_id: str) -> bool:
|
|
5615
|
+
"""Cancel a running agent execution.
|
|
5616
|
+
|
|
5617
|
+
Args:
|
|
5618
|
+
run_id (str): The run_id to cancel.
|
|
5619
|
+
|
|
5620
|
+
Returns:
|
|
5621
|
+
bool: True if the run was found and marked for cancellation, False otherwise.
|
|
5622
|
+
"""
|
|
5623
|
+
return cancel_run_global(run_id)
|
|
5624
|
+
|
|
5667
5625
|
# -*- Session Database Functions
|
|
5668
5626
|
def _read_session(
|
|
5669
5627
|
self, session_id: str, session_type: SessionType = SessionType.AGENT
|
|
@@ -5767,8 +5725,8 @@ class Agent:
|
|
|
5767
5725
|
from time import time
|
|
5768
5726
|
|
|
5769
5727
|
# Returning cached session if we have one
|
|
5770
|
-
if self.
|
|
5771
|
-
return self.
|
|
5728
|
+
if self._cached_session is not None and self._cached_session.session_id == session_id:
|
|
5729
|
+
return self._cached_session
|
|
5772
5730
|
|
|
5773
5731
|
# Try to load from database
|
|
5774
5732
|
agent_session = None
|
|
@@ -5796,7 +5754,7 @@ class Agent:
|
|
|
5796
5754
|
)
|
|
5797
5755
|
|
|
5798
5756
|
if self.cache_session:
|
|
5799
|
-
self.
|
|
5757
|
+
self._cached_session = agent_session
|
|
5800
5758
|
|
|
5801
5759
|
return agent_session
|
|
5802
5760
|
|
|
@@ -5808,8 +5766,8 @@ class Agent:
|
|
|
5808
5766
|
from time import time
|
|
5809
5767
|
|
|
5810
5768
|
# Returning cached session if we have one
|
|
5811
|
-
if self.
|
|
5812
|
-
return self.
|
|
5769
|
+
if self._cached_session is not None and self._cached_session.session_id == session_id:
|
|
5770
|
+
return self._cached_session
|
|
5813
5771
|
|
|
5814
5772
|
# Try to load from database
|
|
5815
5773
|
agent_session = None
|
|
@@ -5839,10 +5797,11 @@ class Agent:
|
|
|
5839
5797
|
)
|
|
5840
5798
|
|
|
5841
5799
|
if self.cache_session:
|
|
5842
|
-
self.
|
|
5800
|
+
self._cached_session = agent_session
|
|
5843
5801
|
|
|
5844
5802
|
return agent_session
|
|
5845
5803
|
|
|
5804
|
+
# -*- Public Convenience Functions
|
|
5846
5805
|
def get_run_output(self, run_id: str, session_id: Optional[str] = None) -> Optional[RunOutput]:
|
|
5847
5806
|
"""
|
|
5848
5807
|
Get a RunOutput from the database.
|
|
@@ -5850,23 +5809,30 @@ class Agent:
|
|
|
5850
5809
|
Args:
|
|
5851
5810
|
run_id (str): The run_id to load from storage.
|
|
5852
5811
|
session_id (Optional[str]): The session_id to load from storage.
|
|
5812
|
+
Returns:
|
|
5813
|
+
Optional[RunOutput]: The RunOutput from the database or None if not found.
|
|
5853
5814
|
"""
|
|
5854
|
-
if
|
|
5855
|
-
|
|
5856
|
-
|
|
5857
|
-
|
|
5858
|
-
|
|
5859
|
-
|
|
5860
|
-
|
|
5861
|
-
|
|
5862
|
-
|
|
5863
|
-
|
|
5864
|
-
|
|
5865
|
-
|
|
5866
|
-
|
|
5867
|
-
|
|
5868
|
-
|
|
5869
|
-
|
|
5815
|
+
if not session_id and not self.session_id:
|
|
5816
|
+
raise Exception("No session_id provided")
|
|
5817
|
+
|
|
5818
|
+
session_id_to_load = session_id or self.session_id
|
|
5819
|
+
return cast(RunOutput, get_run_output_util(self, run_id=run_id, session_id=session_id_to_load))
|
|
5820
|
+
|
|
5821
|
+
async def aget_run_output(self, run_id: str, session_id: Optional[str] = None) -> Optional[RunOutput]:
|
|
5822
|
+
"""
|
|
5823
|
+
Get a RunOutput from the database.
|
|
5824
|
+
|
|
5825
|
+
Args:
|
|
5826
|
+
run_id (str): The run_id to load from storage.
|
|
5827
|
+
session_id (Optional[str]): The session_id to load from storage.
|
|
5828
|
+
Returns:
|
|
5829
|
+
Optional[RunOutput]: The RunOutput from the database or None if not found.
|
|
5830
|
+
"""
|
|
5831
|
+
if not session_id and not self.session_id:
|
|
5832
|
+
raise Exception("No session_id provided")
|
|
5833
|
+
|
|
5834
|
+
session_id_to_load = session_id or self.session_id
|
|
5835
|
+
return cast(RunOutput, await aget_run_output_util(self, run_id=run_id, session_id=session_id_to_load))
|
|
5870
5836
|
|
|
5871
5837
|
def get_last_run_output(self, session_id: Optional[str] = None) -> Optional[RunOutput]:
|
|
5872
5838
|
"""
|
|
@@ -5876,36 +5842,29 @@ class Agent:
|
|
|
5876
5842
|
session_id (Optional[str]): The session_id to load from storage.
|
|
5877
5843
|
|
|
5878
5844
|
Returns:
|
|
5879
|
-
RunOutput: The last run response from the database.
|
|
5845
|
+
Optional[RunOutput]: The last run response from the database or None if not found.
|
|
5880
5846
|
"""
|
|
5881
|
-
if
|
|
5882
|
-
|
|
5883
|
-
and self._agent_session.runs is not None
|
|
5884
|
-
and len(self._agent_session.runs) > 0
|
|
5885
|
-
):
|
|
5886
|
-
for run_output in reversed(self._agent_session.runs):
|
|
5887
|
-
if hasattr(run_output, "agent_id") and run_output.agent_id == self.id:
|
|
5888
|
-
return run_output
|
|
5889
|
-
else:
|
|
5890
|
-
session = self.get_session(session_id=session_id)
|
|
5891
|
-
if session is not None and session.runs is not None and len(session.runs) > 0:
|
|
5892
|
-
for run_output in reversed(session.runs):
|
|
5893
|
-
if hasattr(run_output, "agent_id") and run_output.agent_id == self.id:
|
|
5894
|
-
return run_output
|
|
5895
|
-
else:
|
|
5896
|
-
log_warning(f"No run responses found in Session {session_id}")
|
|
5897
|
-
return None
|
|
5847
|
+
if not session_id and not self.session_id:
|
|
5848
|
+
raise Exception("No session_id provided")
|
|
5898
5849
|
|
|
5899
|
-
|
|
5900
|
-
|
|
5850
|
+
session_id_to_load = session_id or self.session_id
|
|
5851
|
+
return cast(RunOutput, get_last_run_output_util(self, session_id=session_id_to_load))
|
|
5852
|
+
|
|
5853
|
+
async def aget_last_run_output(self, session_id: Optional[str] = None) -> Optional[RunOutput]:
|
|
5854
|
+
"""
|
|
5855
|
+
Get the last run response from the database.
|
|
5901
5856
|
|
|
5902
5857
|
Args:
|
|
5903
|
-
|
|
5858
|
+
session_id (Optional[str]): The session_id to load from storage.
|
|
5904
5859
|
|
|
5905
5860
|
Returns:
|
|
5906
|
-
|
|
5861
|
+
Optional[RunOutput]: The last run response from the database or None if not found.
|
|
5907
5862
|
"""
|
|
5908
|
-
|
|
5863
|
+
if not session_id and not self.session_id:
|
|
5864
|
+
raise Exception("No session_id provided")
|
|
5865
|
+
|
|
5866
|
+
session_id_to_load = session_id or self.session_id
|
|
5867
|
+
return cast(RunOutput, await aget_last_run_output_util(self, session_id=session_id_to_load))
|
|
5909
5868
|
|
|
5910
5869
|
def get_session(
|
|
5911
5870
|
self,
|
|
@@ -5925,9 +5884,12 @@ class Agent:
|
|
|
5925
5884
|
session_id_to_load = session_id or self.session_id
|
|
5926
5885
|
|
|
5927
5886
|
# If there is a cached session, return it
|
|
5928
|
-
if self.cache_session and hasattr(self, "
|
|
5929
|
-
if self.
|
|
5930
|
-
return self.
|
|
5887
|
+
if self.cache_session and hasattr(self, "_cached_session") and self._cached_session is not None:
|
|
5888
|
+
if self._cached_session.session_id == session_id_to_load:
|
|
5889
|
+
return self._cached_session
|
|
5890
|
+
|
|
5891
|
+
if self._has_async_db():
|
|
5892
|
+
raise ValueError("Async database not supported for get_session")
|
|
5931
5893
|
|
|
5932
5894
|
# Load and return the session from the database
|
|
5933
5895
|
if self.db is not None:
|
|
@@ -5958,7 +5920,7 @@ class Agent:
|
|
|
5958
5920
|
|
|
5959
5921
|
# Cache the session if relevant
|
|
5960
5922
|
if loaded_session is not None and self.cache_session:
|
|
5961
|
-
self.
|
|
5923
|
+
self._cached_session = loaded_session
|
|
5962
5924
|
|
|
5963
5925
|
return loaded_session
|
|
5964
5926
|
|
|
@@ -5983,29 +5945,53 @@ class Agent:
|
|
|
5983
5945
|
session_id_to_load = session_id or self.session_id
|
|
5984
5946
|
|
|
5985
5947
|
# If there is a cached session, return it
|
|
5986
|
-
if self.cache_session and hasattr(self, "
|
|
5987
|
-
if self.
|
|
5988
|
-
return self.
|
|
5948
|
+
if self.cache_session and hasattr(self, "_cached_session") and self._cached_session is not None:
|
|
5949
|
+
if self._cached_session.session_id == session_id_to_load:
|
|
5950
|
+
return self._cached_session
|
|
5989
5951
|
|
|
5990
5952
|
# Load and return the session from the database
|
|
5991
5953
|
if self.db is not None:
|
|
5992
|
-
|
|
5954
|
+
loaded_session = None
|
|
5955
|
+
|
|
5956
|
+
# We have a standalone agent, so we are loading an AgentSession
|
|
5957
|
+
if self.team_id is None and self.workflow_id is None:
|
|
5958
|
+
loaded_session = cast(
|
|
5959
|
+
AgentSession,
|
|
5960
|
+
await self._aread_session(session_id=session_id_to_load, session_type=SessionType.AGENT), # type: ignore
|
|
5961
|
+
)
|
|
5962
|
+
|
|
5963
|
+
# We have a team member agent, so we are loading a TeamSession
|
|
5964
|
+
if loaded_session is None and self.team_id is not None:
|
|
5965
|
+
# Load session for team member agents
|
|
5966
|
+
loaded_session = cast(
|
|
5967
|
+
TeamSession,
|
|
5968
|
+
await self._aread_session(session_id=session_id_to_load, session_type=SessionType.TEAM), # type: ignore
|
|
5969
|
+
)
|
|
5970
|
+
|
|
5971
|
+
# We have a workflow member agent, so we are loading a WorkflowSession
|
|
5972
|
+
if loaded_session is None and self.workflow_id is not None:
|
|
5973
|
+
# Load session for workflow memberagents
|
|
5974
|
+
loaded_session = cast(
|
|
5975
|
+
WorkflowSession,
|
|
5976
|
+
await self._aread_session(session_id=session_id_to_load, session_type=SessionType.WORKFLOW), # type: ignore
|
|
5977
|
+
)
|
|
5993
5978
|
|
|
5994
5979
|
# Cache the session if relevant
|
|
5995
|
-
if
|
|
5996
|
-
self.
|
|
5980
|
+
if loaded_session is not None and self.cache_session:
|
|
5981
|
+
self._cached_session = loaded_session
|
|
5997
5982
|
|
|
5998
|
-
return
|
|
5983
|
+
return loaded_session
|
|
5999
5984
|
|
|
6000
5985
|
log_debug(f"AgentSession {session_id_to_load} not found in db")
|
|
6001
5986
|
return None
|
|
6002
5987
|
|
|
6003
5988
|
def save_session(self, session: AgentSession) -> None:
|
|
6004
|
-
"""Save the AgentSession to storage
|
|
6005
|
-
|
|
6006
|
-
Returns:
|
|
6007
|
-
Optional[AgentSession]: The saved AgentSession or None if not saved.
|
|
6008
5989
|
"""
|
|
5990
|
+
Save the AgentSession to storage
|
|
5991
|
+
"""
|
|
5992
|
+
if self._has_async_db():
|
|
5993
|
+
raise ValueError("Async database not supported for save_session")
|
|
5994
|
+
|
|
6009
5995
|
# If the agent is a member of a team, do not save the session to the database
|
|
6010
5996
|
if (
|
|
6011
5997
|
self.db is not None
|
|
@@ -6022,10 +6008,8 @@ class Agent:
|
|
|
6022
6008
|
log_debug(f"Created or updated AgentSession record: {session.session_id}")
|
|
6023
6009
|
|
|
6024
6010
|
async def asave_session(self, session: AgentSession) -> None:
|
|
6025
|
-
"""
|
|
6026
|
-
|
|
6027
|
-
Returns:
|
|
6028
|
-
Optional[AgentSession]: The saved AgentSession or None if not saved.
|
|
6011
|
+
"""
|
|
6012
|
+
Save the AgentSession to storage
|
|
6029
6013
|
"""
|
|
6030
6014
|
# If the agent is a member of a team, do not save the session to the database
|
|
6031
6015
|
if (
|
|
@@ -6045,27 +6029,56 @@ class Agent:
|
|
|
6045
6029
|
log_debug(f"Created or updated AgentSession record: {session.session_id}")
|
|
6046
6030
|
|
|
6047
6031
|
def get_chat_history(self, session_id: Optional[str] = None) -> List[Message]:
|
|
6048
|
-
"""Read the chat history from the session
|
|
6049
|
-
if not session_id and not self.session_id:
|
|
6050
|
-
raise Exception("No session_id provided")
|
|
6032
|
+
"""Read the chat history from the session
|
|
6051
6033
|
|
|
6052
|
-
|
|
6053
|
-
|
|
6034
|
+
Args:
|
|
6035
|
+
session_id: The session ID to get the chat history for. If not provided, the current cached session ID is used.
|
|
6036
|
+
Returns:
|
|
6037
|
+
List[Message]: The chat history from the session.
|
|
6038
|
+
"""
|
|
6039
|
+
session_id = session_id or self.session_id
|
|
6040
|
+
if session_id is None:
|
|
6041
|
+
log_warning("Session ID is not set, cannot get chat history")
|
|
6042
|
+
return []
|
|
6054
6043
|
|
|
6055
|
-
|
|
6056
|
-
|
|
6044
|
+
return get_chat_history_util(self, session_id=session_id)
|
|
6045
|
+
|
|
6046
|
+
async def aget_chat_history(self, session_id: Optional[str] = None) -> List[Message]:
|
|
6047
|
+
"""Read the chat history from the session
|
|
6048
|
+
|
|
6049
|
+
Args:
|
|
6050
|
+
session_id: The session ID to get the chat history for. If not provided, the current cached session ID is used.
|
|
6051
|
+
Returns:
|
|
6052
|
+
List[Message]: The chat history from the session.
|
|
6053
|
+
"""
|
|
6054
|
+
session_id = session_id or self.session_id
|
|
6055
|
+
if session_id is None:
|
|
6056
|
+
log_warning("Session ID is not set, cannot get chat history")
|
|
6057
|
+
return []
|
|
6057
6058
|
|
|
6058
|
-
return
|
|
6059
|
+
return await aget_chat_history_util(self, session_id=session_id)
|
|
6059
6060
|
|
|
6061
|
+
# -*- Session Management Functions
|
|
6060
6062
|
def rename(self, name: str, session_id: Optional[str] = None) -> None:
|
|
6061
|
-
"""
|
|
6063
|
+
"""
|
|
6064
|
+
Rename the Agent and save to storage
|
|
6065
|
+
|
|
6066
|
+
Args:
|
|
6067
|
+
name (str): The new name for the Agent.
|
|
6068
|
+
session_id (Optional[str]): The session_id of the session where to store the new name. If not provided, the current cached session ID is used.
|
|
6069
|
+
"""
|
|
6062
6070
|
|
|
6063
6071
|
session_id = session_id or self.session_id
|
|
6064
6072
|
|
|
6065
6073
|
if session_id is None:
|
|
6066
6074
|
raise Exception("Session ID is not set")
|
|
6067
6075
|
|
|
6068
|
-
|
|
6076
|
+
if self._has_async_db():
|
|
6077
|
+
import asyncio
|
|
6078
|
+
|
|
6079
|
+
session = asyncio.run(self.aget_session(session_id=session_id))
|
|
6080
|
+
else:
|
|
6081
|
+
session = self.get_session(session_id=session_id)
|
|
6069
6082
|
|
|
6070
6083
|
if session is None:
|
|
6071
6084
|
raise Exception("Session not found")
|
|
@@ -6078,7 +6091,12 @@ class Agent:
|
|
|
6078
6091
|
session.agent_data = {"name": name}
|
|
6079
6092
|
|
|
6080
6093
|
# -*- Save to storage
|
|
6081
|
-
self.
|
|
6094
|
+
if self._has_async_db():
|
|
6095
|
+
import asyncio
|
|
6096
|
+
|
|
6097
|
+
asyncio.run(self.asave_session(session=session))
|
|
6098
|
+
else:
|
|
6099
|
+
self.save_session(session=session)
|
|
6082
6100
|
|
|
6083
6101
|
def set_session_name(
|
|
6084
6102
|
self,
|
|
@@ -6086,38 +6104,63 @@ class Agent:
|
|
|
6086
6104
|
autogenerate: bool = False,
|
|
6087
6105
|
session_name: Optional[str] = None,
|
|
6088
6106
|
) -> AgentSession:
|
|
6089
|
-
"""
|
|
6107
|
+
"""
|
|
6108
|
+
Set the session name and save to storage
|
|
6109
|
+
|
|
6110
|
+
Args:
|
|
6111
|
+
session_id: The session ID to set the name for. If not provided, the current cached session ID is used.
|
|
6112
|
+
autogenerate: Whether to autogenerate the session name.
|
|
6113
|
+
session_name: The session name to set. If not provided, the session name will be autogenerated.
|
|
6114
|
+
Returns:
|
|
6115
|
+
AgentSession: The updated session.
|
|
6116
|
+
"""
|
|
6090
6117
|
session_id = session_id or self.session_id
|
|
6091
6118
|
|
|
6092
6119
|
if session_id is None:
|
|
6093
6120
|
raise Exception("Session ID is not set")
|
|
6094
6121
|
|
|
6095
|
-
|
|
6096
|
-
|
|
6122
|
+
return cast(
|
|
6123
|
+
AgentSession,
|
|
6124
|
+
set_session_name_util(self, session_id=session_id, autogenerate=autogenerate, session_name=session_name),
|
|
6125
|
+
)
|
|
6097
6126
|
|
|
6098
|
-
|
|
6099
|
-
|
|
6127
|
+
async def aset_session_name(
|
|
6128
|
+
self,
|
|
6129
|
+
session_id: Optional[str] = None,
|
|
6130
|
+
autogenerate: bool = False,
|
|
6131
|
+
session_name: Optional[str] = None,
|
|
6132
|
+
) -> AgentSession:
|
|
6133
|
+
"""
|
|
6134
|
+
Set the session name and save to storage
|
|
6100
6135
|
|
|
6101
|
-
|
|
6102
|
-
|
|
6103
|
-
|
|
6104
|
-
|
|
6105
|
-
|
|
6106
|
-
|
|
6136
|
+
Args:
|
|
6137
|
+
session_id: The session ID to set the name for. If not provided, the current cached session ID is used.
|
|
6138
|
+
autogenerate: Whether to autogenerate the session name.
|
|
6139
|
+
session_name: The session name to set. If not provided, the session name will be autogenerated.
|
|
6140
|
+
Returns:
|
|
6141
|
+
AgentSession: The updated session.
|
|
6142
|
+
"""
|
|
6143
|
+
session_id = session_id or self.session_id
|
|
6107
6144
|
|
|
6108
|
-
|
|
6109
|
-
|
|
6110
|
-
session.session_data["session_name"] = session_name
|
|
6111
|
-
else:
|
|
6112
|
-
session.session_data = {"session_name": session_name}
|
|
6145
|
+
if session_id is None:
|
|
6146
|
+
raise Exception("Session ID is not set")
|
|
6113
6147
|
|
|
6114
|
-
|
|
6115
|
-
|
|
6148
|
+
return cast(
|
|
6149
|
+
AgentSession,
|
|
6150
|
+
await aset_session_name_util(
|
|
6151
|
+
self, session_id=session_id, autogenerate=autogenerate, session_name=session_name
|
|
6152
|
+
),
|
|
6153
|
+
)
|
|
6116
6154
|
|
|
6117
|
-
|
|
6155
|
+
def generate_session_name(self, session: AgentSession) -> str:
|
|
6156
|
+
"""
|
|
6157
|
+
Generate a name for the session using the first 6 messages from the memory
|
|
6118
6158
|
|
|
6119
|
-
|
|
6120
|
-
|
|
6159
|
+
Args:
|
|
6160
|
+
session (AgentSession): The session to generate a name for.
|
|
6161
|
+
Returns:
|
|
6162
|
+
str: The generated session name.
|
|
6163
|
+
"""
|
|
6121
6164
|
|
|
6122
6165
|
if self.model is None:
|
|
6123
6166
|
raise Exception("Model not set")
|
|
@@ -6144,32 +6187,68 @@ class Agent:
|
|
|
6144
6187
|
content = generated_name.content
|
|
6145
6188
|
if content is None:
|
|
6146
6189
|
log_error("Generated name is None. Trying again.")
|
|
6147
|
-
return self.
|
|
6190
|
+
return self.generate_session_name(session=session)
|
|
6148
6191
|
|
|
6149
6192
|
if len(content.split()) > 5:
|
|
6150
6193
|
log_error("Generated name is too long. It should be less than 5 words. Trying again.")
|
|
6151
|
-
return self.
|
|
6194
|
+
return self.generate_session_name(session=session)
|
|
6152
6195
|
return content.replace('"', "").strip()
|
|
6153
6196
|
|
|
6154
6197
|
def get_session_name(self, session_id: Optional[str] = None) -> str:
|
|
6155
|
-
"""
|
|
6198
|
+
"""
|
|
6199
|
+
Get the session name for the given session ID.
|
|
6200
|
+
|
|
6201
|
+
Args:
|
|
6202
|
+
session_id: The session ID to get the name for. If not provided, the current cached session ID is used.
|
|
6203
|
+
Returns:
|
|
6204
|
+
str: The session name.
|
|
6205
|
+
"""
|
|
6156
6206
|
session_id = session_id or self.session_id
|
|
6157
6207
|
if session_id is None:
|
|
6158
6208
|
raise Exception("Session ID is not set")
|
|
6159
|
-
|
|
6160
|
-
|
|
6161
|
-
|
|
6162
|
-
|
|
6209
|
+
return get_session_name_util(self, session_id=session_id)
|
|
6210
|
+
|
|
6211
|
+
async def aget_session_name(self, session_id: Optional[str] = None) -> str:
|
|
6212
|
+
"""
|
|
6213
|
+
Get the session name for the given session ID.
|
|
6214
|
+
|
|
6215
|
+
Args:
|
|
6216
|
+
session_id: The session ID to get the name for. If not provided, the current cached session ID is used.
|
|
6217
|
+
Returns:
|
|
6218
|
+
str: The session name.
|
|
6219
|
+
"""
|
|
6220
|
+
session_id = session_id or self.session_id
|
|
6221
|
+
if session_id is None:
|
|
6222
|
+
raise Exception("Session ID is not set")
|
|
6223
|
+
return await aget_session_name_util(self, session_id=session_id)
|
|
6163
6224
|
|
|
6164
6225
|
def get_session_state(self, session_id: Optional[str] = None) -> Dict[str, Any]:
|
|
6165
|
-
"""
|
|
6226
|
+
"""
|
|
6227
|
+
Get the session state for the given session ID.
|
|
6228
|
+
|
|
6229
|
+
Args:
|
|
6230
|
+
session_id: The session ID to get the state for. If not provided, the current cached session ID is used.
|
|
6231
|
+
Returns:
|
|
6232
|
+
Dict[str, Any]: The session state.
|
|
6233
|
+
"""
|
|
6166
6234
|
session_id = session_id or self.session_id
|
|
6167
6235
|
if session_id is None:
|
|
6168
6236
|
raise Exception("Session ID is not set")
|
|
6169
|
-
|
|
6170
|
-
|
|
6171
|
-
|
|
6172
|
-
|
|
6237
|
+
return get_session_state_util(self, session_id=session_id)
|
|
6238
|
+
|
|
6239
|
+
async def aget_session_state(self, session_id: Optional[str] = None) -> Dict[str, Any]:
|
|
6240
|
+
"""
|
|
6241
|
+
Get the session state for the given session ID.
|
|
6242
|
+
|
|
6243
|
+
Args:
|
|
6244
|
+
session_id: The session ID to get the state for. If not provided, the current cached session ID is used.
|
|
6245
|
+
Returns:
|
|
6246
|
+
Dict[str, Any]: The session state.
|
|
6247
|
+
"""
|
|
6248
|
+
session_id = session_id or self.session_id
|
|
6249
|
+
if session_id is None:
|
|
6250
|
+
raise Exception("Session ID is not set")
|
|
6251
|
+
return await aget_session_state_util(self, session_id=session_id)
|
|
6173
6252
|
|
|
6174
6253
|
def update_session_state(self, session_state_updates: Dict[str, Any], session_id: Optional[str] = None) -> str:
|
|
6175
6254
|
"""
|
|
@@ -6183,20 +6262,7 @@ class Agent:
|
|
|
6183
6262
|
session_id = session_id or self.session_id
|
|
6184
6263
|
if session_id is None:
|
|
6185
6264
|
raise Exception("Session ID is not set")
|
|
6186
|
-
|
|
6187
|
-
if session is None:
|
|
6188
|
-
raise Exception("Session not found")
|
|
6189
|
-
|
|
6190
|
-
if session.session_data is not None and "session_state" not in session.session_data:
|
|
6191
|
-
session.session_data["session_state"] = {}
|
|
6192
|
-
|
|
6193
|
-
# Overwrite the loaded DB session state with the new session state
|
|
6194
|
-
for key, value in session_state_updates.items():
|
|
6195
|
-
session.session_data["session_state"][key] = value # type: ignore
|
|
6196
|
-
|
|
6197
|
-
self.save_session(session=session)
|
|
6198
|
-
|
|
6199
|
-
return session.session_data["session_state"] # type: ignore
|
|
6265
|
+
return update_session_state_util(self, session_state_updates=session_state_updates, session_id=session_id)
|
|
6200
6266
|
|
|
6201
6267
|
async def aupdate_session_state(
|
|
6202
6268
|
self, session_state_updates: Dict[str, Any], session_id: Optional[str] = None
|
|
@@ -6212,52 +6278,65 @@ class Agent:
|
|
|
6212
6278
|
session_id = session_id or self.session_id
|
|
6213
6279
|
if session_id is None:
|
|
6214
6280
|
raise Exception("Session ID is not set")
|
|
6215
|
-
|
|
6216
|
-
|
|
6217
|
-
|
|
6281
|
+
return await aupdate_session_state_util(
|
|
6282
|
+
self, session_state_updates=session_state_updates, session_id=session_id
|
|
6283
|
+
)
|
|
6218
6284
|
|
|
6219
|
-
|
|
6220
|
-
|
|
6285
|
+
def get_session_metrics(self, session_id: Optional[str] = None) -> Optional[Metrics]:
|
|
6286
|
+
"""Get the session metrics for the given session ID.
|
|
6221
6287
|
|
|
6222
|
-
|
|
6223
|
-
session.
|
|
6288
|
+
Args:
|
|
6289
|
+
session_id: The session ID to get the metrics for. If not provided, the current cached session ID is used.
|
|
6290
|
+
Returns:
|
|
6291
|
+
Optional[Metrics]: The session metrics.
|
|
6292
|
+
"""
|
|
6293
|
+
session_id = session_id or self.session_id
|
|
6294
|
+
if session_id is None:
|
|
6295
|
+
raise Exception("Session ID is not set")
|
|
6224
6296
|
|
|
6225
|
-
|
|
6297
|
+
return get_session_metrics_util(self, session_id=session_id)
|
|
6226
6298
|
|
|
6227
|
-
|
|
6299
|
+
async def aget_session_metrics(self, session_id: Optional[str] = None) -> Optional[Metrics]:
|
|
6300
|
+
"""Get the session metrics for the given session ID.
|
|
6228
6301
|
|
|
6229
|
-
|
|
6230
|
-
|
|
6302
|
+
Args:
|
|
6303
|
+
session_id: The session ID to get the metrics for. If not provided, the current cached session ID is used.
|
|
6304
|
+
Returns:
|
|
6305
|
+
Optional[Metrics]: The session metrics.
|
|
6306
|
+
"""
|
|
6231
6307
|
session_id = session_id or self.session_id
|
|
6232
6308
|
if session_id is None:
|
|
6233
6309
|
raise Exception("Session ID is not set")
|
|
6234
6310
|
|
|
6235
|
-
|
|
6236
|
-
if session is None:
|
|
6237
|
-
raise Exception("Session not found")
|
|
6238
|
-
|
|
6239
|
-
if session.session_data is not None and session.session_data.get("session_metrics") is not None:
|
|
6240
|
-
if isinstance(session.session_data.get("session_metrics"), dict):
|
|
6241
|
-
return Metrics(**session.session_data.get("session_metrics", {}))
|
|
6242
|
-
elif isinstance(session.session_data.get("session_metrics"), Metrics):
|
|
6243
|
-
return session.session_data.get("session_metrics", None)
|
|
6244
|
-
return None
|
|
6311
|
+
return await aget_session_metrics_util(self, session_id=session_id)
|
|
6245
6312
|
|
|
6246
6313
|
def delete_session(self, session_id: str):
|
|
6247
6314
|
"""Delete the current session and save to storage"""
|
|
6248
6315
|
if self.db is None:
|
|
6249
6316
|
return
|
|
6250
|
-
|
|
6317
|
+
|
|
6251
6318
|
self.db.delete_session(session_id=session_id)
|
|
6252
6319
|
|
|
6320
|
+
async def adelete_session(self, session_id: str):
|
|
6321
|
+
"""Delete the current session and save to storage"""
|
|
6322
|
+
if self.db is None:
|
|
6323
|
+
return
|
|
6324
|
+
await self.db.delete_session(session_id=session_id) # type: ignore
|
|
6325
|
+
|
|
6253
6326
|
def get_messages_for_session(self, session_id: Optional[str] = None) -> List[Message]:
|
|
6254
|
-
"""Get messages for a session
|
|
6327
|
+
"""Get messages for a session
|
|
6328
|
+
|
|
6329
|
+
Args:
|
|
6330
|
+
session_id: The session ID to get the messages for. If not provided, the current cached session ID is used.
|
|
6331
|
+
Returns:
|
|
6332
|
+
List[Message]: The messages for the session.
|
|
6333
|
+
"""
|
|
6255
6334
|
session_id = session_id or self.session_id
|
|
6256
6335
|
if session_id is None:
|
|
6257
6336
|
log_warning("Session ID is not set, cannot get messages for session")
|
|
6258
6337
|
return []
|
|
6259
6338
|
|
|
6260
|
-
session = self.get_session(session_id=session_id)
|
|
6339
|
+
session = self.get_session(session_id=session_id)
|
|
6261
6340
|
|
|
6262
6341
|
if session is None:
|
|
6263
6342
|
raise Exception("Session not found")
|
|
@@ -6267,8 +6346,37 @@ class Agent:
|
|
|
6267
6346
|
agent_id=self.id if self.team_id is not None else None,
|
|
6268
6347
|
)
|
|
6269
6348
|
|
|
6270
|
-
def
|
|
6271
|
-
"""Get
|
|
6349
|
+
async def aget_messages_for_session(self, session_id: Optional[str] = None) -> List[Message]:
|
|
6350
|
+
"""Get messages for a session
|
|
6351
|
+
|
|
6352
|
+
Args:
|
|
6353
|
+
session_id: The session ID to get the messages for. If not provided, the current cached session ID is used.
|
|
6354
|
+
Returns:
|
|
6355
|
+
List[Message]: The messages for the session.
|
|
6356
|
+
"""
|
|
6357
|
+
session_id = session_id or self.session_id
|
|
6358
|
+
if session_id is None:
|
|
6359
|
+
log_warning("Session ID is not set, cannot get messages for session")
|
|
6360
|
+
return []
|
|
6361
|
+
|
|
6362
|
+
session = await self.aget_session(session_id=session_id)
|
|
6363
|
+
|
|
6364
|
+
if session is None:
|
|
6365
|
+
raise Exception("Session not found")
|
|
6366
|
+
|
|
6367
|
+
# Only filter by agent_id if this is part of a team
|
|
6368
|
+
return session.get_messages_from_last_n_runs(
|
|
6369
|
+
agent_id=self.id if self.team_id is not None else None,
|
|
6370
|
+
)
|
|
6371
|
+
|
|
6372
|
+
def get_session_summary(self, session_id: Optional[str] = None) -> Optional[SessionSummary]:
|
|
6373
|
+
"""Get the session summary for the given session ID and user ID
|
|
6374
|
+
|
|
6375
|
+
Args:
|
|
6376
|
+
session_id: The session ID to get the summary for. If not provided, the current cached session ID is used.
|
|
6377
|
+
Returns:
|
|
6378
|
+
SessionSummary: The session summary.
|
|
6379
|
+
"""
|
|
6272
6380
|
session_id = session_id if session_id is not None else self.session_id
|
|
6273
6381
|
if session_id is None:
|
|
6274
6382
|
raise ValueError("Session ID is required")
|
|
@@ -6280,8 +6388,33 @@ class Agent:
|
|
|
6280
6388
|
|
|
6281
6389
|
return session.get_session_summary()
|
|
6282
6390
|
|
|
6391
|
+
async def aget_session_summary(self, session_id: Optional[str] = None) -> Optional[SessionSummary]:
|
|
6392
|
+
"""Get the session summary for the given session ID and user ID.
|
|
6393
|
+
|
|
6394
|
+
Args:
|
|
6395
|
+
session_id: The session ID to get the summary for. If not provided, the current cached session ID is used.
|
|
6396
|
+
Returns:
|
|
6397
|
+
SessionSummary: The session summary.
|
|
6398
|
+
"""
|
|
6399
|
+
session_id = session_id if session_id is not None else self.session_id
|
|
6400
|
+
if session_id is None:
|
|
6401
|
+
raise ValueError("Session ID is required")
|
|
6402
|
+
|
|
6403
|
+
session = await self.aget_session(session_id=session_id)
|
|
6404
|
+
|
|
6405
|
+
if session is None:
|
|
6406
|
+
raise Exception(f"Session {session_id} not found")
|
|
6407
|
+
|
|
6408
|
+
return session.get_session_summary()
|
|
6409
|
+
|
|
6283
6410
|
def get_user_memories(self, user_id: Optional[str] = None) -> Optional[List[UserMemory]]:
|
|
6284
|
-
"""Get the user memories for the given user ID.
|
|
6411
|
+
"""Get the user memories for the given user ID.
|
|
6412
|
+
|
|
6413
|
+
Args:
|
|
6414
|
+
user_id: The user ID to get the memories for. If not provided, the current cached user ID is used.
|
|
6415
|
+
Returns:
|
|
6416
|
+
Optional[List[UserMemory]]: The user memories.
|
|
6417
|
+
"""
|
|
6285
6418
|
if self.memory_manager is None:
|
|
6286
6419
|
return None
|
|
6287
6420
|
user_id = user_id if user_id is not None else self.user_id
|
|
@@ -6291,7 +6424,13 @@ class Agent:
|
|
|
6291
6424
|
return self.memory_manager.get_user_memories(user_id=user_id)
|
|
6292
6425
|
|
|
6293
6426
|
async def aget_user_memories(self, user_id: Optional[str] = None) -> Optional[List[UserMemory]]:
|
|
6294
|
-
"""Get the user memories for the given user ID.
|
|
6427
|
+
"""Get the user memories for the given user ID.
|
|
6428
|
+
|
|
6429
|
+
Args:
|
|
6430
|
+
user_id: The user ID to get the memories for. If not provided, the current cached user ID is used.
|
|
6431
|
+
Returns:
|
|
6432
|
+
Optional[List[UserMemory]]: The user memories.
|
|
6433
|
+
"""
|
|
6295
6434
|
if self.memory_manager is None:
|
|
6296
6435
|
return None
|
|
6297
6436
|
user_id = user_id if user_id is not None else self.user_id
|
|
@@ -6301,19 +6440,28 @@ class Agent:
|
|
|
6301
6440
|
return await self.memory_manager.aget_user_memories(user_id=user_id)
|
|
6302
6441
|
|
|
6303
6442
|
def get_culture_knowledge(self) -> Optional[List[CulturalKnowledge]]:
|
|
6304
|
-
"""Get the cultural knowledge the agent has access to
|
|
6443
|
+
"""Get the cultural knowledge the agent has access to
|
|
6444
|
+
|
|
6445
|
+
Returns:
|
|
6446
|
+
Optional[List[CulturalKnowledge]]: The cultural knowledge.
|
|
6447
|
+
"""
|
|
6305
6448
|
if self.culture_manager is None:
|
|
6306
6449
|
return None
|
|
6307
6450
|
|
|
6308
6451
|
return self.culture_manager.get_all_knowledge()
|
|
6309
6452
|
|
|
6310
6453
|
async def aget_culture_knowledge(self) -> Optional[List[CulturalKnowledge]]:
|
|
6311
|
-
"""Get the cultural knowledge the agent has access to
|
|
6454
|
+
"""Get the cultural knowledge the agent has access to
|
|
6455
|
+
|
|
6456
|
+
Returns:
|
|
6457
|
+
Optional[List[CulturalKnowledge]]: The cultural knowledge.
|
|
6458
|
+
"""
|
|
6312
6459
|
if self.culture_manager is None:
|
|
6313
6460
|
return None
|
|
6314
6461
|
|
|
6315
6462
|
return await self.culture_manager.aget_all_knowledge()
|
|
6316
6463
|
|
|
6464
|
+
# -*- System & User Message Functions
|
|
6317
6465
|
def _format_message_with_state_variables(
|
|
6318
6466
|
self,
|
|
6319
6467
|
message: Any,
|
|
@@ -6358,6 +6506,7 @@ class Agent:
|
|
|
6358
6506
|
session: AgentSession,
|
|
6359
6507
|
session_state: Optional[Dict[str, Any]] = None,
|
|
6360
6508
|
user_id: Optional[str] = None,
|
|
6509
|
+
tools: Optional[List[Union[Function, dict]]] = None,
|
|
6361
6510
|
dependencies: Optional[Dict[str, Any]] = None,
|
|
6362
6511
|
metadata: Optional[Dict[str, Any]] = None,
|
|
6363
6512
|
add_session_state_to_context: Optional[bool] = None,
|
|
@@ -6429,7 +6578,7 @@ class Agent:
|
|
|
6429
6578
|
instructions.extend(_instructions)
|
|
6430
6579
|
|
|
6431
6580
|
# 3.1.1 Add instructions from the Model
|
|
6432
|
-
_model_instructions = self.model.get_instructions_for_model(
|
|
6581
|
+
_model_instructions = self.model.get_instructions_for_model(tools)
|
|
6433
6582
|
if _model_instructions is not None:
|
|
6434
6583
|
instructions.extend(_model_instructions)
|
|
6435
6584
|
|
|
@@ -6664,7 +6813,7 @@ class Agent:
|
|
|
6664
6813
|
)
|
|
6665
6814
|
|
|
6666
6815
|
# 3.3.12 Add the system message from the Model
|
|
6667
|
-
system_message_from_model = self.model.get_system_message_for_model(
|
|
6816
|
+
system_message_from_model = self.model.get_system_message_for_model(tools)
|
|
6668
6817
|
if system_message_from_model is not None:
|
|
6669
6818
|
system_message_content += system_message_from_model
|
|
6670
6819
|
|
|
@@ -6700,6 +6849,7 @@ class Agent:
|
|
|
6700
6849
|
session: AgentSession,
|
|
6701
6850
|
session_state: Optional[Dict[str, Any]] = None,
|
|
6702
6851
|
user_id: Optional[str] = None,
|
|
6852
|
+
tools: Optional[List[Union[Function, dict]]] = None,
|
|
6703
6853
|
dependencies: Optional[Dict[str, Any]] = None,
|
|
6704
6854
|
metadata: Optional[Dict[str, Any]] = None,
|
|
6705
6855
|
) -> Optional[Message]:
|
|
@@ -6770,7 +6920,7 @@ class Agent:
|
|
|
6770
6920
|
instructions.extend(_instructions)
|
|
6771
6921
|
|
|
6772
6922
|
# 3.1.1 Add instructions from the Model
|
|
6773
|
-
_model_instructions = self.model.get_instructions_for_model(
|
|
6923
|
+
_model_instructions = self.model.get_instructions_for_model(tools)
|
|
6774
6924
|
if _model_instructions is not None:
|
|
6775
6925
|
instructions.extend(_model_instructions)
|
|
6776
6926
|
|
|
@@ -7008,7 +7158,7 @@ class Agent:
|
|
|
7008
7158
|
)
|
|
7009
7159
|
|
|
7010
7160
|
# 3.3.12 Add the system message from the Model
|
|
7011
|
-
system_message_from_model = self.model.get_system_message_for_model(
|
|
7161
|
+
system_message_from_model = self.model.get_system_message_for_model(tools)
|
|
7012
7162
|
if system_message_from_model is not None:
|
|
7013
7163
|
system_message_content += system_message_from_model
|
|
7014
7164
|
|
|
@@ -7227,6 +7377,7 @@ class Agent:
|
|
|
7227
7377
|
add_dependencies_to_context: Optional[bool] = None,
|
|
7228
7378
|
add_session_state_to_context: Optional[bool] = None,
|
|
7229
7379
|
metadata: Optional[Dict[str, Any]] = None,
|
|
7380
|
+
tools: Optional[List[Union[Function, dict]]] = None,
|
|
7230
7381
|
**kwargs: Any,
|
|
7231
7382
|
) -> RunMessages:
|
|
7232
7383
|
"""This function returns a RunMessages object with the following attributes:
|
|
@@ -7261,6 +7412,7 @@ class Agent:
|
|
|
7261
7412
|
session=session,
|
|
7262
7413
|
session_state=session_state,
|
|
7263
7414
|
user_id=user_id,
|
|
7415
|
+
tools=tools,
|
|
7264
7416
|
dependencies=dependencies,
|
|
7265
7417
|
metadata=metadata,
|
|
7266
7418
|
add_session_state_to_context=add_session_state_to_context,
|
|
@@ -7321,6 +7473,10 @@ class Agent:
|
|
|
7321
7473
|
for _msg in history_copy:
|
|
7322
7474
|
_msg.from_history = True
|
|
7323
7475
|
|
|
7476
|
+
# Filter tool calls from history if limit is set (before adding to run_messages)
|
|
7477
|
+
if self.max_tool_calls_from_history is not None:
|
|
7478
|
+
filter_tool_calls(history_copy, self.max_tool_calls_from_history)
|
|
7479
|
+
|
|
7324
7480
|
log_debug(f"Adding {len(history_copy)} messages from history")
|
|
7325
7481
|
|
|
7326
7482
|
run_messages.messages += history_copy
|
|
@@ -7428,6 +7584,7 @@ class Agent:
|
|
|
7428
7584
|
add_dependencies_to_context: Optional[bool] = None,
|
|
7429
7585
|
add_session_state_to_context: Optional[bool] = None,
|
|
7430
7586
|
metadata: Optional[Dict[str, Any]] = None,
|
|
7587
|
+
tools: Optional[List[Union[Function, dict]]] = None,
|
|
7431
7588
|
**kwargs: Any,
|
|
7432
7589
|
) -> RunMessages:
|
|
7433
7590
|
"""This function returns a RunMessages object with the following attributes:
|
|
@@ -7462,6 +7619,7 @@ class Agent:
|
|
|
7462
7619
|
session=session,
|
|
7463
7620
|
session_state=session_state,
|
|
7464
7621
|
user_id=user_id,
|
|
7622
|
+
tools=tools,
|
|
7465
7623
|
dependencies=dependencies,
|
|
7466
7624
|
metadata=metadata,
|
|
7467
7625
|
)
|
|
@@ -7514,6 +7672,10 @@ class Agent:
|
|
|
7514
7672
|
for _msg in history_copy:
|
|
7515
7673
|
_msg.from_history = True
|
|
7516
7674
|
|
|
7675
|
+
# Filter tool calls from history if limit is set (before adding to run_messages)
|
|
7676
|
+
if self.max_tool_calls_from_history is not None:
|
|
7677
|
+
filter_tool_calls(history_copy, self.max_tool_calls_from_history)
|
|
7678
|
+
|
|
7517
7679
|
log_debug(f"Adding {len(history_copy)} messages from history")
|
|
7518
7680
|
|
|
7519
7681
|
run_messages.messages += history_copy
|
|
@@ -9325,6 +9487,8 @@ class Agent:
|
|
|
9325
9487
|
document_name = query.replace(" ", "_").replace("?", "").replace("!", "").replace(".", "")
|
|
9326
9488
|
document_content = json.dumps({"query": query, "result": result})
|
|
9327
9489
|
log_info(f"Adding document to Knowledge: {document_name}: {document_content}")
|
|
9490
|
+
import asyncio
|
|
9491
|
+
|
|
9328
9492
|
from agno.knowledge.reader.text_reader import TextReader
|
|
9329
9493
|
|
|
9330
9494
|
asyncio.run(
|
|
@@ -9399,12 +9563,14 @@ class Agent:
|
|
|
9399
9563
|
|
|
9400
9564
|
return get_previous_session_messages
|
|
9401
9565
|
|
|
9402
|
-
async def _aget_previous_sessions_messages_function(
|
|
9566
|
+
async def _aget_previous_sessions_messages_function(
|
|
9567
|
+
self, num_history_sessions: Optional[int] = 2, user_id: Optional[str] = None
|
|
9568
|
+
) -> Function:
|
|
9403
9569
|
"""Factory function to create a get_previous_session_messages function.
|
|
9404
9570
|
|
|
9405
9571
|
Args:
|
|
9406
9572
|
num_history_sessions: The last n sessions to be taken from db
|
|
9407
|
-
|
|
9573
|
+
user_id: The user ID to filter sessions by
|
|
9408
9574
|
Returns:
|
|
9409
9575
|
Callable: A function that retrieves messages from previous sessions
|
|
9410
9576
|
"""
|
|
@@ -9422,12 +9588,22 @@ class Agent:
|
|
|
9422
9588
|
if self.db is None:
|
|
9423
9589
|
return "Previous session messages not available"
|
|
9424
9590
|
|
|
9425
|
-
if
|
|
9426
|
-
selected_sessions = await self.db.get_sessions(
|
|
9427
|
-
session_type=SessionType.AGENT,
|
|
9591
|
+
if self._has_async_db():
|
|
9592
|
+
selected_sessions = await self.db.get_sessions( # type: ignore
|
|
9593
|
+
session_type=SessionType.AGENT,
|
|
9594
|
+
limit=num_history_sessions,
|
|
9595
|
+
user_id=user_id,
|
|
9596
|
+
sort_by="created_at",
|
|
9597
|
+
sort_order="desc",
|
|
9428
9598
|
)
|
|
9429
9599
|
else:
|
|
9430
|
-
selected_sessions = self.db.get_sessions(
|
|
9600
|
+
selected_sessions = self.db.get_sessions(
|
|
9601
|
+
session_type=SessionType.AGENT,
|
|
9602
|
+
limit=num_history_sessions,
|
|
9603
|
+
user_id=user_id,
|
|
9604
|
+
sort_by="created_at",
|
|
9605
|
+
sort_order="desc",
|
|
9606
|
+
)
|
|
9431
9607
|
|
|
9432
9608
|
all_messages = []
|
|
9433
9609
|
seen_message_pairs = set()
|
|
@@ -9460,7 +9636,7 @@ class Agent:
|
|
|
9460
9636
|
|
|
9461
9637
|
return json.dumps([msg.to_dict() for msg in all_messages]) if all_messages else "No history found"
|
|
9462
9638
|
|
|
9463
|
-
return aget_previous_session_messages
|
|
9639
|
+
return Function.from_callable(aget_previous_session_messages, name="get_previous_session_messages")
|
|
9464
9640
|
|
|
9465
9641
|
###########################################################################
|
|
9466
9642
|
# Print Response
|