Fix alias pre-commit errors (#28)
This commit is contained in:
@@ -66,6 +66,7 @@ repos:
|
||||
| \.html$
|
||||
)
|
||||
args: [
|
||||
"--init-hook=import sys; sys.path.insert(0, 'alias/src')",
|
||||
--disable=W0511,
|
||||
--disable=W0718,
|
||||
--disable=W0122,
|
||||
|
||||
@@ -3,3 +3,9 @@
|
||||
|
||||
__version__ = "0.0.1"
|
||||
|
||||
__all__ = ["agent", "runtime", "__version__"]
|
||||
|
||||
# Import submodules to make them accessible via alias.agent, alias.runtime
|
||||
# Import at the end to avoid circular import issues
|
||||
from . import agent # noqa: E402, F401
|
||||
from . import runtime # noqa: E402, F401
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""Agent module for Alias"""
|
||||
|
||||
__all__ = ["agents", "tools", "mock", "utils"]
|
||||
|
||||
# Import submodules to make them accessible via alias.agent.agents, etc.
|
||||
from . import agents # noqa: E402, F401
|
||||
from . import tools # noqa: E402, F401
|
||||
from . import mock # noqa: E402, F401
|
||||
from . import utils # noqa: E402, F401
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from typing import Optional, Any, Type, Callable
|
||||
import asyncio
|
||||
import time
|
||||
from pydantic import BaseModel
|
||||
from loguru import logger
|
||||
import traceback
|
||||
import json
|
||||
import time
|
||||
import traceback
|
||||
from typing import Any, Optional, Type
|
||||
|
||||
from loguru import logger
|
||||
from pydantic import BaseModel
|
||||
|
||||
from agentscope.agent import ReActAgent
|
||||
from agentscope.model import ChatModelBase
|
||||
@@ -49,6 +50,7 @@ class AliasAgentBase(ReActAgent):
|
||||
|
||||
async def _reasoning(self):
|
||||
"""Override _reasoning to add retry logic."""
|
||||
|
||||
# Call the parent class's _reasoning method directly to
|
||||
# avoid double hook execution
|
||||
# We need to call the underlying implementation without hooks
|
||||
@@ -57,10 +59,10 @@ class AliasAgentBase(ReActAgent):
|
||||
# metaclass processing
|
||||
# Access the method from the class that defines it
|
||||
# (before metaclass wrapping)
|
||||
original_method = ReActAgent.__dict__['_reasoning']
|
||||
original_method = ReActAgent.__dict__["_reasoning"]
|
||||
# Check if this is the wrapped version by looking for
|
||||
# the wrapper attributes
|
||||
if hasattr(original_method, '__wrapped__'):
|
||||
if hasattr(original_method, "__wrapped__"):
|
||||
# This is the wrapped version, get the original
|
||||
original_method = original_method.__wrapped__
|
||||
return await original_method(self)
|
||||
@@ -68,17 +70,17 @@ class AliasAgentBase(ReActAgent):
|
||||
for i in range(MODEL_MAX_RETRIES - 1):
|
||||
try:
|
||||
return await call_parent_reasoning()
|
||||
except Exception as e:
|
||||
except Exception:
|
||||
logger.warning(
|
||||
f"Reasoning fail at attempt {i + 1}. "
|
||||
f"Max attempts {MODEL_MAX_RETRIES}\n"
|
||||
f"{traceback.format_exc()}"
|
||||
f"{traceback.format_exc()}",
|
||||
)
|
||||
memory_msgs = await self.memory.get_memory()
|
||||
mem_len = len(memory_msgs)
|
||||
# ensure the last message has no tool_use before next attempt
|
||||
if mem_len > 0 and memory_msgs[-1].has_content_blocks(
|
||||
"tool_use"
|
||||
"tool_use",
|
||||
):
|
||||
await self.memory.delete(index=mem_len - 1)
|
||||
time.sleep(2)
|
||||
@@ -241,7 +243,6 @@ class AliasAgentBase(ReActAgent):
|
||||
# Skip non-serializable values
|
||||
pass
|
||||
|
||||
|
||||
# Skip the printing of the finish function call
|
||||
if (
|
||||
tool_call["name"] != self.finish_function_name
|
||||
|
||||
@@ -1,51 +1,40 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""Deep Research Agent"""
|
||||
# pylint: disable=too-many-lines, no-name-in-module
|
||||
import os
|
||||
import json
|
||||
import os
|
||||
import traceback
|
||||
import uuid
|
||||
|
||||
from typing import Type, Optional, Any, Tuple
|
||||
from datetime import datetime
|
||||
from typing import Any, Optional, Tuple, Type
|
||||
|
||||
# from copy import deepcopy
|
||||
import shortuuid
|
||||
from pydantic import BaseModel
|
||||
|
||||
from agentscope import logger
|
||||
from agentscope.formatter import FormatterBase
|
||||
from agentscope.memory import MemoryBase
|
||||
from agentscope.message import Msg, TextBlock, ToolResultBlock, ToolUseBlock
|
||||
from agentscope.model import ChatModelBase
|
||||
from agentscope.tool import ToolResponse
|
||||
|
||||
from alias.agent.agents import AliasAgentBase
|
||||
from alias.agent.tools import AliasToolkit
|
||||
from alias.agent.agents._dragent_utils.built_in_prompt.promptmodule import (
|
||||
FollowupJudge,
|
||||
ReflectFailure,
|
||||
SubtasksDecomposition,
|
||||
WebExtraction,
|
||||
)
|
||||
from alias.agent.agents._dragent_utils.utils import (
|
||||
get_dynamic_tool_call_json,
|
||||
get_structure_output,
|
||||
load_prompt_dict,
|
||||
)
|
||||
from alias.agent.agents._planning_tools._planning_notebook import (
|
||||
WorkerResponse,
|
||||
)
|
||||
|
||||
from alias.agent.agents._dragent_utils.built_in_prompt.promptmodule import (
|
||||
SubtasksDecomposition,
|
||||
WebExtraction,
|
||||
FollowupJudge,
|
||||
ReflectFailure,
|
||||
)
|
||||
from alias.agent.agents._dragent_utils.utils import (
|
||||
load_prompt_dict,
|
||||
get_dynamic_tool_call_json,
|
||||
get_structure_output,
|
||||
)
|
||||
|
||||
from agentscope import logger, setup_logger
|
||||
# from agentscope.mcp import StatefulClientBase
|
||||
# from agentscope.agent import ReActAgent
|
||||
from agentscope.model import ChatModelBase
|
||||
from agentscope.formatter import FormatterBase
|
||||
from agentscope.memory import MemoryBase
|
||||
from agentscope.tool import (
|
||||
ToolResponse,
|
||||
Toolkit,
|
||||
)
|
||||
from agentscope.message import (
|
||||
Msg,
|
||||
ToolUseBlock,
|
||||
TextBlock,
|
||||
ToolResultBlock,
|
||||
)
|
||||
from alias.agent.tools import AliasToolkit
|
||||
|
||||
|
||||
_DEEP_RESEARCH_AGENT_DEFAULT_SYS_PROMPT = "You're a helpful assistant."
|
||||
@@ -61,7 +50,7 @@ class SubTaskItem(BaseModel):
|
||||
|
||||
async def deep_research_pre_reply_hook(
|
||||
self: "DeepResearchAgent",
|
||||
kwargs: dict[str, Any], # pylint: disable=W0613
|
||||
kwargs: dict[str, Any],
|
||||
):
|
||||
# Maintain the subtask list
|
||||
msg: Msg = kwargs.get("msg")
|
||||
@@ -82,29 +71,40 @@ async def deep_research_pre_reply_hook(
|
||||
|
||||
async def deep_research_post_reply_hook(
|
||||
self: "DeepResearchAgent",
|
||||
kwargs: Any,
|
||||
output: Any,
|
||||
kwargs: Any, # pylint: disable=W0613
|
||||
output: Any, # pylint: disable=W0613
|
||||
):
|
||||
self.current_subtask = []
|
||||
|
||||
|
||||
def _dump_json(
|
||||
save_info: list[Msg] | dict,
|
||||
dir: str = "./dr_execution_trac"
|
||||
directory: str = "./dr_execution_trac",
|
||||
):
|
||||
if not os.path.isdir(dir):
|
||||
os.makedirs(dir, exist_ok=True)
|
||||
if isinstance(save_info, list) and len(save_info) > 0 and isinstance(save_info[0], Msg):
|
||||
if not os.path.isdir(directory):
|
||||
os.makedirs(directory, exist_ok=True)
|
||||
if (
|
||||
isinstance(save_info, list)
|
||||
and len(save_info) > 0
|
||||
and isinstance(save_info[0], Msg)
|
||||
):
|
||||
save_info = [msg.to_dict() for msg in save_info]
|
||||
file_path = os.path.join(dir, "memory-" + str(uuid.uuid4().hex) + ".json")
|
||||
file_path = os.path.join(
|
||||
directory,
|
||||
"memory-" + str(uuid.uuid4().hex) + ".json",
|
||||
)
|
||||
else:
|
||||
file_path = os.path.join(dir, "plane-" + str(uuid.uuid4().hex) + ".json")
|
||||
with open(file_path, "w") as f:
|
||||
file_path = os.path.join(
|
||||
directory,
|
||||
"plane-" + str(uuid.uuid4().hex) + ".json",
|
||||
)
|
||||
with open(file_path, "w", encoding="utf-8") as f:
|
||||
json.dump(save_info, f, ensure_ascii=False, indent=4)
|
||||
|
||||
|
||||
async def deep_research_pre_reasoning_hook(
|
||||
self: "DeepResearchAgent",
|
||||
kwargs: Any,
|
||||
kwargs: Any, # pylint: disable=W0613
|
||||
):
|
||||
memory = await self.memory.get_memory()
|
||||
_dump_json(memory)
|
||||
@@ -117,15 +117,17 @@ async def deep_research_pre_reasoning_hook(
|
||||
]
|
||||
research_results = []
|
||||
for tool_call in self.search_call_buffer:
|
||||
msg = await self._get_research_result(tool_call.get("id"))
|
||||
msg = await self._get_research_result( # pylint: disable=W0212
|
||||
tool_call.get("id"),
|
||||
)
|
||||
if msg is not None:
|
||||
research_results.append(
|
||||
json.dumps(
|
||||
msg.get_content_blocks("tool_result"),
|
||||
ensure_ascii=False,
|
||||
)
|
||||
),
|
||||
)
|
||||
await self._follow_up(
|
||||
await self._follow_up( # pylint: disable=W0212
|
||||
search_results="\n".join(research_results),
|
||||
search_queries="\n".join(search_queries),
|
||||
)
|
||||
@@ -142,9 +144,7 @@ async def deep_research_pre_reasoning_hook(
|
||||
reasoning_prompt = self.prompt_dict["reasoning_prompt"].format_map(
|
||||
{
|
||||
"objective": self.current_subtask[-1].objective,
|
||||
"plan": cur_plan
|
||||
if cur_plan
|
||||
else "There is no working plan now.",
|
||||
"plan": cur_plan if cur_plan else "There is no working plan now.",
|
||||
"knowledge_gap": f"## Knowledge Gaps:\n {cur_know_gap}"
|
||||
if cur_know_gap
|
||||
else "",
|
||||
@@ -166,8 +166,8 @@ async def deep_research_pre_reasoning_hook(
|
||||
|
||||
async def deep_research_post_reasoning_hook(
|
||||
self: "DeepResearchAgent", # pylint: disable=W0613
|
||||
kwargs: Any,
|
||||
output_msg: Msg,
|
||||
kwargs: Any, # pylint: disable=W0613
|
||||
output_msg: Msg, # pylint: disable=W0613
|
||||
):
|
||||
num_msgs = await self.memory.size()
|
||||
if num_msgs > 1:
|
||||
@@ -178,7 +178,7 @@ async def deep_research_post_reasoning_hook(
|
||||
async def deep_research_post_action_hook(
|
||||
self: "DeepResearchAgent",
|
||||
kwargs: Any,
|
||||
output_msg: Msg,
|
||||
output_msg: Msg, # pylint: disable=W0613
|
||||
):
|
||||
tool_call = kwargs.get("tool_call", {})
|
||||
if tool_call and tool_call.get("name") == self.search_function:
|
||||
@@ -274,7 +274,7 @@ class DeepResearchAgent(AliasAgentBase):
|
||||
"intermediate_summarize": self.summarize_function,
|
||||
"reflect_failure": "reflect_failure",
|
||||
"subtask_finish": "finish_current_subtask",
|
||||
"finish_function_name": self.finish_function_name
|
||||
"finish_function_name": self.finish_function_name,
|
||||
},
|
||||
)
|
||||
tool_use_rule = self.prompt_dict["tool_use_rule"].format_map(
|
||||
@@ -311,34 +311,34 @@ class DeepResearchAgent(AliasAgentBase):
|
||||
self.summarize_intermediate_results,
|
||||
)
|
||||
self.toolkit.register_tool_function(
|
||||
self.finish_current_subtask
|
||||
self.finish_current_subtask,
|
||||
)
|
||||
|
||||
# add hooks
|
||||
self.register_instance_hook(
|
||||
"pre_reply",
|
||||
"deep_research_pre_reply_hook",
|
||||
deep_research_pre_reply_hook
|
||||
deep_research_pre_reply_hook,
|
||||
)
|
||||
self.register_instance_hook(
|
||||
"post_reply",
|
||||
"deep_research_post_reply_hook",
|
||||
deep_research_post_reply_hook
|
||||
deep_research_post_reply_hook,
|
||||
)
|
||||
self.register_instance_hook(
|
||||
"pre_reasoning",
|
||||
"deep_research_pre_reasoning_hook",
|
||||
deep_research_pre_reasoning_hook
|
||||
deep_research_pre_reasoning_hook,
|
||||
)
|
||||
self.register_instance_hook(
|
||||
"post_reasoning",
|
||||
"deep_research_post_reasoning_hook",
|
||||
deep_research_post_reasoning_hook
|
||||
deep_research_post_reasoning_hook,
|
||||
)
|
||||
self.register_instance_hook(
|
||||
"post_acting",
|
||||
"deep_research_post_action_hook",
|
||||
deep_research_post_action_hook
|
||||
deep_research_post_action_hook,
|
||||
)
|
||||
self.search_call_buffer = []
|
||||
|
||||
@@ -471,7 +471,7 @@ class DeepResearchAgent(AliasAgentBase):
|
||||
"Identify the knowledge gaps of the current "
|
||||
"subtask and generate a working plan by subtask "
|
||||
"decomposition",
|
||||
"assistant"
|
||||
"assistant",
|
||||
),
|
||||
)
|
||||
|
||||
@@ -548,7 +548,7 @@ class DeepResearchAgent(AliasAgentBase):
|
||||
"(Follow-up by extraction)"
|
||||
"Read the website more intensively to mine more "
|
||||
"information.",
|
||||
"assistant"
|
||||
"assistant",
|
||||
),
|
||||
)
|
||||
try:
|
||||
@@ -568,9 +568,9 @@ class DeepResearchAgent(AliasAgentBase):
|
||||
text=json.dumps(
|
||||
extraction_check,
|
||||
ensure_ascii=False,
|
||||
indent=2
|
||||
)
|
||||
)
|
||||
indent=2,
|
||||
),
|
||||
),
|
||||
],
|
||||
role="assistant",
|
||||
)
|
||||
@@ -579,7 +579,7 @@ class DeepResearchAgent(AliasAgentBase):
|
||||
except Exception as e: # noqa: F841
|
||||
logger.warning(
|
||||
f"Error when checking subtask finish status {e}"
|
||||
f"{traceback.format_exc()}"
|
||||
f"{traceback.format_exc()}",
|
||||
)
|
||||
extraction_check = {}
|
||||
|
||||
@@ -599,9 +599,9 @@ class DeepResearchAgent(AliasAgentBase):
|
||||
Msg(
|
||||
self.name,
|
||||
[TextBlock(type="text", text=f"Reading {urls}")],
|
||||
"assistant"
|
||||
"assistant",
|
||||
),
|
||||
last=True
|
||||
last=True,
|
||||
)
|
||||
|
||||
# call the extract_function
|
||||
@@ -629,7 +629,7 @@ class DeepResearchAgent(AliasAgentBase):
|
||||
self.name,
|
||||
"(Follow-up to explore)"
|
||||
"Check if current subtask knowledge gaps are fulfilled",
|
||||
"assistant"
|
||||
"assistant",
|
||||
),
|
||||
)
|
||||
msgs = [
|
||||
@@ -646,7 +646,7 @@ class DeepResearchAgent(AliasAgentBase):
|
||||
"user",
|
||||
self.prompt_dict["follow_up_judge_sys_prompt"],
|
||||
role="user",
|
||||
)
|
||||
),
|
||||
]
|
||||
follow_up_judge = await self.get_model_output(
|
||||
msgs=msgs,
|
||||
@@ -660,23 +660,26 @@ class DeepResearchAgent(AliasAgentBase):
|
||||
type="text",
|
||||
text=json.dumps(
|
||||
follow_up_judge,
|
||||
ensure_ascii=False, indent=2
|
||||
)
|
||||
)
|
||||
ensure_ascii=False,
|
||||
indent=2,
|
||||
),
|
||||
),
|
||||
],
|
||||
role="assistant",
|
||||
)
|
||||
await self.memory.add(follow_up_msg)
|
||||
except Exception as e: # noqa: F841
|
||||
logger.warning(
|
||||
f"Error when checking subtask finish status {e}"
|
||||
f"Error when checking subtask finish status {e}",
|
||||
)
|
||||
logger.error(traceback.format_exc())
|
||||
follow_up_judge = {}
|
||||
|
||||
if follow_up_judge.get("knowledge_gap_revision", ""):
|
||||
self.current_subtask[-1].knowledge_gaps = \
|
||||
follow_up_judge.get("knowledge_gap_revision", "")
|
||||
self.current_subtask[-1].knowledge_gaps = follow_up_judge.get(
|
||||
"knowledge_gap_revision",
|
||||
"",
|
||||
)
|
||||
|
||||
if (
|
||||
follow_up_judge.get("to_further_explore", False)
|
||||
@@ -690,15 +693,13 @@ class DeepResearchAgent(AliasAgentBase):
|
||||
TextBlock(
|
||||
type="text",
|
||||
text="Still need to do more research "
|
||||
f"to figure out {subtask}",
|
||||
)
|
||||
f"to figure out {subtask}",
|
||||
),
|
||||
],
|
||||
role="assistant"
|
||||
)
|
||||
)
|
||||
intermediate_report = (
|
||||
await self.summarize_intermediate_results()
|
||||
role="assistant",
|
||||
),
|
||||
)
|
||||
intermediate_report = await self.summarize_intermediate_results()
|
||||
self.current_subtask.append(
|
||||
SubTaskItem(objective=subtask),
|
||||
)
|
||||
@@ -748,14 +749,12 @@ class DeepResearchAgent(AliasAgentBase):
|
||||
for msg in reversed(memory_msgs):
|
||||
if msg.metadata and msg.metadata.get("is_report_msg"):
|
||||
break
|
||||
else:
|
||||
intermediate_memory.append(msg)
|
||||
intermediate_memory.append(msg)
|
||||
intermediate_memory.reverse()
|
||||
if remove_last_tool_use:
|
||||
while (
|
||||
len(intermediate_memory) > 0 and
|
||||
intermediate_memory[-1].has_content_blocks("tool_use")
|
||||
):
|
||||
while len(intermediate_memory) > 0 and intermediate_memory[
|
||||
-1
|
||||
].has_content_blocks("tool_use"):
|
||||
intermediate_memory.pop(-1)
|
||||
return intermediate_memory
|
||||
|
||||
@@ -765,34 +764,33 @@ class DeepResearchAgent(AliasAgentBase):
|
||||
for msg in reversed(memory_msgs):
|
||||
if msg.metadata and msg.metadata.get("is_report_msg"):
|
||||
break
|
||||
elif msg.role == "user":
|
||||
if msg.role == "user":
|
||||
break
|
||||
elif msg.has_content_blocks("tool_use"):
|
||||
if msg.has_content_blocks("tool_use"):
|
||||
stop = False
|
||||
for block in msg.get_content_blocks("tool_use"):
|
||||
if block.get("name") == self.summarize_function:
|
||||
stop = True
|
||||
if stop:
|
||||
break
|
||||
else:
|
||||
remove_num += 1
|
||||
remove_num += 1
|
||||
else:
|
||||
remove_num += 1
|
||||
start_index = len(memory_msgs) - remove_num
|
||||
logger.info(
|
||||
"---> delete messages: "
|
||||
f"{list(range(start_index, len(memory_msgs)))}"
|
||||
f"{list(range(start_index, len(memory_msgs)))}",
|
||||
)
|
||||
await self.memory.delete(list(range(start_index, len(memory_msgs))))
|
||||
|
||||
async def _get_research_result(
|
||||
self,
|
||||
tool_call_id: str
|
||||
tool_call_id: str,
|
||||
) -> Msg | None:
|
||||
memory_msgs = await self.memory.get_memory()
|
||||
for msg in reversed(memory_msgs):
|
||||
if msg.has_content_blocks("tool_result"):
|
||||
for block in msg.get_content_blocks('tool_result'):
|
||||
for block in msg.get_content_blocks("tool_result"):
|
||||
if block.get("id") == tool_call_id:
|
||||
return msg
|
||||
return None
|
||||
@@ -823,7 +821,7 @@ class DeepResearchAgent(AliasAgentBase):
|
||||
"[summarize_intermediate_results]"
|
||||
"Examine whether the knowledge gaps or objective"
|
||||
"have been fulfill",
|
||||
"assistant"
|
||||
"assistant",
|
||||
),
|
||||
)
|
||||
|
||||
@@ -883,7 +881,7 @@ class DeepResearchAgent(AliasAgentBase):
|
||||
Msg(
|
||||
self.name,
|
||||
"Summarize the intermediate results into a report",
|
||||
"assistant"
|
||||
"assistant",
|
||||
),
|
||||
)
|
||||
|
||||
@@ -934,7 +932,7 @@ class DeepResearchAgent(AliasAgentBase):
|
||||
),
|
||||
),
|
||||
],
|
||||
metadata={"is_report_msg": True,}
|
||||
metadata={"is_report_msg": True},
|
||||
)
|
||||
else:
|
||||
# add to memory for the follow-up case
|
||||
@@ -948,7 +946,7 @@ class DeepResearchAgent(AliasAgentBase):
|
||||
),
|
||||
],
|
||||
role="assistant",
|
||||
metadata={"is_report_msg": True}
|
||||
metadata={"is_report_msg": True},
|
||||
),
|
||||
)
|
||||
return ToolResponse(
|
||||
@@ -989,7 +987,7 @@ class DeepResearchAgent(AliasAgentBase):
|
||||
tmp_report_path = os.path.join(
|
||||
self.tmp_file_storage_dir,
|
||||
f"{self.report_path_based}_"
|
||||
f"inprocess_report_{index + 1}.md"
|
||||
f"inprocess_report_{index + 1}.md",
|
||||
)
|
||||
params = {
|
||||
"file_path": tmp_report_path,
|
||||
@@ -1010,11 +1008,11 @@ class DeepResearchAgent(AliasAgentBase):
|
||||
TextBlock(
|
||||
type="text",
|
||||
text="Reading progress report: "
|
||||
f"{tmp_report_path}"
|
||||
)
|
||||
f"{tmp_report_path}",
|
||||
),
|
||||
],
|
||||
"assistant"
|
||||
)
|
||||
"assistant",
|
||||
),
|
||||
)
|
||||
|
||||
msgs = [
|
||||
@@ -1031,7 +1029,7 @@ class DeepResearchAgent(AliasAgentBase):
|
||||
]
|
||||
else: # Use only intermediate memory to generate report
|
||||
intermediate_memory = await self._get_intermediate_memory(
|
||||
remove_last_tool_use=True
|
||||
remove_last_tool_use=True,
|
||||
)
|
||||
msgs = [
|
||||
Msg(
|
||||
@@ -1045,7 +1043,7 @@ class DeepResearchAgent(AliasAgentBase):
|
||||
Msg(
|
||||
self.name,
|
||||
"Collect and polish all draft reports into a final report",
|
||||
"assistant"
|
||||
"assistant",
|
||||
),
|
||||
)
|
||||
try:
|
||||
@@ -1104,8 +1102,10 @@ class DeepResearchAgent(AliasAgentBase):
|
||||
name=self.name,
|
||||
role="assistant",
|
||||
content=[
|
||||
TextBlock(type="text",
|
||||
text=subtask_progress_summary, )
|
||||
TextBlock(
|
||||
type="text",
|
||||
text=subtask_progress_summary,
|
||||
),
|
||||
],
|
||||
metadata=structure_response.model_dump(),
|
||||
)
|
||||
@@ -1122,7 +1122,7 @@ class DeepResearchAgent(AliasAgentBase):
|
||||
The reflection about plan rephrasing and subtask decomposition.
|
||||
"""
|
||||
intermediate_memory = await self._get_intermediate_memory(
|
||||
remove_last_tool_use=True
|
||||
remove_last_tool_use=True,
|
||||
)
|
||||
reflect_sys_prompt = self.prompt_dict["reflect_sys_prompt"]
|
||||
conversation_history = ""
|
||||
@@ -1148,7 +1148,7 @@ class DeepResearchAgent(AliasAgentBase):
|
||||
Msg(
|
||||
self.name,
|
||||
"Reflect on the failure of the action",
|
||||
"assistant"
|
||||
"assistant",
|
||||
),
|
||||
)
|
||||
reflection = await self.get_model_output(
|
||||
@@ -1193,7 +1193,7 @@ class DeepResearchAgent(AliasAgentBase):
|
||||
save_msg = None
|
||||
for msg in reversed(msgs):
|
||||
for i, block in enumerate(
|
||||
msg.get_content_blocks("tool_use")
|
||||
msg.get_content_blocks("tool_use"),
|
||||
):
|
||||
if block.get("name") == "reflect_failure":
|
||||
save_msg = msg
|
||||
@@ -1286,8 +1286,8 @@ class DeepResearchAgent(AliasAgentBase):
|
||||
TextBlock(
|
||||
type="text",
|
||||
text="All subtasks are done. "
|
||||
"Consider using generate_response to"
|
||||
"generate final report",
|
||||
"Consider using generate_response to"
|
||||
"generate final report",
|
||||
),
|
||||
],
|
||||
metadata={
|
||||
@@ -1296,7 +1296,6 @@ class DeepResearchAgent(AliasAgentBase):
|
||||
is_last=True,
|
||||
)
|
||||
|
||||
|
||||
# pylint: disable=invalid-overridden-method, unused-argument
|
||||
async def generate_response( #
|
||||
self,
|
||||
@@ -1333,15 +1332,17 @@ class DeepResearchAgent(AliasAgentBase):
|
||||
detailed_report_path: (
|
||||
f"Final detailed report generated by {self.name}"
|
||||
f"for '{str(self.user_query)}'"
|
||||
)
|
||||
),
|
||||
},
|
||||
)
|
||||
response_msg = Msg(
|
||||
name=self.name,
|
||||
role="assistant",
|
||||
content=[
|
||||
TextBlock(type="text",
|
||||
text=subtask_progress_summary,)
|
||||
TextBlock(
|
||||
type="text",
|
||||
text=subtask_progress_summary,
|
||||
),
|
||||
],
|
||||
metadata=structure_response.model_dump(),
|
||||
)
|
||||
@@ -1377,4 +1378,4 @@ class DeepResearchAgent(AliasAgentBase):
|
||||
"success": False, # do not allow to exit
|
||||
},
|
||||
is_last=True,
|
||||
)
|
||||
)
|
||||
|
||||
@@ -29,7 +29,7 @@ The successful research plan must meet these standards:
|
||||
2. **Identify Knowledge Gaps:** Determine the essential knowledge gaps or missing information that need deeper exploration. Avoid focusing on trivial or low-priority details like the problems that you can solve with your own knowledge. Instead, concentrate on:
|
||||
- Foundational gaps critical to task completion
|
||||
- Identifying opportunities for step expansion by considering alternative approaches, connections to related topics, or ways to enrich the final output. Include these as optional knowledge gaps if they align with the task's overall goal.
|
||||
The knowledge gaps should strictly be in the format of a markdown checklist and flag gaps requiring perspective expansion with `(EXPANSION)` tag (e.g., "- [ ] (EXPANSION) Analysis report of X").
|
||||
The knowledge gaps should strictly be in the format of a markdown checklist and flag gaps requiring perspective expansion with `(EXPANSION)` tag (e.g., "- [ ] (EXPANSION) Analysis report of X").
|
||||
3. **Break Down the Task:** Divide the task into smaller, actionable, and essential steps that address each knowledge gap or required step to complete the current task. Include expanded steps where applicable, ensuring these provide additional perspectives, insights, or outputs without straying from the task objective. These expanded steps should enhance the richness of the final output.
|
||||
4. **Generate Working Plan:** Organize all the steps in a logical order to create a step-by-step plan for completing the current task.
|
||||
|
||||
@@ -40,7 +40,7 @@ When generating extension steps, you can refer to the following perspectives tha
|
||||
- Timeline Researcher: Examine how the subject has evolved over time, previous iterations, and historical context. Think systemically about long-term impacts, scalability, and paradigm shifts in the future.
|
||||
- Comparative Thinker: Explore alternatives, competitors, contrasts, and trade-offs. Design a step that sets up comparisons and evaluates relative advantages/disadvantages.
|
||||
- Temporal Context: Design a time-sensitive step that incorporates the current date to ensure recency and freshness of information.
|
||||
- Public Opinion Collector: Design a step to aggregate user-generated content like text posts or comments, digital photos or videos from Twitter, Youtube, Facebook and other social media.
|
||||
- Public Opinion Collector: Design a step to aggregate user-generated content like text posts or comments, digital photos or videos from Twitter, Youtube, Facebook and other social media.
|
||||
- Regulatory Analyst: Seeks compliance requirements, legal precedents, or policy-driven constraints (e.g. "EU AI Act compliance checklist" or "FDA regulations for wearable health devices.")
|
||||
- Academic Professor: Design a step based on the necessary steps of doing an academic research (e.g. "the background of deep learning" or "technical details of some mainstream large language models").
|
||||
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
You are a sharp-eyed Knowledge Discoverer, capable of identifying and leveraging any potentially useful piece of information gathered from web search, no matter how brief. And the information will later be deeper extracted for more contents.
|
||||
|
||||
## Instructions
|
||||
1. **Find information with valuable, but insufficient or shallow content**: Carefully review the web search results to assess whether there is any snippet or web content that
|
||||
1. **Find information with valuable, but insufficient or shallow content**: Carefully review the web search results to assess whether there is any snippet or web content that
|
||||
- could potentially help address the given query as the content increases
|
||||
- **but whose content is limited or only briefly mentioned**!
|
||||
2. **Identify the snippet**: If such information is found, you are encouraged to set `need_extraction` to true, and locate the specific **url** of the information snippet you have found for later extraction.
|
||||
2. **Identify the snippet**: If such information is found, you are encouraged to set `need_extraction` to true, and locate the specific **url** of the information snippet you have found for later extraction.
|
||||
3. **Reduce unnecessary extraction**: If all snippets are only generally related, or unlikely to address the query, or their contents are rich and sufficient enough, or incomplete but not essential, set `need_extraction` to false.
|
||||
|
||||
## Important Notes
|
||||
|
||||
@@ -34,19 +34,19 @@ Please revise the provided draft research report into a finalized, professional,
|
||||
- The tone must be formal, objective, and professional throughout.
|
||||
- Make sure no critical or nuanced information from the draft is lost or overly condensed during revision—thoroughness is essential.
|
||||
- Check that all cited sources are accurately referenced.
|
||||
- Each section, subsection, and even bullet point MUST contain enough depth, relevant details, and specific information rather than being a brief summary of only a few sentences.
|
||||
- Each section, subsection, and even bullet point MUST contain enough depth, relevant details, and specific information rather than being a brief summary of only a few sentences.
|
||||
|
||||
### Report Format (Fill in appropriate content in [] and ... parts):
|
||||
[Your Report Title]
|
||||
# Introduction:
|
||||
# Introduction:
|
||||
[Introduction to the report]
|
||||
# [Section 1 title]:
|
||||
# [Section 1 title]:
|
||||
[Section 1 content]
|
||||
## [Subsection 1.1 title]:
|
||||
## [Subsection 1.1 title]:
|
||||
[Subsection 1.1 content]
|
||||
# [Section 2 title]:
|
||||
# [Section 2 title]:
|
||||
...
|
||||
# Conclusion:
|
||||
# Conclusion:
|
||||
[Conclusion to the report]
|
||||
|
||||
Format your report professionally with consistent heading levels and proper spacing.
|
||||
|
||||
@@ -17,5 +17,5 @@ You are a professional researcher expert in writing comprehensive reports from y
|
||||
|
||||
## Important Notes
|
||||
1. Avoid combining, excessively paraphrasing, omitting, or condensing any individual snippet that provides unique or relevant details. The final report must cover ALL key information as presented in the original results.
|
||||
2. Each bullet point should be sufficiently detailed (at least **2000 chars**)
|
||||
2. Each bullet point should be sufficiently detailed (at least **2000 chars**)
|
||||
3. Both items with and without `(EXPANSION)` tag in knowledge gaps list are important and useful for task completion.
|
||||
@@ -2,7 +2,7 @@ Your job is to reflect on your failure based on your work history and generate t
|
||||
|
||||
## Instructions
|
||||
1. Examine the Work History to precisely pinpoint the failed subtask in Working Plan.
|
||||
2. Review the Current Subtask and Task Final Objective provided in Work History. Carefully analyze whether this subtask was designed incorrectly due to a misunderstanding of the task. If so,
|
||||
2. Review the Current Subtask and Task Final Objective provided in Work History. Carefully analyze whether this subtask was designed incorrectly due to a misunderstanding of the task. If so,
|
||||
* set `need_rephrase` in `rephrase_subtask` to true
|
||||
* Only replace the inappropriate subtask with the modified subtask, while keeping the rest of the Working Plan unchanged. You should output the updated Working Plan in `rephrased_plan`.
|
||||
* If the subtask was not poorly designed, proceed to Step 3.
|
||||
@@ -15,10 +15,10 @@ Your job is to reflect on your failure based on your work history and generate t
|
||||
2. Set `need_decompose` and `need_rephrase` to false simultaneously when you find that you are getting stuck in a repetitive failure pattern.
|
||||
|
||||
## Example
|
||||
Work History:
|
||||
Work History:
|
||||
1. Reflect on the failure of this subtask and identify the failed subtask "Convert the extracted geographic coordinates or landmarks into corresponding five-digit zip codes by mapping tools or geo-mapping APIs".
|
||||
2. Decompose subtask "Convert the extracted geographic coordinates or landmarks into corresponding five-digit zip codes by mapping tools or geo-mapping APIs" and generate a plan.
|
||||
Working Plan:
|
||||
2. Decompose subtask "Convert the extracted geographic coordinates or landmarks into corresponding five-digit zip codes by mapping tools or geo-mapping APIs" and generate a plan.
|
||||
Working Plan:
|
||||
1. Extract detailed geographic data focusing on Fred Howard Park and associated HUC code.
|
||||
2. Use mapping tools or geo-mapping APIs (e.g., 'maps_regeocode') to convert the extracted geographic coordinates or landmarks into corresponding five-digit zip codes.
|
||||
3. Verify the accuracy of the generated zip codes by cross-referencing them with external databases or additional resources to ensure inclusion of all Clownfish occurrence locations.
|
||||
|
||||
@@ -53,14 +53,14 @@
|
||||
### Important Constraints
|
||||
1. DO NOT TRY TO MAKE A PLAN yourself.
|
||||
2. ALWAYS FOLLOW THE WORKING PLAN SEQUENCE STEP BY STEP!!
|
||||
3. For each step, you MUST provide a reason or analysis to **review what was done in the previous step** and **explain why to call a function / use a tool in this step**.
|
||||
4. After each action, YOU MUST seriously confirm that the current item in the plan is done before starting the next item, referring to the following rules:
|
||||
- Carefully analyze whether the information obtained from the tool is sufficient to fill the knowledge gap corresponding to the current item.
|
||||
3. For each step, you MUST provide a reason or analysis to **review what was done in the previous step** and **explain why to call a function / use a tool in this step**.
|
||||
4. After each action, YOU MUST seriously confirm that the current item in the plan is done before starting the next item, referring to the following rules:
|
||||
- Carefully analyze whether the information obtained from the tool is sufficient to fill the knowledge gap corresponding to the current item.
|
||||
- Pay more attention to details. Confidently assuming that all tool calls will bring complete information often leads to serious errors (e.g., mistaking the rental website name for the apartment name when renting).
|
||||
If the current item in the plan is done, call `summarize_inprocess_results_into_report` to generate an in-process report, then move on to the next item.
|
||||
5. Always pay attention to the current subtask and working plan as they may be updated during the workflow.
|
||||
6. Each time you reason and act, remember that **Current Subtask** is your primary goal, while **Final Task Objective** constrains your process from deviating from the final goal.
|
||||
7. You should use `{subtask_finish}` to mark that you have finished a subtask and proceed to the next one.
|
||||
7. You should use `{subtask_finish}` to mark that you have finished a subtask and proceed to the next one.
|
||||
8. You should use the `{finish_function_name}` tool to return your research results when Research Depth = 1 and all checklist items are completed.
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
|
||||
class SubtasksDecomposition(BaseModel):
|
||||
"""
|
||||
Model for structured subtask decomposition output in deep research.
|
||||
@@ -7,20 +9,23 @@ class SubtasksDecomposition(BaseModel):
|
||||
|
||||
knowledge_gaps: str = Field(
|
||||
description=(
|
||||
"A markdown checklist of essential knowledge gaps and optional "
|
||||
"perspective-expansion gaps (flagged with (EXPANSION)), each on its own line. "
|
||||
"E.g. '- [ ] Detailed analysis of JD.com's ...\\n- [ ] (EXPANSION) X...'."
|
||||
"A markdown checklist of essential knowledge gaps and "
|
||||
"optional perspective-expansion gaps (flagged with "
|
||||
"(EXPANSION)), each on its own line. E.g. '- [ ] Detailed "
|
||||
"analysis of JD.com's ...\\n- [ ] (EXPANSION) X...'."
|
||||
),
|
||||
)
|
||||
working_plan: str = Field(
|
||||
description=(
|
||||
"A logically ordered step-by-step working plan (3-5 steps), "
|
||||
"each step starting with its number (1., 2., etc), including both "
|
||||
"core and expansion steps. Expanded steps should be clearly marked "
|
||||
"with (EXPANSION) and provide contextual or analytical depth.."
|
||||
"A logically ordered step-by-step working plan (3-5 steps),"
|
||||
" each step starting with its number (1., 2., etc), "
|
||||
"including both core and expansion steps. Expanded steps "
|
||||
"should be clearly marked with (EXPANSION) and provide "
|
||||
"contextual or analytical depth.."
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
class WebExtraction(BaseModel):
|
||||
"""
|
||||
Model for structured follow-up web extraction output in deep research.
|
||||
@@ -42,6 +47,7 @@ class WebExtraction(BaseModel):
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
class FollowupJudge(BaseModel):
|
||||
"""
|
||||
Model for structured follow-up decompose judging output in deep research.
|
||||
@@ -49,15 +55,15 @@ class FollowupJudge(BaseModel):
|
||||
|
||||
reasoning: str = Field(
|
||||
description=(
|
||||
"The reasoning for your decision, including a summary of evidence "
|
||||
"and logic for whether more information is needed. You should "
|
||||
"include specific gaps or opportunities if the current "
|
||||
"information is still insufficient"
|
||||
"The reasoning for your decision, including a summary of "
|
||||
"evidence and logic for whether more information is needed. "
|
||||
"You should include specific gaps or opportunities if the "
|
||||
"current information is still insufficient"
|
||||
),
|
||||
)
|
||||
knowledge_gap_revision: str = Field(
|
||||
"Revise the knowledge gaps in the current. "
|
||||
"Mark the gaps with sufficient information as [x]."
|
||||
"Mark the gaps with sufficient information as [x].",
|
||||
)
|
||||
to_further_explore: bool = Field(
|
||||
description=(
|
||||
@@ -81,10 +87,11 @@ class ReflectFailure(BaseModel):
|
||||
|
||||
rephrase_subtask: dict = Field(
|
||||
description=(
|
||||
"Information about whether the problematic subtask needs to be "
|
||||
"rephrased due to a design flaw or misunderstanding. If rephrasing "
|
||||
"is needed, provide the modified working plan with only the inappropriate "
|
||||
"subtask replaced by its improved version."
|
||||
"Information about whether the problematic subtask needs to "
|
||||
"be rephrased due to a design flaw or misunderstanding. If "
|
||||
"rephrasing is needed, provide the modified working plan with"
|
||||
" only the inappropriate subtask replaced by its improved "
|
||||
"version."
|
||||
),
|
||||
json_schema_extra={
|
||||
"additionalProperties": {
|
||||
@@ -92,25 +99,30 @@ class ReflectFailure(BaseModel):
|
||||
"properties": {
|
||||
"need_rephrase": {
|
||||
"type": "boolean",
|
||||
"description": "Set to 'true' if the failed subtask "
|
||||
"needs to be rephrased due to a design "
|
||||
"flaw or misunderstanding; otherwise, 'false'.",
|
||||
"description": (
|
||||
"Set to 'true' if the failed subtask needs "
|
||||
"to be rephrased due to a design flaw or "
|
||||
"misunderstanding; otherwise, 'false'."
|
||||
),
|
||||
},
|
||||
"rephrased_plan": {
|
||||
"type": "string",
|
||||
"description": "The modified working plan with only the inappropriate "
|
||||
"subtask replaced by its improved version. If no "
|
||||
"rephrasing is needed, provide an empty string.",
|
||||
"description": (
|
||||
"The modified working plan with only the "
|
||||
"inappropriate subtask replaced by its "
|
||||
"improved version. If no rephrasing is "
|
||||
"needed, provide an empty string."
|
||||
),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
)
|
||||
decompose_subtask: dict = Field(
|
||||
description=(
|
||||
"Information about whether the problematic subtask should be further "
|
||||
"decomposed. If decomposition is required, provide the failed subtask "
|
||||
"and the reason for its decomposition."
|
||||
"Information about whether the problematic subtask should be "
|
||||
"further decomposed. If decomposition is required, provide "
|
||||
"the failed subtask and the reason for its decomposition."
|
||||
),
|
||||
json_schema_extra={
|
||||
"additionalProperties": {
|
||||
@@ -118,15 +130,19 @@ class ReflectFailure(BaseModel):
|
||||
"properties": {
|
||||
"need_decompose": {
|
||||
"type": "boolean",
|
||||
"description": "Set to 'true' if the failed subtask should "
|
||||
"be further decomposed; otherwise, 'false'.",
|
||||
"description": (
|
||||
"Set to 'true' if the failed subtask should "
|
||||
"be further decomposed; otherwise, 'false'."
|
||||
),
|
||||
},
|
||||
"failed_subtask": {
|
||||
"type": "string",
|
||||
"description": "The failed subtask that needs to be further "
|
||||
"decomposed.",
|
||||
"description": (
|
||||
"The failed subtask that needs to be further "
|
||||
"decomposed."
|
||||
),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""The utilities for deep research agent"""
|
||||
import os
|
||||
import json
|
||||
from typing import Union, Sequence, Any, Type
|
||||
from pydantic import BaseModel
|
||||
import os
|
||||
import re
|
||||
from typing import Any, Sequence, Type, Union
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
from agentscope.tool import Toolkit, ToolResponse
|
||||
from agentscope.agent import ReActAgent
|
||||
|
||||
TOOL_RESULTS_MAX_WORDS = 30000
|
||||
|
||||
@@ -24,12 +24,13 @@ def get_prompt_from_file(
|
||||
prompt = f.read()
|
||||
return prompt
|
||||
|
||||
|
||||
async def count_by_words(sentence: str) -> float:
|
||||
"""Count words of a sentence"""
|
||||
words = re.findall(
|
||||
r"\w+|[^\w\s]",
|
||||
sentence,
|
||||
re.UNICODE
|
||||
re.UNICODE,
|
||||
)
|
||||
|
||||
word_count = 0.0
|
||||
|
||||
@@ -4,19 +4,20 @@ Meta Planner agent class that can handle complicated tasks with
|
||||
planning-execution pattern.
|
||||
"""
|
||||
# pylint: disable=W0613
|
||||
import json
|
||||
import os
|
||||
import uuid
|
||||
from functools import partial
|
||||
from typing import Optional, Any, Literal, Callable
|
||||
import json
|
||||
from pathlib import Path
|
||||
from typing import Any, Callable, Literal, Optional
|
||||
|
||||
from pydantic import BaseModel, Field
|
||||
from agentscope import logger
|
||||
from agentscope.message import Msg, ToolUseBlock, TextBlock, ToolResultBlock
|
||||
from agentscope.tool import ToolResponse
|
||||
from agentscope.model import ChatModelBase
|
||||
|
||||
from agentscope.formatter import FormatterBase
|
||||
from agentscope.memory import MemoryBase
|
||||
from agentscope.message import Msg, TextBlock, ToolResultBlock, ToolUseBlock
|
||||
from agentscope.model import ChatModelBase
|
||||
from agentscope.tool import ToolResponse
|
||||
|
||||
from alias.agent.agents import AliasAgentBase
|
||||
from alias.agent.tools import AliasToolkit
|
||||
@@ -306,7 +307,6 @@ class MetaPlanner(AliasAgentBase):
|
||||
generate_response_post_action_hook,
|
||||
)
|
||||
|
||||
|
||||
def prepare_planner_tools(
|
||||
self,
|
||||
planner_mode: Literal["disable", "enforced", "dynamic"],
|
||||
|
||||
@@ -43,7 +43,7 @@ class RoadmapManager(StateModule):
|
||||
|
||||
async def decompose_task_and_build_roadmap(
|
||||
self,
|
||||
user_latest_input: str,
|
||||
user_latest_input: str, # pylint: disable=W0613
|
||||
given_task_conclusion: str,
|
||||
detail_analysis_for_plan: str,
|
||||
decomposed_subtasks: list[SubTaskSpecification],
|
||||
|
||||
@@ -514,7 +514,7 @@ class WorkerManager(StateModule):
|
||||
subtask_idx: int,
|
||||
selected_worker_name: str,
|
||||
detailed_instruction: str,
|
||||
reset_worker_memory: bool = False
|
||||
reset_worker_memory: bool = False,
|
||||
) -> ToolResponse:
|
||||
"""
|
||||
Execute a worker agent for the next unfinished subtask.
|
||||
@@ -539,7 +539,7 @@ class WorkerManager(StateModule):
|
||||
memory can also be reset for better performance (but require
|
||||
providing sufficient context information in
|
||||
`detailed_instruction`); 3) if a worker is stopped just because
|
||||
hitting th maximum round constraint in the previous execution
|
||||
hitting the maximum round constraint in the previous execution
|
||||
and it's going to work on the sam task, DO NOT reset the
|
||||
memory.
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ from .mock_message_models import (
|
||||
BaseMessage,
|
||||
MessageState,
|
||||
MockMessage,
|
||||
UserMessage
|
||||
UserMessage,
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""Mock message models for local testing without api_server dependency."""
|
||||
from enum import Enum
|
||||
import uuid
|
||||
from typing import Any, Optional, List
|
||||
from enum import Enum
|
||||
from typing import Any, Optional
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class MessageState(str, Enum):
|
||||
"""Message state enumeration."""
|
||||
|
||||
RUNNING = "running"
|
||||
FINISHED = "finished"
|
||||
FAILED = "failed"
|
||||
@@ -15,6 +17,7 @@ class MessageState(str, Enum):
|
||||
|
||||
class MessageType(str, Enum):
|
||||
"""Message type enumeration."""
|
||||
|
||||
RESPONSE = "response"
|
||||
SUB_RESPONSE = "sub_response"
|
||||
THOUGHT = "thought"
|
||||
@@ -27,6 +30,7 @@ class MessageType(str, Enum):
|
||||
|
||||
class BaseMessage(BaseModel):
|
||||
"""Base message class for local testing."""
|
||||
|
||||
role: str = "assistant"
|
||||
content: Any = ""
|
||||
name: Optional[str] = None
|
||||
@@ -36,6 +40,7 @@ class BaseMessage(BaseModel):
|
||||
|
||||
class UserMessage(BaseMessage):
|
||||
"""User message for local testing."""
|
||||
|
||||
role: str = "user"
|
||||
name: str = "User"
|
||||
|
||||
@@ -43,4 +48,4 @@ class UserMessage(BaseMessage):
|
||||
class MockMessage:
|
||||
id: uuid.UUID = uuid.uuid4()
|
||||
message: Optional[dict] = None
|
||||
files: list[Any] = []
|
||||
files: list[Any] = []
|
||||
|
||||
@@ -211,4 +211,3 @@ class MockSessionService:
|
||||
|
||||
async def get_state(self) -> dict:
|
||||
return self.state
|
||||
|
||||
|
||||
@@ -1,41 +1,27 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# pylint: disable=W0612,E0611,C2801
|
||||
import os
|
||||
from typing import Optional
|
||||
from datetime import datetime
|
||||
import traceback
|
||||
from datetime import datetime
|
||||
|
||||
from loguru import logger
|
||||
|
||||
from agentscope.message import Msg
|
||||
from agentscope.model import (
|
||||
OpenAIChatModel,
|
||||
AnthropicChatModel,
|
||||
DashScopeChatModel,
|
||||
)
|
||||
from agentscope.formatter import (
|
||||
OpenAIChatFormatter,
|
||||
AnthropicChatFormatter,
|
||||
DashScopeChatFormatter,
|
||||
)
|
||||
from agentscope.formatter import DashScopeChatFormatter
|
||||
from agentscope.mcp import StdIOStatefulClient
|
||||
from agentscope.memory import InMemoryMemory
|
||||
from agentscope.mcp import StdIOStatefulClient, StatefulClientBase
|
||||
from agentscope.token import OpenAITokenCounter
|
||||
from agentscope.message import Msg
|
||||
from agentscope.model import DashScopeChatModel
|
||||
from agentscope_runtime.sandbox.box.sandbox import Sandbox
|
||||
|
||||
from alias.agent.agents import (
|
||||
MetaPlanner,
|
||||
DeepResearchAgent,
|
||||
BrowserAgent,
|
||||
)
|
||||
from alias.agent.tools import AliasToolkit
|
||||
from alias.agent.agents import BrowserAgent, DeepResearchAgent, MetaPlanner
|
||||
from alias.agent.agents._planning_tools._worker_manager import share_tools
|
||||
from alias.agent.utils.constants import BROWSER_AGENT_DESCRIPTION
|
||||
from alias.agent.mock import MockSessionService
|
||||
from alias.agent.tools import AliasToolkit
|
||||
from alias.agent.tools.improved_tools import DashScopeMultiModalTools
|
||||
from alias.agent.tools.toolkit_hooks import LongTextPostHook
|
||||
from alias.agent.utils.constants import BROWSER_AGENT_DESCRIPTION
|
||||
|
||||
# Open source version always uses mock services
|
||||
from alias.agent.mock import MockSessionService
|
||||
|
||||
SessionService = MockSessionService
|
||||
|
||||
|
||||
@@ -126,7 +112,7 @@ async def add_tools(
|
||||
|
||||
|
||||
async def arun_agents(
|
||||
session_service: SessionService,
|
||||
session_service: SessionService, # type: ignore[valid-type]
|
||||
sandbox: Sandbox = None,
|
||||
enable_clarification: bool = True,
|
||||
):
|
||||
@@ -188,7 +174,7 @@ async def arun_agents(
|
||||
|
||||
async def test_deepresearch_agent(
|
||||
task_str: str,
|
||||
session_service: SessionService,
|
||||
session_service: SessionService, # type: ignore[valid-type]
|
||||
sandbox: Sandbox = None,
|
||||
):
|
||||
instruction = Msg(
|
||||
@@ -234,7 +220,7 @@ async def test_deepresearch_agent(
|
||||
|
||||
async def test_browseruse_agent(
|
||||
task_str: str,
|
||||
session_service: SessionService,
|
||||
session_service: SessionService, # type: ignore[valid-type]
|
||||
sandbox: Sandbox = None,
|
||||
):
|
||||
time_str = datetime.now().strftime("%Y%m%d%H%M%S")
|
||||
|
||||
@@ -1,19 +1,16 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# pylint: disable=R1724
|
||||
from typing import Optional, Callable, Any
|
||||
import asyncio
|
||||
from typing import Any, Callable, Optional
|
||||
|
||||
from loguru import logger
|
||||
|
||||
from agentscope.mcp import StatefulClientBase, MCPClientBase
|
||||
from agentscope.tool import (
|
||||
Toolkit,
|
||||
ToolResponse,
|
||||
)
|
||||
from agentscope.message import ToolUseBlock, TextBlock
|
||||
from agentscope_runtime.sandbox import FilesystemSandbox, BrowserSandbox
|
||||
from agentscope.mcp import MCPClientBase, StatefulClientBase
|
||||
from agentscope.message import TextBlock, ToolUseBlock
|
||||
from agentscope.tool import ToolResponse, Toolkit
|
||||
|
||||
from alias.agent.tools.toolkit_hooks import (
|
||||
LongTextPostHook
|
||||
LongTextPostHook,
|
||||
)
|
||||
from alias.agent.tools.improved_tools import ImprovedFileOperations
|
||||
from alias.agent.tools.tool_blacklist import TOOL_BLACKLIST
|
||||
@@ -21,6 +18,9 @@ from alias.agent.tools.toolkit_hooks import read_file_post_hook
|
||||
from alias.runtime.alias_sandbox.alias_sandbox import AliasSandbox
|
||||
|
||||
|
||||
FilesystemSandbox = AliasSandbox
|
||||
|
||||
|
||||
class AliasToolkit(Toolkit):
|
||||
def __init__( # pylint: disable=W0102
|
||||
self,
|
||||
@@ -44,9 +44,8 @@ class AliasToolkit(Toolkit):
|
||||
# Get tools
|
||||
tools_schema = self.sandbox.list_tools()
|
||||
for category, function_dicts in tools_schema.items():
|
||||
if (
|
||||
(is_browser_toolkit and category == "playwright")
|
||||
or (not is_browser_toolkit and category != "playwright")
|
||||
if (is_browser_toolkit and category == "playwright") or (
|
||||
not is_browser_toolkit and category != "playwright"
|
||||
):
|
||||
for _, function_json in function_dicts.items():
|
||||
if function_json["name"] not in self.tool_blacklist:
|
||||
@@ -66,7 +65,7 @@ class AliasToolkit(Toolkit):
|
||||
def _add_io_function(
|
||||
self,
|
||||
json_schema: dict,
|
||||
is_browser_tool: bool = False
|
||||
is_browser_tool: bool = False, # pylint: disable=W0613
|
||||
) -> None:
|
||||
tool_name = json_schema["name"]
|
||||
|
||||
@@ -142,8 +141,9 @@ class AliasToolkit(Toolkit):
|
||||
if tool_func.startswith(("read_file", "read_multiple_files")):
|
||||
self.tools[tool_func].postprocess_func = read_file_post_hook
|
||||
if tool_func.startswith("tavily"):
|
||||
self.tools[tool_func].postprocess_func = \
|
||||
long_text_hook.truncate_and_save_response
|
||||
self.tools[
|
||||
tool_func
|
||||
].postprocess_func = long_text_hook.truncate_and_save_response
|
||||
|
||||
async def add_and_connet_mcp_client(
|
||||
self,
|
||||
@@ -193,10 +193,10 @@ async def test_toolkit():
|
||||
type="tool_use",
|
||||
id="",
|
||||
name="list_allowed_directories",
|
||||
input={}
|
||||
)
|
||||
input={},
|
||||
),
|
||||
)
|
||||
print(f"Allow directory:")
|
||||
print("Allow directory:")
|
||||
async for response in res:
|
||||
print(response)
|
||||
|
||||
@@ -216,5 +216,6 @@ async def test_toolkit():
|
||||
|
||||
await toolkit.close_mcp_clients()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(test_toolkit())
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
"""
|
||||
Improved tools module for Alias agent toolkit.
|
||||
|
||||
This module contains enhanced tool functions that provide additional functionality
|
||||
beyond the basic tools available in the standard toolkit.
|
||||
This module contains enhanced tool functions that provide additional
|
||||
functionality beyond the basic tools available in the standard toolkit.
|
||||
"""
|
||||
|
||||
from .file_operations import ImprovedFileOperations
|
||||
|
||||
@@ -7,19 +7,20 @@ original read_file functionality and adds support for
|
||||
reading specific line ranges from files.
|
||||
"""
|
||||
|
||||
from typing import Optional
|
||||
from loguru import logger
|
||||
import asyncio
|
||||
import os
|
||||
from typing import Optional
|
||||
|
||||
from loguru import logger
|
||||
|
||||
from agentscope.tool import ToolResponse
|
||||
from agentscope.message import TextBlock
|
||||
from agentscope.tool import ToolResponse
|
||||
|
||||
from alias.agent.utils.constants import TMP_FILE_DIR
|
||||
from alias.agent.tools.sandbox_util import (
|
||||
TEXT_EXTENSIONS,
|
||||
create_or_edit_workspace_file,
|
||||
create_workspace_directory
|
||||
create_workspace_directory,
|
||||
)
|
||||
from alias.runtime.alias_sandbox import AliasSandbox
|
||||
|
||||
@@ -41,7 +42,7 @@ class ImprovedFileOperations:
|
||||
"""init with sandbox"""
|
||||
self.sandbox = sandbox
|
||||
|
||||
async def read_file(
|
||||
async def read_file( # pylint: disable=R0911,R0912
|
||||
self,
|
||||
file_path: str,
|
||||
offset: int = 0,
|
||||
@@ -96,13 +97,14 @@ class ImprovedFileOperations:
|
||||
if self.sandbox is None:
|
||||
return ToolResponse(
|
||||
metadata={
|
||||
"success": False, "error": "No sandbox provided"
|
||||
"success": False,
|
||||
"error": "No sandbox provided",
|
||||
},
|
||||
content=[
|
||||
TextBlock(
|
||||
type="text",
|
||||
text="Error: No sandbox provided to "
|
||||
"call the original read_file tool",
|
||||
"call the original read_file tool",
|
||||
),
|
||||
],
|
||||
)
|
||||
@@ -116,7 +118,7 @@ class ImprovedFileOperations:
|
||||
# Call the original read_file tool
|
||||
tool_res = self.sandbox.call_tool(
|
||||
name="read_file",
|
||||
arguments=params
|
||||
arguments=params,
|
||||
)
|
||||
elif file_extension in TO_MARKDOWN_SUPPORT_MAPPING:
|
||||
tool_res = _transfer_to_markdown_text(file_path, self.sandbox)
|
||||
@@ -130,9 +132,10 @@ class ImprovedFileOperations:
|
||||
):
|
||||
return ToolResponse(
|
||||
metadata={
|
||||
"success": False, "error": "Error when read file"
|
||||
"success": False,
|
||||
"error": "Error when read file",
|
||||
},
|
||||
content=tool_res.get("content", [])
|
||||
content=tool_res.get("content", []),
|
||||
)
|
||||
elif (
|
||||
tool_res.get("isError", True)
|
||||
@@ -144,15 +147,15 @@ class ImprovedFileOperations:
|
||||
TextBlock(
|
||||
type="text",
|
||||
text=f"Fail to read file on path {file_path}",
|
||||
)
|
||||
]
|
||||
),
|
||||
],
|
||||
)
|
||||
|
||||
# Get the text content from the first content block
|
||||
full_content = ""
|
||||
for block in tool_res.get("content", []):
|
||||
if isinstance(block, dict) and 'text' in block:
|
||||
full_content += block['text'] + "\n"
|
||||
if isinstance(block, dict) and "text" in block:
|
||||
full_content += block["text"] + "\n"
|
||||
|
||||
# Split into lines
|
||||
lines = full_content.splitlines(keepends=True)
|
||||
@@ -171,7 +174,7 @@ class ImprovedFileOperations:
|
||||
)
|
||||
|
||||
# Handle offset and limit
|
||||
start_line = (offset or 0) # 0-based index
|
||||
start_line = offset or 0 # 0-based index
|
||||
end_line = start_line + (limit or total_lines)
|
||||
|
||||
# Validate range
|
||||
@@ -182,7 +185,7 @@ class ImprovedFileOperations:
|
||||
TextBlock(
|
||||
type="text",
|
||||
text=f"Error: Start line {offset} is "
|
||||
f"beyond file length ({total_lines} lines).",
|
||||
f"beyond file length ({total_lines} lines).",
|
||||
),
|
||||
],
|
||||
)
|
||||
@@ -193,11 +196,13 @@ class ImprovedFileOperations:
|
||||
# Extract the requested lines
|
||||
selected_lines = lines[start_line:end_line]
|
||||
|
||||
content = ''.join(selected_lines)
|
||||
content = "".join(selected_lines)
|
||||
|
||||
# Add summary information
|
||||
summary = (f"Read lines {start_line}-{end_line} of "
|
||||
f"{total_lines} total lines from '{file_path}'")
|
||||
summary = (
|
||||
f"Read lines {start_line}-{end_line} of "
|
||||
f"{total_lines} total lines from '{file_path}'"
|
||||
)
|
||||
|
||||
# save as markdown
|
||||
return_content = [
|
||||
@@ -208,18 +213,20 @@ class ImprovedFileOperations:
|
||||
TextBlock(
|
||||
type="text",
|
||||
text=summary,
|
||||
)
|
||||
),
|
||||
]
|
||||
if file_extension in TO_MARKDOWN_SUPPORT_MAPPING:
|
||||
file_name_with_ext = os.path.basename(file_path)
|
||||
filename_without_ext = os.path.splitext(file_name_with_ext)[0]
|
||||
file_path = os.path.join(
|
||||
TMP_FILE_DIR,
|
||||
filename_without_ext + ".md"
|
||||
filename_without_ext + ".md",
|
||||
)
|
||||
create_workspace_directory(self.sandbox, TMP_FILE_DIR)
|
||||
create_or_edit_workspace_file(
|
||||
self.sandbox, file_path, full_content
|
||||
self.sandbox,
|
||||
file_path,
|
||||
full_content,
|
||||
)
|
||||
return_content.append(
|
||||
TextBlock(
|
||||
@@ -229,8 +236,8 @@ class ImprovedFileOperations:
|
||||
"The (full) file is converted as markdown file"
|
||||
" and saved completely at: "
|
||||
f"{file_path}"
|
||||
)
|
||||
)
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
return ToolResponse(
|
||||
@@ -257,7 +264,8 @@ class ImprovedFileOperations:
|
||||
|
||||
|
||||
def _transfer_to_markdown_text(
|
||||
file_path: str, sandbox: AliasSandbox = None
|
||||
file_path: str,
|
||||
sandbox: AliasSandbox = None,
|
||||
) -> dict:
|
||||
ext = os.path.splitext(file_path)[1].lower()
|
||||
|
||||
@@ -268,46 +276,46 @@ def _transfer_to_markdown_text(
|
||||
{
|
||||
"type": "text",
|
||||
"text": f"File extension '{ext}' not supported in "
|
||||
f"{TO_MARKDOWN_SUPPORT_MAPPING}."
|
||||
}
|
||||
]
|
||||
f"{TO_MARKDOWN_SUPPORT_MAPPING}.",
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
params = {
|
||||
"uri": "file:" + file_path
|
||||
"uri": "file:" + file_path,
|
||||
}
|
||||
try:
|
||||
res = sandbox.call_tool(
|
||||
result = sandbox.call_tool( # pylint: disable=W0621
|
||||
name="convert_to_markdown",
|
||||
arguments=params
|
||||
arguments=params,
|
||||
)
|
||||
content = res.get("content", [])
|
||||
content = result.get("content", [])
|
||||
new_content = []
|
||||
for i, block in enumerate(content):
|
||||
for i, _block in enumerate(content):
|
||||
if content[i].get("text", "").startswith("Converted content:"):
|
||||
continue
|
||||
elif content[i].get("text", "").startswith("Output file:"):
|
||||
if content[i].get("text", "").startswith("Output file:"):
|
||||
continue
|
||||
else:
|
||||
new_content.append(res["content"][i])
|
||||
new_content.append(result["content"][i])
|
||||
|
||||
res["content"] = new_content
|
||||
result["content"] = new_content
|
||||
except Exception as e:
|
||||
res = {
|
||||
result = {
|
||||
"isError": True,
|
||||
"error": str(e)
|
||||
"error": str(e),
|
||||
}
|
||||
|
||||
return res
|
||||
return result
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
from alias.agent.tools.sandbox_util import copy_local_file_to_workspace
|
||||
|
||||
with AliasSandbox() as box:
|
||||
res = copy_local_file_to_workspace(
|
||||
box,
|
||||
"/Users/zitao.l/Downloads/22051_Which_LLM_Multi_Agent.pdf",
|
||||
"/workspace/test.pdf"
|
||||
"/workspace/test.pdf",
|
||||
)
|
||||
print(res)
|
||||
toolset = ImprovedFileOperations(box)
|
||||
|
||||
@@ -95,7 +95,7 @@ class DashScopeMultiModalTools:
|
||||
"content": [
|
||||
{
|
||||
"text": "Transcript the content in the audio "
|
||||
"to text."
|
||||
"to text.",
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -253,6 +253,7 @@ class DashScopeMultiModalTools:
|
||||
)
|
||||
except Exception as e:
|
||||
import traceback
|
||||
|
||||
print(traceback.format_exc())
|
||||
return ToolResponse(
|
||||
[
|
||||
@@ -268,7 +269,7 @@ if __name__ == "__main__":
|
||||
with AliasSandbox() as box:
|
||||
tool_result = box.call_tool(
|
||||
"run_shell_command",
|
||||
arguments={"command": "apt update"}
|
||||
arguments={"command": "apt update"},
|
||||
)
|
||||
print(tool_result)
|
||||
tool_result = box.call_tool(
|
||||
@@ -299,7 +300,7 @@ if __name__ == "__main__":
|
||||
)
|
||||
toolset = DashScopeMultiModalTools(
|
||||
sandbox=box,
|
||||
dashscope_api_key=os.getenv("DASHSCOPE_API_KEY", "")
|
||||
dashscope_api_key=os.getenv("DASHSCOPE_API_KEY", ""),
|
||||
)
|
||||
result = toolset.dashscope_image_to_text(
|
||||
image_url=picture_path,
|
||||
|
||||
@@ -1,14 +1,17 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import os
|
||||
from typing import Optional
|
||||
import json
|
||||
from pathlib import Path
|
||||
import base64
|
||||
from loguru import logger
|
||||
import io
|
||||
import json
|
||||
import os
|
||||
import tarfile
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
|
||||
from agentscope_runtime.sandbox.manager.container_clients.docker_client import DockerClient
|
||||
from loguru import logger
|
||||
|
||||
from agentscope_runtime.sandbox.manager.container_clients.docker_client import ( # noqa: E501 # pylint: disable=C0301
|
||||
DockerClient,
|
||||
)
|
||||
from alias.runtime.alias_sandbox import AliasSandbox
|
||||
|
||||
|
||||
@@ -386,7 +389,7 @@ def copy_local_file_to_workspace(
|
||||
{
|
||||
"type": "text",
|
||||
"text": "Copying file is not support sandbox "
|
||||
f"with client type {type(client)}",
|
||||
f"with client type {type(client)}",
|
||||
},
|
||||
],
|
||||
}
|
||||
@@ -395,7 +398,8 @@ def copy_local_file_to_workspace(
|
||||
|
||||
# Create a tar archive in memory
|
||||
tar_stream = io.BytesIO()
|
||||
tar = tarfile.open(fileobj=tar_stream, mode='w')
|
||||
# pylint: disable=R1732
|
||||
tar = tarfile.open(fileobj=tar_stream, mode="w")
|
||||
|
||||
# Add file to tar archive
|
||||
tar.add(local_path, arcname=os.path.basename(target_path))
|
||||
@@ -418,7 +422,6 @@ def copy_local_file_to_workspace(
|
||||
}
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
with AliasSandbox() as box:
|
||||
create_or_edit_workspace_file(
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from .long_text_post_hook import LongTextPostHook
|
||||
from .read_file_post_hook import read_file_post_hook
|
||||
|
||||
__all__ = [
|
||||
"LongTextPostHook",
|
||||
"read_file_post_hook",
|
||||
]
|
||||
]
|
||||
|
||||
@@ -10,7 +10,7 @@ from agentscope.message import ToolUseBlock, TextBlock
|
||||
from alias.agent.utils.constants import TMP_FILE_DIR
|
||||
from alias.agent.tools.sandbox_util import (
|
||||
create_or_edit_workspace_file,
|
||||
create_workspace_directory
|
||||
create_workspace_directory,
|
||||
)
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ class LongTextPostHook:
|
||||
def __init__(self, sandbox):
|
||||
self.sandbox = sandbox
|
||||
|
||||
def truncate_and_save_response(
|
||||
def truncate_and_save_response( # pylint: disable=R1710
|
||||
self,
|
||||
tool_use: ToolUseBlock, # pylint: disable=W0613
|
||||
tool_response: ToolResponse,
|
||||
@@ -35,25 +35,24 @@ class LongTextPostHook:
|
||||
tool_response: The tool response to potentially truncate.
|
||||
|
||||
Note:
|
||||
The budget is set to approximately 80K tokens (8194 * 10 characters)
|
||||
to ensure responses remain manageable for the language model.
|
||||
The budget is set to approximately 80K tokens
|
||||
(8194 * 10 characters) to ensure responses remain
|
||||
manageable for the language model.
|
||||
"""
|
||||
# Set budget to prevent overwhelming the model with too much content
|
||||
budget = 8194 * 10 # Approximately 80K tokens of content
|
||||
append_hint = (
|
||||
"\n\n[Content is too long and truncated....]"
|
||||
)
|
||||
append_hint = "\n\n[Content is too long and truncated....]"
|
||||
|
||||
new_tool_response = ToolResponse(
|
||||
id=tool_response.id,
|
||||
stream=tool_response.stream,
|
||||
is_last=tool_response.is_last,
|
||||
is_interrupted=tool_response.is_interrupted,
|
||||
content=[]
|
||||
content=[],
|
||||
)
|
||||
if isinstance(tool_response.content, list):
|
||||
save_text_block = None
|
||||
for i, block in enumerate(tool_response.content):
|
||||
for _i, block in enumerate(tool_response.content):
|
||||
if block["type"] == "text":
|
||||
text = block["text"]
|
||||
text_len = len(text)
|
||||
@@ -67,7 +66,7 @@ class LongTextPostHook:
|
||||
tmp_file_name_prefix = tool_use.get("name", "")
|
||||
save_text_block = self._save_tmp_file(
|
||||
tmp_file_name_prefix,
|
||||
tool_response.content
|
||||
tool_response.content,
|
||||
)
|
||||
new_tool_response.append = (
|
||||
text[:threshold] + append_hint
|
||||
@@ -75,8 +74,8 @@ class LongTextPostHook:
|
||||
new_tool_response.content.append(
|
||||
TextBlock(
|
||||
type="text",
|
||||
text=text[:threshold] + append_hint
|
||||
)
|
||||
text=text[:threshold] + append_hint,
|
||||
),
|
||||
)
|
||||
else:
|
||||
new_tool_response.content.append(block)
|
||||
@@ -91,29 +90,32 @@ class LongTextPostHook:
|
||||
tmp_file_name_prefix = tool_use.get("name", "")
|
||||
save_text_block = self._save_tmp_file(
|
||||
tmp_file_name_prefix,
|
||||
tool_response.content
|
||||
tool_response.content,
|
||||
)
|
||||
# Calculate truncation threshold (80% of proportional budget)
|
||||
threshold = int(budget / text_len * len(text) * 0.8)
|
||||
tool_response.content = (
|
||||
text[:threshold] + append_hint
|
||||
)
|
||||
tool_response.content = text[:threshold] + append_hint
|
||||
tool_response.content = [
|
||||
TextBlock(type="text", text=tool_response.content),
|
||||
save_text_block
|
||||
save_text_block,
|
||||
]
|
||||
|
||||
return tool_response
|
||||
return tool_response
|
||||
|
||||
def _save_tmp_file(self, save_file_name_prefix: str, content: list | str):
|
||||
create_workspace_directory(self.sandbox, TMP_FILE_DIR)
|
||||
save_file_name = save_file_name_prefix + "-" + str(
|
||||
uuid.uuid4().hex[:8]
|
||||
save_file_name = (
|
||||
save_file_name_prefix
|
||||
+ "-"
|
||||
+ str(
|
||||
uuid.uuid4().hex[:8],
|
||||
)
|
||||
)
|
||||
file_path = os.path.join(TMP_FILE_DIR, save_file_name)
|
||||
json_str = json.dumps(content, ensure_ascii=False, indent=2)
|
||||
wrapped = '\\n'.join(
|
||||
[textwrap.fill(line, width=500) for line in json_str.split('\\n')])
|
||||
wrapped = "\\n".join(
|
||||
[textwrap.fill(line, width=500) for line in json_str.split("\\n")],
|
||||
)
|
||||
create_or_edit_workspace_file(
|
||||
self.sandbox,
|
||||
file_path,
|
||||
@@ -122,10 +124,8 @@ class LongTextPostHook:
|
||||
return TextBlock(
|
||||
type="text",
|
||||
text=f"Dump the complete long file at {file_path}. "
|
||||
"Don't try to read the complete file directly. "
|
||||
"Use `grep -C 10 'YOUR_PATTERN' {file_path}` or "
|
||||
"other bash command to extract "
|
||||
"useful information.",
|
||||
"Don't try to read the complete file directly. "
|
||||
"Use `grep -C 10 'YOUR_PATTERN' {file_path}` or "
|
||||
"other bash command to extract "
|
||||
"useful information.",
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from agentscope.message import ToolUseBlock, TextBlock
|
||||
from agentscope.tool import ToolResponse
|
||||
|
||||
@@ -21,8 +22,8 @@ def _summarize_csv(text_block: TextBlock) -> None:
|
||||
|
||||
|
||||
def read_file_post_hook(
|
||||
tool_use: ToolUseBlock,
|
||||
tool_response: ToolResponse,
|
||||
tool_use: ToolUseBlock,
|
||||
tool_response: ToolResponse,
|
||||
) -> ToolResponse:
|
||||
"""
|
||||
Condense large CSV outputs after `read_file` or `read_multiple_files`.
|
||||
|
||||
@@ -6,20 +6,17 @@ Alias Command Line Interface
|
||||
This module provides a terminal executable entry point
|
||||
for the Alias agent application.
|
||||
"""
|
||||
import json
|
||||
from typing import Optional
|
||||
import asyncio
|
||||
import argparse
|
||||
import sys
|
||||
import asyncio
|
||||
import os
|
||||
import sys
|
||||
import traceback
|
||||
import webbrowser
|
||||
from typing import Optional
|
||||
|
||||
from loguru import logger
|
||||
from agentscope.agent import UserAgent, TerminalUserInput
|
||||
from agentscope_runtime.sandbox import FilesystemSandbox, BrowserSandbox
|
||||
from agentscope_runtime.sandbox.box.sandbox import Sandbox
|
||||
from agentscope.mcp import StdIOStatefulClient
|
||||
|
||||
from agentscope.agent import TerminalUserInput, UserAgent
|
||||
|
||||
from alias.agent.mock import MockSessionService, UserMessage
|
||||
from alias.agent.run import (
|
||||
@@ -27,10 +24,8 @@ from alias.agent.run import (
|
||||
test_browseruse_agent,
|
||||
test_deepresearch_agent,
|
||||
)
|
||||
from alias.agent.tools import AliasToolkit
|
||||
from alias.agent.tools.improved_tools import DashScopeMultiModalTools
|
||||
from alias.runtime.alias_sandbox.alias_sandbox import AliasSandbox
|
||||
from alias.agent.tools.sandbox_util import copy_local_file_to_workspace
|
||||
from alias.runtime.alias_sandbox.alias_sandbox import AliasSandbox
|
||||
|
||||
|
||||
async def run_agent_task(
|
||||
@@ -40,7 +35,7 @@ async def run_agent_task(
|
||||
) -> None:
|
||||
"""
|
||||
Run an agent task with the specified configuration.
|
||||
|
||||
|
||||
Args:
|
||||
user_msg: The user's task/query
|
||||
mode: Agent mode ('all', 'worker', 'dr', 'browser')
|
||||
@@ -52,15 +47,15 @@ async def run_agent_task(
|
||||
# Create initial user message
|
||||
user_agent = UserAgent(name="User")
|
||||
user_agent.override_instance_input_method(
|
||||
input_method = TerminalUserInput(
|
||||
input_hint = "User (Enter `exit` or `quit` to exit): "
|
||||
)
|
||||
input_method=TerminalUserInput(
|
||||
input_hint="User (Enter `exit` or `quit` to exit): ",
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
# Run agent with sandbox context
|
||||
with AliasSandbox() as sandbox:
|
||||
logger.info(
|
||||
f"Sandbox mount dir: {sandbox.get_info().get('mount_dir')}"
|
||||
f"Sandbox mount dir: {sandbox.get_info().get('mount_dir')}",
|
||||
)
|
||||
logger.info(f"Sandbox desktop URL: {sandbox.desktop_url}")
|
||||
webbrowser.open(sandbox.desktop_url)
|
||||
@@ -68,28 +63,27 @@ async def run_agent_task(
|
||||
if files:
|
||||
target_paths = []
|
||||
logger.info(
|
||||
f"Uploading {len(files)} file(s) to sandbox workspace..."
|
||||
f"Uploading {len(files)} file(s) to sandbox workspace...",
|
||||
)
|
||||
for file_path in files:
|
||||
if not os.path.exists(file_path):
|
||||
logger.error(f"File not found: {file_path}")
|
||||
continue
|
||||
|
||||
|
||||
# Get the filename and construct target path in workspace
|
||||
filename = os.path.basename(file_path)
|
||||
target_path = f"/workspace/{filename}"
|
||||
|
||||
|
||||
logger.info(f"Uploading {file_path} to {target_path}")
|
||||
result = copy_local_file_to_workspace(
|
||||
sandbox=sandbox,
|
||||
local_path=file_path,
|
||||
target_path=target_path,
|
||||
)
|
||||
|
||||
|
||||
if result.get("isError"):
|
||||
raise ValueError(f"Failed to upload {file_path}: {result}")
|
||||
else:
|
||||
logger.info(f"Successfully uploaded to {result}")
|
||||
logger.info(f"Successfully uploaded to {result}")
|
||||
|
||||
target_paths.append(result.get("content", [])[0].get("text"))
|
||||
|
||||
@@ -99,23 +93,24 @@ async def run_agent_task(
|
||||
content=user_msg,
|
||||
)
|
||||
await session.create_message(initial_user_message)
|
||||
|
||||
|
||||
await _run_agent_loop(
|
||||
mode=mode,
|
||||
session=session,
|
||||
user_agent=user_agent,
|
||||
sandbox=sandbox
|
||||
sandbox=sandbox,
|
||||
)
|
||||
|
||||
|
||||
async def _run_agent_loop(
|
||||
mode: str,
|
||||
session: MockSessionService,
|
||||
user_agent: UserAgent,
|
||||
sandbox: FilesystemSandbox,
|
||||
sandbox: AliasSandbox,
|
||||
) -> None:
|
||||
"""
|
||||
Execute the agent loop with follow-up interactions.
|
||||
|
||||
|
||||
Args:
|
||||
mode: Agent mode to run
|
||||
session: Session service instance
|
||||
@@ -133,8 +128,10 @@ async def _run_agent_loop(
|
||||
sandbox=sandbox,
|
||||
)
|
||||
break
|
||||
elif mode == "dr":
|
||||
usr_msg = (await session.get_messages())[-1].message.get("content")
|
||||
if mode == "dr":
|
||||
usr_msg = (await session.get_messages())[-1].message.get(
|
||||
"content",
|
||||
)
|
||||
logger.info(f"--> user_msg: {usr_msg}")
|
||||
await test_deepresearch_agent(
|
||||
usr_msg,
|
||||
@@ -142,7 +139,7 @@ async def _run_agent_loop(
|
||||
sandbox=sandbox,
|
||||
)
|
||||
break
|
||||
elif mode == "all":
|
||||
if mode == "all":
|
||||
await arun_agents(
|
||||
session,
|
||||
sandbox=sandbox,
|
||||
@@ -150,16 +147,16 @@ async def _run_agent_loop(
|
||||
)
|
||||
else:
|
||||
raise ValueError(f"Unknown mode: {mode}")
|
||||
|
||||
|
||||
# Check for follow-up interaction
|
||||
follow_msg = await user_agent()
|
||||
if (
|
||||
len(follow_msg.content) == 0
|
||||
or follow_msg.content.lower() in ["exit", "quit"]
|
||||
):
|
||||
if len(follow_msg.content) == 0 or follow_msg.content.lower() in [
|
||||
"exit",
|
||||
"quit",
|
||||
]:
|
||||
logger.info("Exiting agent loop")
|
||||
break
|
||||
|
||||
|
||||
await session.create_message(UserMessage(content=follow_msg.content))
|
||||
|
||||
|
||||
@@ -174,25 +171,26 @@ def main():
|
||||
),
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
)
|
||||
|
||||
|
||||
subparsers = parser.add_subparsers(
|
||||
dest="command", help="Available commands"
|
||||
dest="command",
|
||||
help="Available commands",
|
||||
)
|
||||
|
||||
|
||||
# Run command
|
||||
run_parser = subparsers.add_parser(
|
||||
"run",
|
||||
help="Run an agent task",
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
)
|
||||
|
||||
|
||||
run_parser.add_argument(
|
||||
"--task",
|
||||
type=str,
|
||||
required=True,
|
||||
help="The task or query for the agent to execute",
|
||||
)
|
||||
|
||||
|
||||
run_parser.add_argument(
|
||||
"--mode",
|
||||
choices=["all", "worker", "dr", "browser"],
|
||||
@@ -205,37 +203,37 @@ def main():
|
||||
"'browser' (browser agent)"
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
run_parser.add_argument(
|
||||
"--verbose",
|
||||
"-v",
|
||||
action="store_true",
|
||||
help="Enable verbose logging",
|
||||
)
|
||||
|
||||
|
||||
run_parser.add_argument(
|
||||
"--files",
|
||||
"-f",
|
||||
type=str,
|
||||
nargs="+",
|
||||
help="Local file paths to upload to sandbox workspace "
|
||||
"for agent to use (e.g., --files file1.txt file2.csv)",
|
||||
"for agent to use (e.g., --files file1.txt file2.csv)",
|
||||
)
|
||||
|
||||
|
||||
# Version command
|
||||
parser.add_argument(
|
||||
"--version",
|
||||
action="version",
|
||||
version="Alias 0.1.0",
|
||||
)
|
||||
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
|
||||
# Configure logging
|
||||
if hasattr(args, "verbose") and args.verbose:
|
||||
logger.remove()
|
||||
logger.add(sys.stderr, level="DEBUG")
|
||||
|
||||
|
||||
# Handle commands
|
||||
if args.command == "run":
|
||||
try:
|
||||
@@ -244,7 +242,7 @@ def main():
|
||||
user_msg=args.task,
|
||||
mode=args.mode,
|
||||
files=args.files if hasattr(args, "files") else None,
|
||||
)
|
||||
),
|
||||
)
|
||||
except KeyboardInterrupt:
|
||||
logger.info("\nInterrupted by user")
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""Runtime module for Alias"""
|
||||
|
||||
from agentscope_runtime.sandbox.box.sandbox import Sandbox
|
||||
__all__ = ["alias_sandbox"]
|
||||
|
||||
# Import submodule to make it accessible via alias.runtime.alias_sandbox
|
||||
from . import alias_sandbox # noqa: E402, F401
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from .alias_sandbox import AliasSandbox
|
||||
|
||||
__all__ = ['AliasSandbox']
|
||||
__all__ = ["AliasSandbox"]
|
||||
|
||||
Reference in New Issue
Block a user