132 lines
5.0 KiB
Python
132 lines
5.0 KiB
Python
"""当前用户的 Agent Run 与模型服务商配置页面。"""
|
|
|
|
from django.contrib import messages
|
|
from django.contrib.auth.decorators import login_required
|
|
from django.core.paginator import Paginator
|
|
from django.shortcuts import get_object_or_404, redirect, render
|
|
|
|
from common.exceptions import AgentConfigurationError, ProviderConnectionError
|
|
|
|
from .forms import ProviderConfigurationForm
|
|
from .providers import CATEGORY_LABELS, PROVIDERS, get_provider
|
|
from .services import (
|
|
disconnect_provider,
|
|
provider_configs_for_user,
|
|
runs_for_user,
|
|
save_verified_provider_config,
|
|
verify_provider_api_key,
|
|
)
|
|
|
|
|
|
@login_required
|
|
def run_list(request):
|
|
"""分页显示当前用户的运行记录。"""
|
|
|
|
page = Paginator(runs_for_user(request.user), 20).get_page(request.GET.get("page"))
|
|
return render(request, "agent_runtime/run_list.html", {"page": page})
|
|
|
|
|
|
@login_required
|
|
def run_detail(request, run_id):
|
|
"""越权与不存在统一返回 404,避免泄露资源存在性。"""
|
|
|
|
run = get_object_or_404(
|
|
runs_for_user(request.user).prefetch_related("events", "tool_calls", "approvals"), pk=run_id
|
|
)
|
|
return render(request, "agent_runtime/run_detail.html", {"run": run})
|
|
|
|
|
|
def _provider_page_context(request, form=None):
|
|
"""构造纯展示视图模型,模板不承担查询、权限或协议判断。"""
|
|
|
|
configs = {item.provider_code: item for item in provider_configs_for_user(request.user)}
|
|
category = request.GET.get("category", "all")
|
|
query = request.GET.get("q", "").strip().lower()
|
|
selected = get_provider(request.GET.get("provider", ""))
|
|
cards = []
|
|
for provider in PROVIDERS:
|
|
config = configs.get(provider.code)
|
|
if category == "connected" and config is None:
|
|
continue
|
|
if category not in {"all", "connected"} and provider.category != category:
|
|
continue
|
|
searchable = f"{provider.name} {provider.vendor} {provider.code}".lower()
|
|
if query and query not in searchable:
|
|
continue
|
|
cards.append({"provider": provider, "config": config})
|
|
selected_config = configs.get(selected.code) if selected else None
|
|
return {
|
|
"cards": cards,
|
|
"provider_count": len(PROVIDERS),
|
|
"connected_count": len(configs),
|
|
"categories": CATEGORY_LABELS,
|
|
"active_category": category if category in CATEGORY_LABELS else "all",
|
|
"query": request.GET.get("q", "").strip(),
|
|
"selected_provider": selected,
|
|
"selected_config": selected_config,
|
|
"provider_form": form
|
|
or ProviderConfigurationForm(
|
|
initial={
|
|
"model_id": selected_config.default_model_id if selected_config else "",
|
|
"use_as_default": selected_config.is_default if selected_config else False,
|
|
}
|
|
),
|
|
}
|
|
|
|
|
|
@login_required
|
|
def provider_list(request):
|
|
"""展示当前用户的服务商目录和已连接状态。"""
|
|
|
|
return render(request, "agent_runtime/provider_list.html", _provider_page_context(request))
|
|
|
|
|
|
@login_required
|
|
def provider_configure(request, provider_code):
|
|
"""验证并保存当前用户的 API Key;失败时不覆盖已有配置。"""
|
|
|
|
provider = get_provider(provider_code)
|
|
if provider is None:
|
|
return redirect("agent_runtime:provider-list")
|
|
if request.method != "POST":
|
|
return redirect(f"{redirect('agent_runtime:provider-list').url}?provider={provider.code}")
|
|
form = ProviderConfigurationForm(request.POST)
|
|
if not provider.key_only:
|
|
messages.info(request, provider.note or "该服务商需要额外配置,当前版本尚未开放。")
|
|
return redirect(f"{redirect('agent_runtime:provider-list').url}?provider={provider.code}")
|
|
if form.is_valid():
|
|
try:
|
|
result = verify_provider_api_key(provider, form.cleaned_data["api_key"])
|
|
save_verified_provider_config(
|
|
request.user,
|
|
provider,
|
|
form.cleaned_data["api_key"],
|
|
result,
|
|
model_id=form.cleaned_data["model_id"],
|
|
use_as_default=form.cleaned_data["use_as_default"],
|
|
)
|
|
except (ProviderConnectionError, AgentConfigurationError) as exc:
|
|
form.add_error(None, str(exc))
|
|
else:
|
|
messages.success(request, f"{provider.name} 已验证并启用。")
|
|
return redirect("agent_runtime:provider-list")
|
|
# POST 出错时保持配置抽屉打开,且表单只回显非敏感错误,不回显已有密钥。
|
|
query = request.GET.copy()
|
|
query["provider"] = provider.code
|
|
request.GET = query
|
|
return render(
|
|
request,
|
|
"agent_runtime/provider_list.html",
|
|
_provider_page_context(request, form),
|
|
status=400,
|
|
)
|
|
|
|
|
|
@login_required
|
|
def provider_disconnect(request, provider_code):
|
|
"""仅接受 POST 断开当前用户自己的服务商配置。"""
|
|
|
|
if request.method == "POST" and disconnect_provider(request.user, provider_code):
|
|
messages.success(request, "服务商配置已移除。")
|
|
return redirect("agent_runtime:provider-list")
|