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(directory):
|
||||
os.makedirs(directory, exist_ok=True)
|
||||
if (
|
||||
isinstance(save_info, list)
|
||||
and len(save_info) > 0
|
||||
and isinstance(save_info[0], Msg)
|
||||
):
|
||||
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):
|
||||
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)
|
||||
@@ -691,14 +694,12 @@ class DeepResearchAgent(AliasAgentBase):
|
||||
type="text",
|
||||
text="Still need to do more research "
|
||||
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.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
|
||||
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
|
||||
@@ -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(),
|
||||
)
|
||||
|
||||
@@ -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.."
|
||||
" 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"
|
||||
|
||||
|
||||
@@ -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,7 +97,8 @@ class ImprovedFileOperations:
|
||||
if self.sandbox is None:
|
||||
return ToolResponse(
|
||||
metadata={
|
||||
"success": False, "error": "No sandbox provided"
|
||||
"success": False,
|
||||
"error": "No sandbox provided",
|
||||
},
|
||||
content=[
|
||||
TextBlock(
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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,3 +1,4 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from .long_text_post_hook import LongTextPostHook
|
||||
from .read_file_post_hook import 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,
|
||||
@@ -127,5 +129,3 @@ class LongTextPostHook:
|
||||
"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
|
||||
|
||||
|
||||
@@ -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(
|
||||
@@ -53,14 +48,14 @@ async def run_agent_task(
|
||||
user_agent = UserAgent(name="User")
|
||||
user_agent.override_instance_input_method(
|
||||
input_method=TerminalUserInput(
|
||||
input_hint = "User (Enter `exit` or `quit` to exit): "
|
||||
)
|
||||
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,7 +63,7 @@ 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):
|
||||
@@ -88,7 +83,6 @@ async def run_agent_task(
|
||||
|
||||
if result.get("isError"):
|
||||
raise ValueError(f"Failed to upload {file_path}: {result}")
|
||||
else:
|
||||
logger.info(f"Successfully uploaded to {result}")
|
||||
|
||||
target_paths.append(result.get("content", [])[0].get("text"))
|
||||
@@ -104,14 +98,15 @@ async def run_agent_task(
|
||||
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.
|
||||
@@ -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,
|
||||
@@ -153,10 +150,10 @@ async def _run_agent_loop(
|
||||
|
||||
# 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
|
||||
|
||||
@@ -176,7 +173,8 @@ def main():
|
||||
)
|
||||
|
||||
subparsers = parser.add_subparsers(
|
||||
dest="command", help="Available commands"
|
||||
dest="command",
|
||||
help="Available commands",
|
||||
)
|
||||
|
||||
# Run command
|
||||
@@ -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