499c511140
- 删除 opencode_search.py / mcp_search_server.py 及所有 MCP 引用 - 移除搜索缓存定时任务(scheduled_refresh_search_cache) - 清理前后端所有 opencode/MCP 代码和注释 - LLM 提供商量换:opencode-go→nvidia(默认)+sensenova(合规审查) - llm_configs 新增 is_default 字段,API 层互斥逻辑 - 所有定时任务支持独立 LLM 模型选择(LLM_TASK_PROVIDER env) - compliance_optimizer.py 修复:import os / 解硬编码 / 关键词过滤 - Scheduler 日志修复:始终 INSERT,避免僵尸 running 行 - Systemd 服务化:Restart=always / 单 worker / Type=exec - 搜索提供商:替换 opencode→360/搜狗/微信(免 Key) - 更新 AGENTS.md / PROGRESS.md
132 lines
5.4 KiB
Python
132 lines
5.4 KiB
Python
import logging
|
|
import sys
|
|
import json
|
|
from pathlib import Path
|
|
from fastapi import APIRouter, Depends, HTTPException
|
|
from sqlalchemy.orm import Session
|
|
from typing import List, Optional
|
|
from datetime import datetime, timezone
|
|
|
|
from ..database import get_db
|
|
from ..models import SearchProvider
|
|
from .auth import get_current_user
|
|
|
|
logger = logging.getLogger(__name__)
|
|
router = APIRouter(prefix="/api/search-providers", tags=["search_providers"])
|
|
|
|
|
|
@router.get("")
|
|
def list_providers(db: Session = Depends(get_db), current_user=Depends(get_current_user)):
|
|
providers = db.query(SearchProvider).order_by(SearchProvider.priority).all()
|
|
return [p.to_dict() for p in providers]
|
|
|
|
|
|
@router.post("")
|
|
def create_provider(data: dict, db: Session = Depends(get_db), current_user=Depends(get_current_user)):
|
|
p = SearchProvider(
|
|
name=data.get("name", ""),
|
|
provider_type=data.get("provider_type", ""),
|
|
api_key=data.get("api_key", ""),
|
|
api_url=data.get("api_url", ""),
|
|
console_url=data.get("console_url", ""),
|
|
priority=data.get("priority", 99),
|
|
enabled=data.get("enabled", True),
|
|
daily_limit=data.get("daily_limit", 1500),
|
|
extra_config=data.get("extra_config", {}),
|
|
)
|
|
db.add(p)
|
|
db.commit()
|
|
db.refresh(p)
|
|
return p.to_dict()
|
|
|
|
|
|
@router.put("/{provider_id}")
|
|
def update_provider(provider_id: int, data: dict, db: Session = Depends(get_db), current_user=Depends(get_current_user)):
|
|
p = db.query(SearchProvider).filter(SearchProvider.id == provider_id).first()
|
|
if not p:
|
|
raise HTTPException(status_code=404, detail="Provider not found")
|
|
for key in ("name", "provider_type", "api_key", "api_url", "console_url", "priority", "enabled", "daily_limit"):
|
|
if key in data:
|
|
setattr(p, key, data[key])
|
|
if "extra_config" in data:
|
|
p.extra_config = data["extra_config"]
|
|
p.updated_at = datetime.now(timezone.utc)
|
|
db.commit()
|
|
db.refresh(p)
|
|
return p.to_dict()
|
|
|
|
|
|
@router.delete("/{provider_id}")
|
|
def delete_provider(provider_id: int, db: Session = Depends(get_db), current_user=Depends(get_current_user)):
|
|
p = db.query(SearchProvider).filter(SearchProvider.id == provider_id).first()
|
|
if not p:
|
|
raise HTTPException(status_code=404, detail="Provider not found")
|
|
db.delete(p)
|
|
db.commit()
|
|
return {"ok": True}
|
|
|
|
|
|
@router.post("/{provider_id}/test")
|
|
def test_provider(provider_id: int, data: dict = {}, db: Session = Depends(get_db), current_user=Depends(get_current_user)):
|
|
p = db.query(SearchProvider).filter(SearchProvider.id == provider_id).first()
|
|
if not p:
|
|
raise HTTPException(status_code=404, detail="Provider not found")
|
|
query = data.get("query", "测试搜索")
|
|
try:
|
|
test_key = data.get("api_key") or p.api_key
|
|
test_url = data.get("api_url") or p.api_url
|
|
if p.provider_type == "baidu":
|
|
import requests
|
|
resp = requests.post(
|
|
test_url,
|
|
headers={"Authorization": f"Bearer {test_key}", "Content-Type": "application/json"},
|
|
json={"messages": [{"role": "user", "content": query}], "search_source": "baidu_search_v2", "resource_type_filter": [{"type": "web", "top_k": 3}]},
|
|
timeout=15
|
|
)
|
|
if resp.status_code != 200:
|
|
return {"ok": False, "error": f"HTTP {resp.status_code}: {resp.text[:200]}"}
|
|
return {"ok": True, "results": resp.json().get("results", [])[:3]}
|
|
elif p.provider_type == "qiniu":
|
|
import requests
|
|
resp = requests.post(
|
|
test_url,
|
|
headers={"Authorization": f"Bearer {test_key}", "Content-Type": "application/json"},
|
|
json={"query": query, "max_results": 3, "search_type": "web"},
|
|
timeout=15
|
|
)
|
|
if resp.status_code != 200:
|
|
return {"ok": False, "error": f"HTTP {resp.status_code}: {resp.text[:200]}"}
|
|
return {"ok": True, "results": resp.json().get("results", resp.json().get("data", []))[:3]}
|
|
elif p.provider_type == "tinyfish":
|
|
import requests
|
|
resp = requests.get(
|
|
test_url,
|
|
params={"query": query, "max_results": 3},
|
|
headers={"X-API-Key": test_key},
|
|
timeout=15
|
|
)
|
|
if resp.status_code != 200:
|
|
return {"ok": False, "error": f"HTTP {resp.status_code}: {resp.text[:200]}"}
|
|
return {"ok": True, "results": resp.json().get("results", resp.json().get("data", []))[:3]}
|
|
elif p.provider_type == "bing":
|
|
import requests
|
|
resp = requests.get(
|
|
test_url,
|
|
params={"q": query, "count": 3, "mkt": "zh-CN"},
|
|
headers={"Ocp-Apim-Subscription-Key": test_key},
|
|
timeout=15
|
|
)
|
|
if resp.status_code != 200:
|
|
return {"ok": False, "error": f"HTTP {resp.status_code}: {resp.text[:200]}"}
|
|
return {"ok": True, "results": resp.json().get("webPages", {}).get("value", [])[:3]}
|
|
return {"ok": False, "error": f"Unknown provider_type: {p.provider_type}"}
|
|
except Exception as e:
|
|
return {"ok": False, "error": str(e)}
|
|
|
|
|
|
@router.post("/reset-usage")
|
|
def reset_usage(db: Session = Depends(get_db), current_user=Depends(get_current_user)):
|
|
db.query(SearchProvider).update({SearchProvider.usage_today: 0})
|
|
db.commit()
|
|
return {"ok": True}
|