227 lines
7.2 KiB
Python
227 lines
7.2 KiB
Python
"""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"
|