diff --git a/gateway/agent.py b/gateway/agent.py index 1c25169..b860a9c 100644 --- a/gateway/agent.py +++ b/gateway/agent.py @@ -35,10 +35,12 @@ AGENT_SYSTEM_PROMPT = ( "你是端云协同 LLM 系统中的智能体(Agent),正在操作用户选择的**真实项目工作目录**。" "你拥有的工具:list_dir(列目录)、read_file(读文件)、write_file(写文件/新建)、" "edit_file(精确替换编辑:old_string 须唯一匹配)、search_files(跨文件搜索内容)、" - "run_command(执行 shell 命令,仅当系统开启 allow_shell 时可用,否则不要尝试)。" + "run_command(执行 shell 命令,仅当系统开启 allow_shell 时可用,否则不要尝试)、" + "web_fetch(抓取公网 http/https 文档页面,私网地址会被拒绝)。" "像编程助手一样工作:先列目录/搜索了解项目结构,读文件核对原文后再用 edit_file 小步修改" - "(或 write_file 新建),需要时运行命令验证。任务完成或给出结论后," - "直接输出给用户的最终答复(中文,不要再调用工具)。" + "(或 write_file 新建),需要查外部资料时用 web_fetch,需要时运行命令验证。" + "任务完成或给出结论后,直接输出给用户的最终答复(中文,不要再调用工具)。" + "注意:不要反复以完全相同的参数调用同一工具——那不会带来新信息。" ) # ── 两级智能体(D7):规划者(大模型)+ 执行者(本地小模型),交接走 handoff 文档 ── @@ -92,6 +94,9 @@ class OpenAICompatChat: temperature: float = 0.3, max_tokens: int = 4096, timeout_s: float = 120.0, + stream: bool = True, + max_retries: int = 2, + retry_delay_s: float = 1.0, transport: Any = None, _client: Any = None, ): @@ -101,6 +106,9 @@ class OpenAICompatChat: self.temperature = temperature self.max_tokens = max_tokens self.timeout_s = timeout_s + self.stream = stream # D10:默认流式;解析失败自动回退非流式 + self.max_retries = max(0, int(max_retries)) # 可重试错误的重试次数(dsh llm-retry 同款) + self.retry_delay_s = max(0.0, float(retry_delay_s)) # 指数退避基数 self._transport = transport self._client = _client self._owns = _client is None @@ -120,7 +128,114 @@ class OpenAICompatChat: self._client = None async def __call__(self, messages: List[Dict[str, Any]], - tools_spec: List[Dict[str, Any]]) -> Dict[str, Any]: + tools_spec: List[Dict[str, Any]], + on_delta: Optional[Callable[[str], None]] = None) -> Dict[str, Any]: + """ToolLoop.chat_fn:默认流式(D10);流式不可用时回退非流式(带重试退避)。""" + if self.stream: + try: + return await self._stream_call(messages, tools_spec, on_delta) + except Exception: + # 已有部分增量输出则如实抛出;否则回退非流式 + if getattr(self, "_stream_partial", False): + raise + return await self._post_with_retry(messages, tools_spec) + + @staticmethod + def _is_retryable(exc: Exception) -> bool: + """可重试错误:网络传输类 / 408 / 429 / 5xx(dsh retryableCodes 同思路)。""" + import httpx + if isinstance(exc, httpx.TransportError): + return True + if isinstance(exc, httpx.HTTPStatusError): + code = exc.response.status_code + return code in (408, 429) or code >= 500 + return False + + async def _post_with_retry(self, messages: List[Dict[str, Any]], + tools_spec: List[Dict[str, Any]]) -> Dict[str, Any]: + """非流式调用 + 指数退避重试(仅针对可重试错误)。""" + for attempt in range(self.max_retries + 1): + try: + return await self._post_once(messages, tools_spec) + except Exception as exc: + if attempt >= self.max_retries or not self._is_retryable(exc): + raise + await asyncio.sleep(self.retry_delay_s * (2 ** attempt)) + + async def _stream_call(self, messages: List[Dict[str, Any]], + tools_spec: List[Dict[str, Any]], + on_delta: Optional[Callable[[str], None]]) -> Dict[str, Any]: + """流式调用:逐段转发 content 增量;tool_calls 碎片按 index 组装(不在正文展示)。""" + import json as _json + self._stream_partial = False # 每次调用前复位(防上次的标志影响本次回退判定) + body: Dict[str, Any] = { + "model": self.model, + "messages": messages, + "temperature": self.temperature, + "max_tokens": self.max_tokens, + "stream": True, + "stream_options": {"include_usage": True}, + } + if tools_spec: + body["tools"] = tools_spec + body["tool_choice"] = "auto" + headers = {"Authorization": f"Bearer {self.api_key}"} if self.api_key else {} + client = self._get_client() + content_parts: List[str] = [] + tc_slots: Dict[int, Dict[str, str]] = {} + usage: Dict[str, Any] = {} + async with client.stream("POST", f"{self.base_url}/chat/completions", + headers=headers, json=body) as resp: + resp.raise_for_status() + async for line in resp.aiter_lines(): + line = line.strip() + if not line.startswith("data:"): + continue + payload = line[5:].strip() + if payload == "[DONE]": + break + try: + obj = _json.loads(payload) + except _json.JSONDecodeError: + continue + choices = obj.get("choices") or [{}] + delta = (choices[0].get("delta") or {}) if choices else {} + piece = delta.get("content") + if piece: + self._stream_partial = True + content_parts.append(piece) + if on_delta is not None: + try: + on_delta(piece) + except Exception: + pass + for tc in delta.get("tool_calls") or []: + idx = int(tc.get("index", 0)) + slot = tc_slots.setdefault(idx, {"id": "", "name": "", "args": ""}) + if tc.get("id"): + slot["id"] = tc["id"] + fn = tc.get("function") or {} + if fn.get("name"): + slot["name"] = fn["name"] + if fn.get("arguments"): + slot["args"] += fn["arguments"] + if obj.get("usage"): + usage = obj["usage"] + content = "".join(content_parts) or None + from router_system.tools import _loads_json_object + tool_calls = [] + for idx in sorted(tc_slots): + slot = tc_slots[idx] + tool_calls.append({ + "id": slot["id"] or f"call_{idx}", + "name": slot["name"], + "arguments": _loads_json_object(slot["args"]), + }) + return {"content": content, "tool_calls": tool_calls, "usage": usage} + + async def _post_once(self, messages: List[Dict[str, Any]], + tools_spec: List[Dict[str, Any]]) -> Dict[str, Any]: + """非流式调用(回退路径)。""" body: Dict[str, Any] = { "model": self.model, "messages": messages, @@ -137,7 +252,6 @@ class OpenAICompatChat: resp.raise_for_status() data = resp.json() msg = (data.get("choices") or [{}])[0].get("message") or {} - # tool_calls 解析放这里(网关层),内核 tools.parse_tool_calls 供其他调用方复用 from router_system.tools import parse_tool_calls return { "content": msg.get("content"), @@ -232,6 +346,7 @@ class AgentService: async def run(self, info: AgentRunInfo, chat: Any, workspace_dir: str | Path, max_rounds: int = 8, token_cap: int = 0, allow_shell: bool = False, shell_timeout_s: int = 20, + allow_net: bool = True, executor_chat: Any = None, max_handoffs: int = DEFAULT_MAX_HANDOFFS, session: Optional["AgentSession"] = None, @@ -246,6 +361,7 @@ class AgentService: history = self._history_from_session(session) approval_mgr = ApprovalManager() info._approval_manager = approval_mgr # 供 /approve 端点裁决(瞬态属性) + throttle = DeltaThrottle(lambda ev: self._append_event(info, ev)) async def approval_hook(name: str, args: Dict[str, Any]) -> bool: """按策略判定;需审批则挂起等用户裁决,超时 fail-closed。""" @@ -281,16 +397,20 @@ class AgentService: info, chat, executor_chat, workspace_dir, max_rounds=max_rounds, token_cap=token_cap, allow_shell=allow_shell, shell_timeout_s=shell_timeout_s, + allow_net=allow_net, max_handoffs=max_handoffs, approval_hook=approval_hook) else: tools = WorkspaceTools(workspace_dir, allow_shell=allow_shell, - shell_timeout_s=shell_timeout_s) + shell_timeout_s=shell_timeout_s, + allow_net=allow_net) loop = ToolLoop(tools, chat, max_rounds=max_rounds, token_cap=token_cap, on_event=self._make_event_writer(info), - approval_hook=approval_hook) + approval_hook=approval_hook, + on_delta=throttle.make_cb("executor")) result = await loop.run(info.task, system=AGENT_SYSTEM_PROMPT, history=history) + throttle.flush("executor") self._apply_result(info, result) except Exception as exc: # pragma: no cover info.state = STATE_FAILED @@ -348,14 +468,15 @@ class AgentService: async def run_dual(self, info: AgentRunInfo, planner_chat: Any, executor_chat: Any, workspace_dir: str | Path, max_rounds: int = 8, token_cap: int = 0, allow_shell: bool = False, - shell_timeout_s: int = 20, + shell_timeout_s: int = 20, allow_net: bool = True, max_handoffs: int = DEFAULT_MAX_HANDOFFS, approval_hook: Optional[Callable[[str, Dict[str, Any]], Awaitable[bool]]] = None ) -> Dict[str, Any]: """大模型拆解/审查 + 小模型执行工具轮,交接状态写 handoff.json(智能体版交流文本)。""" info.mode = "dual" tools = WorkspaceTools(workspace_dir, allow_shell=allow_shell, - shell_timeout_s=shell_timeout_s) + shell_timeout_s=shell_timeout_s, allow_net=allow_net) + throttle = DeltaThrottle(lambda ev: self._append_event(info, ev)) handoff: Dict[str, Any] = { "task": info.task, "planner_model": info.model, "executor_model": info.executor_model, "workspace": info.workspace, @@ -380,13 +501,23 @@ class AgentService: async def _planner_json(user_msg: str) -> Dict[str, Any]: """调规划者并解析 JSON;解析失败回喂重试一次,再失败降级为 {}(禁止带病继续的软版本)。""" + import inspect messages = [{"role": "system", "content": PLANNER_SYSTEM_PROMPT}, {"role": "user", "content": user_msg}] content = "" for attempt in (1, 2): - resp = await planner_chat(messages, []) + # 规划者同样流式(前端弱化展示其 JSON 草稿) + try: + accepts = len(inspect.signature(planner_chat).parameters) >= 3 + except (TypeError, ValueError): + accepts = False + if accepts: + resp = await planner_chat(messages, [], throttle.make_cb("planner")) + else: + resp = await planner_chat(messages, []) _account(resp.get("usage")) content = resp.get("content") or "" + throttle.flush("planner") obj = _parse_json_loose(content) if obj: break @@ -424,7 +555,8 @@ class AgentService: token_cap=max(1, _remaining_cap()), on_event=self._make_event_writer(info), emit_final=False, - approval_hook=approval_hook) + approval_hook=approval_hook, + on_delta=throttle.make_cb("executor")) exec_result = await loop.run(instructions, system=EXECUTOR_SYSTEM_PROMPT) _account({"prompt_tokens": exec_result.get("prompt_tokens", 0), "completion_tokens": exec_result.get("completion_tokens", 0)}) @@ -587,7 +719,7 @@ def new_request_id() -> str: # ───────────────────────────────────────────────────────────────────────────── # 审批流(D9):dsh 式 allow-once / deny,fail-closed # ───────────────────────────────────────────────────────────────────────────── -READ_ONLY_TOOLS = {"list_dir", "read_file", "search_files"} +READ_ONLY_TOOLS = {"list_dir", "read_file", "search_files", "web_fetch"} def needs_approval(policy: str, tool_name: str) -> bool: @@ -599,6 +731,39 @@ def needs_approval(policy: str, tool_name: str) -> bool: return False +class DeltaThrottle: + """流式增量节流(D10):积攒超过阈值才落一条 delta 事件,防事件爆炸。""" + + THRESHOLD = 48 + + def __init__(self, append_event): + self._append = append_event # (ev: dict) -> None + self._buf: Dict[str, str] = {} + + def make_cb(self, role: str): + def cb(text: str) -> None: + self.add(role, text) + return cb + + def add(self, role: str, text: str) -> None: + buf = self._buf.get(role, "") + (text or "") + if len(buf) >= self.THRESHOLD: + self._flush(role, buf) + buf = "" + self._buf[role] = buf + + def flush(self, role: Optional[str] = None) -> None: + roles = [role] if role else list(self._buf.keys()) + for r in roles: + buf = self._buf.get(r, "") + if buf: + self._flush(r, buf) + self._buf[r] = "" + + def _flush(self, role: str, text: str) -> None: + self._append({"type": "delta", "role": role, "text": text}) + + class ApprovalManager: """单次智能体运行内的审批挂起/裁决(asyncio Event 实现,dsh 式 allow-once)。""" @@ -705,6 +870,18 @@ class SessionStore: return True return False + def rename(self, sid: str, title: str) -> Optional[AgentSession]: + """重命名会话标题(dsh session.rename 对齐)。""" + sess = self.get(sid) + if sess is None: + return None + title = (title or "").strip() + if not title: + return sess + sess.data["title"] = title[:24] + self.save(sess) + return sess + def save(self, sess: AgentSession) -> None: self._cache[sess.data["id"]] = sess self._save(sess) diff --git a/gateway/api.py b/gateway/api.py index 52edc2c..efdd760 100644 --- a/gateway/api.py +++ b/gateway/api.py @@ -8,7 +8,8 @@ v2 端点(《实现方案_v2》5.3): GET /review/queue、POST /review/{id} 人工检验 GET /metrics 含 v2 token/快路径统计 -启动:uvicorn gateway.api:app --host 0.0.0.0 --port 8000 +启动:uvicorn gateway.api:app --host 127.0.0.1 --port 8000 +(默认仅回环;如需局域网访问改 --host 0.0.0.0 并设置 GATEWAY_TRUSTED_HOSTS) """ from __future__ import annotations @@ -22,6 +23,7 @@ if _dotenv_path.exists(): load_dotenv(_dotenv_path) import asyncio +import re from pathlib import Path from typing import List, Optional @@ -38,6 +40,10 @@ from gateway.model_pool import ( get_pool, ) +# 路径参数 ID 白名单(runs/agent/sessions 的 ID 都由此系统生成; +# 拒绝任意其他字符可一并杀灭 Windows 反斜杠穿越 ../..%5C 等变体) +_ID_RE = re.compile(r"^[a-zA-Z0-9_-]{1,64}$") + # ---- v2 依赖(惰性导入,缺依赖时降级提示) ---- try: from router_system.architect import build_architect @@ -179,6 +185,19 @@ try: version="2.0.0", ) + # Web 信任围栏(dsh browser-auth 同款思路):只信任本机/显式放行的 Host, + # 防 DNS rebinding 把浏览器请求打到本网关。GATEWAY_TRUSTED_HOSTS 可覆盖("*" = 放行全部)。 + _trusted = _os.environ.get( + "GATEWAY_TRUSTED_HOSTS", + "localhost,127.0.0.1,0.0.0.0,[::1],testserver,testclient", + ) + try: + from starlette.middleware.trustedhost import TrustedHostMiddleware + app.add_middleware(TrustedHostMiddleware, + allowed_hosts=[h.strip() for h in _trusted.split(",")]) + except ImportError: # pragma: no cover + pass + # Vue SPA 静态资源(html=True:对不存在的路径 fallback 到 index.html,支持 SPA 路由) app.mount("/static", StaticFiles(directory=str(_STATIC_DIR), html=True), name="static") @@ -189,6 +208,12 @@ try: return HTMLResponse(_INDEX_PATH.read_text(encoding="utf-8")) return HTMLResponse("
请先构建前端:cd webapp && npm run build
") + def _check_id(value: str, what: str = "ID") -> str: + """校验路径参数 ID(防目录穿越/注入:仅允许系统生成的字符集)。""" + if not _ID_RE.fullmatch(value or ""): + raise HTTPException(status_code=400, detail=f"非法 {what}: {value!r}") + return value + @app.get("/health", response_model=HealthResponse, tags=["system"]) async def health(): return get_router().health() @@ -265,6 +290,7 @@ try: @app.get("/runs/{request_id}/status", tags=["v2"]) async def get_run_status(request_id: str): """查询任务当前状态(pending / running / done / failed)。""" + _check_id(request_id, "request_id") info = get_job_store().get(request_id) if info is None: raise HTTPException(status_code=404, detail=f"任务 {request_id} 不存在") @@ -302,6 +328,7 @@ try: @app.get("/runs/{request_id}/stream", tags=["v2"]) async def stream_run(request_id: str): """SSE 端点:实时推送 workspace.json 状态变化(供前端协作可视化)。""" + _check_id(request_id, "request_id") from fastapi.responses import StreamingResponse async def event_generator(): @@ -354,6 +381,7 @@ try: @app.get("/traces/{request_id}", tags=["system"]) async def get_trace(request_id: str): + _check_id(request_id, "request_id") trace = get_router().trace_store.get(request_id) if trace is None: raise HTTPException(status_code=404, detail=f"未找到请求 {request_id} 的推理链") @@ -362,6 +390,7 @@ try: # ---------------- v2:workspace / artifacts ---------------- @app.get("/runs/{request_id}/workspace", tags=["v2"]) async def get_workspace(request_id: str): + _check_id(request_id, "request_id") p = Path("runs") / request_id / "workspace.json" if not p.exists(): raise HTTPException(status_code=404, detail=f"未找到运行 {request_id}") @@ -370,7 +399,12 @@ try: @app.get("/runs/{request_id}/artifacts/{name}", tags=["v2"]) async def get_artifact(request_id: str, name: str): - p = Path("runs") / request_id / "artifacts" / name + _check_id(request_id, "request_id") + d = (Path("runs") / request_id / "artifacts").resolve() + # 工件名关押:解析后必须仍在 artifacts 目录内(防 ..\ 与绝对路径逃逸) + p = (d / name).resolve() + if p != d and d not in p.parents: + raise HTTPException(status_code=400, detail=f"非法工件名: {name!r}") if not p.exists(): raise HTTPException(status_code=404, detail=f"未找到工件 {name}") return {"name": name, "content": p.read_text(encoding="utf-8")} @@ -599,6 +633,7 @@ try: token_cap=int(agent_cfg.get("token_cap", 20000)), allow_shell=bool(agent_cfg.get("allow_shell", False)), shell_timeout_s=int(agent_cfg.get("shell_timeout_s", 20)), + allow_net=bool(agent_cfg.get("allow_net", True)), executor_chat=executor_chat, max_handoffs=int(agent_cfg.get("max_handoffs", 2)), session=session, @@ -625,6 +660,7 @@ try: @app.post("/agent/{request_id}/cancel", tags=["agent"]) async def agent_cancel(request_id: str): """停止运行中的智能体任务。""" + _check_id(request_id, "request_id") from gateway.agent import get_agent_service service = get_agent_service() info = service.get(request_id) @@ -643,6 +679,7 @@ try: @app.post("/agent/{request_id}/approve", tags=["agent"]) async def agent_approve(request_id: str, req: dict): """裁决待审批操作:{"approval_id", "allowed"}(dsh 式 allow-once / deny)。""" + _check_id(request_id, "request_id") from gateway.agent import get_agent_service info = get_agent_service().get(request_id) if info is None: @@ -678,14 +715,30 @@ try: @app.get("/agent/sessions/{sid}", tags=["agent"]) async def agent_session_detail(sid: str): + """会话详情(含轮次)。""" + _check_id(sid, "会话 ID") from gateway.agent import get_session_store sess = get_session_store().get(sid) if sess is None: raise HTTPException(status_code=404, detail=f"会话不存在: {sid}") return sess.view() + @app.patch("/agent/sessions/{sid}", tags=["agent"]) + async def agent_session_rename(sid: str, req: dict = None): + """重命名会话:{"title"}(dsh session.rename 对齐)。""" + _check_id(sid, "会话 ID") + from gateway.agent import get_session_store + title = str((req or {}).get("title") or "").strip() + if not title: + raise HTTPException(status_code=400, detail="title 不能为空") + sess = get_session_store().rename(sid, title) + if sess is None: + raise HTTPException(status_code=404, detail=f"会话不存在: {sid}") + return sess.view() + @app.delete("/agent/sessions/{sid}", tags=["agent"]) async def agent_session_delete(sid: str): + _check_id(sid, "会话 ID") from gateway.agent import get_session_store return {"ok": get_session_store().delete(sid)} @@ -733,6 +786,7 @@ try: @app.get("/agent/{request_id}/status", tags=["agent"]) async def agent_status(request_id: str): + _check_id(request_id, "request_id") from gateway.agent import get_agent_service info = get_agent_service().get(request_id) if info is None: @@ -747,12 +801,14 @@ try: @app.get("/agent/{request_id}/events", tags=["agent"]) async def agent_events(request_id: str): """完整事件列表(JSON,刷新后恢复用)。""" + _check_id(request_id, "request_id") from gateway.agent import get_agent_service return get_agent_service().read_events(request_id) @app.get("/agent/{request_id}/stream", tags=["agent"]) async def agent_stream(request_id: str): """SSE:实时推送智能体过程事件(round/tool_call/tool_result/usage/final)。""" + _check_id(request_id, "request_id") from fastapi.responses import StreamingResponse from gateway.agent import get_agent_service @@ -811,19 +867,36 @@ try: # ---------------- 模型设置(用户可调整) ---------------- @app.get("/config", tags=["settings"]) async def get_config(): - """读取当前可调整设置(小模型 / 大模型 / 管线)。""" - return settings_store().to_dict() + """读取当前可调整设置(小模型 / 大模型 / 管线)。 + + architect.api_key 打码返回(api_key_set + 前 6 位,对齐 D2 池条目语义); + 修改时留空/不传 = 保留服务端已存值。 + """ + out = settings_store().to_dict() + arch = out.get("architect") or {} + key = arch.get("api_key") or "" + arch["api_key_set"] = bool(key) + arch["api_key"] = (key[:6] + "…") if key else "" + return out @app.put("/config", tags=["settings"]) async def put_config(patch: dict): """部分更新设置并重建管线。示例: {"worker": {"backend": "openai", "base_url": "http://127.0.0.1:11434/v1", "temperature": 0.4}} + + architect.api_key 传空串 = 保留原值(与打码返回配套)。 """ + arch_patch = (patch or {}).get("architect") + if isinstance(arch_patch, dict) and not str(arch_patch.get("api_key") or "").strip(): + arch_patch.pop("api_key", None) try: merged = settings_store().update(patch) except Exception as e: raise HTTPException(status_code=400, detail=f"设置非法: {e}") rebuild_pipeline() + key = merged.get("architect", {}).get("api_key") or "" + merged["architect"]["api_key_set"] = bool(key) + merged["architect"]["api_key"] = (key[:6] + "…") if key else "" return merged @app.post("/config/reset", tags=["settings"]) @@ -1110,4 +1183,6 @@ def _maybe_enqueue(result) -> None: if __name__ == "__main__": import uvicorn - uvicorn.run("gateway.api:app", host="0.0.0.0", port=8000, reload=False) + # 默认只绑定回环地址:网关能读写工作区文件/执行命令,不宜默认暴露到局域网 + # (需要局域网访问时显式 --host 0.0.0.0,并设置 GATEWAY_TRUSTED_HOSTS 放行对应主机名) + uvicorn.run("gateway.api:app", host="127.0.0.1", port=8000, reload=False) diff --git a/gateway/settings.py b/gateway/settings.py index 1a92127..889e8d6 100644 --- a/gateway/settings.py +++ b/gateway/settings.py @@ -43,6 +43,7 @@ DEFAULTS: Dict[str, Any] = { "token_cap": 20000, # 单次智能体任务 token 熔断 "allow_shell": False, # 允许 run_command 执行 shell(默认关) "shell_timeout_s": 20, # shell 命令超时 + "allow_net": True, # 允许 web_fetch 抓取公网页面(SSRF 防护内置) "max_handoffs": 2, # 两级模式:规划者<->执行者交接轮数上限 "approval_policy": "dangerous", # 审批策略:off | dangerous(写/编辑/命令询问)| all "approval_timeout_s": 120, # 审批等待超时(超时自动拒绝) diff --git a/gateway/static/assets/CollaborationView-DHMzJVru.js b/gateway/static/assets/CollaborationView-BzBpxfYH.js similarity index 98% rename from gateway/static/assets/CollaborationView-DHMzJVru.js rename to gateway/static/assets/CollaborationView-BzBpxfYH.js index 355c723..7753f51 100644 --- a/gateway/static/assets/CollaborationView-DHMzJVru.js +++ b/gateway/static/assets/CollaborationView-BzBpxfYH.js @@ -1 +1 @@ -import{A as e,C as t,D as n,G as r,H as ee,I as i,L as a,M as o,N as s,O as c,R as l,U as u,V as d,W as te,j as f,k as p,n as ne,t as m}from"./index-C8Za1808.js";var re={class:`collab-view`},ie={class:`collab-sidebar`},ae={class:`session-list`},oe=[`onClick`],se={class:`meta`},ce={key:0,class:`stats`},h={class:`stat-grid`},g={class:`route-flow`},_={class:`collab-main`},v={key:0,class:`empty`},y={class:`section brief-section`},b={class:`brief-card`},x={class:`goal`},S={class:`tags`},C={key:0,class:`domain`},w={key:0,class:`constraints`},T={class:`section plan-section`},E={class:`plan-timeline`},D={class:`step-content`},O={class:`step-header`},k={class:`step-id`},A={class:`step-status`},j={class:`step-task`},M={key:0,class:`step-deps`},N={key:1,class:`step-artifact`},P={class:`section progress-section`},le={class:`progress-bar-wrap`},F={class:`progress-label`},I={class:`progress-bar`},L={class:`progress-steps`},R={class:`pg-icon`},z={key:0,class:`pg-note`},B={key:0,class:`section issues-section`},V={class:`issue-header`},H={class:`issue-id`},U={class:`issue-step`},W={key:0,class:`decision`},ue={key:1,class:`section archive-section`},de={class:`archive-list`},fe={key:2,class:`section deliver-section`},pe={class:`response-block`},G=m(s({__name:`CollaborationView`,setup(s){let m=ne(),G=d(m.currentId),K=d(null),q=c(()=>m.sessions.find(e=>e.requestId===G.value)??null),J=c(()=>K.value??q.value?.workspace??null);function Y(e){G.value=e,K.value=null}let X=null;function me(e){X?.close(),X=t(e),X.subscribe({onWorkspace(t){t.request_id===G.value&&(K.value=t,m.updateWorkspace(e,t))},onStatus(){m.pollStatus(e)},onDone(){m.pollStatus(e)},onError(){}})}l(()=>m.sessions,e=>{G.value&&(e.find(e=>e.requestId===G.value)||(G.value=e[0]?.requestId??null))},{deep:!0}),l(G,e=>{if(!e)return;let t=m.sessions.find(t=>t.requestId===e);t?.workspace&&(K.value=t.workspace),me(e)},{immediate:!0});function Z(e){return J.value?.progress?.find(t=>t.step===e)?.status??`pending`}function Q(e){return J.value?.progress?.find(t=>t.step===e)?.artifact??null}function $(e){return J.value?.decisions?.find(t=>t.ref===e)??null}return(t,s)=>(i(),f(`div`,re,[p(`aside`,ie,[s[6]||=p(`h3`,null,`协作会话`,-1),p(`ul`,ae,[(i(!0),f(n,null,a(ee(m).sessions,e=>(i(),f(`li`,{key:e.requestId,class:u([`session-item`,{active:e.requestId===G.value}]),onClick:t=>Y(e.requestId)},[p(`span`,null,r(e.query.slice(0,24))+r(e.query.length>24?`…`:``),1),p(`span`,se,r(e.status?.rounds_used??`?`)+` 轮 · `+r(e.status?.model_used??`—`),1)],10,oe))),128))]),q.value?(i(),f(`div`,ce,[s[4]||=p(`h4`,null,`运行统计`,-1),p(`div`,h,[s[0]||=p(`span`,null,`输入 Token`,-1),p(`b`,null,r(q.value.status?.api_input_tokens??0),1),s[1]||=p(`span`,null,`输出 Token`,-1),p(`b`,null,r(q.value.status?.api_output_tokens??0),1),s[2]||=p(`span`,null,`延迟`,-1),p(`b`,null,r(q.value.status?.latency_ms?.toFixed(0)??`—`)+` ms`,1),s[3]||=p(`span`,null,`快路径`,-1),p(`b`,null,r(q.value.status?.fast_path?`是`:`否`),1)]),s[5]||=p(`h4`,null,`路由路径`,-1),p(`div`,g,[(i(!0),f(n,null,a(q.value.status?.route??[],(e,t)=>(i(),f(`span`,{key:t,class:`route-node`},r(e),1))),128))])])):e(``,!0)]),p(`main`,_,[J.value?(i(),f(n,{key:1},[p(`section`,y,[s[8]||=p(`h2`,{class:`section-title`},`📋 Brief(任务简报)`,-1),p(`div`,b,[p(`div`,x,r(J.value.brief?.goal??`(未生成)`),1),p(`div`,S,[(i(!0),f(n,null,a(J.value.brief?.tags??[],e=>(i(),f(`span`,{key:e,class:`tag`},r(e),1))),128)),J.value.brief?.domain?(i(),f(`span`,C,r(J.value.brief.domain),1)):e(``,!0)]),J.value.brief?.constraints?.length?(i(),f(`ul`,w,[(i(!0),f(n,null,a(J.value.brief.constraints,(e,t)=>(i(),f(`li`,{key:t},r(e),1))),128))])):e(``,!0)])]),p(`section`,T,[s[12]||=p(`h2`,{class:`section-title`},`📌 Plan(执行计划)`,-1),p(`div`,E,[(i(!0),f(n,null,a(J.value.plan??[],t=>(i(),f(`div`,{key:t.id,class:u([`plan-step`,Z(t.id)])},[s[11]||=p(`div`,{class:`step-dot`},null,-1),p(`div`,D,[p(`div`,O,[p(`span`,k,r(t.id),1),p(`span`,A,r(Z(t.id)),1)]),p(`p`,j,r(t.task),1),t.deps.length?(i(),f(`div`,M,[s[9]||=o(` 依赖:`,-1),(i(!0),f(n,null,a(t.deps,e=>(i(),f(`span`,{key:e,class:`dep`},r(e),1))),128))])):e(``,!0),Q(t.id)?(i(),f(`div`,N,[p(`details`,null,[s[10]||=p(`summary`,null,`📄 工件内容`,-1),p(`pre`,null,r(Q(t.id)),1)])])):e(``,!0)])],2))),128))])]),p(`section`,P,[s[13]||=p(`h2`,{class:`section-title`},`🔄 进度(实时)`,-1),p(`div`,le,[p(`div`,F,` 第 `+r(J.value.meta.round)+` / `+r(J.value.meta.rounds_cap)+` 轮 `,1),p(`div`,I,[p(`div`,{class:`progress-fill`,style:te({width:`${J.value.meta.round/J.value.meta.rounds_cap*100}%`})},null,4)])]),p(`div`,L,[(i(!0),f(n,null,a(J.value.progress??[],t=>(i(),f(`div`,{key:t.step,class:u([`pg-step`,t.status])},[p(`span`,R,r(t.status===`done`?`✅`:t.status===`running`?`⏳`:`⭕`),1),p(`span`,null,r(t.step),1),t.note?(i(),f(`span`,z,r(t.note),1)):e(``,!0)],2))),128))])]),J.value.issues?.length?(i(),f(`section`,B,[s[15]||=p(`h2`,{class:`section-title`},`⚠️ Issues & 裁决`,-1),(i(!0),f(n,null,a(J.value.issues,t=>(i(),f(`div`,{key:t.id,class:`issue-card`},[p(`div`,V,[p(`span`,H,r(t.id),1),p(`span`,U,`Step: `+r(t.step),1)]),p(`p`,null,r(t.description),1),$(t.id)?(i(),f(`div`,W,[s[14]||=p(`strong`,null,`Architect 裁决:`,-1),o(r($(t.id)?.reply),1)])):e(``,!0)]))),128))])):e(``,!0),J.value.archive?.length?(i(),f(`section`,ue,[s[16]||=p(`h2`,{class:`section-title`},`📦 Archive(摘要归档)`,-1),p(`ul`,de,[(i(!0),f(n,null,a(J.value.archive,(e,t)=>(i(),f(`li`,{key:t},r(e),1))),128))])])):e(``,!0),J.value.meta.state===`done`?(i(),f(`section`,fe,[s[17]||=p(`h2`,{class:`section-title`},`🎉 交付`,-1),p(`div`,pe,[p(`pre`,null,r(q.value?.status?.response??`(无内容)`),1)])])):e(``,!0)],64)):(i(),f(`div`,v,[...s[7]||=[p(`p`,null,`从左侧选择一个会话,或在「对话」页发起新提问。`,-1),p(`p`,null,`协作过程通过 SSE 实时推送,无需刷新。`,-1)]]))])]))}}),[[`__scopeId`,`data-v-556e88a4`]]);export{G as default}; \ No newline at end of file +import{A as e,C as t,D as n,G as r,H as ee,I as i,L as a,M as o,N as s,O as c,R as l,U as u,V as d,W as te,j as f,k as p,n as ne,t as m}from"./index-C4nTxB0s.js";var re={class:`collab-view`},ie={class:`collab-sidebar`},ae={class:`session-list`},oe=[`onClick`],se={class:`meta`},ce={key:0,class:`stats`},h={class:`stat-grid`},g={class:`route-flow`},_={class:`collab-main`},v={key:0,class:`empty`},y={class:`section brief-section`},b={class:`brief-card`},x={class:`goal`},S={class:`tags`},C={key:0,class:`domain`},w={key:0,class:`constraints`},T={class:`section plan-section`},E={class:`plan-timeline`},D={class:`step-content`},O={class:`step-header`},k={class:`step-id`},A={class:`step-status`},j={class:`step-task`},M={key:0,class:`step-deps`},N={key:1,class:`step-artifact`},P={class:`section progress-section`},le={class:`progress-bar-wrap`},F={class:`progress-label`},I={class:`progress-bar`},L={class:`progress-steps`},R={class:`pg-icon`},z={key:0,class:`pg-note`},B={key:0,class:`section issues-section`},V={class:`issue-header`},H={class:`issue-id`},U={class:`issue-step`},W={key:0,class:`decision`},ue={key:1,class:`section archive-section`},de={class:`archive-list`},fe={key:2,class:`section deliver-section`},pe={class:`response-block`},G=m(s({__name:`CollaborationView`,setup(s){let m=ne(),G=d(m.currentId),K=d(null),q=c(()=>m.sessions.find(e=>e.requestId===G.value)??null),J=c(()=>K.value??q.value?.workspace??null);function Y(e){G.value=e,K.value=null}let X=null;function me(e){X?.close(),X=t(e),X.subscribe({onWorkspace(t){t.request_id===G.value&&(K.value=t,m.updateWorkspace(e,t))},onStatus(){m.pollStatus(e)},onDone(){m.pollStatus(e)},onError(){}})}l(()=>m.sessions,e=>{G.value&&(e.find(e=>e.requestId===G.value)||(G.value=e[0]?.requestId??null))},{deep:!0}),l(G,e=>{if(!e)return;let t=m.sessions.find(t=>t.requestId===e);t?.workspace&&(K.value=t.workspace),me(e)},{immediate:!0});function Z(e){return J.value?.progress?.find(t=>t.step===e)?.status??`pending`}function Q(e){return J.value?.progress?.find(t=>t.step===e)?.artifact??null}function $(e){return J.value?.decisions?.find(t=>t.ref===e)??null}return(t,s)=>(i(),f(`div`,re,[p(`aside`,ie,[s[6]||=p(`h3`,null,`协作会话`,-1),p(`ul`,ae,[(i(!0),f(n,null,a(ee(m).sessions,e=>(i(),f(`li`,{key:e.requestId,class:u([`session-item`,{active:e.requestId===G.value}]),onClick:t=>Y(e.requestId)},[p(`span`,null,r(e.query.slice(0,24))+r(e.query.length>24?`…`:``),1),p(`span`,se,r(e.status?.rounds_used??`?`)+` 轮 · `+r(e.status?.model_used??`—`),1)],10,oe))),128))]),q.value?(i(),f(`div`,ce,[s[4]||=p(`h4`,null,`运行统计`,-1),p(`div`,h,[s[0]||=p(`span`,null,`输入 Token`,-1),p(`b`,null,r(q.value.status?.api_input_tokens??0),1),s[1]||=p(`span`,null,`输出 Token`,-1),p(`b`,null,r(q.value.status?.api_output_tokens??0),1),s[2]||=p(`span`,null,`延迟`,-1),p(`b`,null,r(q.value.status?.latency_ms?.toFixed(0)??`—`)+` ms`,1),s[3]||=p(`span`,null,`快路径`,-1),p(`b`,null,r(q.value.status?.fast_path?`是`:`否`),1)]),s[5]||=p(`h4`,null,`路由路径`,-1),p(`div`,g,[(i(!0),f(n,null,a(q.value.status?.route??[],(e,t)=>(i(),f(`span`,{key:t,class:`route-node`},r(e),1))),128))])])):e(``,!0)]),p(`main`,_,[J.value?(i(),f(n,{key:1},[p(`section`,y,[s[8]||=p(`h2`,{class:`section-title`},`📋 Brief(任务简报)`,-1),p(`div`,b,[p(`div`,x,r(J.value.brief?.goal??`(未生成)`),1),p(`div`,S,[(i(!0),f(n,null,a(J.value.brief?.tags??[],e=>(i(),f(`span`,{key:e,class:`tag`},r(e),1))),128)),J.value.brief?.domain?(i(),f(`span`,C,r(J.value.brief.domain),1)):e(``,!0)]),J.value.brief?.constraints?.length?(i(),f(`ul`,w,[(i(!0),f(n,null,a(J.value.brief.constraints,(e,t)=>(i(),f(`li`,{key:t},r(e),1))),128))])):e(``,!0)])]),p(`section`,T,[s[12]||=p(`h2`,{class:`section-title`},`📌 Plan(执行计划)`,-1),p(`div`,E,[(i(!0),f(n,null,a(J.value.plan??[],t=>(i(),f(`div`,{key:t.id,class:u([`plan-step`,Z(t.id)])},[s[11]||=p(`div`,{class:`step-dot`},null,-1),p(`div`,D,[p(`div`,O,[p(`span`,k,r(t.id),1),p(`span`,A,r(Z(t.id)),1)]),p(`p`,j,r(t.task),1),t.deps.length?(i(),f(`div`,M,[s[9]||=o(` 依赖:`,-1),(i(!0),f(n,null,a(t.deps,e=>(i(),f(`span`,{key:e,class:`dep`},r(e),1))),128))])):e(``,!0),Q(t.id)?(i(),f(`div`,N,[p(`details`,null,[s[10]||=p(`summary`,null,`📄 工件内容`,-1),p(`pre`,null,r(Q(t.id)),1)])])):e(``,!0)])],2))),128))])]),p(`section`,P,[s[13]||=p(`h2`,{class:`section-title`},`🔄 进度(实时)`,-1),p(`div`,le,[p(`div`,F,` 第 `+r(J.value.meta.round)+` / `+r(J.value.meta.rounds_cap)+` 轮 `,1),p(`div`,I,[p(`div`,{class:`progress-fill`,style:te({width:`${J.value.meta.round/J.value.meta.rounds_cap*100}%`})},null,4)])]),p(`div`,L,[(i(!0),f(n,null,a(J.value.progress??[],t=>(i(),f(`div`,{key:t.step,class:u([`pg-step`,t.status])},[p(`span`,R,r(t.status===`done`?`✅`:t.status===`running`?`⏳`:`⭕`),1),p(`span`,null,r(t.step),1),t.note?(i(),f(`span`,z,r(t.note),1)):e(``,!0)],2))),128))])]),J.value.issues?.length?(i(),f(`section`,B,[s[15]||=p(`h2`,{class:`section-title`},`⚠️ Issues & 裁决`,-1),(i(!0),f(n,null,a(J.value.issues,t=>(i(),f(`div`,{key:t.id,class:`issue-card`},[p(`div`,V,[p(`span`,H,r(t.id),1),p(`span`,U,`Step: `+r(t.step),1)]),p(`p`,null,r(t.description),1),$(t.id)?(i(),f(`div`,W,[s[14]||=p(`strong`,null,`Architect 裁决:`,-1),o(r($(t.id)?.reply),1)])):e(``,!0)]))),128))])):e(``,!0),J.value.archive?.length?(i(),f(`section`,ue,[s[16]||=p(`h2`,{class:`section-title`},`📦 Archive(摘要归档)`,-1),p(`ul`,de,[(i(!0),f(n,null,a(J.value.archive,(e,t)=>(i(),f(`li`,{key:t},r(e),1))),128))])])):e(``,!0),J.value.meta.state===`done`?(i(),f(`section`,fe,[s[17]||=p(`h2`,{class:`section-title`},`🎉 交付`,-1),p(`div`,pe,[p(`pre`,null,r(q.value?.status?.response??`(无内容)`),1)])])):e(``,!0)],64)):(i(),f(`div`,v,[...s[7]||=[p(`p`,null,`从左侧选择一个会话,或在「对话」页发起新提问。`,-1),p(`p`,null,`协作过程通过 SSE 实时推送,无需刷新。`,-1)]]))])]))}}),[[`__scopeId`,`data-v-556e88a4`]]);export{G as default}; \ No newline at end of file diff --git a/gateway/static/assets/MetricsView-BMWM9u4c.js b/gateway/static/assets/MetricsView-Dm03Grst.js similarity index 98% rename from gateway/static/assets/MetricsView-BMWM9u4c.js rename to gateway/static/assets/MetricsView-Dm03Grst.js index d5f37bb..5c10cd5 100644 --- a/gateway/static/assets/MetricsView-BMWM9u4c.js +++ b/gateway/static/assets/MetricsView-Dm03Grst.js @@ -1 +1 @@ -import{A as e,D as t,G as n,I as r,L as i,N as a,O as o,P as s,V as c,W as l,j as u,k as d,s as f,t as p}from"./index-C8Za1808.js";var m={class:`metrics-view`},h={key:0,class:`loading`},g={key:1,class:`error`},_={class:`card-grid`},v={class:`metric-card`},y={class:`kv-list`},b={class:`metric-card`},x={class:`kv-list`},S={key:0,class:`metric-card highlight`},C={class:`kv-list`},w={key:0},T={key:1},E={key:1,class:`metric-card`},D={class:`by-model`},O={class:`mono`},k={key:2,class:`metric-card review-card`},A={class:`review-stats`},j={class:`stat-item`},M={class:`stat-num`},N={class:`stat-item`},P={class:`stat-num`},F={key:0,class:`progress-wrap`},I={class:`review-rate`},L={class:`raw-json`},R=p(a({__name:`MetricsView`,setup(a){let p=c(null),R=c(!1),z=c(``),B=o(()=>p.value?.v2?.by_model||null);async function V(){R.value=!0,z.value=``;try{p.value=await f()}catch(e){z.value=e instanceof Error?e.message:`指标加载失败,请检查后端服务`}finally{R.value=!1}}return s(V),(a,o)=>(r(),u(`div`,m,[d(`header`,{class:`metrics-header`},[o[0]||=d(`h2`,null,`系统指标`,-1),d(`button`,{class:`refresh`,onClick:V},`🔄 刷新`)]),R.value?(r(),u(`div`,h,`加载中…`)):z.value?(r(),u(`div`,g,n(z.value),1)):p.value?(r(),u(t,{key:2},[d(`div`,_,[d(`div`,v,[o[1]||=d(`h3`,null,`路由器(v1)`,-1),d(`div`,y,[(r(!0),u(t,null,i(p.value.router,(e,i)=>(r(),u(t,{key:i},[d(`span`,null,n(i),1),d(`b`,null,n(e),1)],64))),128))])]),d(`div`,b,[o[2]||=d(`h3`,null,`缓存`,-1),d(`div`,x,[(r(!0),u(t,null,i(p.value.cache,(e,i)=>(r(),u(t,{key:i},[d(`span`,null,n(i),1),d(`b`,null,n(e),1)],64))),128))])]),p.value.v2?(r(),u(`div`,S,[o[3]||=d(`h3`,null,`协作管线(v2)`,-1),d(`div`,C,[(r(!0),u(t,null,i(p.value.v2,(i,a)=>(r(),u(t,{key:a},[a===`by_model`?e(``,!0):(r(),u(`span`,w,n(a),1)),a===`by_model`?e(``,!0):(r(),u(`b`,T,n(i),1))],64))),128))])])):e(``,!0),B.value&&Object.keys(B.value).length?(r(),u(`div`,E,[o[5]||=d(`h3`,null,`按模型分账(token / 成本)`,-1),d(`table`,D,[o[4]||=d(`thead`,null,[d(`tr`,null,[d(`th`,null,`模型`),d(`th`,null,`次数`),d(`th`,null,`入`),d(`th`,null,`出`),d(`th`,null,`成本 $`)])],-1),d(`tbody`,null,[(r(!0),u(t,null,i(B.value,(e,t)=>(r(),u(`tr`,{key:t},[d(`td`,O,n(t),1),d(`td`,null,n(e.requests),1),d(`td`,null,n(e.input_tokens),1),d(`td`,null,n(e.output_tokens),1),d(`td`,null,n(e.cost_est_usd),1)]))),128))])]),o[6]||=d(`p`,{class:`hint`},`单价来自模型池条目($/1M tokens);经典设置下的模型成本不计入。`,-1)])):e(``,!0),p.value.review?(r(),u(`div`,k,[o[9]||=d(`h3`,null,`人工检验`,-1),d(`div`,A,[d(`div`,j,[d(`span`,M,n(p.value.review.pending),1),o[7]||=d(`span`,{class:`stat-label`},`待审核`,-1)]),d(`div`,N,[d(`span`,P,n(p.value.review.total),1),o[8]||=d(`span`,{class:`stat-label`},`总提交`,-1)])]),p.value.review.total>0?(r(),u(`div`,F,[d(`div`,{class:`reviewed-bar`,style:l({width:`${(p.value.review.total-p.value.review.pending)/p.value.review.total*100}%`})},null,4)])):e(``,!0),d(`p`,I,` 通过率: `+n(((p.value.review.total-p.value.review.pending)/p.value.review.total*100).toFixed(1))+`% `,1)])):e(``,!0)]),d(`details`,L,[o[10]||=d(`summary`,null,`原始 JSON`,-1),d(`pre`,null,n(JSON.stringify(p.value,null,2)),1)])],64)):e(``,!0)]))}}),[[`__scopeId`,`data-v-8b237097`]]);export{R as default}; \ No newline at end of file +import{A as e,D as t,G as n,I as r,L as i,N as a,O as o,P as s,V as c,W as l,j as u,k as d,s as f,t as p}from"./index-C4nTxB0s.js";var m={class:`metrics-view`},h={key:0,class:`loading`},g={key:1,class:`error`},_={class:`card-grid`},v={class:`metric-card`},y={class:`kv-list`},b={class:`metric-card`},x={class:`kv-list`},S={key:0,class:`metric-card highlight`},C={class:`kv-list`},w={key:0},T={key:1},E={key:1,class:`metric-card`},D={class:`by-model`},O={class:`mono`},k={key:2,class:`metric-card review-card`},A={class:`review-stats`},j={class:`stat-item`},M={class:`stat-num`},N={class:`stat-item`},P={class:`stat-num`},F={key:0,class:`progress-wrap`},I={class:`review-rate`},L={class:`raw-json`},R=p(a({__name:`MetricsView`,setup(a){let p=c(null),R=c(!1),z=c(``),B=o(()=>p.value?.v2?.by_model||null);async function V(){R.value=!0,z.value=``;try{p.value=await f()}catch(e){z.value=e instanceof Error?e.message:`指标加载失败,请检查后端服务`}finally{R.value=!1}}return s(V),(a,o)=>(r(),u(`div`,m,[d(`header`,{class:`metrics-header`},[o[0]||=d(`h2`,null,`系统指标`,-1),d(`button`,{class:`refresh`,onClick:V},`🔄 刷新`)]),R.value?(r(),u(`div`,h,`加载中…`)):z.value?(r(),u(`div`,g,n(z.value),1)):p.value?(r(),u(t,{key:2},[d(`div`,_,[d(`div`,v,[o[1]||=d(`h3`,null,`路由器(v1)`,-1),d(`div`,y,[(r(!0),u(t,null,i(p.value.router,(e,i)=>(r(),u(t,{key:i},[d(`span`,null,n(i),1),d(`b`,null,n(e),1)],64))),128))])]),d(`div`,b,[o[2]||=d(`h3`,null,`缓存`,-1),d(`div`,x,[(r(!0),u(t,null,i(p.value.cache,(e,i)=>(r(),u(t,{key:i},[d(`span`,null,n(i),1),d(`b`,null,n(e),1)],64))),128))])]),p.value.v2?(r(),u(`div`,S,[o[3]||=d(`h3`,null,`协作管线(v2)`,-1),d(`div`,C,[(r(!0),u(t,null,i(p.value.v2,(i,a)=>(r(),u(t,{key:a},[a===`by_model`?e(``,!0):(r(),u(`span`,w,n(a),1)),a===`by_model`?e(``,!0):(r(),u(`b`,T,n(i),1))],64))),128))])])):e(``,!0),B.value&&Object.keys(B.value).length?(r(),u(`div`,E,[o[5]||=d(`h3`,null,`按模型分账(token / 成本)`,-1),d(`table`,D,[o[4]||=d(`thead`,null,[d(`tr`,null,[d(`th`,null,`模型`),d(`th`,null,`次数`),d(`th`,null,`入`),d(`th`,null,`出`),d(`th`,null,`成本 $`)])],-1),d(`tbody`,null,[(r(!0),u(t,null,i(B.value,(e,t)=>(r(),u(`tr`,{key:t},[d(`td`,O,n(t),1),d(`td`,null,n(e.requests),1),d(`td`,null,n(e.input_tokens),1),d(`td`,null,n(e.output_tokens),1),d(`td`,null,n(e.cost_est_usd),1)]))),128))])]),o[6]||=d(`p`,{class:`hint`},`单价来自模型池条目($/1M tokens);经典设置下的模型成本不计入。`,-1)])):e(``,!0),p.value.review?(r(),u(`div`,k,[o[9]||=d(`h3`,null,`人工检验`,-1),d(`div`,A,[d(`div`,j,[d(`span`,M,n(p.value.review.pending),1),o[7]||=d(`span`,{class:`stat-label`},`待审核`,-1)]),d(`div`,N,[d(`span`,P,n(p.value.review.total),1),o[8]||=d(`span`,{class:`stat-label`},`总提交`,-1)])]),p.value.review.total>0?(r(),u(`div`,F,[d(`div`,{class:`reviewed-bar`,style:l({width:`${(p.value.review.total-p.value.review.pending)/p.value.review.total*100}%`})},null,4)])):e(``,!0),d(`p`,I,` 通过率: `+n(((p.value.review.total-p.value.review.pending)/p.value.review.total*100).toFixed(1))+`% `,1)])):e(``,!0)]),d(`details`,L,[o[10]||=d(`summary`,null,`原始 JSON`,-1),d(`pre`,null,n(JSON.stringify(p.value,null,2)),1)])],64)):e(``,!0)]))}}),[[`__scopeId`,`data-v-8b237097`]]);export{R as default}; \ No newline at end of file diff --git a/gateway/static/assets/ReviewView-Cm34POvM.js b/gateway/static/assets/ReviewView-wY3DEmtF.js similarity index 97% rename from gateway/static/assets/ReviewView-Cm34POvM.js rename to gateway/static/assets/ReviewView-wY3DEmtF.js index f11f4e9..ca34b8a 100644 --- a/gateway/static/assets/ReviewView-Cm34POvM.js +++ b/gateway/static/assets/ReviewView-wY3DEmtF.js @@ -1 +1 @@ -import{A as e,D as t,E as n,G as r,I as i,L as a,M as o,N as s,O as c,P as l,U as u,V as d,f,j as p,k as m,t as h,v as g,z as _}from"./index-C8Za1808.js";var v={class:`review-view`},y={class:`review-header`},b={class:`controls`},x={key:0,class:`loading`},S={key:1,class:`error`},C={key:2,class:`queue-list`},w={key:0,class:`empty`},T={class:`card-header`},E={class:`card-id`},D={class:`tags`},O={class:`date`},k={class:`query-block`},A={class:`response-block`},j={key:0,class:`actions`},M=[`onUpdate:modelValue`],N={class:`btn-row`},P=[`onClick`],F=[`onClick`],I={key:1,class:`correction`},L=h(s({__name:`ReviewView`,setup(s){let h=d([]),L=d(!1),R=d(``),z=d(`pending`),B=d({}),V=c(()=>z.value===`all`?h.value:h.value.filter(e=>e.verdict===z.value));async function H(){L.value=!0,R.value=``;try{h.value=await f()}catch(e){R.value=e instanceof Error?e.message:String(e)}finally{L.value=!1}}async function U(e,t){try{await g(e,t,B.value[e]||void 0),await H()}catch(e){R.value=e instanceof Error?e.message:String(e)}}return l(H),(s,c)=>(i(),p(`div`,v,[m(`header`,y,[c[4]||=m(`h2`,null,`人工检验队列`,-1),m(`div`,b,[m(`button`,{class:u({active:z.value===`all`}),onClick:c[0]||=e=>z.value=`all`},`全部`,2),m(`button`,{class:u({active:z.value===`pending`}),onClick:c[1]||=e=>z.value=`pending`},`待审核`,2),m(`button`,{class:u({active:z.value===`approved`}),onClick:c[2]||=e=>z.value=`approved`},`已通过`,2),m(`button`,{class:u({active:z.value===`rejected`}),onClick:c[3]||=e=>z.value=`rejected`},`已拒绝`,2),m(`button`,{class:`refresh-btn`,onClick:H},`🔄 刷新`)])]),L.value?(i(),p(`div`,x,`加载中…`)):R.value?(i(),p(`div`,S,r(R.value),1)):(i(),p(`div`,C,[V.value.length?e(``,!0):(i(),p(`div`,w,`队列为空。`)),(i(!0),p(t,null,a(V.value,s=>(i(),p(`div`,{key:s.id,class:`review-card`},[m(`div`,T,[m(`span`,E,`#`+r(s.id),1),m(`span`,{class:u([`verdict-badge`,s.verdict])},r(s.verdict),3),m(`span`,D,[(i(!0),p(t,null,a(s.tags,e=>(i(),p(`span`,{key:e,class:`tag`},r(e),1))),128))]),m(`span`,O,r(s.created_at),1)]),m(`div`,k,[c[5]||=m(`strong`,null,`Query:`,-1),o(r(s.query),1)]),m(`div`,A,[c[6]||=m(`strong`,null,`Response:`,-1),m(`pre`,null,r(s.response),1)]),s.verdict===`pending`?(i(),p(`div`,j,[_(m(`textarea`,{"onUpdate:modelValue":e=>B.value[s.id]=e,placeholder:`修正意见(可选)`,rows:`2`},null,8,M),[[n,B.value[s.id]]]),m(`div`,N,[m(`button`,{class:`approve`,onClick:e=>U(s.id,`approved`)},`✅ 通过`,8,P),m(`button`,{class:`reject`,onClick:e=>U(s.id,`rejected`)},`❌ 拒绝`,8,F)])])):s.correction?(i(),p(`div`,I,[c[7]||=m(`strong`,null,`修正:`,-1),o(r(s.correction),1)])):e(``,!0)]))),128))]))]))}}),[[`__scopeId`,`data-v-d5b38f1c`]]);export{L as default}; \ No newline at end of file +import{A as e,D as t,E as n,G as r,I as i,L as a,M as o,N as s,O as c,P as l,U as u,V as d,f,j as p,k as m,t as h,v as g,z as _}from"./index-C4nTxB0s.js";var v={class:`review-view`},y={class:`review-header`},b={class:`controls`},x={key:0,class:`loading`},S={key:1,class:`error`},C={key:2,class:`queue-list`},w={key:0,class:`empty`},T={class:`card-header`},E={class:`card-id`},D={class:`tags`},O={class:`date`},k={class:`query-block`},A={class:`response-block`},j={key:0,class:`actions`},M=[`onUpdate:modelValue`],N={class:`btn-row`},P=[`onClick`],F=[`onClick`],I={key:1,class:`correction`},L=h(s({__name:`ReviewView`,setup(s){let h=d([]),L=d(!1),R=d(``),z=d(`pending`),B=d({}),V=c(()=>z.value===`all`?h.value:h.value.filter(e=>e.verdict===z.value));async function H(){L.value=!0,R.value=``;try{h.value=await f()}catch(e){R.value=e instanceof Error?e.message:String(e)}finally{L.value=!1}}async function U(e,t){try{await g(e,t,B.value[e]||void 0),await H()}catch(e){R.value=e instanceof Error?e.message:String(e)}}return l(H),(s,c)=>(i(),p(`div`,v,[m(`header`,y,[c[4]||=m(`h2`,null,`人工检验队列`,-1),m(`div`,b,[m(`button`,{class:u({active:z.value===`all`}),onClick:c[0]||=e=>z.value=`all`},`全部`,2),m(`button`,{class:u({active:z.value===`pending`}),onClick:c[1]||=e=>z.value=`pending`},`待审核`,2),m(`button`,{class:u({active:z.value===`approved`}),onClick:c[2]||=e=>z.value=`approved`},`已通过`,2),m(`button`,{class:u({active:z.value===`rejected`}),onClick:c[3]||=e=>z.value=`rejected`},`已拒绝`,2),m(`button`,{class:`refresh-btn`,onClick:H},`🔄 刷新`)])]),L.value?(i(),p(`div`,x,`加载中…`)):R.value?(i(),p(`div`,S,r(R.value),1)):(i(),p(`div`,C,[V.value.length?e(``,!0):(i(),p(`div`,w,`队列为空。`)),(i(!0),p(t,null,a(V.value,s=>(i(),p(`div`,{key:s.id,class:`review-card`},[m(`div`,T,[m(`span`,E,`#`+r(s.id),1),m(`span`,{class:u([`verdict-badge`,s.verdict])},r(s.verdict),3),m(`span`,D,[(i(!0),p(t,null,a(s.tags,e=>(i(),p(`span`,{key:e,class:`tag`},r(e),1))),128))]),m(`span`,O,r(s.created_at),1)]),m(`div`,k,[c[5]||=m(`strong`,null,`Query:`,-1),o(r(s.query),1)]),m(`div`,A,[c[6]||=m(`strong`,null,`Response:`,-1),m(`pre`,null,r(s.response),1)]),s.verdict===`pending`?(i(),p(`div`,j,[_(m(`textarea`,{"onUpdate:modelValue":e=>B.value[s.id]=e,placeholder:`修正意见(可选)`,rows:`2`},null,8,M),[[n,B.value[s.id]]]),m(`div`,N,[m(`button`,{class:`approve`,onClick:e=>U(s.id,`approved`)},`✅ 通过`,8,P),m(`button`,{class:`reject`,onClick:e=>U(s.id,`rejected`)},`❌ 拒绝`,8,F)])])):s.correction?(i(),p(`div`,I,[c[7]||=m(`strong`,null,`修正:`,-1),o(r(s.correction),1)])):e(``,!0)]))),128))]))]))}}),[[`__scopeId`,`data-v-d5b38f1c`]]);export{L as default}; \ No newline at end of file diff --git a/gateway/static/assets/SettingsView-DMG9EXA2.js b/gateway/static/assets/SettingsView-B4tzUqnq.js similarity index 99% rename from gateway/static/assets/SettingsView-DMG9EXA2.js rename to gateway/static/assets/SettingsView-B4tzUqnq.js index ac9f3b4..b59e102 100644 --- a/gateway/static/assets/SettingsView-DMG9EXA2.js +++ b/gateway/static/assets/SettingsView-B4tzUqnq.js @@ -1 +1 @@ -import{A as e,B as t,D as n,E as r,F as i,G as a,I as o,L as s,M as c,N as ee,O as l,P as te,S as ne,T as u,U as d,V as f,W as re,_ as ie,a as ae,b as oe,c as se,d as ce,g as le,h as ue,i as de,j as p,k as m,l as fe,m as pe,o as me,p as he,r as ge,t as h,u as _e,w as ve,x as ye,y as be,z as g}from"./index-C8Za1808.js";var xe={class:`settings-view`},Se={class:`settings-section`},Ce={class:`section-body`},we={class:`role-bar`},Te=[`onUpdate:modelValue`],Ee=[`value`],De={key:0,class:`pool-table`},Oe={class:`mono`},ke=[`onUpdate:modelValue`,`onChange`],Ae={class:`row-actions`},je=[`disabled`,`onClick`],Me=[`onClick`],Ne=[`onClick`],Pe={key:1,class:`no-models`},Fe={key:3,class:`pool-edit`},Ie={class:`pool-edit-title`},Le={class:`form-grid`},Re={class:`field`},ze={class:`field`},Be={class:`field`},Ve={class:`field`},He=[`disabled`],Ue={id:`pool-model-options`},We=[`value`],Ge={key:0,class:`field full-width`},Ke={key:1,class:`field`},qe={class:`field`},Je={class:`field`},Ye={class:`field`},Xe={class:`field`},Ze={class:`pool-edit-actions`},Qe={class:`checkbox-label`},$e=[`disabled`],et={class:`settings-section`},tt={class:`section-title`},nt={class:`title-right`},rt={class:`section-body`},it={class:`server-control-bar`},at={class:`server-info`},ot={class:`model-chip`},st={class:`model-chip port-chip`},ct={class:`model-chip pid-chip`},lt={key:1,class:`server-idle`},ut={class:`server-actions`},dt=[`disabled`],ft=[`disabled`],pt={key:0,class:`error-msg llama-error`},mt={class:`local-models`},ht={class:`local-models-header`},gt=[`disabled`],_t={key:0,class:`no-models`},vt={key:1,class:`model-list`},yt=[`onClick`],bt={class:`model-item-name`},xt={class:`model-item-size`},St={class:`download-section`},Ct={class:`download-form`},wt=[`disabled`],Tt=[`disabled`],Et={key:0,class:`progress-wrap`},Dt={class:`progress-bar`},Ot={class:`progress-info`},kt={key:1,class:`error-msg download-error`},At={key:2,class:`done-msg`},jt={class:`form-grid`,style:{"margin-top":`16px`}},Mt={class:`field full-width`},Nt={key:0,class:`field full-width`},Pt=[`disabled`],Ft={class:`input-row`},It=[`placeholder`],Lt={key:1,class:`field full-width`},Rt=[`disabled`],zt=[`value`],Bt=[`value`],Vt={key:3,class:`field-hint warn`},Ht={key:2,class:`field`},Ut={class:`field`},Wt={class:`field`},Gt={class:`field`},Kt={class:`settings-section`},qt={class:`section-title`},Jt={class:`title-right`},Yt={class:`section-body`},Xt={class:`form-grid`},Zt={class:`field full-width`},Qt=[`disabled`],$t={class:`field full-width`},en={class:`field full-width`},tn=[`disabled`],nn=[`value`],rn={key:0,class:`field-hint warn`},an={class:`settings-section`},on={class:`section-body`},sn={class:`form-grid`},cn={class:`field full-width`},ln={class:`checkbox-label`},un={class:`field`},dn={class:`field`},fn={class:`field full-width`},pn={class:`actions`},mn=[`disabled`],hn=[`disabled`],_=h(ee({__name:`SettingsView`,setup(ee){let h=f(!1),_=f(``),v=f(null),gn=[{key:`architect`,label:`🧠 Architect(决策/终审)`},{key:`worker`,label:`🔧 Worker(实现/自验证)`},{key:`agent`,label:`🤖 Agent(智能体工具)`}],_n={local:`本地/零成本`,budget:`低价 API`,premium:`旗舰 API`},y=f({roles:{architect:``,worker:``,agent:``},entries:[]}),b=t({architect:``,worker:``,agent:``}),x=f(null),S=f(!1),C=f(``),w=f(``),T=f(!1),E=f(!1),D=f(!1),vn=f([]),yn=l(()=>y.value.entries.filter(e=>e.enabled));function bn(e){return _n[e]??e}async function xn(){try{y.value=await se(),b.architect=y.value.roles.architect||``,b.worker=y.value.roles.worker||``,b.agent=y.value.roles.agent||``}catch{}}async function Sn(){try{y.value=await ue({...b}),v.value={ok:!0,msg:`✅ 角色已更新,管线已重建`}}catch(e){v.value={ok:!1,msg:`❌ 角色指派失败:`+(e?.response?.data?.detail||e?.message||e)},await xn()}setTimeout(()=>v.value=null,3e3)}function Cn(){S.value=!0,x.value={name:``,tier:`budget`,backend:`openai`,base_url:``,model:``,api_key:``,price_in:.1,price_out:.1,temperature:.3,max_tokens:4096,enabled:!0}}function wn(e){S.value=!1,x.value={...e,api_key:``}}async function Tn(){if(x.value){E.value=!0;try{y.value=await ye(x.value),x.value=null,v.value={ok:!0,msg:`✅ 模型条目已保存`}}catch(e){v.value={ok:!1,msg:`❌ 保存失败:`+(e?.response?.data?.detail||e?.message||e)}}finally{E.value=!1,setTimeout(()=>v.value=null,3e3)}}}async function En(e){confirm(`删除模型条目「${e.name}」?相关角色指派会一并清空。`)&&(y.value=await ge(e.id))}async function Dn(e){y.value=await ye({...e,api_key:``})}async function On(e){C.value=e.id,w.value=``;try{let t=await be(e.id);T.value=t.ok,w.value=t.ok?`✅ ${e.name} 连接正常`:`❌ ${e.name}: ${t.detail||`连接失败`}`}catch(e){T.value=!1,w.value=`❌ `+(e?.message||String(e))}finally{C.value=``}}async function kn(){if(x.value){D.value=!0;try{let e;e=!S.value&&x.value.id?await ce(x.value.id):x.value.backend===`mock`?{models:[{id:`mock`,name:`mock(内置模拟)`}]}:await _e(x.value.backend||`openai`,x.value.base_url||``,x.value.api_key||``),e.error?(T.value=!1,w.value=e.error,vn.value=[]):vn.value=e.models||[]}finally{D.value=!1}}}let O=f({running:!1}),k=f(!1),A=f(!1),j=f([]),M=f(!1),N=f(``),P=f(!1),F=f(``),I=f(0),L=f(``),An=f(``),jn=f(``),R=f(null),Mn=f(``),z=f(``),Nn=f(!1),B=null,V=f([]),H=f([]),U=t({worker:!1,architect:!1}),W=t({worker:``,architect:``}),G=t({worker:!1,architect:!1}),K=t({worker:null,architect:null}),q=t({worker:``,architect:``}),J=[{id:`deepseek-v4-flash`,name:`deepseek-v4-flash(推荐)`},{id:`deepseek-v4-pro`,name:`deepseek-v4-pro(推理模式)`},{id:`deepseek-coder`,name:`deepseek-coder`},{id:`gpt-4o-mini`,name:`gpt-4o-mini`},{id:`gpt-4o`,name:`gpt-4o`},{id:`claude-3-5-sonnet-20241022`,name:`claude-3.5-sonnet`},{id:`qwen-plus`,name:`qwen-plus`},{id:`qwen-max`,name:`qwen-max`}],Y=f(null),X=t({worker:{backend:`mock`,model:``,base_url:``,port:8901,temperature:.3,max_fix_attempts:2,code_timeout_s:10},architect:{model:`deepseek-v4-flash`,base_url:`https://api.deepseek.com`,api_key:``},pipeline:{fast_path:!0,rounds_cap:6,api_token_cap:8e3,breach_policy:`architect_do`}}),Pn=l(()=>X.worker.backend===`llama_server`?`http://127.0.0.1:${X.worker.port}/v1`:`http://127.0.0.1:11434/v1`),Fn=l(()=>Y.value?JSON.stringify(X)!==JSON.stringify(Y.value):!1);function In(e){return e>=1e9?(e/1e9).toFixed(1)+` GB`:e>=1e6?(e/1e6).toFixed(1)+` MB`:e>=1e3?(e/1e3).toFixed(0)+` KB`:e+` B`}async function Ln(){try{let e=await me();return O.value=e,e}catch{return O.value={running:!1},{running:!1}}}async function Z(){M.value=!0;try{j.value=await fe()}catch{j.value=[]}finally{M.value=!1}}async function Rn(){if(!X.worker.model){alert(`请先从上方「本地模型」列表中选择一个模型,再启动服务`);return}k.value=!0;try{let e=await le(X.worker.model,X.worker.port,99,4096);O.value=e,e.running&&(X.worker.backend=`llama_server`,X.worker.base_url=`http://127.0.0.1:${e.port}/v1`,await Z(),await Q())}catch(e){O.value={running:!1,error:String(e?.message||e)}}finally{k.value=!1}}async function zn(){A.value=!0;try{await ie(),O.value={running:!1}}catch{O.value={running:!1}}finally{A.value=!1}}function Bn(e){N.value=e.id,X.worker.model=e.id}function Vn(){F.value.trim()&&(z.value=``,Nn.value=!1,I.value=0,L.value=``,An.value=``,jn.value=`0 B`,R.value=null,P.value=!0,de(F.value.trim()).then(e=>{if(e.error){z.value=e.error,P.value=!1;return}if(e.done){Hn(e);return}B=ne(F.value.trim()),B.subscribe({onProgress:Hn,onDone:e=>{Hn(e),B?.close(),B=null}})}))}function Hn(e){I.value=Math.round(e.progress_pct),L.value=e.speed,An.value=e.eta,jn.value=In(e.downloaded_bytes),R.value=e.total_bytes?In(e.total_bytes):null,Mn.value=e.dest,e.done&&(P.value=!1,e.error?z.value=e.error:(Nn.value=!0,Z()))}async function Un(){W.worker=``,V.value=[],X.worker.backend!==`mock`&&await Q()}async function Wn(){W.architect=``,H.value=[],await Gn()}async function Q(){if(X.worker.backend!==`mock`){U.worker=!0,W.worker=``;try{let e=await _e(X.worker.backend,X.worker.base_url,``);e.error?X.worker.backend===`llama_server`?W.worker=`💡 llama-server 未启动,请先点击上方「启动服务」`:W.worker=e.error||`连接失败`:V.value=e.models||[]}catch{W.worker=`获取模型列表失败`}finally{U.worker=!1}}}async function Gn(){if(U.architect=!0,W.architect=``,!X.architect.base_url){H.value=[...J],U.architect=!1;return}try{let e=await _e(`openai`,X.architect.base_url,X.architect.api_key||``);e.error?(W.architect=e.error,H.value=[...J]):(e.models||[]).length>0?H.value=e.models||[]:H.value=[...J]}catch{W.architect=`获取模型列表失败`,H.value=[...J]}finally{U.architect=!1}}async function Kn(){if(X.worker.backend!==`mock`){G.worker=!0,K.worker=null,q.worker=``;try{let e=await he(X.worker.backend,X.worker.base_url,``);K.worker=e.ok,e.ok||(q.worker=e.detail||`连接失败`)}catch(e){K.worker=!1,q.worker=e?.message||String(e)}finally{G.worker=!1}}}async function qn(){if(!X.architect.base_url){K.architect=!1,q.architect=`请先填写 API 端点`;return}G.architect=!0,K.architect=null,q.architect=``;try{let e=await he(`openai`,X.architect.base_url,X.architect.api_key||``);K.architect=e.ok,e.ok||(q.architect=e.detail||`连接失败`)}catch(e){K.architect=!1,q.architect=e?.message||String(e)}finally{G.architect=!1}}function $(e,t){if(t)for(let n of Object.keys(e))t[n]!==void 0&&(typeof e[n]==`object`&&typeof t[n]==`object`&&e[n]!==null?$(e[n],t[n]):e[n]=t[n])}async function Jn(){_.value=``;try{let[e,t]=await Promise.all([ae(),Ln()]);O.value=t,xn(),e.worker&&$(X.worker,e.worker),e.architect&&$(X.architect,e.architect),e.pipeline&&$(X.pipeline,e.pipeline),Y.value=JSON.parse(JSON.stringify(X)),await Z(),H.value=[...J],X.worker.backend!==`mock`&&Q(),X.worker.model&&(N.value=X.worker.model)}catch(e){_.value=`加载配置失败:`+(e?.message||String(e))}}async function Yn(){h.value=!0,v.value=null;try{let e=JSON.parse(JSON.stringify(X));e.architect.api_key||delete e.architect.api_key;let t=await oe(e);$(X.worker,t.worker||{}),$(X.architect,t.architect||{}),$(X.pipeline,t.pipeline||{}),Y.value=JSON.parse(JSON.stringify(X)),v.value={ok:!0,msg:`✅ 设置已保存,管线已重建`},setTimeout(()=>v.value=null,3e3)}catch(e){v.value={ok:!1,msg:`❌ 保存失败:`+(e?.message||String(e))}}finally{h.value=!1}}async function Xn(){if(confirm(`确定恢复默认设置?`)){h.value=!0,v.value=null;try{let e=await pe();$(X.worker,e.worker||{}),$(X.architect,e.architect||{}),$(X.pipeline,e.pipeline||{}),Y.value=JSON.parse(JSON.stringify(X)),V.value=[],H.value=[...J],K.worker=null,K.architect=null,v.value={ok:!0,msg:`✅ 已恢复默认设置`},setTimeout(()=>v.value=null,3e3)}catch(e){v.value={ok:!1,msg:`❌ 重置失败:`+(e?.message||String(e))}}finally{h.value=!1}}}return te(Jn),i(()=>{B?.close()}),(t,i)=>(o(),p(`div`,xe,[i[68]||=m(`header`,{class:`settings-header`},[m(`h2`,null,`⚙️ 模型设置`),m(`p`,{class:`subtitle`},`配置小模型(Worker)与大模型(Architect)参数,保存后实时生效`)],-1),m(`section`,Se,[m(`div`,{class:`section-title`},[i[29]||=m(`span`,null,`🗄️ 模型池(多价位异构模型)`,-1),m(`div`,{class:`title-right`},[m(`button`,{class:`btn-refresh`,onClick:Cn},`+ 添加模型`)])]),m(`div`,Ce,[m(`div`,we,[(o(),p(n,null,s(gn,e=>m(`div`,{class:`role-item`,key:e.key},[m(`label`,null,a(e.label),1),g(m(`select`,{"onUpdate:modelValue":t=>b[e.key]=t,onChange:Sn},[i[30]||=m(`option`,{value:``},`经典设置(下方表单)`,-1),(o(!0),p(n,null,s(yn.value,e=>(o(),p(`option`,{key:e.id,value:e.id},a(e.name),9,Ee))),128))],40,Te),[[u,b[e.key]]])])),64))]),i[44]||=m(`p`,{class:`role-hint`},` 按价位组织模型:本地 llama.cpp 为零成本档,低价 API 干批量活,旗舰 API 做决策—— 随时切换,保存后管线自动重建。 `,-1),y.value.entries.length?(o(),p(`table`,De,[i[31]||=m(`thead`,null,[m(`tr`,null,[m(`th`,null,`名称`),m(`th`,null,`档位`),m(`th`,null,`后端`),m(`th`,null,`模型`),m(`th`,null,`单价 $/1M(入/出)`),m(`th`,null,`启用`),m(`th`,null,`操作`)])],-1),m(`tbody`,null,[(o(!0),p(n,null,s(y.value.entries,e=>(o(),p(`tr`,{key:e.id},[m(`td`,null,a(e.name),1),m(`td`,null,[m(`span`,{class:d([`tier-badge`,`tier-`+e.tier])},a(bn(e.tier)),3)]),m(`td`,null,a(e.backend),1),m(`td`,Oe,a(e.model||`—`),1),m(`td`,null,a(e.price_in)+` / `+a(e.price_out),1),m(`td`,null,[g(m(`input`,{type:`checkbox`,"onUpdate:modelValue":t=>e.enabled=t,onChange:t=>Dn(e)},null,40,ke),[[ve,e.enabled]])]),m(`td`,Ae,[m(`button`,{class:`btn-refresh`,disabled:C.value===e.id,onClick:t=>On(e)},a(C.value===e.id?`…`:`测试`),9,je),m(`button`,{class:`btn-refresh`,onClick:t=>wn(e)},`编辑`,8,Me),m(`button`,{class:`btn-refresh danger`,onClick:t=>En(e)},`删除`,8,Ne)])]))),128))])])):(o(),p(`div`,Pe,` 池为空——可添加本地 llama.cpp 模型(零成本档)、低价 API、旗舰 API 等,随时指派给 Architect / Worker / Agent。 `)),w.value?(o(),p(`div`,{key:2,class:d([`field-hint`,T.value?`ok`:`warn`])},a(w.value),3)):e(``,!0),x.value?(o(),p(`div`,Fe,[m(`div`,Ie,a(S.value?`添加模型`:`编辑模型`),1),m(`div`,Le,[m(`div`,Re,[i[32]||=m(`label`,null,`名称`,-1),g(m(`input`,{"onUpdate:modelValue":i[0]||=e=>x.value.name=e,placeholder:`如:DeepSeek 旗舰 / 本地 Qwen4B`},null,512),[[r,x.value.name]])]),m(`div`,ze,[i[34]||=m(`label`,null,`档位`,-1),g(m(`select`,{"onUpdate:modelValue":i[1]||=e=>x.value.tier=e},[...i[33]||=[m(`option`,{value:`local`},`local(本地 / 零边际成本)`,-1),m(`option`,{value:`budget`},`budget(低价 API)`,-1),m(`option`,{value:`premium`},`premium(旗舰 API)`,-1)]],512),[[u,x.value.tier]])]),m(`div`,Be,[i[36]||=m(`label`,null,`后端`,-1),g(m(`select`,{"onUpdate:modelValue":i[2]||=e=>x.value.backend=e},[...i[35]||=[m(`option`,{value:`openai`},`openai / 兼容 API`,-1),m(`option`,{value:`llama_server`},`llama_server(内置本地)`,-1),m(`option`,{value:`mock`},`mock(模拟)`,-1)]],512),[[u,x.value.backend]])]),m(`div`,Ve,[m(`label`,null,[i[37]||=c(` 模型 `,-1),m(`button`,{class:`btn-refresh btn-inline`,disabled:D.value,onClick:kn},a(D.value?`…`:`🔄 刷新列表`),9,He)]),g(m(`input`,{"onUpdate:modelValue":i[3]||=e=>x.value.model=e,list:`pool-model-options`,placeholder:`模型名`},null,512),[[r,x.value.model]]),m(`datalist`,Ue,[(o(!0),p(n,null,s(vn.value,e=>(o(),p(`option`,{key:e.id,value:e.id},null,8,We))),128))])]),x.value.backend===`mock`?e(``,!0):(o(),p(`div`,Ge,[i[38]||=m(`label`,null,`API 地址`,-1),g(m(`input`,{"onUpdate:modelValue":i[4]||=e=>x.value.base_url=e,placeholder:`https://api.deepseek.com 或 http://127.0.0.1:8901/v1`},null,512),[[r,x.value.base_url]])])),x.value.backend===`openai`?(o(),p(`div`,Ke,[m(`label`,null,`API Key`+a(x.value.api_key_set?`(已设置,留空保留)`:``),1),g(m(`input`,{"onUpdate:modelValue":i[5]||=e=>x.value.api_key=e,type:`password`,autocomplete:`off`,placeholder:`sk-…`},null,512),[[r,x.value.api_key]])])):e(``,!0),m(`div`,qe,[i[39]||=m(`label`,null,`输入单价 $/1M`,-1),g(m(`input`,{"onUpdate:modelValue":i[6]||=e=>x.value.price_in=e,type:`number`,min:`0`,step:`0.01`},null,512),[[r,x.value.price_in,void 0,{number:!0}]])]),m(`div`,Je,[i[40]||=m(`label`,null,`输出单价 $/1M`,-1),g(m(`input`,{"onUpdate:modelValue":i[7]||=e=>x.value.price_out=e,type:`number`,min:`0`,step:`0.01`},null,512),[[r,x.value.price_out,void 0,{number:!0}]])]),m(`div`,Ye,[i[41]||=m(`label`,null,`Temperature`,-1),g(m(`input`,{"onUpdate:modelValue":i[8]||=e=>x.value.temperature=e,type:`number`,min:`0`,max:`2`,step:`0.05`},null,512),[[r,x.value.temperature,void 0,{number:!0}]])]),m(`div`,Xe,[i[42]||=m(`label`,null,`Max Tokens`,-1),g(m(`input`,{"onUpdate:modelValue":i[9]||=e=>x.value.max_tokens=e,type:`number`,min:`256`,max:`32768`},null,512),[[r,x.value.max_tokens,void 0,{number:!0}]])])]),m(`div`,Ze,[m(`label`,Qe,[g(m(`input`,{type:`checkbox`,"onUpdate:modelValue":i[10]||=e=>x.value.enabled=e},null,512),[[ve,x.value.enabled]]),i[43]||=c(` 启用 `,-1)]),m(`button`,{class:`btn-primary`,disabled:E.value,onClick:Tn},a(E.value?`保存中…`:`保存条目`),9,$e),m(`button`,{class:`btn-secondary`,onClick:i[11]||=e=>x.value=null},`取消`)])])):e(``,!0)])]),m(`section`,et,[m(`div`,tt,[i[45]||=m(`span`,null,`🔧 小模型 (Worker · 本地 llama.cpp)`,-1),m(`div`,nt,[m(`span`,{class:d([`server-badge`,O.value.running?`badge-running`:`badge-stopped`])},a(O.value.running?`● 运行中`:`○ 已停止`),3)])]),m(`div`,rt,[m(`div`,it,[m(`div`,at,[O.value.running?(o(),p(n,{key:0},[m(`span`,ot,a(O.value.model?.split(/[/\\]/).pop()),1),m(`span`,st,`:`+a(O.value.port),1),m(`span`,ct,`PID `+a(O.value.pid),1)],64)):(o(),p(`span`,lt,`llama-server 未运行 — 点击「启动服务」即可启用`))]),m(`div`,ut,[O.value.running?(o(),p(`button`,{key:1,class:`btn-stop`,disabled:k.value||A.value,onClick:zn},a(A.value?`停止中…`:`■ 停止服务`),9,ft)):(o(),p(`button`,{key:0,class:`btn-start`,disabled:k.value||A.value,onClick:Rn},a(k.value?`启动中…`:`▶ 启动服务`),9,dt))])]),O.value.error?(o(),p(`div`,pt,a(O.value.error),1)):e(``,!0),m(`div`,mt,[m(`div`,ht,[m(`span`,null,`📁 本地模型 (`+a(j.value.length)+`)`,1),m(`button`,{class:`btn-refresh`,disabled:M.value,onClick:Z},a(M.value?`…`:`🔄`),9,gt)]),j.value.length===0&&!M.value?(o(),p(`div`,_t,` 暂无本地模型,请下载或放置 .gguf 文件到 models/ 目录 `)):(o(),p(`div`,vt,[(o(!0),p(n,null,s(j.value,e=>(o(),p(`div`,{key:e.id,class:d([`model-item`,N.value===e.id?`selected`:``]),onClick:t=>Bn(e)},[m(`span`,bt,a(e.name),1),m(`span`,xt,a(e.size_mb)+` MB`,1)],10,yt))),128))]))]),m(`div`,St,[i[46]||=m(`div`,{class:`download-header`},`⬇️ 下载模型`,-1),m(`div`,Ct,[g(m(`input`,{"onUpdate:modelValue":i[12]||=e=>F.value=e,class:`download-input`,placeholder:`HuggingFace 路径或直链,如:Qwen/Qwen3-4B-GGUF/Qwen3-4B-Q4_K_M.gguf`,disabled:P.value},null,8,wt),[[r,F.value]]),m(`button`,{class:`btn-download`,disabled:P.value||!F.value.trim(),onClick:Vn},a(P.value?I.value+`%`:`下载`),9,Tt)]),P.value?(o(),p(`div`,Et,[m(`div`,Dt,[m(`div`,{class:`progress-fill`,style:re({width:I.value+`%`})},null,4)]),m(`div`,Ot,[m(`span`,null,a(L.value),1),m(`span`,null,a(An.value),1),m(`span`,null,a(jn.value)+`/`+a(R.value||`?`),1)])])):e(``,!0),z.value?(o(),p(`div`,kt,a(z.value),1)):e(``,!0),Nn.value&&!z.value?(o(),p(`div`,At,` ✅ 下载完成:`+a(Mn.value),1)):e(``,!0)]),m(`div`,jt,[m(`div`,Mt,[i[48]||=m(`label`,null,`后端类型`,-1),g(m(`select`,{"onUpdate:modelValue":i[13]||=e=>X.worker.backend=e,onChange:Un},[...i[47]||=[m(`option`,{value:`mock`},`mock(模拟,不真实调用)`,-1),m(`option`,{value:`llama_server`},`llama_server(内置本地服务,推荐)`,-1),m(`option`,{value:`openai`},`openai / 兼容 API(Ollama 等)`,-1)]],544),[[u,X.worker.backend]])]),X.worker.backend===`mock`?e(``,!0):(o(),p(`div`,Nt,[m(`label`,null,[i[49]||=c(` API 地址 `,-1),m(`button`,{class:`btn-refresh btn-inline`,disabled:G.worker,onClick:Kn},a(G.worker?`…`:`🔗 连接测试`),9,Pt)]),m(`div`,Ft,[g(m(`input`,{"onUpdate:modelValue":i[14]||=e=>X.worker.base_url=e,placeholder:Pn.value,onBlur:Un},null,40,It),[[r,X.worker.base_url]]),K.worker===null?e(``,!0):(o(),p(`span`,{key:0,class:d([`ping-badge`,K.worker?`ping-ok`:`ping-err`])},a(K.worker?`✅ 正常`:`❌ `+q.worker),3))])])),X.worker.backend===`mock`?e(``,!0):(o(),p(`div`,Lt,[m(`label`,null,[i[50]||=c(` 模型 `,-1),m(`button`,{class:`btn-refresh btn-inline`,disabled:U.worker,onClick:Q},a(U.worker?`…`:`🔄 刷新列表`),9,Rt)]),X.worker.backend===`llama_server`?g((o(),p(`select`,{key:0,"onUpdate:modelValue":i[15]||=e=>X.worker.model=e},[i[51]||=m(`option`,{value:``,disabled:``},`—— 从本地模型中选择 ——`,-1),(o(!0),p(n,null,s(j.value,e=>(o(),p(`option`,{key:e.id,value:e.id},a(e.name),9,zt))),128))],512)),[[u,X.worker.model]]):X.worker.backend===`openai`?g((o(),p(`select`,{key:1,"onUpdate:modelValue":i[16]||=e=>X.worker.model=e},[i[52]||=m(`option`,{value:``,disabled:``},`—— 从 API 获取 ——`,-1),(o(!0),p(n,null,s(V.value,e=>(o(),p(`option`,{key:e.id,value:e.id},a(e.name||e.id),9,Bt))),128))],512)),[[u,X.worker.model]]):g((o(),p(`input`,{key:2,"onUpdate:modelValue":i[17]||=e=>X.worker.model=e,placeholder:`输入模型名称…`},null,512)),[[r,X.worker.model]]),W.worker?(o(),p(`span`,Vt,a(W.worker),1)):e(``,!0)])),X.worker.backend===`llama_server`?(o(),p(`div`,Ht,[i[53]||=m(`label`,null,`端口`,-1),g(m(`input`,{"onUpdate:modelValue":i[18]||=e=>X.worker.port=e,type:`number`,min:`1`,max:`65535`},null,512),[[r,X.worker.port,void 0,{number:!0}]])])):e(``,!0),m(`div`,Ut,[i[54]||=m(`label`,null,`Temperature`,-1),g(m(`input`,{"onUpdate:modelValue":i[19]||=e=>X.worker.temperature=e,type:`number`,min:`0`,max:`2`,step:`0.05`},null,512),[[r,X.worker.temperature,void 0,{number:!0}]])]),m(`div`,Wt,[i[55]||=m(`label`,null,`最大修复轮次`,-1),g(m(`input`,{"onUpdate:modelValue":i[20]||=e=>X.worker.max_fix_attempts=e,type:`number`,min:`0`,max:`10`},null,512),[[r,X.worker.max_fix_attempts,void 0,{number:!0}]])]),m(`div`,Gt,[i[56]||=m(`label`,null,`代码执行超时 (秒)`,-1),g(m(`input`,{"onUpdate:modelValue":i[21]||=e=>X.worker.code_timeout_s=e,type:`number`,min:`1`,max:`300`},null,512),[[r,X.worker.code_timeout_s,void 0,{number:!0}]])])])])]),m(`section`,Kt,[m(`div`,qt,[i[57]||=m(`span`,null,`🧠 大模型 (Architect · API 云端)`,-1),m(`div`,Jt,[K.architect===null?e(``,!0):(o(),p(`span`,{key:0,class:d([`ping-badge`,K.architect?`ping-ok`:`ping-err`])},a(K.architect?`✅ 已连接`:`❌ `+q.architect),3))])]),m(`div`,Yt,[m(`div`,Xt,[m(`div`,Zt,[m(`label`,null,[i[58]||=c(` API 端点 `,-1),m(`button`,{class:`btn-refresh btn-inline`,disabled:G.architect,onClick:qn},a(G.architect?`…`:`🔗 连接测试`),9,Qt)]),g(m(`input`,{"onUpdate:modelValue":i[22]||=e=>X.architect.base_url=e,placeholder:`https://api.deepseek.com`,onBlur:Wn},null,544),[[r,X.architect.base_url]])]),m(`div`,$t,[i[59]||=m(`label`,null,`API Key(保存在本地 config/settings.json,不入代码库;优先级高于 .env)`,-1),g(m(`input`,{"onUpdate:modelValue":i[23]||=e=>X.architect.api_key=e,type:`password`,placeholder:`sk-xxxxxxxxxxxxxxxx`,autocomplete:`off`},null,512),[[r,X.architect.api_key]])]),m(`div`,en,[m(`label`,null,[i[60]||=c(` 模型 `,-1),m(`button`,{class:`btn-refresh btn-inline`,disabled:U.architect,onClick:Gn},a(U.architect?`…`:`🔄 刷新列表`),9,tn)]),g(m(`select`,{"onUpdate:modelValue":i[24]||=e=>X.architect.model=e,class:`model-select`},[i[61]||=m(`option`,{value:``,disabled:``},`—— 从 API 获取模型列表 ——`,-1),(o(!0),p(n,null,s(H.value,e=>(o(),p(`option`,{key:e.id,value:e.id},a(e.name||e.id),9,nn))),128))],512),[[u,X.architect.model]]),W.architect?(o(),p(`span`,rn,a(W.architect),1)):e(``,!0)])])])]),m(`section`,an,[i[67]||=m(`div`,{class:`section-title`},[m(`span`,null,`⚙️ 协作管线 (Pipeline)`)],-1),m(`div`,on,[m(`div`,sn,[m(`div`,cn,[m(`label`,ln,[g(m(`input`,{type:`checkbox`,"onUpdate:modelValue":i[25]||=e=>X.pipeline.fast_path=e},null,512),[[ve,X.pipeline.fast_path]]),i[62]||=c(` 启用快路径(简单查询跳过本地模型,直接由大模型回答,省 API 费用) `,-1)])]),m(`div`,un,[i[63]||=m(`label`,null,`最大协作轮次`,-1),g(m(`input`,{"onUpdate:modelValue":i[26]||=e=>X.pipeline.rounds_cap=e,type:`number`,min:`1`,max:`20`},null,512),[[r,X.pipeline.rounds_cap,void 0,{number:!0}]])]),m(`div`,dn,[i[64]||=m(`label`,null,`API Token 熔断上限`,-1),g(m(`input`,{"onUpdate:modelValue":i[27]||=e=>X.pipeline.api_token_cap=e,type:`number`,min:`100`,max:`100000`},null,512),[[r,X.pipeline.api_token_cap,void 0,{number:!0}]])]),m(`div`,fn,[i[66]||=m(`label`,null,`超限策略`,-1),g(m(`select`,{"onUpdate:modelValue":i[28]||=e=>X.pipeline.breach_policy=e},[...i[65]||=[m(`option`,{value:`architect_do`},`architect_do(超限交给大模型处理,推荐)`,-1),m(`option`,{value:`local_only`},`local_only(强制留在本地小模型)`,-1)]],512),[[u,X.pipeline.breach_policy]])])])])]),m(`div`,pn,[m(`button`,{class:`btn-primary`,disabled:h.value||!Fn.value,onClick:Yn},a(h.value?`保存中…`:`💾 保存设置`),9,mn),m(`button`,{class:`btn-secondary`,disabled:h.value,onClick:Xn},` 🔁 恢复默认 `,8,hn)]),v.value?(o(),p(`div`,{key:0,class:d([`toast`,v.value.ok?`toast-ok`:`toast-err`])},a(v.value.msg),3)):e(``,!0)]))}}),[[`__scopeId`,`data-v-7b21dc2a`]]);export{_ as default}; \ No newline at end of file +import{A as e,B as t,D as n,E as r,F as i,G as a,I as o,L as s,M as c,N as ee,O as l,P as te,S as ne,T as u,U as d,V as f,W as re,_ as ie,a as ae,b as oe,c as se,d as ce,g as le,h as ue,i as de,j as p,k as m,l as fe,m as pe,o as me,p as he,r as ge,t as h,u as _e,w as ve,x as ye,y as be,z as g}from"./index-C4nTxB0s.js";var xe={class:`settings-view`},Se={class:`settings-section`},Ce={class:`section-body`},we={class:`role-bar`},Te=[`onUpdate:modelValue`],Ee=[`value`],De={key:0,class:`pool-table`},Oe={class:`mono`},ke=[`onUpdate:modelValue`,`onChange`],Ae={class:`row-actions`},je=[`disabled`,`onClick`],Me=[`onClick`],Ne=[`onClick`],Pe={key:1,class:`no-models`},Fe={key:3,class:`pool-edit`},Ie={class:`pool-edit-title`},Le={class:`form-grid`},Re={class:`field`},ze={class:`field`},Be={class:`field`},Ve={class:`field`},He=[`disabled`],Ue={id:`pool-model-options`},We=[`value`],Ge={key:0,class:`field full-width`},Ke={key:1,class:`field`},qe={class:`field`},Je={class:`field`},Ye={class:`field`},Xe={class:`field`},Ze={class:`pool-edit-actions`},Qe={class:`checkbox-label`},$e=[`disabled`],et={class:`settings-section`},tt={class:`section-title`},nt={class:`title-right`},rt={class:`section-body`},it={class:`server-control-bar`},at={class:`server-info`},ot={class:`model-chip`},st={class:`model-chip port-chip`},ct={class:`model-chip pid-chip`},lt={key:1,class:`server-idle`},ut={class:`server-actions`},dt=[`disabled`],ft=[`disabled`],pt={key:0,class:`error-msg llama-error`},mt={class:`local-models`},ht={class:`local-models-header`},gt=[`disabled`],_t={key:0,class:`no-models`},vt={key:1,class:`model-list`},yt=[`onClick`],bt={class:`model-item-name`},xt={class:`model-item-size`},St={class:`download-section`},Ct={class:`download-form`},wt=[`disabled`],Tt=[`disabled`],Et={key:0,class:`progress-wrap`},Dt={class:`progress-bar`},Ot={class:`progress-info`},kt={key:1,class:`error-msg download-error`},At={key:2,class:`done-msg`},jt={class:`form-grid`,style:{"margin-top":`16px`}},Mt={class:`field full-width`},Nt={key:0,class:`field full-width`},Pt=[`disabled`],Ft={class:`input-row`},It=[`placeholder`],Lt={key:1,class:`field full-width`},Rt=[`disabled`],zt=[`value`],Bt=[`value`],Vt={key:3,class:`field-hint warn`},Ht={key:2,class:`field`},Ut={class:`field`},Wt={class:`field`},Gt={class:`field`},Kt={class:`settings-section`},qt={class:`section-title`},Jt={class:`title-right`},Yt={class:`section-body`},Xt={class:`form-grid`},Zt={class:`field full-width`},Qt=[`disabled`],$t={class:`field full-width`},en={class:`field full-width`},tn=[`disabled`],nn=[`value`],rn={key:0,class:`field-hint warn`},an={class:`settings-section`},on={class:`section-body`},sn={class:`form-grid`},cn={class:`field full-width`},ln={class:`checkbox-label`},un={class:`field`},dn={class:`field`},fn={class:`field full-width`},pn={class:`actions`},mn=[`disabled`],hn=[`disabled`],_=h(ee({__name:`SettingsView`,setup(ee){let h=f(!1),_=f(``),v=f(null),gn=[{key:`architect`,label:`🧠 Architect(决策/终审)`},{key:`worker`,label:`🔧 Worker(实现/自验证)`},{key:`agent`,label:`🤖 Agent(智能体工具)`}],_n={local:`本地/零成本`,budget:`低价 API`,premium:`旗舰 API`},y=f({roles:{architect:``,worker:``,agent:``},entries:[]}),b=t({architect:``,worker:``,agent:``}),x=f(null),S=f(!1),C=f(``),w=f(``),T=f(!1),E=f(!1),D=f(!1),vn=f([]),yn=l(()=>y.value.entries.filter(e=>e.enabled));function bn(e){return _n[e]??e}async function xn(){try{y.value=await se(),b.architect=y.value.roles.architect||``,b.worker=y.value.roles.worker||``,b.agent=y.value.roles.agent||``}catch{}}async function Sn(){try{y.value=await ue({...b}),v.value={ok:!0,msg:`✅ 角色已更新,管线已重建`}}catch(e){v.value={ok:!1,msg:`❌ 角色指派失败:`+(e?.response?.data?.detail||e?.message||e)},await xn()}setTimeout(()=>v.value=null,3e3)}function Cn(){S.value=!0,x.value={name:``,tier:`budget`,backend:`openai`,base_url:``,model:``,api_key:``,price_in:.1,price_out:.1,temperature:.3,max_tokens:4096,enabled:!0}}function wn(e){S.value=!1,x.value={...e,api_key:``}}async function Tn(){if(x.value){E.value=!0;try{y.value=await ye(x.value),x.value=null,v.value={ok:!0,msg:`✅ 模型条目已保存`}}catch(e){v.value={ok:!1,msg:`❌ 保存失败:`+(e?.response?.data?.detail||e?.message||e)}}finally{E.value=!1,setTimeout(()=>v.value=null,3e3)}}}async function En(e){confirm(`删除模型条目「${e.name}」?相关角色指派会一并清空。`)&&(y.value=await ge(e.id))}async function Dn(e){y.value=await ye({...e,api_key:``})}async function On(e){C.value=e.id,w.value=``;try{let t=await be(e.id);T.value=t.ok,w.value=t.ok?`✅ ${e.name} 连接正常`:`❌ ${e.name}: ${t.detail||`连接失败`}`}catch(e){T.value=!1,w.value=`❌ `+(e?.message||String(e))}finally{C.value=``}}async function kn(){if(x.value){D.value=!0;try{let e;e=!S.value&&x.value.id?await ce(x.value.id):x.value.backend===`mock`?{models:[{id:`mock`,name:`mock(内置模拟)`}]}:await _e(x.value.backend||`openai`,x.value.base_url||``,x.value.api_key||``),e.error?(T.value=!1,w.value=e.error,vn.value=[]):vn.value=e.models||[]}finally{D.value=!1}}}let O=f({running:!1}),k=f(!1),A=f(!1),j=f([]),M=f(!1),N=f(``),P=f(!1),F=f(``),I=f(0),L=f(``),An=f(``),jn=f(``),R=f(null),Mn=f(``),z=f(``),Nn=f(!1),B=null,V=f([]),H=f([]),U=t({worker:!1,architect:!1}),W=t({worker:``,architect:``}),G=t({worker:!1,architect:!1}),K=t({worker:null,architect:null}),q=t({worker:``,architect:``}),J=[{id:`deepseek-v4-flash`,name:`deepseek-v4-flash(推荐)`},{id:`deepseek-v4-pro`,name:`deepseek-v4-pro(推理模式)`},{id:`deepseek-coder`,name:`deepseek-coder`},{id:`gpt-4o-mini`,name:`gpt-4o-mini`},{id:`gpt-4o`,name:`gpt-4o`},{id:`claude-3-5-sonnet-20241022`,name:`claude-3.5-sonnet`},{id:`qwen-plus`,name:`qwen-plus`},{id:`qwen-max`,name:`qwen-max`}],Y=f(null),X=t({worker:{backend:`mock`,model:``,base_url:``,port:8901,temperature:.3,max_fix_attempts:2,code_timeout_s:10},architect:{model:`deepseek-v4-flash`,base_url:`https://api.deepseek.com`,api_key:``},pipeline:{fast_path:!0,rounds_cap:6,api_token_cap:8e3,breach_policy:`architect_do`}}),Pn=l(()=>X.worker.backend===`llama_server`?`http://127.0.0.1:${X.worker.port}/v1`:`http://127.0.0.1:11434/v1`),Fn=l(()=>Y.value?JSON.stringify(X)!==JSON.stringify(Y.value):!1);function In(e){return e>=1e9?(e/1e9).toFixed(1)+` GB`:e>=1e6?(e/1e6).toFixed(1)+` MB`:e>=1e3?(e/1e3).toFixed(0)+` KB`:e+` B`}async function Ln(){try{let e=await me();return O.value=e,e}catch{return O.value={running:!1},{running:!1}}}async function Z(){M.value=!0;try{j.value=await fe()}catch{j.value=[]}finally{M.value=!1}}async function Rn(){if(!X.worker.model){alert(`请先从上方「本地模型」列表中选择一个模型,再启动服务`);return}k.value=!0;try{let e=await le(X.worker.model,X.worker.port,99,4096);O.value=e,e.running&&(X.worker.backend=`llama_server`,X.worker.base_url=`http://127.0.0.1:${e.port}/v1`,await Z(),await Q())}catch(e){O.value={running:!1,error:String(e?.message||e)}}finally{k.value=!1}}async function zn(){A.value=!0;try{await ie(),O.value={running:!1}}catch{O.value={running:!1}}finally{A.value=!1}}function Bn(e){N.value=e.id,X.worker.model=e.id}function Vn(){F.value.trim()&&(z.value=``,Nn.value=!1,I.value=0,L.value=``,An.value=``,jn.value=`0 B`,R.value=null,P.value=!0,de(F.value.trim()).then(e=>{if(e.error){z.value=e.error,P.value=!1;return}if(e.done){Hn(e);return}B=ne(F.value.trim()),B.subscribe({onProgress:Hn,onDone:e=>{Hn(e),B?.close(),B=null}})}))}function Hn(e){I.value=Math.round(e.progress_pct),L.value=e.speed,An.value=e.eta,jn.value=In(e.downloaded_bytes),R.value=e.total_bytes?In(e.total_bytes):null,Mn.value=e.dest,e.done&&(P.value=!1,e.error?z.value=e.error:(Nn.value=!0,Z()))}async function Un(){W.worker=``,V.value=[],X.worker.backend!==`mock`&&await Q()}async function Wn(){W.architect=``,H.value=[],await Gn()}async function Q(){if(X.worker.backend!==`mock`){U.worker=!0,W.worker=``;try{let e=await _e(X.worker.backend,X.worker.base_url,``);e.error?X.worker.backend===`llama_server`?W.worker=`💡 llama-server 未启动,请先点击上方「启动服务」`:W.worker=e.error||`连接失败`:V.value=e.models||[]}catch{W.worker=`获取模型列表失败`}finally{U.worker=!1}}}async function Gn(){if(U.architect=!0,W.architect=``,!X.architect.base_url){H.value=[...J],U.architect=!1;return}try{let e=await _e(`openai`,X.architect.base_url,X.architect.api_key||``);e.error?(W.architect=e.error,H.value=[...J]):(e.models||[]).length>0?H.value=e.models||[]:H.value=[...J]}catch{W.architect=`获取模型列表失败`,H.value=[...J]}finally{U.architect=!1}}async function Kn(){if(X.worker.backend!==`mock`){G.worker=!0,K.worker=null,q.worker=``;try{let e=await he(X.worker.backend,X.worker.base_url,``);K.worker=e.ok,e.ok||(q.worker=e.detail||`连接失败`)}catch(e){K.worker=!1,q.worker=e?.message||String(e)}finally{G.worker=!1}}}async function qn(){if(!X.architect.base_url){K.architect=!1,q.architect=`请先填写 API 端点`;return}G.architect=!0,K.architect=null,q.architect=``;try{let e=await he(`openai`,X.architect.base_url,X.architect.api_key||``);K.architect=e.ok,e.ok||(q.architect=e.detail||`连接失败`)}catch(e){K.architect=!1,q.architect=e?.message||String(e)}finally{G.architect=!1}}function $(e,t){if(t)for(let n of Object.keys(e))t[n]!==void 0&&(typeof e[n]==`object`&&typeof t[n]==`object`&&e[n]!==null?$(e[n],t[n]):e[n]=t[n])}async function Jn(){_.value=``;try{let[e,t]=await Promise.all([ae(),Ln()]);O.value=t,xn(),e.worker&&$(X.worker,e.worker),e.architect&&$(X.architect,e.architect),e.pipeline&&$(X.pipeline,e.pipeline),Y.value=JSON.parse(JSON.stringify(X)),await Z(),H.value=[...J],X.worker.backend!==`mock`&&Q(),X.worker.model&&(N.value=X.worker.model)}catch(e){_.value=`加载配置失败:`+(e?.message||String(e))}}async function Yn(){h.value=!0,v.value=null;try{let e=JSON.parse(JSON.stringify(X));e.architect.api_key||delete e.architect.api_key;let t=await oe(e);$(X.worker,t.worker||{}),$(X.architect,t.architect||{}),$(X.pipeline,t.pipeline||{}),Y.value=JSON.parse(JSON.stringify(X)),v.value={ok:!0,msg:`✅ 设置已保存,管线已重建`},setTimeout(()=>v.value=null,3e3)}catch(e){v.value={ok:!1,msg:`❌ 保存失败:`+(e?.message||String(e))}}finally{h.value=!1}}async function Xn(){if(confirm(`确定恢复默认设置?`)){h.value=!0,v.value=null;try{let e=await pe();$(X.worker,e.worker||{}),$(X.architect,e.architect||{}),$(X.pipeline,e.pipeline||{}),Y.value=JSON.parse(JSON.stringify(X)),V.value=[],H.value=[...J],K.worker=null,K.architect=null,v.value={ok:!0,msg:`✅ 已恢复默认设置`},setTimeout(()=>v.value=null,3e3)}catch(e){v.value={ok:!1,msg:`❌ 重置失败:`+(e?.message||String(e))}}finally{h.value=!1}}}return te(Jn),i(()=>{B?.close()}),(t,i)=>(o(),p(`div`,xe,[i[68]||=m(`header`,{class:`settings-header`},[m(`h2`,null,`⚙️ 模型设置`),m(`p`,{class:`subtitle`},`配置小模型(Worker)与大模型(Architect)参数,保存后实时生效`)],-1),m(`section`,Se,[m(`div`,{class:`section-title`},[i[29]||=m(`span`,null,`🗄️ 模型池(多价位异构模型)`,-1),m(`div`,{class:`title-right`},[m(`button`,{class:`btn-refresh`,onClick:Cn},`+ 添加模型`)])]),m(`div`,Ce,[m(`div`,we,[(o(),p(n,null,s(gn,e=>m(`div`,{class:`role-item`,key:e.key},[m(`label`,null,a(e.label),1),g(m(`select`,{"onUpdate:modelValue":t=>b[e.key]=t,onChange:Sn},[i[30]||=m(`option`,{value:``},`经典设置(下方表单)`,-1),(o(!0),p(n,null,s(yn.value,e=>(o(),p(`option`,{key:e.id,value:e.id},a(e.name),9,Ee))),128))],40,Te),[[u,b[e.key]]])])),64))]),i[44]||=m(`p`,{class:`role-hint`},` 按价位组织模型:本地 llama.cpp 为零成本档,低价 API 干批量活,旗舰 API 做决策—— 随时切换,保存后管线自动重建。 `,-1),y.value.entries.length?(o(),p(`table`,De,[i[31]||=m(`thead`,null,[m(`tr`,null,[m(`th`,null,`名称`),m(`th`,null,`档位`),m(`th`,null,`后端`),m(`th`,null,`模型`),m(`th`,null,`单价 $/1M(入/出)`),m(`th`,null,`启用`),m(`th`,null,`操作`)])],-1),m(`tbody`,null,[(o(!0),p(n,null,s(y.value.entries,e=>(o(),p(`tr`,{key:e.id},[m(`td`,null,a(e.name),1),m(`td`,null,[m(`span`,{class:d([`tier-badge`,`tier-`+e.tier])},a(bn(e.tier)),3)]),m(`td`,null,a(e.backend),1),m(`td`,Oe,a(e.model||`—`),1),m(`td`,null,a(e.price_in)+` / `+a(e.price_out),1),m(`td`,null,[g(m(`input`,{type:`checkbox`,"onUpdate:modelValue":t=>e.enabled=t,onChange:t=>Dn(e)},null,40,ke),[[ve,e.enabled]])]),m(`td`,Ae,[m(`button`,{class:`btn-refresh`,disabled:C.value===e.id,onClick:t=>On(e)},a(C.value===e.id?`…`:`测试`),9,je),m(`button`,{class:`btn-refresh`,onClick:t=>wn(e)},`编辑`,8,Me),m(`button`,{class:`btn-refresh danger`,onClick:t=>En(e)},`删除`,8,Ne)])]))),128))])])):(o(),p(`div`,Pe,` 池为空——可添加本地 llama.cpp 模型(零成本档)、低价 API、旗舰 API 等,随时指派给 Architect / Worker / Agent。 `)),w.value?(o(),p(`div`,{key:2,class:d([`field-hint`,T.value?`ok`:`warn`])},a(w.value),3)):e(``,!0),x.value?(o(),p(`div`,Fe,[m(`div`,Ie,a(S.value?`添加模型`:`编辑模型`),1),m(`div`,Le,[m(`div`,Re,[i[32]||=m(`label`,null,`名称`,-1),g(m(`input`,{"onUpdate:modelValue":i[0]||=e=>x.value.name=e,placeholder:`如:DeepSeek 旗舰 / 本地 Qwen4B`},null,512),[[r,x.value.name]])]),m(`div`,ze,[i[34]||=m(`label`,null,`档位`,-1),g(m(`select`,{"onUpdate:modelValue":i[1]||=e=>x.value.tier=e},[...i[33]||=[m(`option`,{value:`local`},`local(本地 / 零边际成本)`,-1),m(`option`,{value:`budget`},`budget(低价 API)`,-1),m(`option`,{value:`premium`},`premium(旗舰 API)`,-1)]],512),[[u,x.value.tier]])]),m(`div`,Be,[i[36]||=m(`label`,null,`后端`,-1),g(m(`select`,{"onUpdate:modelValue":i[2]||=e=>x.value.backend=e},[...i[35]||=[m(`option`,{value:`openai`},`openai / 兼容 API`,-1),m(`option`,{value:`llama_server`},`llama_server(内置本地)`,-1),m(`option`,{value:`mock`},`mock(模拟)`,-1)]],512),[[u,x.value.backend]])]),m(`div`,Ve,[m(`label`,null,[i[37]||=c(` 模型 `,-1),m(`button`,{class:`btn-refresh btn-inline`,disabled:D.value,onClick:kn},a(D.value?`…`:`🔄 刷新列表`),9,He)]),g(m(`input`,{"onUpdate:modelValue":i[3]||=e=>x.value.model=e,list:`pool-model-options`,placeholder:`模型名`},null,512),[[r,x.value.model]]),m(`datalist`,Ue,[(o(!0),p(n,null,s(vn.value,e=>(o(),p(`option`,{key:e.id,value:e.id},null,8,We))),128))])]),x.value.backend===`mock`?e(``,!0):(o(),p(`div`,Ge,[i[38]||=m(`label`,null,`API 地址`,-1),g(m(`input`,{"onUpdate:modelValue":i[4]||=e=>x.value.base_url=e,placeholder:`https://api.deepseek.com 或 http://127.0.0.1:8901/v1`},null,512),[[r,x.value.base_url]])])),x.value.backend===`openai`?(o(),p(`div`,Ke,[m(`label`,null,`API Key`+a(x.value.api_key_set?`(已设置,留空保留)`:``),1),g(m(`input`,{"onUpdate:modelValue":i[5]||=e=>x.value.api_key=e,type:`password`,autocomplete:`off`,placeholder:`sk-…`},null,512),[[r,x.value.api_key]])])):e(``,!0),m(`div`,qe,[i[39]||=m(`label`,null,`输入单价 $/1M`,-1),g(m(`input`,{"onUpdate:modelValue":i[6]||=e=>x.value.price_in=e,type:`number`,min:`0`,step:`0.01`},null,512),[[r,x.value.price_in,void 0,{number:!0}]])]),m(`div`,Je,[i[40]||=m(`label`,null,`输出单价 $/1M`,-1),g(m(`input`,{"onUpdate:modelValue":i[7]||=e=>x.value.price_out=e,type:`number`,min:`0`,step:`0.01`},null,512),[[r,x.value.price_out,void 0,{number:!0}]])]),m(`div`,Ye,[i[41]||=m(`label`,null,`Temperature`,-1),g(m(`input`,{"onUpdate:modelValue":i[8]||=e=>x.value.temperature=e,type:`number`,min:`0`,max:`2`,step:`0.05`},null,512),[[r,x.value.temperature,void 0,{number:!0}]])]),m(`div`,Xe,[i[42]||=m(`label`,null,`Max Tokens`,-1),g(m(`input`,{"onUpdate:modelValue":i[9]||=e=>x.value.max_tokens=e,type:`number`,min:`256`,max:`32768`},null,512),[[r,x.value.max_tokens,void 0,{number:!0}]])])]),m(`div`,Ze,[m(`label`,Qe,[g(m(`input`,{type:`checkbox`,"onUpdate:modelValue":i[10]||=e=>x.value.enabled=e},null,512),[[ve,x.value.enabled]]),i[43]||=c(` 启用 `,-1)]),m(`button`,{class:`btn-primary`,disabled:E.value,onClick:Tn},a(E.value?`保存中…`:`保存条目`),9,$e),m(`button`,{class:`btn-secondary`,onClick:i[11]||=e=>x.value=null},`取消`)])])):e(``,!0)])]),m(`section`,et,[m(`div`,tt,[i[45]||=m(`span`,null,`🔧 小模型 (Worker · 本地 llama.cpp)`,-1),m(`div`,nt,[m(`span`,{class:d([`server-badge`,O.value.running?`badge-running`:`badge-stopped`])},a(O.value.running?`● 运行中`:`○ 已停止`),3)])]),m(`div`,rt,[m(`div`,it,[m(`div`,at,[O.value.running?(o(),p(n,{key:0},[m(`span`,ot,a(O.value.model?.split(/[/\\]/).pop()),1),m(`span`,st,`:`+a(O.value.port),1),m(`span`,ct,`PID `+a(O.value.pid),1)],64)):(o(),p(`span`,lt,`llama-server 未运行 — 点击「启动服务」即可启用`))]),m(`div`,ut,[O.value.running?(o(),p(`button`,{key:1,class:`btn-stop`,disabled:k.value||A.value,onClick:zn},a(A.value?`停止中…`:`■ 停止服务`),9,ft)):(o(),p(`button`,{key:0,class:`btn-start`,disabled:k.value||A.value,onClick:Rn},a(k.value?`启动中…`:`▶ 启动服务`),9,dt))])]),O.value.error?(o(),p(`div`,pt,a(O.value.error),1)):e(``,!0),m(`div`,mt,[m(`div`,ht,[m(`span`,null,`📁 本地模型 (`+a(j.value.length)+`)`,1),m(`button`,{class:`btn-refresh`,disabled:M.value,onClick:Z},a(M.value?`…`:`🔄`),9,gt)]),j.value.length===0&&!M.value?(o(),p(`div`,_t,` 暂无本地模型,请下载或放置 .gguf 文件到 models/ 目录 `)):(o(),p(`div`,vt,[(o(!0),p(n,null,s(j.value,e=>(o(),p(`div`,{key:e.id,class:d([`model-item`,N.value===e.id?`selected`:``]),onClick:t=>Bn(e)},[m(`span`,bt,a(e.name),1),m(`span`,xt,a(e.size_mb)+` MB`,1)],10,yt))),128))]))]),m(`div`,St,[i[46]||=m(`div`,{class:`download-header`},`⬇️ 下载模型`,-1),m(`div`,Ct,[g(m(`input`,{"onUpdate:modelValue":i[12]||=e=>F.value=e,class:`download-input`,placeholder:`HuggingFace 路径或直链,如:Qwen/Qwen3-4B-GGUF/Qwen3-4B-Q4_K_M.gguf`,disabled:P.value},null,8,wt),[[r,F.value]]),m(`button`,{class:`btn-download`,disabled:P.value||!F.value.trim(),onClick:Vn},a(P.value?I.value+`%`:`下载`),9,Tt)]),P.value?(o(),p(`div`,Et,[m(`div`,Dt,[m(`div`,{class:`progress-fill`,style:re({width:I.value+`%`})},null,4)]),m(`div`,Ot,[m(`span`,null,a(L.value),1),m(`span`,null,a(An.value),1),m(`span`,null,a(jn.value)+`/`+a(R.value||`?`),1)])])):e(``,!0),z.value?(o(),p(`div`,kt,a(z.value),1)):e(``,!0),Nn.value&&!z.value?(o(),p(`div`,At,` ✅ 下载完成:`+a(Mn.value),1)):e(``,!0)]),m(`div`,jt,[m(`div`,Mt,[i[48]||=m(`label`,null,`后端类型`,-1),g(m(`select`,{"onUpdate:modelValue":i[13]||=e=>X.worker.backend=e,onChange:Un},[...i[47]||=[m(`option`,{value:`mock`},`mock(模拟,不真实调用)`,-1),m(`option`,{value:`llama_server`},`llama_server(内置本地服务,推荐)`,-1),m(`option`,{value:`openai`},`openai / 兼容 API(Ollama 等)`,-1)]],544),[[u,X.worker.backend]])]),X.worker.backend===`mock`?e(``,!0):(o(),p(`div`,Nt,[m(`label`,null,[i[49]||=c(` API 地址 `,-1),m(`button`,{class:`btn-refresh btn-inline`,disabled:G.worker,onClick:Kn},a(G.worker?`…`:`🔗 连接测试`),9,Pt)]),m(`div`,Ft,[g(m(`input`,{"onUpdate:modelValue":i[14]||=e=>X.worker.base_url=e,placeholder:Pn.value,onBlur:Un},null,40,It),[[r,X.worker.base_url]]),K.worker===null?e(``,!0):(o(),p(`span`,{key:0,class:d([`ping-badge`,K.worker?`ping-ok`:`ping-err`])},a(K.worker?`✅ 正常`:`❌ `+q.worker),3))])])),X.worker.backend===`mock`?e(``,!0):(o(),p(`div`,Lt,[m(`label`,null,[i[50]||=c(` 模型 `,-1),m(`button`,{class:`btn-refresh btn-inline`,disabled:U.worker,onClick:Q},a(U.worker?`…`:`🔄 刷新列表`),9,Rt)]),X.worker.backend===`llama_server`?g((o(),p(`select`,{key:0,"onUpdate:modelValue":i[15]||=e=>X.worker.model=e},[i[51]||=m(`option`,{value:``,disabled:``},`—— 从本地模型中选择 ——`,-1),(o(!0),p(n,null,s(j.value,e=>(o(),p(`option`,{key:e.id,value:e.id},a(e.name),9,zt))),128))],512)),[[u,X.worker.model]]):X.worker.backend===`openai`?g((o(),p(`select`,{key:1,"onUpdate:modelValue":i[16]||=e=>X.worker.model=e},[i[52]||=m(`option`,{value:``,disabled:``},`—— 从 API 获取 ——`,-1),(o(!0),p(n,null,s(V.value,e=>(o(),p(`option`,{key:e.id,value:e.id},a(e.name||e.id),9,Bt))),128))],512)),[[u,X.worker.model]]):g((o(),p(`input`,{key:2,"onUpdate:modelValue":i[17]||=e=>X.worker.model=e,placeholder:`输入模型名称…`},null,512)),[[r,X.worker.model]]),W.worker?(o(),p(`span`,Vt,a(W.worker),1)):e(``,!0)])),X.worker.backend===`llama_server`?(o(),p(`div`,Ht,[i[53]||=m(`label`,null,`端口`,-1),g(m(`input`,{"onUpdate:modelValue":i[18]||=e=>X.worker.port=e,type:`number`,min:`1`,max:`65535`},null,512),[[r,X.worker.port,void 0,{number:!0}]])])):e(``,!0),m(`div`,Ut,[i[54]||=m(`label`,null,`Temperature`,-1),g(m(`input`,{"onUpdate:modelValue":i[19]||=e=>X.worker.temperature=e,type:`number`,min:`0`,max:`2`,step:`0.05`},null,512),[[r,X.worker.temperature,void 0,{number:!0}]])]),m(`div`,Wt,[i[55]||=m(`label`,null,`最大修复轮次`,-1),g(m(`input`,{"onUpdate:modelValue":i[20]||=e=>X.worker.max_fix_attempts=e,type:`number`,min:`0`,max:`10`},null,512),[[r,X.worker.max_fix_attempts,void 0,{number:!0}]])]),m(`div`,Gt,[i[56]||=m(`label`,null,`代码执行超时 (秒)`,-1),g(m(`input`,{"onUpdate:modelValue":i[21]||=e=>X.worker.code_timeout_s=e,type:`number`,min:`1`,max:`300`},null,512),[[r,X.worker.code_timeout_s,void 0,{number:!0}]])])])])]),m(`section`,Kt,[m(`div`,qt,[i[57]||=m(`span`,null,`🧠 大模型 (Architect · API 云端)`,-1),m(`div`,Jt,[K.architect===null?e(``,!0):(o(),p(`span`,{key:0,class:d([`ping-badge`,K.architect?`ping-ok`:`ping-err`])},a(K.architect?`✅ 已连接`:`❌ `+q.architect),3))])]),m(`div`,Yt,[m(`div`,Xt,[m(`div`,Zt,[m(`label`,null,[i[58]||=c(` API 端点 `,-1),m(`button`,{class:`btn-refresh btn-inline`,disabled:G.architect,onClick:qn},a(G.architect?`…`:`🔗 连接测试`),9,Qt)]),g(m(`input`,{"onUpdate:modelValue":i[22]||=e=>X.architect.base_url=e,placeholder:`https://api.deepseek.com`,onBlur:Wn},null,544),[[r,X.architect.base_url]])]),m(`div`,$t,[i[59]||=m(`label`,null,`API Key(保存在本地 config/settings.json,不入代码库;优先级高于 .env)`,-1),g(m(`input`,{"onUpdate:modelValue":i[23]||=e=>X.architect.api_key=e,type:`password`,placeholder:`sk-xxxxxxxxxxxxxxxx`,autocomplete:`off`},null,512),[[r,X.architect.api_key]])]),m(`div`,en,[m(`label`,null,[i[60]||=c(` 模型 `,-1),m(`button`,{class:`btn-refresh btn-inline`,disabled:U.architect,onClick:Gn},a(U.architect?`…`:`🔄 刷新列表`),9,tn)]),g(m(`select`,{"onUpdate:modelValue":i[24]||=e=>X.architect.model=e,class:`model-select`},[i[61]||=m(`option`,{value:``,disabled:``},`—— 从 API 获取模型列表 ——`,-1),(o(!0),p(n,null,s(H.value,e=>(o(),p(`option`,{key:e.id,value:e.id},a(e.name||e.id),9,nn))),128))],512),[[u,X.architect.model]]),W.architect?(o(),p(`span`,rn,a(W.architect),1)):e(``,!0)])])])]),m(`section`,an,[i[67]||=m(`div`,{class:`section-title`},[m(`span`,null,`⚙️ 协作管线 (Pipeline)`)],-1),m(`div`,on,[m(`div`,sn,[m(`div`,cn,[m(`label`,ln,[g(m(`input`,{type:`checkbox`,"onUpdate:modelValue":i[25]||=e=>X.pipeline.fast_path=e},null,512),[[ve,X.pipeline.fast_path]]),i[62]||=c(` 启用快路径(简单查询跳过本地模型,直接由大模型回答,省 API 费用) `,-1)])]),m(`div`,un,[i[63]||=m(`label`,null,`最大协作轮次`,-1),g(m(`input`,{"onUpdate:modelValue":i[26]||=e=>X.pipeline.rounds_cap=e,type:`number`,min:`1`,max:`20`},null,512),[[r,X.pipeline.rounds_cap,void 0,{number:!0}]])]),m(`div`,dn,[i[64]||=m(`label`,null,`API Token 熔断上限`,-1),g(m(`input`,{"onUpdate:modelValue":i[27]||=e=>X.pipeline.api_token_cap=e,type:`number`,min:`100`,max:`100000`},null,512),[[r,X.pipeline.api_token_cap,void 0,{number:!0}]])]),m(`div`,fn,[i[66]||=m(`label`,null,`超限策略`,-1),g(m(`select`,{"onUpdate:modelValue":i[28]||=e=>X.pipeline.breach_policy=e},[...i[65]||=[m(`option`,{value:`architect_do`},`architect_do(超限交给大模型处理,推荐)`,-1),m(`option`,{value:`local_only`},`local_only(强制留在本地小模型)`,-1)]],512),[[u,X.pipeline.breach_policy]])])])])]),m(`div`,pn,[m(`button`,{class:`btn-primary`,disabled:h.value||!Fn.value,onClick:Yn},a(h.value?`保存中…`:`💾 保存设置`),9,mn),m(`button`,{class:`btn-secondary`,disabled:h.value,onClick:Xn},` 🔁 恢复默认 `,8,hn)]),v.value?(o(),p(`div`,{key:0,class:d([`toast`,v.value.ok?`toast-ok`:`toast-err`])},a(v.value.msg),3)):e(``,!0)]))}}),[[`__scopeId`,`data-v-7b21dc2a`]]);export{_ as default}; \ No newline at end of file diff --git a/gateway/static/assets/index-C8Za1808.js b/gateway/static/assets/index-C4nTxB0s.js similarity index 94% rename from gateway/static/assets/index-C8Za1808.js rename to gateway/static/assets/index-C4nTxB0s.js index 205f7d7..38b6d3f 100644 --- a/gateway/static/assets/index-C8Za1808.js +++ b/gateway/static/assets/index-C4nTxB0s.js @@ -1,4 +1,4 @@ -const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/CollaborationView-DHMzJVru.js","assets/CollaborationView-CfueFZKz.css","assets/ReviewView-Cm34POvM.js","assets/ReviewView-9PXrv1jO.css","assets/MetricsView-BMWM9u4c.js","assets/MetricsView-JgJGH4Og.css","assets/SettingsView-DMG9EXA2.js","assets/SettingsView-DWdd6um_.css"])))=>i.map(i=>d[i]); +const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/CollaborationView-BzBpxfYH.js","assets/CollaborationView-CfueFZKz.css","assets/ReviewView-wY3DEmtF.js","assets/ReviewView-9PXrv1jO.css","assets/MetricsView-Dm03Grst.js","assets/MetricsView-JgJGH4Og.css","assets/SettingsView-B4tzUqnq.js","assets/SettingsView-DWdd6um_.css"])))=>i.map(i=>d[i]); var e=Object.defineProperty,t=(t,n)=>{let r={};for(var i in t)e(r,i,{get:t[i],enumerable:!0});return n||e(r,Symbol.toStringTag,{value:`Module`}),r};(function(){let e=document.createElement(`link`).relList;if(e&&e.supports&&e.supports(`modulepreload`))return;for(let e of document.querySelectorAll(`link[rel="modulepreload"]`))n(e);new MutationObserver(e=>{for(let t of e)if(t.type===`childList`)for(let e of t.addedNodes)e.tagName===`LINK`&&e.rel===`modulepreload`&&n(e)}).observe(document,{childList:!0,subtree:!0});function t(e){let t={};return e.integrity&&(t.integrity=e.integrity),e.referrerPolicy&&(t.referrerPolicy=e.referrerPolicy),t.credentials=e.crossOrigin===`use-credentials`?`include`:e.crossOrigin===`anonymous`?`omit`:`same-origin`,t}function n(e){if(e.ep)return;e.ep=!0;let n=t(e);fetch(e.href,n)}})();function n(e){let t=Object.create(null);for(let n of e.split(`,`))t[n]=1;return e=>e in t}var r={},i=[],a=()=>{},o=()=>!1,s=e=>e.charCodeAt(0)===111&&e.charCodeAt(1)===110&&(e.charCodeAt(2)>122||e.charCodeAt(2)<97),c=e=>e.startsWith(`onUpdate:`),l=Object.assign,u=(e,t)=>{let n=e.indexOf(t);n>-1&&e.splice(n,1)},d=Object.prototype.hasOwnProperty,f=(e,t)=>d.call(e,t),p=Array.isArray,m=e=>C(e)===`[object Map]`,h=e=>C(e)===`[object Set]`,g=e=>C(e)===`[object Date]`,_=e=>typeof e==`function`,v=e=>typeof e==`string`,y=e=>typeof e==`symbol`,b=e=>typeof e==`object`&&!!e,x=e=>(b(e)||_(e))&&_(e.then)&&_(e.catch),S=Object.prototype.toString,C=e=>S.call(e),w=e=>C(e).slice(8,-1),ee=e=>C(e)===`[object Object]`,te=e=>v(e)&&e!==`NaN`&&e[0]!==`-`&&``+parseInt(e,10)===e,ne=n(`,key,ref,ref_for,ref_key,onVnodeBeforeMount,onVnodeMounted,onVnodeBeforeUpdate,onVnodeUpdated,onVnodeBeforeUnmount,onVnodeUnmounted`),re=e=>{let t=Object.create(null);return(n=>t[n]||(t[n]=e(n)))},ie=/-\w/g,T=re(e=>e.replace(ie,e=>e.slice(1).toUpperCase())),E=/\B([A-Z])/g,D=re(e=>e.replace(E,`-$1`).toLowerCase()),O=re(e=>e.charAt(0).toUpperCase()+e.slice(1)),ae=re(e=>e?`on${O(e)}`:``),k=(e,t)=>!Object.is(e,t),oe=(e,...t)=>{for(let n=0;n