feat: add a subagent frame
This commit is contained in:
226
agentlite/tests/scenarios/test_file_operations_real_api.py
Normal file
226
agentlite/tests/scenarios/test_file_operations_real_api.py
Normal file
@@ -0,0 +1,226 @@
|
||||
"""End-to-end scenario test for file operations with real API.
|
||||
|
||||
This test simulates a realistic scenario where an agent:
|
||||
1. Reads a file
|
||||
2. Explains its content
|
||||
3. Creates a new file with analysis results
|
||||
|
||||
Uses real SiliconFlow qwen3.5-397B API (requires SILICONFLOW_API_KEY env var).
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
from agentlite import Agent, OpenAIProvider, tool
|
||||
|
||||
# =============================================================================
|
||||
# Configuration from model_config.toml
|
||||
# =============================================================================
|
||||
|
||||
# SiliconFlow API configuration (matches qwen35_397b in model_config.toml)
|
||||
SILICONFLOW_BASE_URL = "https://api.siliconflow.cn/v1"
|
||||
SILICONFLOW_MODEL = "Qwen/Qwen3.5-397B-A17B"
|
||||
|
||||
|
||||
def get_siliconflow_provider() -> OpenAIProvider | None:
|
||||
"""Create OpenAIProvider for SiliconFlow API.
|
||||
|
||||
Returns None if SILICONFLOW_API_KEY is not set.
|
||||
"""
|
||||
api_key = os.environ.get("SILICONFLOW_API_KEY")
|
||||
if not api_key:
|
||||
return None
|
||||
|
||||
return OpenAIProvider(
|
||||
api_key=api_key,
|
||||
base_url=SILICONFLOW_BASE_URL,
|
||||
model=SILICONFLOW_MODEL,
|
||||
)
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# File Operation Tools
|
||||
# =============================================================================
|
||||
|
||||
|
||||
@tool()
|
||||
async def read_file(file_path: str) -> str:
|
||||
"""Read the content of a file.
|
||||
|
||||
Args:
|
||||
file_path: Path to the file to read.
|
||||
|
||||
Returns:
|
||||
The content of the file as a string.
|
||||
|
||||
Raises:
|
||||
FileNotFoundError: If the file does not exist.
|
||||
"""
|
||||
with open(file_path) as f:
|
||||
return f.read()
|
||||
|
||||
|
||||
@tool()
|
||||
async def write_file(file_path: str, content: str) -> str:
|
||||
"""Write content to a file, creating it if it doesn't exist.
|
||||
|
||||
Args:
|
||||
file_path: Path to the file to write.
|
||||
content: Content to write to the file.
|
||||
|
||||
Returns:
|
||||
Success message confirming the file was written.
|
||||
"""
|
||||
# Create parent directories if they don't exist
|
||||
Path(file_path).parent.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
with open(file_path, "w") as f:
|
||||
f.write(content)
|
||||
|
||||
return f"File successfully written to {file_path}"
|
||||
|
||||
|
||||
@tool()
|
||||
async def list_files(directory: str) -> str:
|
||||
"""List all files in a directory.
|
||||
|
||||
Args:
|
||||
directory: Path to the directory to list.
|
||||
|
||||
Returns:
|
||||
A newline-separated list of file names in the directory.
|
||||
"""
|
||||
files = os.listdir(directory)
|
||||
return "\n".join(files)
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# Real API E2E Tests
|
||||
# =============================================================================
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def real_provider():
|
||||
"""Create a real SiliconFlow provider.
|
||||
|
||||
Skip tests if SILICONFLOW_API_KEY is not set.
|
||||
"""
|
||||
provider = get_siliconflow_provider()
|
||||
if provider is None:
|
||||
pytest.skip("SILICONFLOW_API_KEY not set, skipping real API tests")
|
||||
return provider
|
||||
|
||||
|
||||
@pytest.mark.scenario
|
||||
@pytest.mark.expensive
|
||||
class TestFileOperationsWithRealAPI:
|
||||
"""End-to-end tests with real SiliconFlow API."""
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_read_and_summarize(self, real_provider):
|
||||
"""Test reading a file and creating a summary with real API."""
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
# Create a source file with meaningful content
|
||||
source_file = os.path.join(tmpdir, "source.txt")
|
||||
source_content = """AgentLite 项目概述
|
||||
================
|
||||
|
||||
AgentLite 是一个轻量级的 Agent 组件库,主要特点:
|
||||
- 异步优先设计
|
||||
- OpenAI 兼容 API
|
||||
- 工具系统 (支持 MCP)
|
||||
- 流式响应支持
|
||||
|
||||
使用示例:
|
||||
```python
|
||||
from agentlite import Agent, OpenAIProvider
|
||||
|
||||
provider = OpenAIProvider(api_key="...", model="gpt-4")
|
||||
agent = Agent(provider=provider)
|
||||
response = await agent.run("Hello!")
|
||||
```
|
||||
"""
|
||||
with open(source_file, "w") as f:
|
||||
f.write(source_content)
|
||||
|
||||
# Create agent with file tools
|
||||
tools = [read_file, write_file, list_files]
|
||||
agent = Agent(
|
||||
provider=real_provider,
|
||||
tools=tools,
|
||||
system_prompt="你是一个文件分析助手。请使用工具来完成任务。",
|
||||
)
|
||||
|
||||
# Run the agent to read, analyze, and write summary
|
||||
output_file = os.path.join(tmpdir, "summary.txt")
|
||||
response = await agent.run(
|
||||
f"请读取 {source_file} 文件,分析其内容,并创建一个摘要文件保存到 {output_file}。"
|
||||
)
|
||||
|
||||
# Verify the agent responded
|
||||
assert response, "Agent should return a response"
|
||||
print(f"\n[Agent 响应]:\n{response}\n")
|
||||
|
||||
# Verify the output file was created
|
||||
if os.path.exists(output_file):
|
||||
with open(output_file) as f:
|
||||
output_content = f.read()
|
||||
print(f"\n[输出文件内容]:\n{output_content}\n")
|
||||
assert len(output_content) > 0, "Output file should not be empty"
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_list_files_and_combine(self, real_provider):
|
||||
"""Test listing files, reading them, and creating combined report."""
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
# Create multiple files
|
||||
files = {
|
||||
"sales.txt": "销售额增长了 20%",
|
||||
"users.txt": "用户满意度达到 85%",
|
||||
"bugs.txt": "修复了 15 个问题",
|
||||
}
|
||||
for name, content in files.items():
|
||||
with open(os.path.join(tmpdir, name), "w") as f:
|
||||
f.write(content)
|
||||
|
||||
# Create agent with file tools
|
||||
tools = [read_file, write_file, list_files]
|
||||
agent = Agent(
|
||||
provider=real_provider,
|
||||
tools=tools,
|
||||
system_prompt="你是一个数据分析助手。请使用工具来完成任务。",
|
||||
)
|
||||
|
||||
# Run the agent
|
||||
report_file = os.path.join(tmpdir, "report.txt")
|
||||
response = await agent.run(
|
||||
f"列出 {tmpdir} 目录中的所有文件,读取每个文件的内容,然后创建一份综合报告保存到 {report_file}。"
|
||||
)
|
||||
|
||||
# Verify the agent responded
|
||||
assert response, "Agent should return a response"
|
||||
print(f"\n[Agent 响应]:\n{response}\n")
|
||||
|
||||
# The agent should have created the report file
|
||||
if os.path.exists(report_file):
|
||||
with open(report_file) as f:
|
||||
report_content = f.read()
|
||||
print(f"\n[报告文件内容]:\n{report_content}\n")
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_simple_conversation(self, real_provider):
|
||||
"""Test basic conversation without tools."""
|
||||
agent = Agent(
|
||||
provider=real_provider,
|
||||
system_prompt="你是一个有帮助的助手。请用中文回答。",
|
||||
)
|
||||
|
||||
response = await agent.run("你好!请简单介绍一下你自己。")
|
||||
|
||||
assert response, "Agent should return a response"
|
||||
print(f"\n[Agent 自我介绍]:\n{response}\n")
|
||||
assert len(response) > 10, "Response should be meaningful"
|
||||
Reference in New Issue
Block a user