docs: v1.5.7 动态API提供商管理,OpenCode Go集成,Bug修复
This commit is contained in:
+28
-5
@@ -467,7 +467,7 @@ def generate_plan():
|
||||
)
|
||||
|
||||
# 真正调用API生成报告
|
||||
_, ai_report, error, _ = generate_ai_report(
|
||||
_, ai_report, error, extra = generate_ai_report(
|
||||
student_name=student.name,
|
||||
wechat_nickname=student.wechat_nickname or "",
|
||||
problems=problem_data,
|
||||
@@ -479,24 +479,46 @@ def generate_plan():
|
||||
)
|
||||
|
||||
if error:
|
||||
# 拼接详细的错误信息用于显示
|
||||
display_error = f"AI生成失败: {error}"
|
||||
if extra:
|
||||
debug_info = []
|
||||
if extra.get("prompt_tokens"):
|
||||
debug_info.append(f"输入token={extra['prompt_tokens']}")
|
||||
if extra.get("completion_tokens"):
|
||||
debug_info.append(f"输出token={extra['completion_tokens']}")
|
||||
if extra.get("finish_reason"):
|
||||
debug_info.append(f"finish={extra['finish_reason']}")
|
||||
if extra.get("raw_keys"):
|
||||
debug_info.append(f"keys={extra['raw_keys']}")
|
||||
if debug_info:
|
||||
display_error += " [" + ", ".join(debug_info) + "]"
|
||||
yield sse_format(
|
||||
{
|
||||
"step": "ai_error",
|
||||
"message": f"AI生成失败: {error}",
|
||||
"message": display_error,
|
||||
"progress": 80,
|
||||
"error": error,
|
||||
"error": display_error,
|
||||
}
|
||||
)
|
||||
plan_content["ai_report_error"] = error
|
||||
plan_content["ai_report_error"] = display_error
|
||||
else:
|
||||
# 显示AI返回的报告长度
|
||||
report_lines = len(ai_report.split("\n")) if ai_report else 0
|
||||
detail_parts = [f"报告长度: {len(ai_report) if ai_report else 0} 字符, {report_lines} 行"]
|
||||
if extra:
|
||||
if extra.get("prompt_tokens"):
|
||||
detail_parts.append(f"输入token: {extra['prompt_tokens']}")
|
||||
if extra.get("completion_tokens"):
|
||||
detail_parts.append(f"输出token: {extra['completion_tokens']}")
|
||||
if extra.get("finish_reason"):
|
||||
detail_parts.append(f"finish_reason: {extra['finish_reason']}")
|
||||
yield sse_format(
|
||||
{
|
||||
"step": "ai_response",
|
||||
"message": f"AI报告已生成",
|
||||
"progress": 75,
|
||||
"detail": f"报告长度: {len(ai_report) if ai_report else 0} 字符, {report_lines} 行",
|
||||
"detail": " | ".join(detail_parts),
|
||||
}
|
||||
)
|
||||
yield sse_format(
|
||||
@@ -558,6 +580,7 @@ def generate_plan():
|
||||
"ai_report": ai_report,
|
||||
"prompt_length": prompt_length,
|
||||
"ai_report_length": len(ai_report) if ai_report else 0,
|
||||
"ai_error": plan_content.get("ai_report_error", ""),
|
||||
}
|
||||
)
|
||||
except Exception as e:
|
||||
|
||||
+130
-19
@@ -6,7 +6,7 @@ from datetime import datetime
|
||||
from flask import request, jsonify, render_template, current_app, session, redirect
|
||||
from app.routes import main_bp
|
||||
from app.models import db, Problem, StudentProblem
|
||||
from app.config import load_api_config, save_api_config
|
||||
from app.config import load_api_config, save_api_config, DEFAULT_PROVIDERS
|
||||
from app.routes.auth import login_required_json, admin_required
|
||||
|
||||
|
||||
@@ -42,8 +42,113 @@ def get_api_config():
|
||||
return jsonify(config)
|
||||
|
||||
|
||||
@main_bp.route("/api/config/providers", methods=["GET"])
|
||||
@admin_required
|
||||
def list_providers():
|
||||
"""列出所有API提供商"""
|
||||
config = load_api_config(current_app.config)
|
||||
providers = config.get("providers", {})
|
||||
result = {}
|
||||
for pid, pdata in providers.items():
|
||||
result[pid] = {
|
||||
"name": pdata.get("name", pid),
|
||||
"endpoint": pdata.get("endpoint", ""),
|
||||
"models": pdata.get("models", []),
|
||||
}
|
||||
return jsonify(result)
|
||||
|
||||
|
||||
@main_bp.route("/api/config/providers", methods=["POST"])
|
||||
@admin_required
|
||||
def add_provider():
|
||||
"""新增API提供商"""
|
||||
data = request.get_json()
|
||||
provider_id = data.get("id", "").strip().lower()
|
||||
name = data.get("name", "").strip()
|
||||
endpoint = data.get("endpoint", "").strip()
|
||||
models = data.get("models", [])
|
||||
|
||||
if not isinstance(models, list):
|
||||
models = [m.strip() for m in str(models).split(",") if m.strip()]
|
||||
|
||||
if not provider_id or not name:
|
||||
return jsonify({"error": "提供商ID和名称不能为空"}), 400
|
||||
if not endpoint:
|
||||
return jsonify({"error": "Endpoint不能为空"}), 400
|
||||
|
||||
config = load_api_config(current_app.config)
|
||||
providers = config.get("providers", {})
|
||||
|
||||
if provider_id in providers:
|
||||
return jsonify({"error": f"提供商 '{provider_id}' 已存在"}), 400
|
||||
|
||||
providers[provider_id] = {
|
||||
"name": name,
|
||||
"endpoint": endpoint,
|
||||
"models": models,
|
||||
}
|
||||
config["providers"] = providers
|
||||
save_api_config(config, current_app.config)
|
||||
return jsonify({"message": "添加成功", "id": provider_id})
|
||||
|
||||
|
||||
@main_bp.route("/api/config/providers/<provider_id>", methods=["DELETE"])
|
||||
@admin_required
|
||||
def delete_provider(provider_id):
|
||||
"""删除API提供商"""
|
||||
provider_id = provider_id.strip().lower()
|
||||
config = load_api_config(current_app.config)
|
||||
providers = config.get("providers", {})
|
||||
|
||||
if provider_id not in providers:
|
||||
return jsonify({"error": f"提供商 '{provider_id}' 不存在"}), 404
|
||||
|
||||
# 不允许删除当前选中的 provider
|
||||
if provider_id == config.get("provider"):
|
||||
return jsonify({"error": "不能删除当前正在使用的提供商,请先切换到其他提供商"}), 400
|
||||
|
||||
# 不允许删除到只剩 0 个
|
||||
if len(providers) <= 1:
|
||||
return jsonify({"error": "至少保留一个提供商"}), 400
|
||||
|
||||
del providers[provider_id]
|
||||
|
||||
# 同时删除 api_keys 中对应的 key
|
||||
if "api_keys" in config and provider_id in config["api_keys"]:
|
||||
del config["api_keys"][provider_id]
|
||||
|
||||
config["providers"] = providers
|
||||
save_api_config(config, current_app.config)
|
||||
return jsonify({"message": "删除成功"})
|
||||
|
||||
|
||||
@main_bp.route("/api/config/providers/<provider_id>", methods=["PUT"])
|
||||
@admin_required
|
||||
def update_provider(provider_id):
|
||||
"""更新API提供商(名称、endpoint、模型列表)"""
|
||||
provider_id = provider_id.strip().lower()
|
||||
data = request.get_json()
|
||||
config = load_api_config(current_app.config)
|
||||
providers = config.get("providers", {})
|
||||
|
||||
if provider_id not in providers:
|
||||
return jsonify({"error": f"提供商 '{provider_id}' 不存在"}), 404
|
||||
|
||||
pdata = providers[provider_id]
|
||||
if "name" in data:
|
||||
pdata["name"] = data["name"].strip()
|
||||
if "endpoint" in data:
|
||||
pdata["endpoint"] = data["endpoint"].strip()
|
||||
if "models" in data:
|
||||
if isinstance(data["models"], list):
|
||||
pdata["models"] = [m.strip() for m in data["models"] if m.strip()]
|
||||
else:
|
||||
pdata["models"] = [m.strip() for m in str(data["models"]).split(",") if m.strip()]
|
||||
|
||||
config["providers"] = providers
|
||||
save_api_config(config, current_app.config)
|
||||
return jsonify({"message": "更新成功"})
|
||||
|
||||
|
||||
@main_bp.route("/api/config", methods=["POST"])
|
||||
@admin_required
|
||||
@@ -51,32 +156,38 @@ def update_api_config():
|
||||
"""更新API配置"""
|
||||
data = request.get_json()
|
||||
|
||||
# 验证必填字段
|
||||
if not data.get("api_key"):
|
||||
return jsonify({"error": "API Key不能为空"}), 400
|
||||
config = load_api_config(current_app.config)
|
||||
provider = data.get("provider", config.get("provider", "volcengine"))
|
||||
|
||||
# 根据provider设置默认endpoint
|
||||
provider = data.get("provider", "volcengine")
|
||||
default_endpoints = {
|
||||
"minimax": "https://api.minimaxi.com/anthropic/v1",
|
||||
"volcengine": "https://ark.cn-beijing.volces.com/api/coding/v3",
|
||||
"deepseek": "https://api.deepseek.com/v1",
|
||||
"openrouter": "https://openrouter.ai/api/v1",
|
||||
}
|
||||
default_endpoint = default_endpoints.get(provider, "https://ark.cn-beijing.volces.com/api/coding/v3")
|
||||
# api_key 留空时,尝试使用已有 key(当前 provider 的 key)
|
||||
api_key = data.get("api_key", "")
|
||||
if not api_key:
|
||||
existing_key = config.get("api_keys", {}).get(provider, "")
|
||||
if not existing_key:
|
||||
return jsonify({"error": "API Key不能为空"}), 400
|
||||
api_key = existing_key
|
||||
providers = config.get("providers", DEFAULT_PROVIDERS)
|
||||
|
||||
# 保存配置
|
||||
config = {
|
||||
# 从 providers 列表获取默认 endpoint 和 model
|
||||
if provider in providers:
|
||||
default_endpoint = providers[provider].get("endpoint", "")
|
||||
models = providers[provider].get("models", [])
|
||||
default_model = models[0] if models else ""
|
||||
else:
|
||||
default_endpoint = ""
|
||||
default_model = ""
|
||||
|
||||
new_config = {
|
||||
"provider": provider,
|
||||
"api_key": data.get("api_key", ""),
|
||||
"api_key": api_key,
|
||||
"base_url": data.get("base_url", default_endpoint),
|
||||
"model": data.get("model", "doubao-seed-2.0-pro"),
|
||||
"model": data.get("model", default_model),
|
||||
"temperature": float(data.get("temperature", 0.7)),
|
||||
"prompt_template": data.get("prompt_template", ""),
|
||||
"watermark_text": data.get("watermark_text", ""),
|
||||
}
|
||||
|
||||
save_api_config(config, current_app.config)
|
||||
save_api_config(new_config, current_app.config)
|
||||
return jsonify({"message": "配置保存成功"})
|
||||
|
||||
|
||||
@@ -171,7 +282,7 @@ def delete_problem(problem_id):
|
||||
|
||||
# 检查是否有关联数据
|
||||
from app.models import StudentProblem
|
||||
if StudentProblem.query.filter_by(problem_db_id=problem_id).first():
|
||||
if StudentProblem.query.filter_by(problem_id=problem_id).first():
|
||||
return jsonify({"error": "该问题已被学员使用,无法删除"}), 400
|
||||
|
||||
db.session.delete(problem)
|
||||
|
||||
Reference in New Issue
Block a user