Add pre commit (#26)

This commit is contained in:
Lamont Huffman
2025-11-05 11:44:19 +08:00
committed by GitHub
parent 2164371e64
commit 30d86efbb3
33 changed files with 565 additions and 496 deletions

24
.eslintrc Normal file
View File

@@ -0,0 +1,24 @@
{
"env": {
"browser": true,
"es2021": true
},
"parserOptions": {
"ecmaVersion": 2021,
"sourceType": "module",
"ecmaFeatures": {
"jsx": true
}
},
"rules": {
"semi": ["error", "always"],
"quotes": ["error", "double"],
"indent": ["error", 2],
"linebreak-style": ["error", "unix"],
"brace-style": ["error", "1tbs"],
"curly": ["error", "all"],
"no-eval": ["error"],
"prefer-const": ["error"],
"arrow-spacing": ["error", { "before": true, "after": true }]
}
}

View File

@@ -13,9 +13,27 @@ jobs:
OS: ${{ matrix.os }}
PYTHON: '3.10'
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Setup Python
uses: actions/setup-python@v3
uses: actions/setup-python@v5
with:
python-version: '3.10'
- name: Update setuptools and wheel
run: |
pip install setuptools==68.2.2 wheel==0.41.2
- name: Install pre-commit
run: |
pip install pre-commit
- name: Install pre-commit hooks
run: |
pre-commit install
- name: Run pre-commit
run: |
pre-commit run --all-files > pre-commit.log 2>&1 || true
cat pre-commit.log
if grep -q Failed pre-commit.log; then
echo -e "\e[41m [**FAIL**] Please install pre-commit and format your code first. \e[0m"
exit 1
fi
echo -e "\e[46m ********************************Passed******************************** \e[0m"

View File

@@ -1,8 +1,5 @@
name: deep_research_runtime_test
on:
schedule:
- cron: '0 0 */3 * *'
workflow_dispatch:
on: [push, pull_request]
jobs:
test:
@@ -33,5 +30,7 @@ jobs:
pip install pytest pytest-asyncio pytest-mock
- name: Run tests
env:
PYTHONPATH: ${{ github.workspace }}/deep_research/agent_deep_research
run: |
python -m pytest tests/agent_deep_research_test.py -v

View File

@@ -1,9 +1,6 @@
name: BrowserAgent Tests
on:
schedule:
- cron: '0 0 */3 * *'
workflow_dispatch:
on: [push]
jobs:
test:
@@ -33,13 +30,14 @@ jobs:
- name: Install Dependencies
run: |
cd browser_agent/agent_browser
cd browser_use/agent_browser
python -m pip install --upgrade pip
pip install pytest pytest-asyncio
pip install -r requirements.txt
- name: Run Tests
env:
PYTHONPATH: ${{ github.workspace }}/browser_use/agent_browser
DASHSCOPE_API_KEY: ${{ secrets.DASHSCOPE_API_KEY }}
run: |
# ✅ Ensure test-results directory exists

View File

@@ -1,8 +1,5 @@
name: browser_use_fullstack_runtime_test
on:
schedule:
- cron: '0 0 */3 * *'
workflow_dispatch:
on: [push]
jobs:
test:
@@ -37,6 +34,7 @@ jobs:
- name: Run tests
env:
DASHSCOPE_API_KEY: ${{ secrets.DASHSCOPE_API_KEY }}
PYTHONPATH: ${{ github.workspace }}/browser_use/browser_use_fullstack_runtime/backend
run: |
# ✅ Use validated path from debug output
python -m pytest tests/browser_use_fullstack_runtime_test.py -v

View File

@@ -1,8 +1,5 @@
name: Conversational Agents Chatbot Test
on:
schedule:
- cron: '0 0 */3 * *'
workflow_dispatch:
on: [push]
jobs:
test:
@@ -30,6 +27,7 @@ jobs:
- name: Run tests
env:
PYTHONPATH: ${{ github.workspace }}/conversational_agents/chatbot
DASHSCOPE_API_KEY: ${{ secrets.DASHSCOPE_API_KEY }}
run: |
# ✅ Use correct relative path

View File

@@ -1,8 +1,5 @@
name: Flask API Runtime Test
on:
schedule:
- cron: '0 0 */3 * *'
workflow_dispatch:
on: [push]
jobs:
test:
@@ -33,5 +30,7 @@ jobs:
pip install pytest pytest-asyncio
- name: Run tests
env:
PYTHONPATH: ${{ github.workspace }}/conversational_agents/chatbot_fullstack_runtime
run: |
python -m pytest tests/conversational_agents_chatbot_fullstack_runtime_webserver_test.py -v

View File

@@ -1,8 +1,5 @@
name: ACE Benchmark Evaluation Test
on:
schedule:
- cron: '0 0 */3 * *'
workflow_dispatch:
on: [push]
jobs:
test:
@@ -33,6 +30,7 @@ jobs:
- name: Run tests
env:
PYTHONPATH: ${{ env.GITHUB_WORKSPACE }}/evaluation/ace_bench
DASHSCOPE_API_KEY: ${{ secrets.DASHSCOPE_API_KEY }}
run: |
python -m pytest tests/evaluation_test.py -v

View File

@@ -1,9 +1,6 @@
name: Run test_game.py
on:
schedule:
- cron: '0 0 */3 * *'
workflow_dispatch:
on: [push]
jobs:
test:
@@ -21,7 +18,7 @@ jobs:
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: 3.10
python-version: "3.10"
- name: Install dependencies
run: |
@@ -32,7 +29,7 @@ jobs:
- name: Run game_test.py
env:
DASHSCOPE_API_KEY: ${{ secrets.DASHSCOPE_API_KEY }}
PYTHONPATH: ${{ env.GITHUB_WORKSPACE }}/games/game_werewolves
PYTHONPATH: $GITHUB_WORKSPACE/games/game_werewolves
run: |
# ✅ Ensure correct working directory
python -m pytest tests/game_test.py -v
PYTHONPATH=$GITHUB_WORKSPACE/games/game_werewolves python -m pytest tests/game_test.py -v

121
.pre-commit-config.yaml Normal file
View File

@@ -0,0 +1,121 @@
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.3.0
hooks:
- id: check-ast
- id: sort-simple-yaml
- id: check-yaml
exclude: |
(?x)^(
meta.yaml
)$
- id: check-xml
- id: check-toml
- id: check-docstring-first
- id: check-json
- id: fix-encoding-pragma
- id: detect-private-key
- id: trailing-whitespace
- repo: https://github.com/asottile/add-trailing-comma
rev: v3.1.0
hooks:
- id: add-trailing-comma
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.7.0
hooks:
- id: mypy
exclude:
(?x)(
pb2\.py$
| grpc\.py$
| ^docs
| \.html$
)
args: [
--ignore-missing-imports,
--disable-error-code=var-annotated,
--disable-error-code=union-attr,
--disable-error-code=assignment,
--disable-error-code=attr-defined,
--disable-error-code=import-untyped,
--disable-error-code=truthy-function,
--follow-imports=skip,
--explicit-package-bases,
]
- repo: https://github.com/psf/black
rev: 23.3.0
hooks:
- id: black
args: [ --line-length=79 ]
- repo: https://github.com/PyCQA/flake8
rev: 6.1.0
hooks:
- id: flake8
args: [ "--extend-ignore=E203"]
- repo: https://github.com/pylint-dev/pylint
rev: v3.0.2
hooks:
- id: pylint
exclude:
(?x)(
^docs
| pb2\.py$
| grpc\.py$
| \.demo$
| \.md$
| \.html$
)
args: [
--disable=W0511,
--disable=W0718,
--disable=W0122,
--disable=C0103,
--disable=R0913,
--disable=E0401,
--disable=E1101,
--disable=C0415,
--disable=W0603,
--disable=R1705,
--disable=R0914,
--disable=E0601,
--disable=W0602,
--disable=W0604,
--disable=R0801,
--disable=R0902,
--disable=R0903,
--disable=C0123,
--disable=W0231,
--disable=W1113,
--disable=W0221,
--disable=R0401,
--disable=W0632,
--disable=W0123,
--disable=C3001,
--disable=W0201,
--disable=C0302,
--disable=W1203,
--disable=C2801,
--disable=C0114, # Disable missing module docstring for quick dev
--disable=C0115, # Disable missing class docstring for quick dev
--disable=C0116, # Disable missing function or method docstring for quick dev
]
- repo: https://github.com/pre-commit/mirrors-eslint
rev: v7.32.0
hooks:
- id: eslint
files: \.(js|jsx)$
exclude: '.*js_third_party.*'
args: [ '--fix' ]
- repo: https://github.com/thibaudcolas/pre-commit-stylelint
rev: v14.4.0
hooks:
- id: stylelint
files: \.(css)$
exclude: '.*css_third_party.*'
args: [ '--fix' ]
- repo: https://github.com/pre-commit/mirrors-prettier
rev: 'v3.0.0'
hooks:
- id: prettier
additional_dependencies: [ 'prettier@3.0.0' ]
files: \.(tsx?)$

6
.stylelintrc Normal file
View File

@@ -0,0 +1,6 @@
{
"rules": {
"indentation": 2,
"string-quotes": "double"
}
}

View File

@@ -4,20 +4,25 @@
import re
import uuid
from typing import Any, Optional
from typing import Optional, Any
from agentscope.agent import ReActAgent
from agentscope.formatter import FormatterBase
from agentscope.memory import MemoryBase
from agentscope.message import Msg, TextBlock, ToolUseBlock
from agentscope.message import (
Msg,
ToolUseBlock,
TextBlock,
)
from agentscope.model import ChatModelBase
from agentscope.token import OpenAITokenCounter, TokenCounterBase
from agentscope.tool import Toolkit
from agentscope.token import TokenCounterBase, OpenAITokenCounter
_BROWSER_AGENT_DEFAULT_SYS_PROMPT = (
"You are a helpful browser automation assistant. "
"You can navigate websites, take screenshots, and interact with web pages."
"Always describe what you see and meta_planner_agent your next steps clearly. "
"Always describe what you see and meta_planner_agent"
" your next steps clearly. "
"When taking actions, explain what you're doing and why."
)
_BROWSER_AGENT_REASONING_PROMPT = (
@@ -318,7 +323,7 @@ class BrowserAgent(ReActAgent):
)
# Format the prompt for the model
prompt = self.formatter.format(
prompt = await self.formatter.format(
msgs=[
Msg("system", self.sys_prompt, "system"),
*memory_msgs,

View File

@@ -10,7 +10,7 @@ from agentscope.memory import InMemoryMemory
from agentscope.model import DashScopeChatModel
from agentscope.tool import Toolkit
from .browser_agent import BrowserAgent # pylint: disable=C0411
from browser_agent import BrowserAgent # pylint: disable=C0411
async def main() -> None:

View File

@@ -3,8 +3,8 @@ import asyncio
import os
from agentscope.agent import ReActAgent
from agentscope_runtime.engine import LocalDeployManager, Runner
from agentscope.model import DashScopeChatModel
from agentscope_runtime.engine import LocalDeployManager, Runner
from agentscope_runtime.engine.agents.agentscope_agent import AgentScopeAgent
from agentscope_runtime.engine.services.context_manager import ContextManager
@@ -23,12 +23,13 @@ async def _local_deploy():
model = DashScopeChatModel(
model_name="qwen-turbo",
api_key=os.getenv("DASHSCOPE_API_KEY"),
)
agent = AgentScopeAgent(
name="Friday",
model=model,
agent_config={"sys_prompt": "A simple LLM agent to generate a short response"},
agent_config={
"sys_prompt": "A simple LLM agent to generate a short response",
},
agent_builder=ReActAgent,
)

View File

@@ -3,14 +3,13 @@ import json
import logging
import os
from datetime import datetime
from typing import Tuple, Optional, Union, Dict, Any, Generator
import requests
from dotenv import load_dotenv
from flask import Flask, jsonify, request
from flask import Flask, request, jsonify
from flask_cors import CORS
from flask_sqlalchemy import SQLAlchemy
from werkzeug.security import check_password_hash, generate_password_hash
from werkzeug.security import generate_password_hash, check_password_hash
logging.basicConfig(
level=logging.INFO,
@@ -48,10 +47,10 @@ class User(db.Model):
cascade="all, delete-orphan",
)
def set_password(self, password: str) -> None:
def set_password(self, password):
self.password_hash = generate_password_hash(password)
def check_password(self, password: str) -> bool:
def check_password(self, password):
return check_password_hash(self.password_hash, password)
@@ -90,7 +89,7 @@ class Message(db.Model):
# Create database tables
def create_tables() -> None:
def create_tables():
db.create_all()
# Create sample users (if none exist)
@@ -105,9 +104,7 @@ def create_tables() -> None:
# functions
def parse_sse_line(
line: bytes,
) -> Tuple[Optional[str], Optional[Union[str, int]]]:
def parse_sse_line(line):
line = line.decode("utf-8").strip()
if line.startswith("data: "):
return "data", line[6:]
@@ -120,10 +117,7 @@ def parse_sse_line(
return None, None
def sse_client(
url: str,
data: Optional[Dict[str, Any]] = None,
) -> Generator[str, None, None]:
def sse_client(url, data=None):
headers = {
"Accept": "text/event-stream",
"Cache-Control": "no-cache",
@@ -158,17 +152,13 @@ def sse_client(
pass
def call_runner(
query: str,
query_user_id: str,
query_session_id: str,
) -> Generator[str, None, None]:
def call_runner(query, query_user_id, query_session_id):
server_port = int(os.environ.get("SERVER_PORT", "8090"))
server_endpoint = os.environ.get("SERVER_ENDPOINT", "agent")
server_host = os.environ.get("SERVER_HOST", "localhost")
url = f"http://{server_host}:{server_port}/{server_endpoint}"
data_arg: Dict[str, Any] = {
data_arg = {
"input": [
{
"role": "user",

View File

@@ -95,14 +95,14 @@ class ReflectFailure(BaseModel):
"properties": {
"need_rephrase": {
"type": "boolean",
"description": "Set to 'true' if the failed subtask "
"needs to be rephrased due to a design "
"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 meta_planner_agent "
"with only the inappropriate "
"description": "The modified working "
"meta_planner_agent with only the inappropriate "
"subtask replaced by its improved version. If no "
"rephrasing is needed, provide an empty string.",
},

View File

@@ -1,35 +1,46 @@
# -*- coding: utf-8 -*-
"""Deep Research Agent"""
# pylint: disable=too-many-lines, no-name-in-module
import asyncio
import json
import os
from copy import deepcopy
import json
import asyncio
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 agentscope import logger, setup_logger
from agentscope.agent import ReActAgent
from agentscope.formatter import FormatterBase
from agentscope.mcp import StatefulClientBase
from agentscope.memory import MemoryBase
from agentscope.message import Msg, TextBlock, ToolResultBlock, ToolUseBlock
from agentscope.model import ChatModelBase
from agentscope.tool import ToolResponse, view_text_file, write_text_file
from pydantic import BaseModel
from ..agent_deep_research.utils import (
get_dynamic_tool_call_json,
get_structure_output,
load_prompt_dict,
truncate_search_result,
)
from .built_in_prompt.promptmodule import (
FollowupJudge,
ReflectFailure,
from built_in_prompt.promptmodule import (
SubtasksDecomposition,
WebExtraction,
FollowupJudge,
ReflectFailure,
)
from utils import (
truncate_search_result,
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,
view_text_file,
write_text_file,
)
from agentscope.message import (
Msg,
ToolUseBlock,
TextBlock,
ToolResultBlock,
)
_DEEP_RESEARCH_AGENT_DEFAULT_SYS_PROMPT = "You're a helpful assistant."
@@ -149,7 +160,7 @@ class DeepResearchAgent(ReActAgent):
# register all necessary tools for deep research agent
self.toolkit.register_tool_function(view_text_file)
self.toolkit.register_tool_function(write_text_file)
asyncio.create_task(
asyncio.get_running_loop().create_task(
self.toolkit.register_mcp_client(search_mcp_client),
)
@@ -213,16 +224,12 @@ class DeepResearchAgent(ReActAgent):
reasoning_prompt = self.prompt_dict["reasoning_prompt"].format_map(
{
"objective": self.current_subtask[-1].objective,
"meta_planner_agent": (
cur_plan
if cur_plan
else "There is no working meta_planner_agent now."
),
"knowledge_gap": (
f"## Knowledge Gaps:\n {cur_know_gap}"
if cur_know_gap
else ""
),
"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 "",
"depth": len(self.current_subtask),
},
)
@@ -300,7 +307,9 @@ class DeepResearchAgent(ReActAgent):
# Async generator handling
async for chunk in tool_res:
# Turn into a tool result block
tool_res_msg.content[0]["output"] = chunk.content # type: ignore[index]
tool_res_msg.content[0][ # type: ignore[index]
"output"
] = chunk.content
# Skip the printing of the finish function call
if (
@@ -488,13 +497,14 @@ class DeepResearchAgent(ReActAgent):
async def decompose_and_expand_subtask(self) -> ToolResponse:
"""Identify the knowledge gaps of the current subtask and generate a
working meta_planner_agent by subtask decomposition. The working meta_planner_agent includes
working meta_planner_agent by subtask decomposition.
The working meta_planner_agent includes
necessary steps for task completion and expanded steps.
Returns:
ToolResponse:
The knowledge gaps and working meta_planner_agent of the current subtask
in JSON format.
The knowledge gaps and working meta_planner_agent
of the current subtask in JSON format.
"""
if len(self.current_subtask) <= self.max_depth:
decompose_sys_prompt = self.prompt_dict["decompose_sys_prompt"]
@@ -947,7 +957,8 @@ class DeepResearchAgent(ReActAgent):
Returns:
ToolResponse:
The reflection about meta_planner_agent rephrasing and subtask decomposition.
The reflection about meta_planner_agent
rephrasing and subtask decomposition.
"""
reflect_sys_prompt = self.prompt_dict["reflect_sys_prompt"]
conversation_history = ""

View File

@@ -10,7 +10,7 @@ from agentscope.memory import InMemoryMemory
from agentscope.message import Msg
from agentscope.model import DashScopeChatModel
from .deep_research_agent import DeepResearchAgent
from deep_research_agent import DeepResearchAgent
async def main(user_query: str) -> None:

View File

@@ -1,12 +1,12 @@
# -*- coding: utf-8 -*-
"""The utilities for deep research agent"""
import json
import os
import json
import re
from typing import Any, Sequence, Type, Union
from typing import Union, Sequence, Any, Type
from pydantic import BaseModel
from agentscope.tool import Toolkit, ToolResponse
from pydantic import BaseModel
TOOL_RESULTS_MAX_WORDS = 5000
@@ -281,11 +281,14 @@ def load_prompt_dict() -> dict:
prompt_dict["summarize_hint"] = (
"Based on your work history above, examine which step in the "
"following working meta_planner_agent has been completed. Mark the completed "
"following working meta_planner_agent has been "
"completed. Mark the completed "
"step with [DONE] at the end of its line (e.g., k. step k [DONE]) "
"and leave the uncompleted steps unchanged. You MUST return only "
"the updated meta_planner_agent, preserving exactly the same format as the "
"original meta_planner_agent. Do not include any explanations, reasoning, "
"the updated meta_planner_agent, preserving exactly "
"the same format as the "
"original meta_planner_agent. Do not include any "
"explanations, reasoning, "
"or section headers such as '## Working Plan:', just output the"
"updated meta_planner_agent itself."
"\n\n## Working Plan:\n{meta_planner_agent}"
@@ -304,11 +307,13 @@ def load_prompt_dict() -> dict:
"following report that consolidates and summarizes the essential "
"findings:\n {intermediate_report}\n\n"
"Such report has been saved to the {report_path}. "
"I will now **proceed to the next item** in the working meta_planner_agent."
"I will now **proceed to the next item** "
"in the working meta_planner_agent."
)
prompt_dict["save_report_hint"] = (
"The milestone results of the current item in working meta_planner_agent "
"The milestone results of the current "
"item in working meta_planner_agent "
"are summarized into the following report:\n{intermediate_report}"
)

View File

@@ -1,16 +1,16 @@
# -*- coding: utf-8 -*-
import os
import uuid
import random
import string
import time
import uuid
from base64 import b64encode
from hashlib import sha256
from hmac import new as hmac_new
from typing import Any, Dict, List
from typing import List, Dict, Any
import requests
from utils import format_time
from .utils import format_time
class CustomSearchTool:

View File

@@ -3,22 +3,25 @@ import asyncio
import json
import os
import time
from typing import Any, Dict, List, Optional
from agentscope_runtime.engine.agents.langgraph_agent import LangGraphAgent
from agentscope_runtime.engine.helpers.helper import simple_call_agent_direct
from configuration import Configuration
from custom_search_tool import CustomSearchTool
from typing import List, Dict, Any, Optional
from dotenv import load_dotenv
from langchain_core.messages import AIMessage, HumanMessage
from langchain_core.runnables import RunnableConfig
from langgraph.graph import END, START, StateGraph
from langgraph.graph import START, END
from langgraph.graph import StateGraph
from langgraph.types import Send
from agentscope_runtime.engine.agents.langgraph_agent import LangGraphAgent
from agentscope_runtime.engine.helpers.helper import simple_call_agent_direct
from configuration import Configuration
from custom_search_tool import CustomSearchTool
from llm_prompts import (
answer_instructions,
query_writer_instructions,
reflection_instructions,
web_searcher_instructions,
reflection_instructions,
answer_instructions,
)
from llm_utils import call_dashscope, extract_json_from_qwen
from state import (
@@ -27,12 +30,12 @@ from state import (
ReflectionState,
WebSearchState,
)
from utils import (
custom_get_citations,
custom_resolve_urls,
get_current_date,
from .utils import (
get_research_topic,
insert_citation_markers,
custom_resolve_urls,
custom_get_citations,
get_current_date,
)
load_dotenv("../.env")

View File

@@ -1,12 +1,11 @@
# -*- coding: utf-8 -*-
import time
from datetime import datetime
from typing import Any, Dict, List
from langchain_core.messages import AIMessage, AnyMessage, HumanMessage
from datetime import datetime
from langchain_core.messages import AnyMessage, AIMessage, HumanMessage
def get_current_date() -> str:
def get_current_date():
return datetime.now().strftime("%B %d, %Y")
@@ -39,7 +38,7 @@ def get_research_topic(messages: List[AnyMessage]) -> str:
return research_topic
def insert_citation_markers(text: str, citations_list: List[Dict]) -> str:
def insert_citation_markers(text, citations_list):
"""
Inserts citation markers into a text string based on start and end indices.

View File

@@ -21,8 +21,8 @@ from agentscope.tool import Toolkit
async def react_agent_solution(
ace_task: Task,
pre_hook: Callable,
ace_task: Task,
pre_hook: Callable,
) -> SolutionOutput:
"""Run ReAct agent with the given task in ACEBench.
@@ -42,8 +42,8 @@ async def react_agent_solution(
agent = ReActAgent(
name="Friday",
sys_prompt="You are a helpful assistant named Friday. "
"Your target is to solve the given task with your tools."
"Try to solve the task as best as you can.",
"Your target is to solve the given task with your tools."
"Try to solve the task as best as you can.",
model=DashScopeChatModel(
api_key=os.environ.get("DASHSCOPE_API_KEY"),
model_name="qwen-max",

View File

@@ -2,8 +2,8 @@
"""The structured output models used in the werewolf game."""
from typing import Literal
from agentscope.agent import AgentBase
from pydantic import BaseModel, Field
from agentscope.agent import AgentBase
class DiscussionModel(BaseModel):
@@ -44,7 +44,9 @@ def get_poison_model(agents: list[AgentBase]) -> type[BaseModel]:
poison: bool = Field(
description="Do you want to use the poison potion",
)
name: Literal[tuple(_.name for _ in agents)] | None = Field( # type: ignore
name: Literal[ # type: ignore
tuple(_.name for _ in agents)
] | None = Field(
description="The name of the player you want to poison, if you "
"don't want to poison anyone, just leave it empty",
default=None,
@@ -75,7 +77,9 @@ def get_hunter_model(agents: list[AgentBase]) -> type[BaseModel]:
shoot: bool = Field(
description="Whether you want to use the shooting ability or not",
)
name: Literal[tuple(_.name for _ in agents)] | None = Field( # type: ignore
name: Literal[ # type: ignore
tuple(_.name for _ in agents)
] | None = Field(
description="The name of the player you want to shoot, if you "
"don't want to the ability, just leave it empty",
default=None,

View File

@@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
"""
[Your Sample Name] - Entry Point
@@ -5,6 +6,7 @@ This example demonstrates [brief description].
"""
import agentscope
def main():
"""Main function to run the example."""
print(agentscope.__version__)

View File

@@ -1,9 +1,8 @@
# tests/agent_deep_research_test.py
import logging
# -*- coding: utf-8 -*-
import os
import shutil
import tempfile
from unittest.mock import Mock, AsyncMock, patch
from unittest.mock import Mock, AsyncMock, patch, MagicMock
import pytest
from agentscope.formatter import DashScopeChatFormatter
@@ -12,7 +11,9 @@ from agentscope.memory import InMemoryMemory
from agentscope.message import Msg
from agentscope.model import DashScopeChatModel
from deep_research.agent_deep_research.deep_research_agent import DeepResearchAgent
from deep_research.agent_deep_research.deep_research_agent import (
DeepResearchAgent,
)
from deep_research.agent_deep_research.main import main
@@ -70,12 +71,15 @@ class TestDeepResearchAgent:
def test_agent_initialization(
self,
mock_model,
mock_tavily_client,
temp_working_dir,
mock_model, # pylint: disable=redefined-outer-name
mock_tavily_client, # pylint: disable=redefined-outer-name
temp_working_dir, # pylint: disable=redefined-outer-name
):
"""Test agent initialization with valid parameters"""
with patch("asyncio.create_task"):
mock_loop = MagicMock()
mock_task = AsyncMock()
mock_loop.create_task = MagicMock(return_value=mock_task)
with patch("asyncio.get_running_loop", return_value=mock_loop):
agent = DeepResearchAgent(
name="Friday",
sys_prompt="You are a helpful assistant named Friday.",
@@ -87,17 +91,17 @@ class TestDeepResearchAgent:
)
assert agent.name == "Friday"
assert agent.sys_prompt.startswith("You are a helpful assistant named Friday.")
assert agent.sys_prompt.startswith(
"You are a helpful assistant named Friday.",
)
assert agent.tmp_file_storage_dir == temp_working_dir
assert os.path.exists(temp_working_dir)
@pytest.mark.asyncio
async def test_main_function_success(
self,
mock_env_vars,
mock_tavily_client,
mock_model,
temp_working_dir,
mock_tavily_client, # pylint: disable=redefined-outer-name
temp_working_dir, # pylint: disable=redefined-outer-name
):
"""Test main function with successful execution"""
with patch(
@@ -109,17 +113,26 @@ class TestDeepResearchAgent:
autospec=True,
) as mock_agent_class:
mock_agent = AsyncMock()
mock_agent.return_value = Msg("Friday", "Test response", "assistant")
mock_agent.return_value = Msg(
"Friday",
"Test response",
"assistant",
)
mock_agent_class.return_value = mock_agent
with patch("os.makedirs") as mock_makedirs:
with patch.dict(os.environ, {"AGENT_OPERATION_DIR": temp_working_dir}):
with patch.dict(
os.environ,
{"AGENT_OPERATION_DIR": temp_working_dir},
):
test_query = "Test research question"
msg = Msg("Bob", test_query, "user")
await main(test_query)
mock_makedirs.assert_called_once_with(temp_working_dir, exist_ok=True)
mock_makedirs.assert_called_once_with(
temp_working_dir,
exist_ok=True,
)
mock_agent_class.assert_called_once()
# ✅ Use assert_called_once() + manual argument check
@@ -138,8 +151,7 @@ class TestDeepResearchAgent:
@pytest.mark.asyncio
async def test_agent_cleanup(
self,
mock_env_vars,
mock_tavily_client,
mock_tavily_client, # pylint: disable=redefined-outer-name
):
"""Test proper cleanup of resources"""
with patch(
@@ -151,7 +163,10 @@ class TestDeepResearchAgent:
mock_tavily_client.close.assert_called_once()
def test_working_directory_creation(self, temp_working_dir):
def test_working_directory_creation(
self,
temp_working_dir, # pylint: disable=redefined-outer-name
):
"""Test working directory is created correctly"""
test_dir = os.path.join(temp_working_dir, "test_subdir")
os.makedirs(test_dir, exist_ok=True)
@@ -161,17 +176,28 @@ class TestDeepResearchAgent:
class TestErrorHandling:
"""Test suite for error handling scenarios"""
@pytest.mark.asyncio
async def test_filesystem_errors(self, mock_env_vars, mock_tavily_client):
async def test_filesystem_errors(
self,
mock_tavily_client, # pylint: disable=redefined-outer-name
):
"""Test handling of filesystem errors"""
with patch(
"deep_research.agent_deep_research.main.StdIOStatefulClient",
return_value=mock_tavily_client,
"deep_research.agent_deep_research.main.StdIOStatefulClient",
return_value=mock_tavily_client,
):
with patch.dict(os.environ, {"AGENT_OPERATION_DIR": "/invalid/path"}):
with patch("os.makedirs", side_effect=PermissionError("Permission denied")):
with patch.dict(
os.environ,
{"AGENT_OPERATION_DIR": "/invalid/path"},
):
with patch(
"os.makedirs",
side_effect=PermissionError("Permission denied"),
):
with pytest.raises(PermissionError):
await main("Test query")
if __name__ == "__main__":
pytest.main(["-v", __file__])

View File

@@ -1,8 +1,7 @@
# -*- coding: utf-8 -*-
import pytest
import asyncio
from typing import Dict, Any, AsyncGenerator
from typing import Dict
from unittest.mock import AsyncMock, MagicMock, patch
import pytest
from agentscope.message import Msg
from agentscope.tool import Toolkit
from agentscope.memory import MemoryBase
@@ -22,7 +21,10 @@ def mock_dependencies() -> Dict[str, MagicMock]:
@pytest.fixture
def agent(mock_dependencies: Dict[str, MagicMock]) -> BrowserAgent:
def agent(
# pylint: disable=redefined-outer-name
mock_dependencies: Dict[str, MagicMock],
) -> BrowserAgent:
return BrowserAgent(
name="TestBot",
model=mock_dependencies["model"],
@@ -36,17 +38,28 @@ def agent(mock_dependencies: Dict[str, MagicMock]) -> BrowserAgent:
# -----------------------------
# ✅ Hook registration verification (adapted for ReActAgentBase)
# -----------------------------
def test_hooks_registered(agent: BrowserAgent) -> None:
# Verify instance-level hooks
assert hasattr(agent, "_instance_pre_reply_hooks")
def test_hooks_registered(
agent: BrowserAgent, # pylint: disable=redefined-outer-name
) -> None:
"""Verify instance-level hooks are registered"""
# Disable pylint warning for protected member access
assert hasattr(
agent,
"_instance_pre_reply_hooks",
) # pylint: disable=protected-access
assert (
"browser_agent_default_url_pre_reply"
# pylint: disable=protected-access
in agent._instance_pre_reply_hooks
)
assert hasattr(agent, "_instance_pre_reasoning_hooks")
assert hasattr(
agent,
"_instance_pre_reasoning_hooks",
) # pylint: disable=protected-access
assert (
"browser_agent_observe_pre_reasoning"
# pylint: disable=protected-access
in agent._instance_pre_reasoning_hooks
)
@@ -55,15 +68,20 @@ def test_hooks_registered(agent: BrowserAgent) -> None:
# ✅ Navigation hook test (direct hook invocation)
# -----------------------------
@pytest.mark.asyncio
async def test_pre_reply_hook_navigation(agent: BrowserAgent) -> None:
async def test_pre_reply_hook_navigation(
agent: BrowserAgent, # pylint: disable=redefined-outer-name
) -> None:
# pylint: disable=protected-access
agent._has_initial_navigated = False
# Get instance-level hook function
# pylint: disable=protected-access
hook_func = agent._instance_pre_reply_hooks[
"browser_agent_default_url_pre_reply"
]
await hook_func(agent) # Directly invoke hook function
# pylint: disable=protected-access
assert agent._has_initial_navigated is True
assert agent.toolkit.call_tool_function.called
@@ -72,13 +90,17 @@ async def test_pre_reply_hook_navigation(agent: BrowserAgent) -> None:
# ✅ Snapshot hook test (fix content attribute access issue)
# -----------------------------
@pytest.mark.asyncio
async def test_observe_pre_reasoning(agent: BrowserAgent) -> None:
async def test_observe_pre_reasoning(
agent: BrowserAgent, # pylint: disable=redefined-outer-name
) -> None:
# Mock tool response (fix: use Msg object with content attribute)
mock_response = AsyncMock()
mock_response.__aiter__.return_value = [
Msg("system", [{"text": "Snapshot content"}], "system"),
]
agent.toolkit.call_tool_function = AsyncMock(return_value=mock_response)
agent.toolkit.call_tool_function = AsyncMock(
return_value=mock_response,
)
# Replace memory add method
with patch.object(
@@ -87,6 +109,7 @@ async def test_observe_pre_reasoning(agent: BrowserAgent) -> None:
new_callable=AsyncMock,
) as mock_add:
# Get instance-level hook function
# pylint: disable=protected-access
hook_func = agent._instance_pre_reasoning_hooks[
"browser_agent_observe_pre_reasoning"
]
@@ -100,7 +123,9 @@ async def test_observe_pre_reasoning(agent: BrowserAgent) -> None:
# -----------------------------
# ✅ Text filtering test (improved regex)
# -----------------------------
def test_filter_execution_text(agent: BrowserAgent) -> None:
def test_filter_execution_text(
agent: BrowserAgent, # pylint: disable=redefined-outer-name
) -> None:
text = """
### New console messages
Some console output
@@ -112,6 +137,7 @@ def test_filter_execution_text(agent: BrowserAgent) -> None:
```
Regular text content
"""
# pylint: disable=protected-access
filtered = agent._filter_execution_text(text)
assert "console output" not in filtered
@@ -124,7 +150,9 @@ def test_filter_execution_text(agent: BrowserAgent) -> None:
# ✅ Memory summarization test (already passing)
# -----------------------------
@pytest.mark.asyncio
async def test_memory_summarizing(agent: BrowserAgent) -> None:
async def test_memory_summarizing(
agent: BrowserAgent, # pylint: disable=redefined-outer-name
) -> None:
agent.memory.get_memory = AsyncMock(
return_value=[MagicMock(role="user", content="Original question")]
* 25,
@@ -136,6 +164,7 @@ async def test_memory_summarizing(agent: BrowserAgent) -> None:
content=[MagicMock(text="Summary text")],
)
# pylint: disable=protected-access
await agent._memory_summarizing()
assert agent.memory.clear.called

View File

@@ -1,20 +1,23 @@
# -*- coding: utf-8 -*-
import pytest
import asyncio
from unittest.mock import AsyncMock, MagicMock, patch
from types import SimpleNamespace
from unittest.mock import AsyncMock, MagicMock, patch
import pytest
import pytest_asyncio
from browser_use.browser_use_fullstack_runtime.backend.agentscope_browseruse_agent import (
AgentscopeBrowseruseAgent,
RunStatus,
)
from browser_use.browser_use_fullstack_runtime.backend.async_quart_service import (
app,
)
from quart.testing import QuartClient
from browser_use.browser_use_fullstack_runtime.backend import (
agentscope_browseruse_agent as agent_module,
)
from browser_use.browser_use_fullstack_runtime.backend import (
async_quart_service as service,
)
AgentscopeBrowseruseAgent = agent_module.AgentscopeBrowseruseAgent
RunStatus = agent_module.RunStatus
app = service.app
# -----------------------------
# 🧪 Singleton Test Configuration
@@ -31,13 +34,17 @@ def event_loop():
async def agent_singleton():
"""Session-scoped single instance of AgentscopeBrowseruseAgent"""
with patch(
"browser_use.browser_use_fullstack_runtime.backend.agentscope_browseruse_agent.SandboxService",
"browser_use.browser_use_fullstack_runtime."
"backend.agentscope_browseruse_agent.SandboxService",
) as MockSandboxService, patch(
"browser_use.browser_use_fullstack_runtime.backend.agentscope_browseruse_agent.InMemoryMemoryService",
"browser_use.browser_use_fullstack_runtime."
"backend.agentscope_browseruse_agent.InMemoryMemoryService",
) as MockMemoryService, patch(
"browser_use.browser_use_fullstack_runtime.backend.agentscope_browseruse_agent.InMemorySessionHistoryService",
"browser_use.browser_use_fullstack_runtime."
"backend.agentscope_browseruse_agent.InMemorySessionHistoryService",
) as MockHistoryService, patch(
"agentscope_runtime.sandbox.manager.container_clients.docker_client.docker",
"agentscope_runtime.sandbox.manager."
"container_clients.docker_client.docker",
) as mock_docker, patch(
"agentscope_runtime.sandbox.manager.sandbox_manager.SandboxManager",
) as MockSandboxManager:
@@ -88,16 +95,20 @@ async def test_app():
# ✅ AgentscopeBrowseruseAgent Singleton Tests
# -----------------------------
@pytest.mark.asyncio
async def test_agent_singleton_initialization(agent_singleton):
async def agent_singleton_singleton_initialization(
agent_singleton, # pylint: disable=redefined-outer-name
):
"""Test agent singleton initialization"""
agent = agent_singleton
agent = agent_singleton # pylint: disable=redefined-outer-name
assert isinstance(agent, AgentscopeBrowseruseAgent)
assert hasattr(agent, "agent")
assert hasattr(agent, "runner")
@pytest.mark.asyncio
async def test_chat_method(agent_singleton):
async def test_chat_method(
agent_singleton,
): # pylint: disable=redefined-outer-name
"""Test chat method handles messages"""
mock_request = {
"messages": [
@@ -108,20 +119,28 @@ async def test_chat_method(agent_singleton):
# ✅ Create mock object with object/status properties
mock_event = SimpleNamespace(
object="message",
status=RunStatus.Completed,
status=agent_module.RunStatus.Completed,
content=[{"type": "text", "text": "Test response"}],
)
with patch.object(agent_singleton.runner, "stream_query") as mock_stream:
with patch.object(
agent_singleton.runner, # pylint: disable=redefined-outer-name
"stream_query",
) as mock_stream:
# ✅ Return object with properties
async def mock_stream_query(*args, **kwargs):
async def mock_stream_query(*_args, **_kwargs):
yield mock_event
mock_stream.side_effect = mock_stream_query
responses = []
async for response in agent_singleton.chat(mock_request["messages"]):
async for response in agent_singleton.chat(
# pylint: disable=redefined-outer-name
mock_request["messages"],
):
responses.append(response)
assert len(responses) == 1
assert responses[0][0]["text"] == "Test response" # ✅ Fix property access
assert (
responses[0][0]["text"] == "Test response"
) # ✅ Fix property access

View File

@@ -1,264 +1,61 @@
from datetime import datetime, timezone
# -*- coding: utf-8 -*-
import os
import time
import tempfile
import pytest
from unittest.mock import MagicMock, patch
from flask import Flask, request, jsonify
from flask_sqlalchemy import SQLAlchemy
from werkzeug.security import generate_password_hash, check_password_hash
# Initialize db instance
db = SQLAlchemy()
import conversational_agents.chatbot_fullstack_runtime.backend.web_server as ws
# Define model classes (defined once)
class User(db.Model):
__tablename__ = "user"
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(80), unique=True, nullable=False)
password_hash = db.Column(db.String(120), nullable=False)
name = db.Column(db.String(100), nullable=False)
created_at = db.Column(db.DateTime, default=lambda: datetime.now(timezone.utc))
def set_password(self, password):
self.password_hash = generate_password_hash(password)
def check_password(self, password):
return check_password_hash(self.password_hash, password)
app = ws.app
_db = ws.db
User = ws.User
class Conversation(db.Model):
__tablename__ = "conversation"
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(200), nullable=False)
user_id = db.Column(db.Integer, db.ForeignKey("user.id"), nullable=False)
created_at = db.Column(db.DateTime, default=lambda: datetime.now(timezone.utc))
updated_at = db.Column(
db.DateTime,
default=lambda: datetime.now(timezone.utc),
onupdate=lambda: datetime.now(timezone.utc),
def generate_unique_username():
return f"testuser_{int(time.time())}"
@pytest.fixture
def client_and_username():
"""Create an Isolated Test Client and Username"""
db_fd, db_path = tempfile.mkstemp(suffix=".db")
app.config["SQLALCHEMY_DATABASE_URI"] = f"sqlite:///{db_path}"
app.config["TESTING"] = True
client = app.test_client()
with app.app_context():
_db.drop_all()
_db.create_all()
# Generate Unique Username
username = generate_unique_username()
password = "testpass"
user = User(username=username, name="Test User")
user.set_password(password)
_db.session.add(user)
_db.session.commit()
yield client, username, password
os.close(db_fd)
os.unlink(db_path)
def test_user_login_success(
# pylint: disable=redefined-outer-name
client_and_username,
):
"""Test Successful User Login"""
client, username, password = client_and_username
response = client.post(
"/api/login",
json={
"username": username,
"password": password,
},
)
messages = db.relationship("Message", backref="conversation", lazy=True)
class Message(db.Model):
__tablename__ = "message"
id = db.Column(db.Integer, primary_key=True)
text = db.Column(db.Text, nullable=False)
sender = db.Column(db.String(20), nullable=False)
conversation_id = db.Column(db.Integer, db.ForeignKey("conversation.id"), nullable=False)
created_at = db.Column(db.DateTime, default=lambda: datetime.now(timezone.utc))
# Thoroughly isolated test Flask application
@pytest.fixture
def app():
"""Create a fresh Flask application instance"""
app = Flask(__name__)
app.config.update({
"SQLALCHEMY_DATABASE_URI": "sqlite:///:memory:",
"SQLALCHEMY_TRACK_MODIFICATIONS": False,
"TESTING": True,
})
# Initialize db
db.init_app(app)
# Define routes
@app.route("/api/login", methods=["POST"])
def login():
data = request.get_json()
username = data.get("username")
password = data.get("password")
if not username or not password:
return jsonify({"error": "Username and password cannot be empty"}), 400
user = User.query.filter_by(username=username).first()
if user and user.check_password(password):
return jsonify({
"id": user.id,
"username": user.username,
"name": user.name,
"created_at": user.created_at.isoformat(),
}), 200
return jsonify({"error": "Invalid username or password"}), 401
@app.route("/api/users/<int:user_id>/conversations", methods=["POST"])
def create_conversation(user_id):
data = request.get_json()
title = data.get("title", f"Conversation {datetime.now().strftime('%Y-%m-%d %H:%M')}")
conversation = Conversation(title=title, user_id=user_id)
db.session.add(conversation)
db.session.commit()
return jsonify({
"id": conversation.id,
"title": conversation.title,
"user_id": conversation.user_id,
"created_at": conversation.created_at.isoformat(),
"updated_at": conversation.updated_at.isoformat(),
}), 201
@app.route("/api/conversations/<int:conversation_id>", methods=["GET"])
def get_conversation(conversation_id):
conversation = Conversation.query.get(conversation_id)
if not conversation:
return jsonify({"error": "Conversation not found"}), 404
messages = Message.query.filter_by(conversation_id=conversation_id).order_by(Message.created_at.asc()).all()
messages_data = [{
"id": msg.id,
"text": msg.text,
"sender": msg.sender,
"created_at": msg.created_at.isoformat(),
} for msg in messages]
return jsonify({
"id": conversation.id,
"title": conversation.title,
"user_id": conversation.user_id,
"messages": messages_data,
"created_at": conversation.created_at.isoformat(),
"updated_at": conversation.updated_at.isoformat(),
}), 200
@app.route("/api/conversations/<int:conversation_id>/messages", methods=["POST"])
def send_message(conversation_id):
conversation = Conversation.query.get(conversation_id)
if not conversation:
return jsonify({"error": "Conversation not found"}), 404
data = request.get_json()
text = data.get("text")
sender = data.get("sender", "user")
if not text:
return jsonify({"error": "Message content cannot be empty"}), 400
# Create user message
user_message = Message(
text=text,
sender=sender,
conversation_id=conversation_id
)
db.session.add(user_message)
# Update conversation title (if this is the first user message)
if sender == "user" and len(conversation.messages) <= 1:
conversation.title = text[:20] + ("..." if len(text) > 20 else "")
db.session.commit()
# Simulate AI response
ai_message = Message(
text="Test response part 1 Test response part 2",
sender="ai",
conversation_id=conversation_id
)
db.session.add(ai_message)
db.session.commit()
return jsonify({
"id": user_message.id,
"text": user_message.text,
"sender": user_message.sender,
"created_at": user_message.created_at.isoformat(),
}), 201
# Initialize database
with app.app_context():
db.create_all()
# Create example users
if not User.query.first():
user1 = User(username="user1", name="Bruce")
user1.set_password("password123")
db.session.add(user1)
db.session.commit()
yield app
with app.app_context():
db.drop_all()
db.session.remove()
@pytest.fixture
def client(app):
"""Flask test client"""
return app.test_client()
# Mock call_runner function
def mock_call_runner(query, session_id, user_id):
"""Mock function for call_runner"""
yield "Test response part 1"
yield " Test response part 2"
def test_login_success(app, client):
"""Test successful user login"""
with app.app_context():
user = User(username="test", name="Test User")
user.set_password("testpass")
db.session.add(user)
db.session.commit()
response = client.post("/api/login", json={
"username": "test",
"password": "testpass",
})
assert response.status_code == 200
data = response.get_json()
assert data["username"] == "test"
def test_login_invalid_credentials(app, client):
"""Test login with invalid credentials"""
response = client.post("/api/login", json={
"username": "test",
"password": "wrongpass"
})
assert response.status_code == 401
def test_conversation_crud_operations(app, client):
"""Test conversation creation and retrieval"""
with app.app_context():
user = User(username="test", name="Test User")
user.set_password("testpass")
db.session.add(user)
db.session.commit()
create_response = client.post("/api/users/1/conversations", json={
"title": "Test Conversation",
})
assert create_response.status_code == 201
conversation_id = create_response.get_json()["id"]
get_response = client.get(f"/api/conversations/{conversation_id}")
assert get_response.status_code == 200
assert "Test Conversation" in get_response.get_json()["title"]
@patch("tests.conversational_agents_chatbot_fullstack_runtime_webserver_test.db", new=db)
def test_send_message(app, client):
"""Test message sending and AI response"""
with app.app_context():
user = User(username="test", name="Test User")
user.set_password("testpass")
conversation = Conversation(title="Test", user_id=1)
db.session.add_all([user, conversation])
db.session.commit()
response = client.post("/api/conversations/1/messages", json={
"text": "Hello",
"sender": "user"
})
assert response.status_code == 201
data = response.get_json()
assert "id" in data
assert "Hello" in data["text"]
# ✅ Move the query into the application context
with app.app_context():
messages = Message.query.filter_by(conversation_id=1).all()
assert len(messages) == 2 # User + AI response
assert data["username"] == username

View File

@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
import pytest
from unittest.mock import AsyncMock
import pytest
from agentscope.message import Msg
from agentscope.agent import ReActAgent
from agentscope.tool import Toolkit
@@ -12,13 +12,14 @@ class TestReActAgent:
@pytest.fixture
def test_agent(self):
"""Fixture to create a test ReAct agent with fully mocked dependencies"""
"""Fixture to create a test ReAct
agent with fully mocked dependencies"""
async def model_response(*args, **kwargs):
async def model_response():
yield Msg(
name="Friday",
content="Mocked model response",
role="assistant"
role="assistant",
)
mock_model = AsyncMock()
@@ -36,10 +37,12 @@ class TestReActAgent:
model=mock_model,
formatter=mock_formatter,
toolkit=Toolkit(),
memory=mock_memory
memory=mock_memory,
)
# pylint: disable=protected-access
agent._reasoning_hint_msgs = AsyncMock()
# pylint: disable=protected-access
agent._reasoning_hint_msgs.get_memory = AsyncMock(return_value=[])
return agent
@@ -47,16 +50,16 @@ class TestReActAgent:
async def test_exit_command(self, test_agent, monkeypatch):
"""Test exit command handling"""
async def exit_model_response(*args, **kwargs):
async def exit_model_response(*_args, **_kwargs):
yield Msg(
name="Friday",
content="exit",
role="assistant"
role="assistant",
)
test_agent.model.side_effect = exit_model_response
monkeypatch.setattr('builtins.input', lambda _: "exit")
monkeypatch.setattr("builtins.input", lambda _: "exit")
msg = Msg(name="User", content="exit", role="user")
response = await test_agent(msg)
@@ -66,11 +69,13 @@ class TestReActAgent:
async def test_conversation_flow(self, monkeypatch):
"""Test full conversation flow"""
async def model_response(*args, **kwargs):
async def model_response(*_args, **_kwargs):
yield Msg(
name="Friday",
content="Thought: I need to use a tool\nAction: execute_shell_command\nAction Input: echo 'Hello World'",
role="assistant"
content="Thought: I need to use a tool\n"
"Action: execute_shell_command\n"
"Action Input: echo 'Hello World'",
role="assistant",
)
mock_model = AsyncMock()
@@ -88,10 +93,10 @@ class TestReActAgent:
model=mock_model,
formatter=mock_formatter,
toolkit=Toolkit(),
memory=mock_memory
memory=mock_memory,
)
monkeypatch.setattr('builtins.input', lambda _: "Test command")
monkeypatch.setattr("builtins.input", lambda _: "Test command")
msg = Msg(name="User", content="Test command", role="user")
response = await agent(msg)

View File

@@ -1,5 +1,4 @@
# tests/evaluation_test.py
import asyncio
# -*- coding: utf-8 -*-
import os
from unittest.mock import Mock, AsyncMock, patch
from typing import List, Dict, Any, Tuple, Callable
@@ -29,19 +28,21 @@ class TestReActAgentSolution:
def mock_pre_hook(self) -> Mock:
"""Create a mock pre-hook function that returns None"""
def pre_hook_return(*args, **kwargs):
def pre_hook_return():
"""Mock function that returns None (no modifications)"""
return None
mock = Mock()
mock.__name__ = "save_logging"
mock.side_effect = pre_hook_return # ✅ Return None to avoid parameter pollution
mock.side_effect = (
pre_hook_return # ✅ Return None to avoid parameter pollution
)
return mock
def _create_mock_tools(self) -> List[Tuple[Callable, Dict[str, Any]]]:
"""Create mock tool functions with schemas"""
def mock_tool(*args, **kwargs):
def mock_tool():
return "tool_response"
tool_schema = {
@@ -110,8 +111,15 @@ class TestMainFunction:
mock_evaluator_class.return_value = mock_evaluator
# ✅ Simulate _download_data and _load_data
with patch("agentscope.evaluate._ace_benchmark._ace_benchmark.ACEBenchmark._download_data"):
with patch("agentscope.evaluate._ace_benchmark._ace_benchmark.ACEBenchmark._load_data", return_value=[]):
with patch(
"agentscope.evaluate._ace_benchmark."
"_ace_benchmark.ACEBenchmark._download_data",
):
with patch(
"agentscope.evaluate._ace_benchmark."
"_ace_benchmark.ACEBenchmark._load_data",
return_value=[],
):
# Run main function
await ace_main.main()
@@ -137,8 +145,15 @@ class TestMainFunction:
mock_evaluator_class.return_value = mock_evaluator
# ✅ Simulate _download_data and _load_data
with patch("agentscope.evaluate._ace_benchmark._ace_benchmark.ACEBenchmark._download_data"):
with patch("agentscope.evaluate._ace_benchmark._ace_benchmark.ACEBenchmark._load_data", return_value=[]):
with patch(
"agentscope.evaluate._ace_benchmark._ace_benchmark."
"ACEBenchmark._download_data",
):
with patch(
"agentscope.evaluate._ace_benchmark."
"_ace_benchmark.ACEBenchmark._load_data",
return_value=[],
):
# Run main function
await ace_main.main()

View File

@@ -1,8 +1,6 @@
# -*- coding: utf-8 -*-
import os
import asyncio
import pytest
from unittest.mock import AsyncMock, patch, MagicMock
import pytest
from agentscope.agent import ReActAgent
from agentscope.model import ChatModelBase
from agentscope.formatter import FormatterBase
@@ -47,9 +45,12 @@ async def test_witch_resurrect() -> None:
async def mock_model(**kwargs):
return {"resurrect": kwargs.get("resurrect", False)}
with patch("games.game_werewolves.game.WitchResurrectModel", side_effect=mock_model):
with patch(
"games.game_werewolves.game.WitchResurrectModel",
side_effect=mock_model,
):
result = await game.WitchResurrectModel(**{"resurrect": True})
assert result["resurrect"] == True
assert result["resurrect"] is True
# -----------------------------
@@ -84,8 +85,9 @@ def test_vote_model_generation() -> None:
name=f"Player{i}",
sys_prompt=f"Vote system prompt {i}",
model=mock_model,
formatter=mock_formatter
) for i in range(3)
formatter=mock_formatter,
)
for i in range(3)
]
VoteModel = structured_model.get_vote_model(agents)
@@ -105,8 +107,8 @@ def test_witch_poison_model_fields() -> None:
name="Player1",
sys_prompt="Poison system prompt",
model=mock_model,
formatter=mock_formatter
)
formatter=mock_formatter,
),
]
PoisonModel = structured_model.get_poison_model(agents)