AstrBot 4.13.2__py3-none-any.whl → 4.14.0__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.
- astrbot/builtin_stars/astrbot/main.py +0 -5
- astrbot/cli/__init__.py +1 -1
- astrbot/core/agent/agent.py +2 -1
- astrbot/core/agent/handoff.py +14 -1
- astrbot/core/agent/runners/tool_loop_agent_runner.py +14 -1
- astrbot/core/agent/tool.py +5 -0
- astrbot/core/astr_agent_run_util.py +21 -3
- astrbot/core/astr_agent_tool_exec.py +178 -3
- astrbot/core/astr_main_agent.py +980 -0
- astrbot/core/astr_main_agent_resources.py +453 -0
- astrbot/core/computer/computer_client.py +10 -1
- astrbot/core/computer/tools/fs.py +22 -14
- astrbot/core/config/default.py +84 -58
- astrbot/core/core_lifecycle.py +43 -1
- astrbot/core/cron/__init__.py +3 -0
- astrbot/core/cron/events.py +67 -0
- astrbot/core/cron/manager.py +376 -0
- astrbot/core/db/__init__.py +60 -0
- astrbot/core/db/po.py +31 -0
- astrbot/core/db/sqlite.py +120 -0
- astrbot/core/event_bus.py +0 -1
- astrbot/core/message/message_event_result.py +21 -3
- astrbot/core/pipeline/process_stage/method/agent_sub_stages/internal.py +111 -580
- astrbot/core/pipeline/scheduler.py +0 -2
- astrbot/core/platform/astr_message_event.py +0 -3
- astrbot/core/platform/platform.py +9 -0
- astrbot/core/platform/platform_metadata.py +2 -0
- astrbot/core/platform/sources/dingtalk/dingtalk_adapter.py +1 -0
- astrbot/core/platform/sources/qqofficial/qqofficial_platform_adapter.py +1 -0
- astrbot/core/platform/sources/qqofficial_webhook/qo_webhook_adapter.py +1 -0
- astrbot/core/platform/sources/webchat/webchat_adapter.py +1 -0
- astrbot/core/platform/sources/wecom/wecom_adapter.py +1 -0
- astrbot/core/platform/sources/wecom_ai_bot/wecomai_adapter.py +1 -0
- astrbot/core/platform/sources/weixin_official_account/weixin_offacc_adapter.py +1 -0
- astrbot/core/provider/entities.py +1 -1
- astrbot/core/skills/skill_manager.py +9 -8
- astrbot/core/star/context.py +8 -0
- astrbot/core/star/filter/custom_filter.py +3 -3
- astrbot/core/star/register/star_handler.py +1 -1
- astrbot/core/subagent_orchestrator.py +96 -0
- astrbot/core/tools/cron_tools.py +174 -0
- astrbot/core/utils/history_saver.py +31 -0
- astrbot/core/utils/trace.py +4 -0
- astrbot/dashboard/routes/__init__.py +4 -0
- astrbot/dashboard/routes/cron.py +174 -0
- astrbot/dashboard/routes/log.py +36 -0
- astrbot/dashboard/routes/plugin.py +11 -0
- astrbot/dashboard/routes/skills.py +12 -37
- astrbot/dashboard/routes/subagent.py +117 -0
- astrbot/dashboard/routes/tools.py +41 -14
- astrbot/dashboard/server.py +3 -0
- {astrbot-4.13.2.dist-info → astrbot-4.14.0.dist-info}/METADATA +21 -2
- {astrbot-4.13.2.dist-info → astrbot-4.14.0.dist-info}/RECORD +56 -50
- astrbot/builtin_stars/astrbot/process_llm_request.py +0 -308
- astrbot/builtin_stars/reminder/main.py +0 -266
- astrbot/builtin_stars/reminder/metadata.yaml +0 -4
- astrbot/core/pipeline/process_stage/utils.py +0 -219
- {astrbot-4.13.2.dist-info → astrbot-4.14.0.dist-info}/WHEEL +0 -0
- {astrbot-4.13.2.dist-info → astrbot-4.14.0.dist-info}/entry_points.txt +0 -0
- {astrbot-4.13.2.dist-info → astrbot-4.14.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -7,7 +7,6 @@ from astrbot.api.provider import LLMResponse, ProviderRequest
|
|
|
7
7
|
from astrbot.core import logger
|
|
8
8
|
|
|
9
9
|
from .long_term_memory import LongTermMemory
|
|
10
|
-
from .process_llm_request import ProcessLLMRequest
|
|
11
10
|
|
|
12
11
|
|
|
13
12
|
class Main(star.Star):
|
|
@@ -19,8 +18,6 @@ class Main(star.Star):
|
|
|
19
18
|
except BaseException as e:
|
|
20
19
|
logger.error(f"聊天增强 err: {e}")
|
|
21
20
|
|
|
22
|
-
self.proc_llm_req = ProcessLLMRequest(self.context)
|
|
23
|
-
|
|
24
21
|
def ltm_enabled(self, event: AstrMessageEvent):
|
|
25
22
|
ltmse = self.context.get_config(umo=event.unified_msg_origin)[
|
|
26
23
|
"provider_ltm_settings"
|
|
@@ -91,8 +88,6 @@ class Main(star.Star):
|
|
|
91
88
|
@filter.on_llm_request()
|
|
92
89
|
async def decorate_llm_req(self, event: AstrMessageEvent, req: ProviderRequest):
|
|
93
90
|
"""在请求 LLM 前注入人格信息、Identifier、时间、回复内容等 System Prompt"""
|
|
94
|
-
await self.proc_llm_req.process_llm_request(event, req)
|
|
95
|
-
|
|
96
91
|
if self.ltm and self.ltm_enabled(event):
|
|
97
92
|
try:
|
|
98
93
|
await self.ltm.on_req_llm(event, req)
|
astrbot/cli/__init__.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = "4.
|
|
1
|
+
__version__ = "4.14.0"
|
astrbot/core/agent/agent.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
from dataclasses import dataclass
|
|
2
|
-
from typing import Generic
|
|
2
|
+
from typing import Any, Generic
|
|
3
3
|
|
|
4
4
|
from .hooks import BaseAgentRunHooks
|
|
5
5
|
from .run_context import TContext
|
|
@@ -12,3 +12,4 @@ class Agent(Generic[TContext]):
|
|
|
12
12
|
instructions: str | None = None
|
|
13
13
|
tools: list[str | FunctionTool] | None = None
|
|
14
14
|
run_hooks: BaseAgentRunHooks[TContext] | None = None
|
|
15
|
+
begin_dialogs: list[Any] | None = None
|
astrbot/core/agent/handoff.py
CHANGED
|
@@ -12,16 +12,29 @@ class HandoffTool(FunctionTool, Generic[TContext]):
|
|
|
12
12
|
self,
|
|
13
13
|
agent: Agent[TContext],
|
|
14
14
|
parameters: dict | None = None,
|
|
15
|
+
tool_description: str | None = None,
|
|
15
16
|
**kwargs,
|
|
16
17
|
):
|
|
17
18
|
self.agent = agent
|
|
19
|
+
|
|
20
|
+
# Avoid passing duplicate `description` to the FunctionTool dataclass.
|
|
21
|
+
# Some call sites (e.g. SubAgentOrchestrator) pass `description` via kwargs
|
|
22
|
+
# to override what the main agent sees, while we also compute a default
|
|
23
|
+
# description here.
|
|
24
|
+
# `tool_description` is the public description shown to the main LLM.
|
|
25
|
+
# Keep a separate kwarg to avoid conflicting with FunctionTool's `description`.
|
|
26
|
+
description = tool_description or self.default_description(agent.name)
|
|
18
27
|
super().__init__(
|
|
19
28
|
name=f"transfer_to_{agent.name}",
|
|
20
29
|
parameters=parameters or self.default_parameters(),
|
|
21
|
-
description=
|
|
30
|
+
description=description,
|
|
22
31
|
**kwargs,
|
|
23
32
|
)
|
|
24
33
|
|
|
34
|
+
# Optional provider override for this subagent. When set, the handoff
|
|
35
|
+
# execution will use this chat provider id instead of the global/default.
|
|
36
|
+
self.provider_id: str | None = None
|
|
37
|
+
|
|
25
38
|
def default_parameters(self) -> dict:
|
|
26
39
|
return {
|
|
27
40
|
"type": "object",
|
|
@@ -111,10 +111,12 @@ class ToolLoopAgentRunner(BaseAgentRunner[TContext]):
|
|
|
111
111
|
# See #4681
|
|
112
112
|
self.tool_schema_mode = tool_schema_mode
|
|
113
113
|
self._tool_schema_param_set = None
|
|
114
|
+
self._skill_like_raw_tool_set = None
|
|
114
115
|
if tool_schema_mode == "skills_like":
|
|
115
116
|
tool_set = self.req.func_tool
|
|
116
117
|
if not tool_set:
|
|
117
118
|
return
|
|
119
|
+
self._skill_like_raw_tool_set = tool_set
|
|
118
120
|
light_set = tool_set.get_light_tool_set()
|
|
119
121
|
self._tool_schema_param_set = tool_set.get_param_only_tool_set()
|
|
120
122
|
# MODIFIE the req.func_tool to use light tool schemas
|
|
@@ -379,7 +381,17 @@ class ToolLoopAgentRunner(BaseAgentRunner[TContext]):
|
|
|
379
381
|
try:
|
|
380
382
|
if not req.func_tool:
|
|
381
383
|
return
|
|
382
|
-
|
|
384
|
+
|
|
385
|
+
if (
|
|
386
|
+
self.tool_schema_mode == "skills_like"
|
|
387
|
+
and self._skill_like_raw_tool_set
|
|
388
|
+
):
|
|
389
|
+
# in 'skills_like' mode, raw.func_tool is light schema, does not have handler
|
|
390
|
+
# so we need to get the tool from the raw tool set
|
|
391
|
+
func_tool = self._skill_like_raw_tool_set.get_tool(func_tool_name)
|
|
392
|
+
else:
|
|
393
|
+
func_tool = req.func_tool.get_tool(func_tool_name)
|
|
394
|
+
|
|
383
395
|
logger.info(f"使用工具:{func_tool_name},参数:{func_tool_args}")
|
|
384
396
|
|
|
385
397
|
if not func_tool:
|
|
@@ -557,6 +569,7 @@ class ToolLoopAgentRunner(BaseAgentRunner[TContext]):
|
|
|
557
569
|
)
|
|
558
570
|
],
|
|
559
571
|
)
|
|
572
|
+
logger.info(f"Tool `{func_tool_name}` Result: {last_tcr_content}")
|
|
560
573
|
|
|
561
574
|
# 处理函数调用响应
|
|
562
575
|
if tool_call_result_blocks:
|
astrbot/core/agent/tool.py
CHANGED
|
@@ -58,6 +58,11 @@ class FunctionTool(ToolSchema, Generic[TContext]):
|
|
|
58
58
|
Whether the tool is active. This field is a special field for AstrBot.
|
|
59
59
|
You can ignore it when integrating with other frameworks.
|
|
60
60
|
"""
|
|
61
|
+
is_background_task: bool = False
|
|
62
|
+
"""
|
|
63
|
+
Declare this tool as a background task. Background tasks return immediately
|
|
64
|
+
with a task identifier while the real work continues asynchronously.
|
|
65
|
+
"""
|
|
61
66
|
|
|
62
67
|
def __repr__(self):
|
|
63
68
|
return f"FuncTool(name={self.name}, parameters={self.parameters}, description={self.description})"
|
|
@@ -54,6 +54,14 @@ async def run_agent(
|
|
|
54
54
|
return
|
|
55
55
|
if resp.type == "tool_call_result":
|
|
56
56
|
msg_chain = resp.data["chain"]
|
|
57
|
+
|
|
58
|
+
astr_event.trace.record(
|
|
59
|
+
"agent_tool_result",
|
|
60
|
+
tool_result=msg_chain.get_plain_text(
|
|
61
|
+
with_other_comps_mark=True
|
|
62
|
+
),
|
|
63
|
+
)
|
|
64
|
+
|
|
57
65
|
if msg_chain.type == "tool_direct_result":
|
|
58
66
|
# tool_direct_result 用于标记 llm tool 需要直接发送给用户的内容
|
|
59
67
|
await astr_event.send(msg_chain)
|
|
@@ -67,12 +75,22 @@ async def run_agent(
|
|
|
67
75
|
# 用来标记流式响应需要分节
|
|
68
76
|
yield MessageChain(chain=[], type="break")
|
|
69
77
|
|
|
78
|
+
tool_info = None
|
|
79
|
+
|
|
80
|
+
if resp.data["chain"].chain:
|
|
81
|
+
json_comp = resp.data["chain"].chain[0]
|
|
82
|
+
if isinstance(json_comp, Json):
|
|
83
|
+
tool_info = json_comp.data
|
|
84
|
+
astr_event.trace.record(
|
|
85
|
+
"agent_tool_call",
|
|
86
|
+
tool_name=tool_info if tool_info else "unknown",
|
|
87
|
+
)
|
|
88
|
+
|
|
70
89
|
if astr_event.get_platform_name() == "webchat":
|
|
71
90
|
await astr_event.send(resp.data["chain"])
|
|
72
91
|
elif show_tool_use:
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
m = f"🔨 调用工具: {json_comp.data.get('name')}"
|
|
92
|
+
if tool_info:
|
|
93
|
+
m = f"🔨 调用工具: {tool_info.get('name', 'unknown')}"
|
|
76
94
|
else:
|
|
77
95
|
m = "🔨 调用工具..."
|
|
78
96
|
chain = MessageChain(type="tool_call").message(m)
|
|
@@ -1,23 +1,34 @@
|
|
|
1
1
|
import asyncio
|
|
2
2
|
import inspect
|
|
3
|
+
import json
|
|
3
4
|
import traceback
|
|
4
5
|
import typing as T
|
|
6
|
+
import uuid
|
|
5
7
|
|
|
6
8
|
import mcp
|
|
7
9
|
|
|
8
10
|
from astrbot import logger
|
|
9
11
|
from astrbot.core.agent.handoff import HandoffTool
|
|
10
12
|
from astrbot.core.agent.mcp_client import MCPTool
|
|
13
|
+
from astrbot.core.agent.message import Message
|
|
11
14
|
from astrbot.core.agent.run_context import ContextWrapper
|
|
12
15
|
from astrbot.core.agent.tool import FunctionTool, ToolSet
|
|
13
16
|
from astrbot.core.agent.tool_executor import BaseFunctionToolExecutor
|
|
14
17
|
from astrbot.core.astr_agent_context import AstrAgentContext
|
|
18
|
+
from astrbot.core.astr_main_agent_resources import (
|
|
19
|
+
BACKGROUND_TASK_RESULT_WOKE_SYSTEM_PROMPT,
|
|
20
|
+
SEND_MESSAGE_TO_USER_TOOL,
|
|
21
|
+
)
|
|
22
|
+
from astrbot.core.cron.events import CronMessageEvent
|
|
15
23
|
from astrbot.core.message.message_event_result import (
|
|
16
24
|
CommandResult,
|
|
17
25
|
MessageChain,
|
|
18
26
|
MessageEventResult,
|
|
19
27
|
)
|
|
28
|
+
from astrbot.core.platform.message_session import MessageSession
|
|
29
|
+
from astrbot.core.provider.entites import ProviderRequest
|
|
20
30
|
from astrbot.core.provider.register import llm_tools
|
|
31
|
+
from astrbot.core.utils.history_saver import persist_agent_history
|
|
21
32
|
|
|
22
33
|
|
|
23
34
|
class FunctionToolExecutor(BaseFunctionToolExecutor[AstrAgentContext]):
|
|
@@ -43,6 +54,31 @@ class FunctionToolExecutor(BaseFunctionToolExecutor[AstrAgentContext]):
|
|
|
43
54
|
yield r
|
|
44
55
|
return
|
|
45
56
|
|
|
57
|
+
elif tool.is_background_task:
|
|
58
|
+
task_id = uuid.uuid4().hex
|
|
59
|
+
|
|
60
|
+
async def _run_in_background():
|
|
61
|
+
try:
|
|
62
|
+
await cls._execute_background(
|
|
63
|
+
tool=tool,
|
|
64
|
+
run_context=run_context,
|
|
65
|
+
task_id=task_id,
|
|
66
|
+
**tool_args,
|
|
67
|
+
)
|
|
68
|
+
except Exception as e: # noqa: BLE001
|
|
69
|
+
logger.error(
|
|
70
|
+
f"Background task {task_id} failed: {e!s}",
|
|
71
|
+
exc_info=True,
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
asyncio.create_task(_run_in_background())
|
|
75
|
+
text_content = mcp.types.TextContent(
|
|
76
|
+
type="text",
|
|
77
|
+
text=f"Background task submitted. task_id={task_id}",
|
|
78
|
+
)
|
|
79
|
+
yield mcp.types.CallToolResult(content=[text_content])
|
|
80
|
+
|
|
81
|
+
return
|
|
46
82
|
else:
|
|
47
83
|
async for r in cls._execute_local(tool, run_context, **tool_args):
|
|
48
84
|
yield r
|
|
@@ -74,13 +110,35 @@ class FunctionToolExecutor(BaseFunctionToolExecutor[AstrAgentContext]):
|
|
|
74
110
|
ctx = run_context.context.context
|
|
75
111
|
event = run_context.context.event
|
|
76
112
|
umo = event.unified_msg_origin
|
|
77
|
-
|
|
113
|
+
|
|
114
|
+
# Use per-subagent provider override if configured; otherwise fall back
|
|
115
|
+
# to the current/default provider resolution.
|
|
116
|
+
prov_id = getattr(
|
|
117
|
+
tool, "provider_id", None
|
|
118
|
+
) or await ctx.get_current_chat_provider_id(umo)
|
|
119
|
+
|
|
120
|
+
# prepare begin dialogs
|
|
121
|
+
contexts = None
|
|
122
|
+
dialogs = tool.agent.begin_dialogs
|
|
123
|
+
if dialogs:
|
|
124
|
+
contexts = []
|
|
125
|
+
for dialog in dialogs:
|
|
126
|
+
try:
|
|
127
|
+
contexts.append(
|
|
128
|
+
dialog
|
|
129
|
+
if isinstance(dialog, Message)
|
|
130
|
+
else Message.model_validate(dialog)
|
|
131
|
+
)
|
|
132
|
+
except Exception:
|
|
133
|
+
continue
|
|
134
|
+
|
|
78
135
|
llm_resp = await ctx.tool_loop_agent(
|
|
79
136
|
event=event,
|
|
80
137
|
chat_provider_id=prov_id,
|
|
81
138
|
prompt=input_,
|
|
82
139
|
system_prompt=tool.agent.instructions,
|
|
83
140
|
tools=toolset,
|
|
141
|
+
contexts=contexts,
|
|
84
142
|
max_steps=30,
|
|
85
143
|
run_hooks=tool.agent.run_hooks,
|
|
86
144
|
)
|
|
@@ -88,11 +146,128 @@ class FunctionToolExecutor(BaseFunctionToolExecutor[AstrAgentContext]):
|
|
|
88
146
|
content=[mcp.types.TextContent(type="text", text=llm_resp.completion_text)]
|
|
89
147
|
)
|
|
90
148
|
|
|
149
|
+
@classmethod
|
|
150
|
+
async def _execute_background(
|
|
151
|
+
cls,
|
|
152
|
+
tool: FunctionTool,
|
|
153
|
+
run_context: ContextWrapper[AstrAgentContext],
|
|
154
|
+
task_id: str,
|
|
155
|
+
**tool_args,
|
|
156
|
+
):
|
|
157
|
+
from astrbot.core.astr_main_agent import (
|
|
158
|
+
MainAgentBuildConfig,
|
|
159
|
+
_get_session_conv,
|
|
160
|
+
build_main_agent,
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
# run the tool
|
|
164
|
+
result_text = ""
|
|
165
|
+
try:
|
|
166
|
+
async for r in cls._execute_local(
|
|
167
|
+
tool, run_context, tool_call_timeout=3600, **tool_args
|
|
168
|
+
):
|
|
169
|
+
# collect results, currently we just collect the text results
|
|
170
|
+
if isinstance(r, mcp.types.CallToolResult):
|
|
171
|
+
result_text = ""
|
|
172
|
+
for content in r.content:
|
|
173
|
+
if isinstance(content, mcp.types.TextContent):
|
|
174
|
+
result_text += content.text + "\n"
|
|
175
|
+
except Exception as e:
|
|
176
|
+
result_text = (
|
|
177
|
+
f"error: Background task execution failed, internal error: {e!s}"
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
event = run_context.context.event
|
|
181
|
+
ctx = run_context.context.context
|
|
182
|
+
|
|
183
|
+
note = (
|
|
184
|
+
event.get_extra("background_note")
|
|
185
|
+
or f"Background task {tool.name} finished."
|
|
186
|
+
)
|
|
187
|
+
extras = {
|
|
188
|
+
"background_task_result": {
|
|
189
|
+
"task_id": task_id,
|
|
190
|
+
"tool_name": tool.name,
|
|
191
|
+
"result": result_text or "",
|
|
192
|
+
"tool_args": tool_args,
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
session = MessageSession.from_str(event.unified_msg_origin)
|
|
196
|
+
cron_event = CronMessageEvent(
|
|
197
|
+
context=ctx,
|
|
198
|
+
session=session,
|
|
199
|
+
message=note,
|
|
200
|
+
extras=extras,
|
|
201
|
+
message_type=session.message_type,
|
|
202
|
+
)
|
|
203
|
+
cron_event.role = event.role
|
|
204
|
+
config = MainAgentBuildConfig(tool_call_timeout=3600)
|
|
205
|
+
|
|
206
|
+
req = ProviderRequest()
|
|
207
|
+
conv = await _get_session_conv(event=cron_event, plugin_context=ctx)
|
|
208
|
+
req.conversation = conv
|
|
209
|
+
context = json.loads(conv.history)
|
|
210
|
+
if context:
|
|
211
|
+
req.contexts = context
|
|
212
|
+
context_dump = req._print_friendly_context()
|
|
213
|
+
req.contexts = []
|
|
214
|
+
req.system_prompt += (
|
|
215
|
+
"\n\nBellow is you and user previous conversation history:\n"
|
|
216
|
+
f"{context_dump}"
|
|
217
|
+
)
|
|
218
|
+
|
|
219
|
+
bg = json.dumps(extras["background_task_result"], ensure_ascii=False)
|
|
220
|
+
req.system_prompt += BACKGROUND_TASK_RESULT_WOKE_SYSTEM_PROMPT.format(
|
|
221
|
+
background_task_result=bg
|
|
222
|
+
)
|
|
223
|
+
req.prompt = (
|
|
224
|
+
"Proceed according to your system instructions. "
|
|
225
|
+
"Output using same language as previous conversation."
|
|
226
|
+
" After completing your task, summarize and output your actions and results."
|
|
227
|
+
)
|
|
228
|
+
if not req.func_tool:
|
|
229
|
+
req.func_tool = ToolSet()
|
|
230
|
+
req.func_tool.add_tool(SEND_MESSAGE_TO_USER_TOOL)
|
|
231
|
+
|
|
232
|
+
result = await build_main_agent(
|
|
233
|
+
event=cron_event, plugin_context=ctx, config=config, req=req
|
|
234
|
+
)
|
|
235
|
+
if not result:
|
|
236
|
+
logger.error("Failed to build main agent for background task job.")
|
|
237
|
+
return
|
|
238
|
+
|
|
239
|
+
runner = result.agent_runner
|
|
240
|
+
async for _ in runner.step_until_done(30):
|
|
241
|
+
# agent will send message to user via using tools
|
|
242
|
+
pass
|
|
243
|
+
llm_resp = runner.get_final_llm_resp()
|
|
244
|
+
task_meta = extras.get("background_task_result", {})
|
|
245
|
+
summary_note = (
|
|
246
|
+
f"[BackgroundTask] {task_meta.get('tool_name', tool.name)} "
|
|
247
|
+
f"(task_id={task_meta.get('task_id', task_id)}) finished. "
|
|
248
|
+
f"Result: {task_meta.get('result') or result_text or 'no content'}"
|
|
249
|
+
)
|
|
250
|
+
if llm_resp and llm_resp.completion_text:
|
|
251
|
+
summary_note += (
|
|
252
|
+
f"I finished the task, here is the result: {llm_resp.completion_text}"
|
|
253
|
+
)
|
|
254
|
+
await persist_agent_history(
|
|
255
|
+
ctx.conversation_manager,
|
|
256
|
+
event=cron_event,
|
|
257
|
+
req=req,
|
|
258
|
+
summary_note=summary_note,
|
|
259
|
+
)
|
|
260
|
+
if not llm_resp:
|
|
261
|
+
logger.warning("background task agent got no response")
|
|
262
|
+
return
|
|
263
|
+
|
|
91
264
|
@classmethod
|
|
92
265
|
async def _execute_local(
|
|
93
266
|
cls,
|
|
94
267
|
tool: FunctionTool,
|
|
95
268
|
run_context: ContextWrapper[AstrAgentContext],
|
|
269
|
+
*,
|
|
270
|
+
tool_call_timeout: int | None = None,
|
|
96
271
|
**tool_args,
|
|
97
272
|
):
|
|
98
273
|
event = run_context.context.event
|
|
@@ -133,7 +308,7 @@ class FunctionToolExecutor(BaseFunctionToolExecutor[AstrAgentContext]):
|
|
|
133
308
|
try:
|
|
134
309
|
resp = await asyncio.wait_for(
|
|
135
310
|
anext(wrapper),
|
|
136
|
-
timeout=run_context.tool_call_timeout,
|
|
311
|
+
timeout=tool_call_timeout or run_context.tool_call_timeout,
|
|
137
312
|
)
|
|
138
313
|
if resp is not None:
|
|
139
314
|
if isinstance(resp, mcp.types.CallToolResult):
|
|
@@ -165,7 +340,7 @@ class FunctionToolExecutor(BaseFunctionToolExecutor[AstrAgentContext]):
|
|
|
165
340
|
yield None
|
|
166
341
|
except asyncio.TimeoutError:
|
|
167
342
|
raise Exception(
|
|
168
|
-
f"tool {tool.name} execution timeout after {run_context.tool_call_timeout} seconds.",
|
|
343
|
+
f"tool {tool.name} execution timeout after {tool_call_timeout or run_context.tool_call_timeout} seconds.",
|
|
169
344
|
)
|
|
170
345
|
except StopAsyncIteration:
|
|
171
346
|
break
|