"""Test utilities for copaw tests.""" import os from agentscope.message import Msg, ThinkingBlock, TextBlock, ToolUseBlock, ToolResultBlock from reme.memory.file_based.utils import AsMsgHandler def get_token_counter(): """Get HF token counter instance.""" from reme.core.utils import get_hf_token_counter return get_hf_token_counter() def get_msg_handler() -> AsMsgHandler: """Get AsMsgHandler instance.""" return AsMsgHandler(token_counter=get_token_counter()) def get_dash_chat_model(model_name: str = "qwen3.5-plus"): """Get DashScope chat model instance.""" from agentscope.model import OpenAIChatModel from reme.core.utils import load_env load_env() return OpenAIChatModel( api_key=os.environ["LLM_API_KEY"], client_kwargs={"base_url": os.environ["LLM_BASE_URL"]}, model_name=model_name, ) def get_formatter(): """Get formatter instance.""" from agentscope.formatter import OpenAIChatFormatter return OpenAIChatFormatter() def generate_large_code_content(target_tokens: int = 50000) -> str: """生成大量代码内容,用于测试超长 tool_result。 Args: target_tokens: 目标 token 数(约 4 字符/token) Returns: 生成的代码内容字符串 """ code_template = ''' # === File: src/module_{idx}/handlers.py === """Handler module {idx} for processing requests.""" import asyncio import logging from typing import Any, Dict, List, Optional from dataclasses import dataclass, field from datetime import datetime logger = logging.getLogger(__name__) @dataclass class RequestContext_{idx}: """Context for request processing in module {idx}.""" request_id: str user_id: str timestamp: datetime = field(default_factory=datetime.now) metadata: Dict[str, Any] = field(default_factory=dict) headers: Dict[str, str] = field(default_factory=dict) query_params: Dict[str, str] = field(default_factory=dict) body: Optional[bytes] = None processed: bool = False error_message: Optional[str] = None class Handler_{idx}: """Main handler class for module {idx}.""" def __init__(self, config: Dict[str, Any]): self.config = config self.cache: Dict[str, Any] = {{}} self.metrics: Dict[str, int] = {{ "requests_processed": 0, "errors": 0, "cache_hits": 0, "cache_misses": 0, }} self._initialized = False logger.info(f"Handler_{idx} initialized with config: {{config}}") async def initialize(self) -> None: """Initialize the handler with async resources.""" if self._initialized: logger.warning("Handler_{idx} already initialized") return # Simulate async initialization await asyncio.sleep(0.01) self._initialized = True logger.info("Handler_{idx} initialization complete") async def process_request(self, context: RequestContext_{idx}) -> Dict[str, Any]: """Process an incoming request. Args: context: The request context containing all request data Returns: Dict containing the response data """ if not self._initialized: raise RuntimeError("Handler not initialized") self.metrics["requests_processed"] += 1 try: # Check cache first cache_key = f"{{context.request_id}}_{{context.user_id}}" if cache_key in self.cache: self.metrics["cache_hits"] += 1 return self.cache[cache_key] self.metrics["cache_misses"] += 1 # Process the request result = await self._do_process(context) # Cache the result self.cache[cache_key] = result context.processed = True return result except Exception as e: self.metrics["errors"] += 1 context.error_message = str(e) logger.exception(f"Error processing request {{context.request_id}}: {{e}}") raise async def _do_process(self, context: RequestContext_{idx}) -> Dict[str, Any]: """Internal processing logic.""" # Simulate some processing await asyncio.sleep(0.001) return {{ "status": "success", "request_id": context.request_id, "user_id": context.user_id, "processed_at": datetime.now().isoformat(), "module": "module_{idx}", "data": {{ "result": f"Processed by handler_{idx}", "metadata": context.metadata, }} }} def get_metrics(self) -> Dict[str, int]: """Return current metrics.""" return self.metrics.copy() async def cleanup(self) -> None: """Cleanup resources.""" self.cache.clear() self._initialized = False logger.info("Handler_{idx} cleaned up") ''' # 每个模块约 2000 字符 ≈ 500 tokens # 目标 target_tokens,需要 target_tokens / 500 个模块 num_modules = max(1, target_tokens // 500) parts = [f"# 大型项目代码检索结果\n# 共找到 {num_modules} 个相关模块\n"] for i in range(num_modules): parts.append(code_template.format(idx=i)) return "".join(parts) def build_sample_messages(include_large_tool_result: bool = True) -> list[Msg]: """构建一段包含多种消息类型的模拟对话。 Args: include_large_tool_result: 是否包含大型 tool_result,确保超过 128K token Returns: 消息列表 """ messages = [ Msg( name="user", role="user", content="我想设置一个 Python 开发环境,你有什么建议?", ), Msg( name="assistant", role="assistant", content=[ ThinkingBlock(type="thinking", thinking="用户想要搭建 Python 开发环境,我需要了解他的需求和偏好..."), TextBlock( type="text", text="好的!我建议使用 Python 3.11 或 3.12 版本,它们性能更好且功能丰富。" "你希望用于什么类型的开发?Web、数据科学还是其他?", ), ], ), Msg(name="user", role="user", content="主要是做 Web 开发,使用 FastAPI 框架。另外我喜欢用 pyenv 管理版本。"), Msg( name="assistant", role="assistant", content=[ ThinkingBlock(type="thinking", thinking="用户要做 FastAPI Web 开发,我需要搜索相关信息..."), ToolUseBlock( type="tool_use", id="call_001", name="search_web", input={"query": "FastAPI Python version compatibility 2024"}, ), ], ), Msg( name="system", role="system", content=[ ToolResultBlock( type="tool_result", id="call_001", name="search_web", output=[ TextBlock( type="text", text="FastAPI 官方推荐使用 Python 3.8+ 版本,但 3.11/3.12 性能最佳。\n" "主要依赖:\n" "- Starlette: ASGI 框架\n" "- Pydantic v2: 数据验证\n" "- Uvicorn: ASGI 服务器\n" "最新版本 FastAPI 0.109+ 完全支持 Python 3.12。\n" "建议搭配 uv 或 pip-tools 进行依赖管理。", ), ], ), ], ), Msg( name="assistant", role="assistant", content=[ ThinkingBlock( type="thinking", thinking="根据搜索结果,FastAPI 完全支持 Python 3.12,结合用户使用 pyenv 的习惯,我来给出完整的环境配置建议...", ), TextBlock( type="text", text="根据最新信息,我为你整理了 FastAPI 开发环境配置方案:\n\n" "**1. Python 版本**\n" "推荐 Python 3.12,性能最佳且完全兼容 FastAPI 0.109+。\n\n" "**2. pyenv 安装步骤**\n" "```bash\n" "pyenv install 3.12.2\n" "pyenv local 3.12.2\n" "```\n\n" "**3. 虚拟环境与依赖管理**\n" "建议使用 `uv` 作为包管理器,它比 pip 快 10-100 倍。\n\n" "需要我帮你生成项目初始化脚本吗?", ), ], ), Msg(name="user", role="user", content="好的,请帮我生成一个完整的项目初始化脚本,包括 pyproject.toml 配置。"), Msg( name="assistant", role="assistant", content=[ ThinkingBlock( type="thinking", thinking="用户需要完整的项目初始化脚本,我需要创建 pyproject.toml 并设置好 FastAPI 相关依赖...", ), ToolUseBlock( type="tool_use", id="call_002", name="create_file", input={ "path": "pyproject.toml", "content": "[project]\nname = 'my-fastapi-app'\nversion = '0.1.0'\nrequires-python = " "'>=3.11'\ndependencies = ['fastapi>=0.109.0', 'uvicorn[standard]>=0.27.0', " "'pydantic>=2.0']", }, ), ], ), Msg( name="system", role="system", content=[ ToolResultBlock( type="tool_result", id="call_002", name="create_file", output=[ TextBlock(type="text", text="文件 pyproject.toml 已创建成功。"), ], ), ], ), Msg( name="assistant", role="assistant", content=[ ToolUseBlock( type="tool_use", id="call_003", name="create_file", input={ "path": "init_project.sh", "content": "#!/bin/bash\nset -e\n\n# 使用 pyenv 设置 Python 版本\npyenv install 3.12.2 " "--skip-existing\npyenv local 3.12.2\n\n# 创建虚拟环境\npython -m venv " ".venv\nsource .venv/bin/activate\n\n# 安装 uv 并使用它安装依赖\npip install uv" "\nuv pip install -e .\n\necho '环境初始化完成!'", }, ), ], ), Msg( name="system", role="system", content=[ ToolResultBlock( type="tool_result", id="call_003", name="create_file", output=[ TextBlock(type="text", text="文件 init_project.sh 已创建成功。"), ], ), ], ), Msg( name="assistant", role="assistant", content=[ TextBlock( type="text", text="我已为你创建了两个文件:\n\n" "1. **pyproject.toml** - 项目配置文件,包含 FastAPI 核心依赖\n" "2. **init_project.sh** - 一键初始化脚本\n\n" "运行以下命令即可初始化项目:\n" "```bash\n" "chmod +x init_project.sh && ./init_project.sh\n" "```\n\n" "还有什么需要帮助的吗?", ), ], ), Msg(name="user", role="user", content="太棒了!请帮我搜索一下项目中所有的 handler 相关代码。"), ] # 添加大型代码搜索结果(确保超过 128K token) if include_large_tool_result: # 生成超大的代码搜索结果,目标 ~140K tokens large_code_content = generate_large_code_content(target_tokens=140000) messages.extend( [ Msg( name="assistant", role="assistant", content=[ ThinkingBlock( type="thinking", thinking="用户要我搜索项目中的 handler 代码,我需要使用代码搜索工具...", ), ToolUseBlock( type="tool_use", id="call_004", name="search_codebase", input={"query": "handler class implementation"}, ), ], ), Msg( name="system", role="system", content=[ ToolResultBlock( type="tool_result", id="call_004", name="search_codebase", output=[ TextBlock(type="text", text=large_code_content), ], ), ], ), Msg( name="assistant", role="assistant", content=[ ThinkingBlock(type="thinking", thinking="搜索返回了大量 handler 代码,我需要为用户整理一下..."), TextBlock( type="text", text="我已经找到了项目中所有的 handler 相关代码。\n\n" "这些 handler 类包含:\n" "- 请求处理逻辑\n" "- 缓存管理\n" "- 指标统计\n" "- 异步初始化\n\n" "你需要我详细解释某个具体的 handler 吗?", ), ], ), ], ) # 添加更多对话 messages.extend( [ Msg( name="user", role="user", content="还有一个问题,我应该如何配置 VS Code 来获得最佳的 FastAPI 开发体验?", ), Msg( name="assistant", role="assistant", content=[ ThinkingBlock( type="thinking", thinking="用户询问 VS Code 配置,我需要推荐适合 FastAPI 开发的扩展和设置...", ), TextBlock( type="text", text="VS Code 的 FastAPI 开发配置建议:\n\n" "**推荐扩展:**\n" "- Python (Microsoft)\n" "- Pylance - 类型检查和智能补全\n" "- Ruff - 快速 linter 和 formatter\n" "- REST Client - API 测试\n\n" "**settings.json 配置:**\n" "```json\n" "{\n" ' "python.defaultInterpreterPath": ".venv/bin/python",\n' ' "[python]": {\n' ' "editor.defaultFormatter": "charliermarsh.ruff",\n' ' "editor.formatOnSave": true\n' " }\n" "}\n" "```\n\n" "这样配置后,你就能获得完整的类型提示和自动格式化支持了!", ), ], ), ], ) return messages