新增前后端AI聊天模块,包括流式对话、配置管理和UI组件优化。
This commit is contained in:
99
backend/app/routers/ai_config.py
Normal file
99
backend/app/routers/ai_config.py
Normal file
@@ -0,0 +1,99 @@
|
||||
from datetime import datetime, timezone
|
||||
|
||||
import httpx
|
||||
from fastapi import APIRouter, Depends
|
||||
from sqlmodel import Session, select
|
||||
|
||||
from ..database import get_session
|
||||
from ..models import AIConfig
|
||||
from ..schemas import AIConfigSave, AIConfigRead
|
||||
|
||||
router = APIRouter(prefix="/api/ai", tags=["ai-config"])
|
||||
|
||||
|
||||
def _mask_key(key: str) -> str:
|
||||
if len(key) <= 8:
|
||||
return "****"
|
||||
return key[:4] + "****" + key[-4:]
|
||||
|
||||
|
||||
def _get_or_create_config(session: Session) -> AIConfig:
|
||||
config = session.exec(select(AIConfig)).first()
|
||||
if not config:
|
||||
config = AIConfig()
|
||||
session.add(config)
|
||||
session.commit()
|
||||
session.refresh(config)
|
||||
return config
|
||||
|
||||
|
||||
def _config_to_read(config: AIConfig) -> AIConfigRead:
|
||||
return AIConfigRead(
|
||||
id=config.id,
|
||||
provider=config.provider,
|
||||
base_url=config.base_url,
|
||||
api_key_masked=_mask_key(config.api_key) if config.api_key else "",
|
||||
model=config.model,
|
||||
updated_at=config.updated_at,
|
||||
)
|
||||
|
||||
|
||||
@router.get("/config", response_model=AIConfigRead)
|
||||
def get_config(session: Session = Depends(get_session)):
|
||||
config = _get_or_create_config(session)
|
||||
return _config_to_read(config)
|
||||
|
||||
|
||||
@router.put("/config", response_model=AIConfigRead)
|
||||
def save_config(data: AIConfigSave, session: Session = Depends(get_session)):
|
||||
config = _get_or_create_config(session)
|
||||
config.provider = data.provider
|
||||
config.base_url = data.base_url
|
||||
config.api_key = data.api_key
|
||||
config.model = data.model
|
||||
config.updated_at = datetime.now(timezone.utc)
|
||||
session.add(config)
|
||||
session.commit()
|
||||
session.refresh(config)
|
||||
return _config_to_read(config)
|
||||
|
||||
|
||||
@router.post("/config/test")
|
||||
async def test_config(data: AIConfigSave):
|
||||
"""测试AI连接是否有效"""
|
||||
try:
|
||||
async with httpx.AsyncClient(timeout=15) as client:
|
||||
if data.provider == "anthropic":
|
||||
resp = await client.post(
|
||||
f"{data.base_url.rstrip('/')}/messages",
|
||||
headers={
|
||||
"x-api-key": data.api_key,
|
||||
"anthropic-version": "2023-06-01",
|
||||
"content-type": "application/json",
|
||||
},
|
||||
json={
|
||||
"model": data.model,
|
||||
"max_tokens": 10,
|
||||
"messages": [{"role": "user", "content": "Hi"}],
|
||||
},
|
||||
)
|
||||
else:
|
||||
resp = await client.post(
|
||||
f"{data.base_url.rstrip('/')}/chat/completions",
|
||||
headers={
|
||||
"Authorization": f"Bearer {data.api_key}",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
json={
|
||||
"model": data.model,
|
||||
"max_tokens": 10,
|
||||
"messages": [{"role": "user", "content": "Hi"}],
|
||||
},
|
||||
)
|
||||
if resp.status_code < 400:
|
||||
return {"ok": True, "message": "连接成功"}
|
||||
return {"ok": False, "message": f"API返回错误: {resp.status_code}"}
|
||||
except httpx.ConnectError:
|
||||
return {"ok": False, "message": "无法连接到API地址"}
|
||||
except Exception as e:
|
||||
return {"ok": False, "message": str(e)}
|
||||
Reference in New Issue
Block a user