chore: opencode冗余清理 + LLM任务级模型选择 + systemd服务化
- 删除 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
This commit is contained in:
@@ -9,6 +9,14 @@ from .auth import get_current_admin
|
||||
|
||||
router = APIRouter(prefix="/api/admin/llmconfigs", tags=["admin"])
|
||||
|
||||
def _apply_default_exclusive(config: LLMConfig, db: Session):
|
||||
"""当 config.is_default=True 时,将其他所有配置的 is_default 置为 False"""
|
||||
if config.is_default:
|
||||
db.query(LLMConfig).filter(LLMConfig.id != config.id).update(
|
||||
{"is_default": False}, synchronize_session=False
|
||||
)
|
||||
db.flush()
|
||||
|
||||
@router.get("", response_model=List[LLMConfigResponse])
|
||||
def list_llm_configs(
|
||||
request: Request,
|
||||
@@ -42,6 +50,8 @@ def create_llm_config(
|
||||
"""创建 LLM 配置"""
|
||||
config = LLMConfig(**config_data.model_dump())
|
||||
db.add(config)
|
||||
db.flush()
|
||||
_apply_default_exclusive(config, db)
|
||||
db.commit()
|
||||
db.refresh(config)
|
||||
return config
|
||||
@@ -61,6 +71,7 @@ def update_llm_config(
|
||||
update_data = config_update.model_dump(exclude_unset=True)
|
||||
for field, value in update_data.items():
|
||||
setattr(config, field, value)
|
||||
_apply_default_exclusive(config, db)
|
||||
db.commit()
|
||||
db.refresh(config)
|
||||
return config
|
||||
|
||||
@@ -119,16 +119,6 @@ def test_provider(provider_id: int, data: dict = {}, db: Session = Depends(get_d
|
||||
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]}
|
||||
elif p.provider_type == "mcp":
|
||||
import subprocess, json as _json
|
||||
mcp_script = Path(__file__).resolve().parent.parent.parent.parent.parent / "scripts" / "mcp_search_server.py"
|
||||
r = subprocess.run(
|
||||
[sys.executable, str(mcp_script), "--query", query],
|
||||
capture_output=True, text=True, timeout=90,
|
||||
)
|
||||
if r.returncode != 0:
|
||||
return {"ok": False, "error": f"子进程失败: {r.stderr[:200]}"}
|
||||
return {"ok": True, "results": _json.loads(r.stdout)[:3]}
|
||||
return {"ok": False, "error": f"Unknown provider_type: {p.provider_type}"}
|
||||
except Exception as e:
|
||||
return {"ok": False, "error": str(e)}
|
||||
|
||||
@@ -280,28 +280,6 @@ def trigger_metrics_sync():
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
@router.post("/refresh-search-cache/run")
|
||||
def trigger_refresh_search_cache(db: Session = Depends(get_db), current_user=Depends(get_current_user)):
|
||||
try:
|
||||
import sys as sys_mod
|
||||
scripts_dir = PROJECT_ROOT / "scripts"
|
||||
from ..database import SessionLocal as _ss
|
||||
proc = subprocess.Popen(
|
||||
[sys_mod.executable, str(scripts_dir / "opencode_search.py"), "--refresh-cache"],
|
||||
stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True,
|
||||
cwd=str(PROJECT_ROOT)
|
||||
)
|
||||
logger.info("Search cache refresh started (pid=%s)", proc.pid)
|
||||
log = TaskLog(module_id="scheduled_refresh_search_cache", task_name="🔍 搜索缓存", status="running", message="搜索缓存刷新已启动", triggered_by="manual", started_at=datetime.now(timezone.utc), result_data={"pid": proc.pid})
|
||||
db.add(log)
|
||||
db.commit()
|
||||
log_id = log.id
|
||||
t = threading.Thread(target=_monitor_subprocess, args=(log_id, proc, "scheduled_refresh_search_cache", "🔍 搜索缓存", _ss), daemon=True)
|
||||
t.start()
|
||||
return {"message": "搜索缓存刷新已后台启动", "pid": proc.pid, "log_id": log_id}
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
@router.post("/trends/run")
|
||||
def trigger_trends_refresh(db: Session = Depends(get_db), current_user=Depends(get_current_user)):
|
||||
try:
|
||||
@@ -370,7 +348,6 @@ def get_modules_status(db: Session = Depends(get_db)):
|
||||
config_map = {c.module_id: c for c in configs}
|
||||
|
||||
MODULE_META = {
|
||||
"scheduled_refresh_search_cache": {"name": "🔍 搜索缓存", "cron": "01:00", "params_desc": {"refresh_queries": "搜索关键词列表"}},
|
||||
"scheduled_fetch_trends": {"name": "🔥 热点趋势", "cron": "01:10", "params_desc": {}},
|
||||
"scheduled_collect": {"name": "📡 内容采集", "cron": "01:30", "params_desc": {"max_topics": "最大选题数", "categories": "采集类别"}},
|
||||
"scheduled_generate": {"name": "🤖 内容创作", "cron": "02:00", "params_desc": {"auto_review": "自动合规审查"}},
|
||||
|
||||
@@ -11,14 +11,13 @@ from .auth import get_current_admin
|
||||
router = APIRouter(prefix="/api/admin/task-configs", tags=["admin"])
|
||||
|
||||
DEFAULT_CONFIGS = {
|
||||
"scheduled_refresh_search_cache": {"name": "🔍 搜索缓存", "cron": "01:00", "params": {}},
|
||||
"scheduled_fetch_trends": {"name": "🔥 热点趋势", "cron": "01:10", "params": {}},
|
||||
"scheduled_collect": {"name": "📡 内容采集", "cron": "01:30", "params": {"max_topics": 20}},
|
||||
"scheduled_generate": {"name": "🤖 内容创作", "cron": "02:00", "params": {"auto_review": True}},
|
||||
"scheduled_optimize": {"name": "🔍 合规审查", "cron": "03:00", "params": {"auto_pass_threshold": 80}},
|
||||
"scheduled_optimize_sources": {"name": "📡 信息源优化", "cron": "05:00", "params": {}},
|
||||
"scheduled_metrics_sync": {"name": "📊 指标同步", "cron": "06:00", "params": {}},
|
||||
"scheduled_task_monitor": {"name": "⏰ 任务监控", "cron": "*", "params": {}},
|
||||
"scheduled_fetch_trends": {"name": "🔥 热点趋势", "cron": "01:10", "params": {"llm_provider": "nvidia"}},
|
||||
"scheduled_collect": {"name": "📡 内容采集", "cron": "01:30", "params": {"max_topics": 20, "llm_provider": "nvidia"}},
|
||||
"scheduled_generate": {"name": "🤖 内容创作", "cron": "02:00", "params": {"auto_review": True, "llm_provider": "nvidia"}},
|
||||
"scheduled_optimize": {"name": "🔍 合规审查", "cron": "03:00", "params": {"auto_pass_threshold": 80, "llm_provider": "sensenova"}},
|
||||
"scheduled_optimize_sources": {"name": "📡 信息源优化", "cron": "05:00", "params": {"llm_provider": "nvidia"}},
|
||||
"scheduled_metrics_sync": {"name": "📊 指标同步", "cron": "06:00", "params": {"llm_provider": "nvidia"}},
|
||||
"scheduled_task_monitor": {"name": "⏰ 任务监控", "cron": "*", "params": {"llm_provider": "nvidia"}},
|
||||
}
|
||||
|
||||
def _attach_last_log(resp: TaskConfigResponse, db: Session, module_id: str) -> TaskConfigResponse:
|
||||
|
||||
@@ -12,7 +12,6 @@ from .auth import get_current_admin
|
||||
router = APIRouter(prefix="/api/admin/task-logs", tags=["admin"])
|
||||
|
||||
MODULES = {
|
||||
"scheduled_refresh_search_cache": "🔍 搜索缓存",
|
||||
"scheduled_fetch_trends": "🔥 热点趋势",
|
||||
"scheduled_collect": "📡 内容采集",
|
||||
"scheduled_generate": "🤖 内容创作",
|
||||
@@ -79,7 +78,6 @@ def list_log_types(db: Session = Depends(get_db), admin_user=Depends(get_current
|
||||
for mid, name in MODULES.items():
|
||||
if mid in used_ids or True:
|
||||
log_file_map = {
|
||||
"scheduled_refresh_search_cache": "opencode_search",
|
||||
"scheduled_fetch_trends": "trends",
|
||||
"scheduled_collect": "collector",
|
||||
"scheduled_generate": "creator",
|
||||
|
||||
@@ -236,7 +236,6 @@ def _get_module_detail_data(module_id: str, db, ROOT, DATA_DIR, LOGS_DIR, today_
|
||||
import json as json_mod
|
||||
import re as re_mod
|
||||
MODULE_META = {
|
||||
"scheduled_refresh_search_cache": {"name": "🔍 搜索缓存", "description": "通过 opencode webfetch 联网搜索,刷新 8 个分类的搜索缓存,供内容采集器使用"},
|
||||
"scheduled_fetch_trends": {"name": "🔥 热点趋势", "description": "从百度、微博、知乎实时热搜 API 抓取当天热点,LLM 补充,存入 trends.json"},
|
||||
"scheduled_collect": {"name": "📡 内容采集", "description": "读取搜索缓存 + 热点趋势 + 历史表现 + AI 建议,经 LLM 分析后生成选题"},
|
||||
"scheduled_generate": {"name": "🤖 内容创作", "description": "基于选题,LLM 生成三平台文章(知乎、微信、小红书),存入 articles 表"},
|
||||
@@ -253,23 +252,7 @@ def _get_module_detail_data(module_id: str, db, ROOT, DATA_DIR, LOGS_DIR, today_
|
||||
outputs = {}
|
||||
history = []
|
||||
|
||||
if module_id == "scheduled_refresh_search_cache":
|
||||
cache_file = DATA_DIR / "search_cache.json"
|
||||
if cache_file.exists():
|
||||
try:
|
||||
cache = json_mod.loads(cache_file.read_text(encoding="utf-8"))
|
||||
meta_ = cache.pop("_metadata", {})
|
||||
for q, results in cache.items():
|
||||
inputs.setdefault("搜索词", []).append(q)
|
||||
outputs.setdefault("各分类结果", []).append({
|
||||
"query": q, "count": len(results),
|
||||
"samples": [r.get("title","")[:50] for r in results[:3]]
|
||||
})
|
||||
outputs["更新时间"] = meta_.get("updated_at", "")
|
||||
outputs["结果总数"] = sum(len(v) for v in cache.values())
|
||||
except Exception:
|
||||
pass
|
||||
# try reading queries from yaml
|
||||
# try reading queries from yaml
|
||||
try:
|
||||
import yaml
|
||||
cfg_path = ROOT / "config" / "sources.yaml"
|
||||
@@ -390,7 +373,6 @@ def _get_module_detail_data(module_id: str, db, ROOT, DATA_DIR, LOGS_DIR, today_
|
||||
|
||||
# History from log files
|
||||
log_map = {
|
||||
"scheduled_refresh_search_cache": LOGS_DIR / f"opencode_search_{today_str}.log",
|
||||
"scheduled_fetch_trends": LOGS_DIR / f"trends_{today_str}.log",
|
||||
"scheduled_collect": LOGS_DIR / f"collector_{today_str}.log",
|
||||
"scheduled_generate": LOGS_DIR / f"creator_{today_str}.log",
|
||||
|
||||
Reference in New Issue
Block a user