fischer-agentkit/tests/integration/test_server_e2e.py

240 lines
7.9 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""Server E2E 集成测试 - 完整流程"""
import pytest
from unittest.mock import AsyncMock
from fastapi.testclient import TestClient
from agentkit.core.protocol import AgentStatus
from agentkit.llm.gateway import LLMGateway
from agentkit.llm.protocol import LLMProvider, LLMRequest, LLMResponse, TokenUsage
from agentkit.skills.base import Skill, SkillConfig
from agentkit.skills.registry import SkillRegistry
from agentkit.tools.registry import ToolRegistry
from agentkit.server.app import create_app
class MockLLMProvider(LLMProvider):
"""Mock LLM Provider for integration tests"""
def __init__(self):
self.call_count = 0
async def chat(self, request: LLMRequest) -> LLMResponse:
self.call_count += 1
return LLMResponse(
content='{"result": "integration test output", "content": "This is the generated content from the skill"}',
model="mock-model",
usage=TokenUsage(prompt_tokens=50, completion_tokens=100),
)
@pytest.fixture
def llm_gateway():
gw = LLMGateway()
gw.register_provider("mock", MockLLMProvider())
return gw
@pytest.fixture
def skill_registry():
return SkillRegistry()
@pytest.fixture
def tool_registry():
return ToolRegistry()
@pytest.fixture
def app(llm_gateway, skill_registry, tool_registry):
return create_app(
llm_gateway=llm_gateway,
skill_registry=skill_registry,
tool_registry=tool_registry,
)
@pytest.fixture
def client(app):
return TestClient(app)
class TestFullFlow:
"""完整流程register skill → create agent → submit task → get result"""
def test_register_skill_create_agent_submit_task(self, client):
# Step 1: Register a skill
skill_response = client.post(
"/api/v1/skills",
json={
"config": {
"name": "content_writer",
"agent_type": "content_generation",
"task_mode": "llm_generate",
"description": "Content writing skill",
"prompt": {
"identity": "You are a content writer",
"instructions": "Write high-quality content",
"output_format": "JSON",
},
"intent": {
"keywords": ["write", "content", "article"],
"description": "Content writing and generation",
},
"quality_gate": {
"required_fields": ["content"],
"min_word_count": 5,
},
}
},
)
assert skill_response.status_code == 201
# Step 2: Create agent from skill
agent_response = client.post(
"/api/v1/agents",
json={"skill_name": "content_writer"},
)
assert agent_response.status_code == 201
agent_data = agent_response.json()
assert agent_data["name"] == "content_writer"
# Step 3: Verify agent is listed
list_response = client.get("/api/v1/agents")
assert list_response.status_code == 200
agents = list_response.json()
assert len(agents) == 1
assert agents[0]["name"] == "content_writer"
# Step 4: Submit task using skill_name
task_response = client.post(
"/api/v1/tasks",
json={
"input_data": {"query": "Write an article about AI"},
"skill_name": "content_writer",
},
)
assert task_response.status_code == 200
task_data = task_response.json()
# Result should contain standardized output
assert "skill_name" in task_data or "data" in task_data or "output" in task_data
# Step 5: Verify skill is listed
skills_response = client.get("/api/v1/skills")
assert skills_response.status_code == 200
skills = skills_response.json()
assert len(skills) >= 1
def test_submit_task_auto_routes_to_skill(self, client):
"""Intent Router 自动路由到正确的 skill"""
# Register two skills with different keywords
client.post(
"/api/v1/skills",
json={
"config": {
"name": "translator",
"agent_type": "translation",
"task_mode": "llm_generate",
"prompt": {"identity": "Translator", "instructions": "Translate text"},
"intent": {
"keywords": ["translate", "翻译"],
"description": "Translation skill",
},
}
},
)
client.post(
"/api/v1/skills",
json={
"config": {
"name": "summarizer",
"agent_type": "summarization",
"task_mode": "llm_generate",
"prompt": {"identity": "Summarizer", "instructions": "Summarize text"},
"intent": {
"keywords": ["summarize", "摘要"],
"description": "Summarization skill",
},
}
},
)
# Submit task with keyword matching "translate"
response = client.post(
"/api/v1/tasks",
json={
"input_data": {"query": "Please translate this text to English"},
},
)
# Should route to translator skill via keyword matching
assert response.status_code == 200
def test_delete_agent_then_submit_task_error(self, client):
"""Delete agent → submit task → appropriate error"""
# Register skill and create agent
client.post(
"/api/v1/skills",
json={
"config": {
"name": "deletable_skill",
"agent_type": "deletable_type",
"task_mode": "llm_generate",
"prompt": {"identity": "Deletable"},
"intent": {"keywords": ["delete"], "description": "Deletable skill"},
}
},
)
client.post(
"/api/v1/agents",
json={"skill_name": "deletable_skill"},
)
# Delete the agent
delete_response = client.delete("/api/v1/agents/deletable_skill")
assert delete_response.status_code == 204
# Submit task referencing deleted agent
task_response = client.post(
"/api/v1/tasks",
json={
"input_data": {"query": "test"},
"agent_name": "deletable_skill",
},
)
# Should return 404 since agent was deleted
assert task_response.status_code == 404
def test_health_check_in_flow(self, client):
"""Health check works during full flow"""
response = client.get("/api/v1/health")
assert response.status_code == 200
data = response.json()
assert data["status"] in ("ok", "healthy")
def test_llm_usage_after_tasks(self, client):
"""LLM usage stats available after task execution"""
# Register skill and submit a task
client.post(
"/api/v1/skills",
json={
"config": {
"name": "usage_skill",
"agent_type": "usage_type",
"task_mode": "llm_generate",
"prompt": {"identity": "Usage Skill"},
"intent": {"keywords": ["usage"], "description": "Usage skill"},
}
},
)
client.post(
"/api/v1/tasks",
json={
"input_data": {"query": "test usage"},
"skill_name": "usage_skill",
},
)
# Check usage
response = client.get("/api/v1/llm/usage")
assert response.status_code == 200