camel-ai 0.2.76a0__py3-none-any.whl → 0.2.76a1__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.
Potentially problematic release.
This version of camel-ai might be problematic. Click here for more details.
- camel/__init__.py +1 -1
- camel/agents/chat_agent.py +8 -1
- camel/memories/__init__.py +2 -1
- camel/memories/agent_memories.py +3 -1
- camel/memories/blocks/chat_history_block.py +17 -2
- camel/societies/workforce/single_agent_worker.py +44 -38
- camel/storages/object_storages/google_cloud.py +1 -1
- camel/toolkits/__init__.py +9 -2
- camel/toolkits/aci_toolkit.py +45 -0
- camel/toolkits/context_summarizer_toolkit.py +683 -0
- camel/toolkits/{file_write_toolkit.py → file_toolkit.py} +194 -34
- camel/toolkits/hybrid_browser_toolkit/config_loader.py +4 -0
- camel/toolkits/hybrid_browser_toolkit/hybrid_browser_toolkit.py +7 -2
- camel/toolkits/hybrid_browser_toolkit/hybrid_browser_toolkit_ts.py +62 -45
- camel/toolkits/hybrid_browser_toolkit/ts/src/browser-session.ts +489 -60
- camel/toolkits/hybrid_browser_toolkit/ts/src/config-loader.ts +5 -2
- camel/toolkits/hybrid_browser_toolkit/ts/src/hybrid-browser-toolkit.ts +72 -12
- camel/toolkits/hybrid_browser_toolkit/ts/src/snapshot-parser.ts +2 -14
- camel/toolkits/hybrid_browser_toolkit/ts/src/types.ts +1 -0
- camel/toolkits/hybrid_browser_toolkit/ws_wrapper.py +196 -60
- camel/toolkits/hybrid_browser_toolkit_py/hybrid_browser_toolkit.py +4 -4
- camel/toolkits/markitdown_toolkit.py +27 -1
- camel/toolkits/note_taking_toolkit.py +18 -8
- camel/toolkits/vertex_ai_veo_toolkit.py +590 -0
- camel/toolkits/wechat_official_toolkit.py +483 -0
- camel/utils/context_utils.py +395 -0
- {camel_ai-0.2.76a0.dist-info → camel_ai-0.2.76a1.dist-info}/METADATA +2 -1
- {camel_ai-0.2.76a0.dist-info → camel_ai-0.2.76a1.dist-info}/RECORD +30 -26
- {camel_ai-0.2.76a0.dist-info → camel_ai-0.2.76a1.dist-info}/WHEEL +0 -0
- {camel_ai-0.2.76a0.dist-info → camel_ai-0.2.76a1.dist-info}/licenses/LICENSE +0 -0
camel/__init__.py
CHANGED
camel/agents/chat_agent.py
CHANGED
|
@@ -63,6 +63,7 @@ from camel.memories import (
|
|
|
63
63
|
MemoryRecord,
|
|
64
64
|
ScoreBasedContextCreator,
|
|
65
65
|
)
|
|
66
|
+
from camel.memories.blocks.chat_history_block import EmptyMemoryWarning
|
|
66
67
|
from camel.messages import (
|
|
67
68
|
BaseMessage,
|
|
68
69
|
FunctionCallingMessage,
|
|
@@ -841,7 +842,12 @@ class ChatAgent(BaseAgent):
|
|
|
841
842
|
current_tokens = token_counter.count_tokens_from_messages(
|
|
842
843
|
[message.to_openai_message(role)]
|
|
843
844
|
)
|
|
844
|
-
|
|
845
|
+
import warnings
|
|
846
|
+
|
|
847
|
+
with warnings.catch_warnings():
|
|
848
|
+
warnings.filterwarnings("ignore", category=EmptyMemoryWarning)
|
|
849
|
+
_, ctx_tokens = self.memory.get_context()
|
|
850
|
+
|
|
845
851
|
remaining_budget = max(0, token_limit - ctx_tokens)
|
|
846
852
|
|
|
847
853
|
if current_tokens <= remaining_budget:
|
|
@@ -1035,6 +1041,7 @@ class ChatAgent(BaseAgent):
|
|
|
1035
1041
|
None
|
|
1036
1042
|
"""
|
|
1037
1043
|
self.memory.clear()
|
|
1044
|
+
|
|
1038
1045
|
if self.system_message is not None:
|
|
1039
1046
|
self.update_memory(self.system_message, OpenAIBackendRole.SYSTEM)
|
|
1040
1047
|
|
camel/memories/__init__.py
CHANGED
|
@@ -18,7 +18,7 @@ from .agent_memories import (
|
|
|
18
18
|
VectorDBMemory,
|
|
19
19
|
)
|
|
20
20
|
from .base import AgentMemory, BaseContextCreator, MemoryBlock
|
|
21
|
-
from .blocks.chat_history_block import ChatHistoryBlock
|
|
21
|
+
from .blocks.chat_history_block import ChatHistoryBlock, EmptyMemoryWarning
|
|
22
22
|
from .blocks.vectordb_block import VectorDBBlock
|
|
23
23
|
from .context_creators.score_based import ScoreBasedContextCreator
|
|
24
24
|
from .records import ContextRecord, MemoryRecord
|
|
@@ -35,4 +35,5 @@ __all__ = [
|
|
|
35
35
|
'ChatHistoryBlock',
|
|
36
36
|
'VectorDBBlock',
|
|
37
37
|
'LongtermAgentMemory',
|
|
38
|
+
'EmptyMemoryWarning',
|
|
38
39
|
]
|
camel/memories/agent_memories.py
CHANGED
|
@@ -51,7 +51,9 @@ class ChatHistoryMemory(AgentMemory):
|
|
|
51
51
|
raise ValueError("`window_size` must be non-negative.")
|
|
52
52
|
self._context_creator = context_creator
|
|
53
53
|
self._window_size = window_size
|
|
54
|
-
self._chat_history_block = ChatHistoryBlock(
|
|
54
|
+
self._chat_history_block = ChatHistoryBlock(
|
|
55
|
+
storage=storage,
|
|
56
|
+
)
|
|
55
57
|
self._agent_id = agent_id
|
|
56
58
|
|
|
57
59
|
@property
|
|
@@ -21,6 +21,17 @@ from camel.storages.key_value_storages.in_memory import InMemoryKeyValueStorage
|
|
|
21
21
|
from camel.types import OpenAIBackendRole
|
|
22
22
|
|
|
23
23
|
|
|
24
|
+
class EmptyMemoryWarning(UserWarning):
|
|
25
|
+
"""Warning raised when attempting to access an empty memory.
|
|
26
|
+
|
|
27
|
+
This warning is raised when operations are performed on memory
|
|
28
|
+
that contains no records. It can be safely caught and suppressed
|
|
29
|
+
in contexts where empty memory is expected.
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
pass
|
|
33
|
+
|
|
34
|
+
|
|
24
35
|
class ChatHistoryBlock(MemoryBlock):
|
|
25
36
|
r"""An implementation of the :obj:`MemoryBlock` abstract base class for
|
|
26
37
|
maintaining a record of chat histories.
|
|
@@ -39,7 +50,7 @@ class ChatHistoryBlock(MemoryBlock):
|
|
|
39
50
|
last message is 1.0, and with each step taken backward, the score
|
|
40
51
|
of the message is multiplied by the `keep_rate`. Higher `keep_rate`
|
|
41
52
|
leads to high possibility to keep history messages during context
|
|
42
|
-
creation.
|
|
53
|
+
creation. (default: :obj:`0.9`)
|
|
43
54
|
"""
|
|
44
55
|
|
|
45
56
|
def __init__(
|
|
@@ -70,7 +81,11 @@ class ChatHistoryBlock(MemoryBlock):
|
|
|
70
81
|
"""
|
|
71
82
|
record_dicts = self.storage.load()
|
|
72
83
|
if len(record_dicts) == 0:
|
|
73
|
-
warnings.warn(
|
|
84
|
+
warnings.warn(
|
|
85
|
+
"The `ChatHistoryMemory` is empty.",
|
|
86
|
+
EmptyMemoryWarning,
|
|
87
|
+
stacklevel=1,
|
|
88
|
+
)
|
|
74
89
|
return list()
|
|
75
90
|
|
|
76
91
|
if window_size is not None and window_size >= 0:
|
|
@@ -35,8 +35,8 @@ from camel.tasks.task import Task, TaskState, is_task_result_insufficient
|
|
|
35
35
|
class AgentPool:
|
|
36
36
|
r"""A pool of agent instances for efficient reuse.
|
|
37
37
|
|
|
38
|
-
This pool manages a collection of pre-cloned agents
|
|
39
|
-
|
|
38
|
+
This pool manages a collection of pre-cloned agents with automatic
|
|
39
|
+
scaling and idle timeout cleanup.
|
|
40
40
|
|
|
41
41
|
Args:
|
|
42
42
|
base_agent (ChatAgent): The base agent to clone from.
|
|
@@ -48,6 +48,8 @@ class AgentPool:
|
|
|
48
48
|
(default: :obj:`True`)
|
|
49
49
|
idle_timeout (float): Time in seconds after which idle agents are
|
|
50
50
|
removed. (default: :obj:`180.0`)
|
|
51
|
+
cleanup_interval (float): Fixed interval in seconds between cleanup
|
|
52
|
+
checks. (default: :obj:`60.0`)
|
|
51
53
|
"""
|
|
52
54
|
|
|
53
55
|
def __init__(
|
|
@@ -56,12 +58,14 @@ class AgentPool:
|
|
|
56
58
|
initial_size: int = 1,
|
|
57
59
|
max_size: int = 10,
|
|
58
60
|
auto_scale: bool = True,
|
|
59
|
-
idle_timeout: float = 180.0,
|
|
61
|
+
idle_timeout: float = 180.0,
|
|
62
|
+
cleanup_interval: float = 60.0,
|
|
60
63
|
):
|
|
61
64
|
self.base_agent = base_agent
|
|
62
65
|
self.max_size = max_size
|
|
63
66
|
self.auto_scale = auto_scale
|
|
64
67
|
self.idle_timeout = idle_timeout
|
|
68
|
+
self.cleanup_interval = cleanup_interval
|
|
65
69
|
|
|
66
70
|
# Pool management
|
|
67
71
|
self._available_agents: deque = deque()
|
|
@@ -73,6 +77,7 @@ class AgentPool:
|
|
|
73
77
|
self._total_borrows = 0
|
|
74
78
|
self._total_clones_created = 0
|
|
75
79
|
self._pool_hits = 0
|
|
80
|
+
self._agents_cleaned = 0
|
|
76
81
|
|
|
77
82
|
# Initialize pool
|
|
78
83
|
self._initialize_pool(initial_size)
|
|
@@ -82,6 +87,7 @@ class AgentPool:
|
|
|
82
87
|
for _ in range(min(size, self.max_size)):
|
|
83
88
|
agent = self._create_fresh_agent()
|
|
84
89
|
self._available_agents.append(agent)
|
|
90
|
+
self._agent_last_used[id(agent)] = time.time()
|
|
85
91
|
|
|
86
92
|
def _create_fresh_agent(self) -> ChatAgent:
|
|
87
93
|
r"""Create a fresh agent instance."""
|
|
@@ -94,53 +100,46 @@ class AgentPool:
|
|
|
94
100
|
async with self._lock:
|
|
95
101
|
self._total_borrows += 1
|
|
96
102
|
|
|
97
|
-
# Try to get from available agents first
|
|
98
103
|
if self._available_agents:
|
|
99
104
|
agent = self._available_agents.popleft()
|
|
100
105
|
self._in_use_agents.add(id(agent))
|
|
101
106
|
self._pool_hits += 1
|
|
102
|
-
|
|
103
|
-
# Reset the agent state
|
|
104
|
-
agent.reset()
|
|
105
107
|
return agent
|
|
106
108
|
|
|
107
|
-
# Check if we can create new
|
|
108
|
-
|
|
109
|
-
self._in_use_agents
|
|
110
|
-
)
|
|
111
|
-
if total_agents < self.max_size:
|
|
109
|
+
# Check if we can create a new agent
|
|
110
|
+
if len(self._in_use_agents) < self.max_size or self.auto_scale:
|
|
112
111
|
agent = self._create_fresh_agent()
|
|
113
112
|
self._in_use_agents.add(id(agent))
|
|
114
113
|
return agent
|
|
115
114
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
agent = self._available_agents.popleft()
|
|
126
|
-
self._in_use_agents.add(id(agent))
|
|
127
|
-
agent.reset()
|
|
128
|
-
return agent
|
|
115
|
+
# Wait for available agent
|
|
116
|
+
while True:
|
|
117
|
+
async with self._lock:
|
|
118
|
+
if self._available_agents:
|
|
119
|
+
agent = self._available_agents.popleft()
|
|
120
|
+
self._in_use_agents.add(id(agent))
|
|
121
|
+
self._pool_hits += 1
|
|
122
|
+
return agent
|
|
123
|
+
await asyncio.sleep(0.05)
|
|
129
124
|
|
|
130
125
|
async def return_agent(self, agent: ChatAgent) -> None:
|
|
131
126
|
r"""Return an agent to the pool."""
|
|
127
|
+
agent_id = id(agent)
|
|
128
|
+
|
|
132
129
|
async with self._lock:
|
|
133
|
-
agent_id
|
|
130
|
+
if agent_id not in self._in_use_agents:
|
|
131
|
+
return
|
|
134
132
|
|
|
135
|
-
|
|
136
|
-
self._in_use_agents.remove(agent_id)
|
|
133
|
+
self._in_use_agents.discard(agent_id)
|
|
137
134
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
135
|
+
# Only add back to pool if under max size
|
|
136
|
+
if len(self._available_agents) < self.max_size:
|
|
137
|
+
agent.reset()
|
|
138
|
+
self._agent_last_used[agent_id] = time.time()
|
|
139
|
+
self._available_agents.append(agent)
|
|
140
|
+
else:
|
|
141
|
+
# Remove tracking for agents not returned to pool
|
|
142
|
+
self._agent_last_used.pop(agent_id, None)
|
|
144
143
|
|
|
145
144
|
async def cleanup_idle_agents(self) -> None:
|
|
146
145
|
r"""Remove idle agents from the pool to free memory."""
|
|
@@ -148,30 +147,35 @@ class AgentPool:
|
|
|
148
147
|
return
|
|
149
148
|
|
|
150
149
|
async with self._lock:
|
|
150
|
+
if not self._available_agents:
|
|
151
|
+
return
|
|
152
|
+
|
|
151
153
|
current_time = time.time()
|
|
152
154
|
agents_to_remove = []
|
|
153
155
|
|
|
154
156
|
for agent in list(self._available_agents):
|
|
155
157
|
agent_id = id(agent)
|
|
156
158
|
last_used = self._agent_last_used.get(agent_id, current_time)
|
|
157
|
-
|
|
158
159
|
if current_time - last_used > self.idle_timeout:
|
|
159
160
|
agents_to_remove.append(agent)
|
|
160
161
|
|
|
161
162
|
for agent in agents_to_remove:
|
|
162
163
|
self._available_agents.remove(agent)
|
|
163
|
-
|
|
164
|
-
self.
|
|
164
|
+
self._agent_last_used.pop(id(agent), None)
|
|
165
|
+
self._agents_cleaned += 1
|
|
165
166
|
|
|
166
167
|
def get_stats(self) -> dict:
|
|
167
168
|
r"""Get pool statistics."""
|
|
168
169
|
return {
|
|
169
170
|
"available_agents": len(self._available_agents),
|
|
170
171
|
"in_use_agents": len(self._in_use_agents),
|
|
172
|
+
"pool_size": len(self._available_agents)
|
|
173
|
+
+ len(self._in_use_agents),
|
|
171
174
|
"total_borrows": self._total_borrows,
|
|
172
175
|
"total_clones_created": self._total_clones_created,
|
|
173
176
|
"pool_hits": self._pool_hits,
|
|
174
177
|
"hit_rate": self._pool_hits / max(self._total_borrows, 1),
|
|
178
|
+
"agents_cleaned_up": self._agents_cleaned,
|
|
175
179
|
}
|
|
176
180
|
|
|
177
181
|
|
|
@@ -477,7 +481,9 @@ class SingleAgentWorker(Worker):
|
|
|
477
481
|
r"""Periodically clean up idle agents from the pool."""
|
|
478
482
|
while True:
|
|
479
483
|
try:
|
|
480
|
-
|
|
484
|
+
# Fixed interval cleanup
|
|
485
|
+
await asyncio.sleep(self.agent_pool.cleanup_interval)
|
|
486
|
+
|
|
481
487
|
if self.agent_pool:
|
|
482
488
|
await self.agent_pool.cleanup_idle_agents()
|
|
483
489
|
except asyncio.CancelledError:
|
|
@@ -46,7 +46,7 @@ class GoogleCloudStorage(BaseObjectStorage):
|
|
|
46
46
|
create_if_not_exists: bool = True,
|
|
47
47
|
anonymous: bool = False,
|
|
48
48
|
) -> None:
|
|
49
|
-
from google.cloud import storage
|
|
49
|
+
from google.cloud import storage # type: ignore[attr-defined]
|
|
50
50
|
|
|
51
51
|
self.create_if_not_exists = create_if_not_exists
|
|
52
52
|
|
camel/toolkits/__init__.py
CHANGED
|
@@ -40,6 +40,7 @@ from .google_calendar_toolkit import GoogleCalendarToolkit
|
|
|
40
40
|
from .arxiv_toolkit import ArxivToolkit
|
|
41
41
|
from .slack_toolkit import SlackToolkit
|
|
42
42
|
from .whatsapp_toolkit import WhatsAppToolkit
|
|
43
|
+
from .wechat_official_toolkit import WeChatOfficialToolkit
|
|
43
44
|
from .twitter_toolkit import TwitterToolkit
|
|
44
45
|
from .open_api_toolkit import OpenAPIToolkit
|
|
45
46
|
from .retrieval_toolkit import RetrievalToolkit
|
|
@@ -61,7 +62,7 @@ from .image_analysis_toolkit import ImageAnalysisToolkit
|
|
|
61
62
|
from .mcp_toolkit import MCPToolkit
|
|
62
63
|
from .browser_toolkit import BrowserToolkit
|
|
63
64
|
from .async_browser_toolkit import AsyncBrowserToolkit
|
|
64
|
-
from .
|
|
65
|
+
from .file_toolkit import FileToolkit, FileWriteToolkit
|
|
65
66
|
from .pptx_toolkit import PPTXToolkit
|
|
66
67
|
from .terminal_toolkit import TerminalToolkit
|
|
67
68
|
from .pubmed_toolkit import PubMedToolkit
|
|
@@ -87,7 +88,9 @@ from .message_agent_toolkit import AgentCommunicationToolkit
|
|
|
87
88
|
from .web_deploy_toolkit import WebDeployToolkit
|
|
88
89
|
from .screenshot_toolkit import ScreenshotToolkit
|
|
89
90
|
from .message_integration import ToolkitMessageIntegration
|
|
91
|
+
from .context_summarizer_toolkit import ContextSummarizerToolkit
|
|
90
92
|
from .notion_mcp_toolkit import NotionMCPToolkit
|
|
93
|
+
from .vertex_ai_veo_toolkit import VertexAIVeoToolkit
|
|
91
94
|
from .minimax_mcp_toolkit import MinimaxMCPToolkit
|
|
92
95
|
|
|
93
96
|
__all__ = [
|
|
@@ -103,6 +106,7 @@ __all__ = [
|
|
|
103
106
|
'SearchToolkit',
|
|
104
107
|
'SlackToolkit',
|
|
105
108
|
'WhatsAppToolkit',
|
|
109
|
+
'WeChatOfficialToolkit',
|
|
106
110
|
'ImageGenToolkit',
|
|
107
111
|
'TwitterToolkit',
|
|
108
112
|
'WeatherToolkit',
|
|
@@ -136,7 +140,8 @@ __all__ = [
|
|
|
136
140
|
'ImageAnalysisToolkit',
|
|
137
141
|
'BrowserToolkit',
|
|
138
142
|
'AsyncBrowserToolkit',
|
|
139
|
-
'
|
|
143
|
+
'FileToolkit',
|
|
144
|
+
'FileWriteToolkit', # Deprecated, use FileToolkit instead
|
|
140
145
|
'PPTXToolkit',
|
|
141
146
|
'TerminalToolkit',
|
|
142
147
|
'PubMedToolkit',
|
|
@@ -165,6 +170,8 @@ __all__ = [
|
|
|
165
170
|
'ScreenshotToolkit',
|
|
166
171
|
'RegisteredAgentToolkit',
|
|
167
172
|
'ToolkitMessageIntegration',
|
|
173
|
+
'ContextSummarizerToolkit',
|
|
168
174
|
'NotionMCPToolkit',
|
|
175
|
+
'VertexAIVeoToolkit',
|
|
169
176
|
'MinimaxMCPToolkit',
|
|
170
177
|
]
|
camel/toolkits/aci_toolkit.py
CHANGED
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
# limitations under the License.
|
|
13
13
|
# ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
|
|
14
14
|
|
|
15
|
+
import asyncio
|
|
15
16
|
import os
|
|
16
17
|
from typing import TYPE_CHECKING, Dict, List, Optional, Union
|
|
17
18
|
|
|
@@ -408,6 +409,38 @@ class ACIToolkit(BaseToolkit):
|
|
|
408
409
|
)
|
|
409
410
|
return result
|
|
410
411
|
|
|
412
|
+
async def aexecute_function(
|
|
413
|
+
self,
|
|
414
|
+
function_name: str,
|
|
415
|
+
function_arguments: Dict,
|
|
416
|
+
linked_account_owner_id: str,
|
|
417
|
+
allowed_apps_only: bool = False,
|
|
418
|
+
) -> Dict:
|
|
419
|
+
r"""Execute a function call asynchronously.
|
|
420
|
+
|
|
421
|
+
Args:
|
|
422
|
+
function_name (str): Name of the function to execute.
|
|
423
|
+
function_arguments (Dict): Arguments to pass to the function.
|
|
424
|
+
linked_account_owner_id (str): To specify the end-user (account
|
|
425
|
+
owner) on behalf of whom you want to execute functions
|
|
426
|
+
You need to first link corresponding account with the same
|
|
427
|
+
owner id in the ACI dashboard (https://platform.aci.dev).
|
|
428
|
+
allowed_apps_only (bool): If true, only returns functions/apps
|
|
429
|
+
that are allowed to be used by the agent/accessor, identified
|
|
430
|
+
by the api key. (default: :obj:`False`)
|
|
431
|
+
|
|
432
|
+
Returns:
|
|
433
|
+
Dict: Result of the function execution
|
|
434
|
+
"""
|
|
435
|
+
result = await asyncio.to_thread(
|
|
436
|
+
self.client.handle_function_call,
|
|
437
|
+
function_name,
|
|
438
|
+
function_arguments,
|
|
439
|
+
linked_account_owner_id,
|
|
440
|
+
allowed_apps_only,
|
|
441
|
+
)
|
|
442
|
+
return result
|
|
443
|
+
|
|
411
444
|
def get_tools(self) -> List[FunctionTool]:
|
|
412
445
|
r"""Get a list of tools (functions) available in the configured apps.
|
|
413
446
|
|
|
@@ -434,6 +467,8 @@ class ACIToolkit(BaseToolkit):
|
|
|
434
467
|
FunctionTool(self.delete_linked_account),
|
|
435
468
|
FunctionTool(self.function_definition),
|
|
436
469
|
FunctionTool(self.search_function),
|
|
470
|
+
FunctionTool(self.execute_function),
|
|
471
|
+
FunctionTool(self.aexecute_function),
|
|
437
472
|
]
|
|
438
473
|
|
|
439
474
|
for function in _all_function:
|
|
@@ -448,6 +483,16 @@ class ACIToolkit(BaseToolkit):
|
|
|
448
483
|
linked_account_owner_id=self.linked_account_owner_id,
|
|
449
484
|
)
|
|
450
485
|
|
|
486
|
+
async def async_dummy_func(*, schema=schema, **kwargs):
|
|
487
|
+
return await self.aexecute_function(
|
|
488
|
+
function_name=schema['function']['name'],
|
|
489
|
+
function_arguments=kwargs,
|
|
490
|
+
linked_account_owner_id=self.linked_account_owner_id,
|
|
491
|
+
)
|
|
492
|
+
|
|
493
|
+
# Add async_call method to the sync function for compatibility
|
|
494
|
+
dummy_func.async_call = async_dummy_func # type: ignore[attr-defined]
|
|
495
|
+
|
|
451
496
|
tool = FunctionTool(
|
|
452
497
|
func=dummy_func,
|
|
453
498
|
openai_tool_schema=schema,
|