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:
1 parent
70dd2b69af
commit
a682dc687e
11 files changed
+4243
-1615
No files matched your search
@@ -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
|
||||
Reference in new issue
Block a user