Phase 2.3: Agent Management Module

Backend:
- agents_admin.py: 10 REST API endpoints for AI Agent CRUD
  - GET/POST/PUT/DELETE /admin/agents/
  - Stats overview, reviews, available skills/models
  - Agent = MarketApp with type='agent'

Frontend:
- Agents.tsx: Full admin page (780 lines)
  - Stats dashboard, filterable table, create/edit modal
  - Config: system prompt, temperature, model, linked skills
- api/index.ts: 10 new Agent API methods
- App.tsx: /admin/agents route
- AdminLayout.tsx: Agent management nav item
This commit is contained in:
qyyfw committed 2026-07-02 20:18:10 +08:00
1 parent 70dd2b69af
commit a682dc687e
11 files changed
+4243 -1615

No files matched your search

+152
View File
@@ -600,3 +600,155 @@ async def get_model_stats(
"total_calls": int(total_calls),
"total_tokens": int(total_tokens),
}
# ============================================================
# 模型连接测试 API (Phase 2)
# ============================================================
@router.post("/{model_id}/test-connection")
async def test_model_connection(
model_id: str,
admin_user=Depends(get_current_admin),
db: Session = Depends(get_db),
):
"""
测试指定模型的连接是否正常
支持从模型记录或提供商记录读取配置
"""
logger = logging.getLogger(__name__)
logger.info(f"Testing connection for model_id: {model_id}")
# 优先从 AIModel 读取
ai_model = db.query(AIModel).filter(AIModel.id == model_id).first()
if not ai_model:
raise HTTPException(status_code=404, detail="模型不存在")
provider = db.query(ModelProvider).filter(ModelProvider.id == ai_model.provider_id).first()
if not provider:
raise HTTPException(status_code=404, detail="模型供应商不存在")
# 确定最终配置
api_key = ai_model.api_key or (provider.api_key if provider else None)
api_url = ai_model.endpoint
# 如果 endpoint 为空,尝试构造
if not api_url and provider and provider.api_base_url:
base = provider.api_base_url.rstrip('/')
if provider.protocol == 'openai_compatible':
api_url = f"{base}/v1/chat/completions"
else:
api_url = f"{base}/v1/chat/completions"
if not api_key:
return {
"success": False,
"message": "未配置 API Key,请在模型或供应商设置中添加",
"latency_ms": None,
}
if not api_url:
return {
"success": False,
"message": "未配置 API 地址,请在模型或供应商设置中添加",
"latency_ms": None,
}
timeout = ai_model.timeout or 60
import time
start = time.time()
try:
async with httpx.AsyncClient(timeout=timeout) as client:
headers = {
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json",
}
# 使用 minimal payload 测试连接
payload = {
"model": ai_model.name,
"messages": [{"role": "user", "content": "hi"}],
"max_tokens": 5,
}
resp = await client.post(api_url, json=payload, headers=headers)
latency = int((time.time() - start) * 1000)
if resp.status_code == 200:
try:
data = resp.json()
model_used = data.get("model", ai_model.name)
return {
"success": True,
"message": f"连接成功,使用模型: {model_used}",
"latency_ms": latency,
"model_in_response": model_used,
}
except Exception:
return {
"success": True,
"message": f"连接成功 (HTTP {resp.status_code})",
"latency_ms": latency,
}
elif resp.status_code == 401:
return {
"success": False,
"message": "认证失败:API Key 无效或已过期",
"latency_ms": latency,
}
elif resp.status_code == 403:
return {
"success": False,
"message": "访问被拒绝:API Key 权限不足",
"latency_ms": latency,
}
elif resp.status_code == 429:
return {
"success": False,
"message": "触发限流:请稍后重试",
"latency_ms": latency,
}
else:
try:
err_data = resp.json()
err_msg = err_data.get("error", {}).get("message", resp.text[:200])
except Exception:
err_msg = resp.text[:200]
return {
"success": False,
"message": f"API 错误 (HTTP {resp.status_code}): {err_msg}",
"latency_ms": latency,
}
except httpx.TimeoutException:
latency = int((time.time() - start) * 1000)
return {
"success": False,
"message": f"连接超时({timeout}s):请检查 API 地址是否正确,或服务是否可访问",
"latency_ms": latency,
}
except Exception as e:
latency = int((time.time() - start) * 1000)
logger.error(f"Model connection test failed: {e}")
return {
"success": False,
"message": f"连接失败: {str(e)}",
"latency_ms": latency,
}
timeout: int = 60
status: str = "paid"
price_per_million_tokens: float = 0.0
is_enabled: bool = True
class UpdateModelRequest(BaseModel):
name: Optional[str] = None
api_key: Optional[str] = None
endpoint: Optional[str] = None
capabilities: Optional[List[str]] = None
timeout: Optional[int] = None
status: Optional[str] = None
price_per_million_tokens: Optional[float] = None
is_enabled: Optional[bool] = None
from typing import Optional