diff --git a/config/config.yaml b/config/config.yaml index 62a2ed1..8e95342 100644 --- a/config/config.yaml +++ b/config/config.yaml @@ -88,8 +88,8 @@ runtime: cpu: {ngl: 0, ctx: 8192} architect: # 大模型(API) - model: deepseek-chat - base_url: https://api.deepseek.com/v1 + model: deepseek-v4-flash + base_url: https://api.deepseek.com api_key_env: DEEPSEEK_API_KEY temperature: 0.2 timeout_s: 60 diff --git a/gateway/jobs.py b/gateway/jobs.py new file mode 100644 index 0000000..016329a --- /dev/null +++ b/gateway/jobs.py @@ -0,0 +1,328 @@ +"""异步后台任务注册表(T1:后端异步化核心)。 + +设计原则(对齐 v3 方案 D1-D6): +- asyncio 原生,无 Celery/Redis/外部队列 +- 每个 request_id -> TaskInfo(状态/开始时间/结果或错误) +- 任务写 runs/{id}/workspace.json,SSE 生成器只读该文件(D3:不改 pipeline) +- 定期清理已完成任务(防内存泄漏) +""" +from __future__ import annotations + +import asyncio +import json +import time +from dataclasses import dataclass, field +from pathlib import Path +from typing import Any, AsyncGenerator, Dict, Optional + +# runs/ 目录(与 pipeline.py 默认一致) +RUNS_DIR = Path("runs") + +# 任务状态 +STATE_PENDING = "pending" +STATE_RUNNING = "running" +STATE_DONE = "done" +STATE_FAILED = "failed" + + +@dataclass +class TaskInfo: + """一个后台任务的状态快照。""" + request_id: str + state: str = STATE_PENDING # pending | running | done | failed + started_at: float = 0.0 # time.time() + finished_at: float = 0.0 # time.time()(done/failed 时) + error: Optional[str] = None # failed 时错误信息 + # PipelineResult 字段(done 时填充) + response: Optional[str] = None + status: Optional[str] = None # done | fast_path | escalated | failed + fast_path: bool = False + rounds_used: int = 0 + api_input_tokens: int = 0 + api_output_tokens: int = 0 + cost_est: float = 0.0 + model_used: Optional[str] = None + latency_ms: float = 0.0 + workspace_path: Optional[str] = None + error_detail: Optional[str] = None + route: list = field(default_factory=list) + + def to_dict(self) -> Dict[str, Any]: + return { + "request_id": self.request_id, + "state": self.state, + "started_at": self.started_at, + "finished_at": self.finished_at, + "error": self.error, + "response": self.response, + "status": self.status, + "fast_path": self.fast_path, + "rounds_used": self.rounds_used, + "api_input_tokens": self.api_input_tokens, + "api_output_tokens": self.api_output_tokens, + "cost_est": self.cost_est, + "model_used": self.model_used, + "latency_ms": round(self.latency_ms, 2), + "workspace_path": self.workspace_path, + "route": self.route, + "error_detail": self.error_detail, + } + + def to_result_event(self) -> Dict[str, Any]: + """终态 SSE result 事件 payload。""" + return { + "type": "result", + "request_id": self.request_id, + "response": self.response or "", + "status": self.status, + "fast_path": self.fast_path, + "rounds_used": self.rounds_used, + "api_input_tokens": self.api_input_tokens, + "api_output_tokens": self.api_output_tokens, + "cost_est": self.cost_est, + "model_used": self.model_used, + "latency_ms": round(self.latency_ms, 2), + "route": self.route, + "error": self.error, + } + + +class JobStore: + """asyncio 后台任务注册表。 + + 线程安全(asyncio 事件循环单线程,不需要额外锁)。 + 任务由 register() 注册、由 _task_done() 填充结果。 + SSE 生成器调用 watch_file() 轮询 workspace.json 变化。 + """ + + def __init__(self, max_age_seconds: float = 3600.0, max_running: int = 50): + """ + Args: + max_age_seconds: 已完成任务在内存中保留时间(秒),超时后自动清理 + max_running: 最大同时运行任务数,超出后拒绝新任务 + """ + self._tasks: Dict[str, TaskInfo] = {} + self._asyncio_tasks: Dict[str, "asyncio.Task[None]"] = {} # request_id -> asyncio.Task + self._cancel_events: Dict[str, asyncio.Event] = {} # request_id -> cancel Event + self.max_age_seconds = max_age_seconds + self.max_running = max_running + self._poll_interval = 0.3 # workspace.json 轮询间隔(秒) + + # ───────────────────────────────────────────────────────────────── + # 公共 API + # ───────────────────────────────────────────────────────────────── + + def register(self, request_id: str) -> tuple[bool, str]: + """注册一个 pending 任务。返回 (True, "") 成功,(False, reason) 容量满。""" + running = [t for t in self._tasks.values() if t.state == STATE_RUNNING] + if len(running) >= self.max_running: + return False, f"同时运行任务已达上限 {self.max_running},请稍后重试" + if request_id in self._tasks: + return False, f"任务 {request_id} 已存在" + self._tasks[request_id] = TaskInfo( + request_id=request_id, + state=STATE_PENDING, + started_at=time.time(), + ) + return True, "" + + def get(self, request_id: str) -> Optional[TaskInfo]: + """查询任务状态。""" + return self._tasks.get(request_id) + + def list_all(self) -> Dict[str, TaskInfo]: + """列出所有任务(含已完成)。""" + return dict(self._tasks) + + def list_running(self) -> Dict[str, TaskInfo]: + """只列出运行中任务。""" + return {k: v for k, v in self._tasks.items() if v.state == STATE_RUNNING} + + def submit( + self, + request_id: str, + coro, # type: asyncio.coroutine + ) -> "asyncio.Task[None]": + """提交协程到后台运行;内部注册 asyncio.Task。""" + task = asyncio.create_task(self._run_wrapper(request_id, coro)) + self._asyncio_tasks[request_id] = task + return task + + def new_cancel(self, request_id: str) -> asyncio.Event: + """为 SSE 连接创建一个取消事件。""" + evt = asyncio.Event() + self._cancel_events[request_id] = evt + return evt + + def get_cancel(self, request_id: str) -> Optional[asyncio.Event]: + """获取已有取消事件。""" + return self._cancel_events.get(request_id) + + # ───────────────────────────────────────────────────────────────── + # 内部:任务运行包装 + # ───────────────────────────────────────────────────────────────── + + async def _run_wrapper(self, request_id: str, coro): + """把用户协程包装成可追踪的任务:更新状态、捕获异常、清理。""" + info = self._tasks.get(request_id) + if info is None: + return + info.state = STATE_RUNNING + try: + await coro + except Exception as exc: # pragma: no cover + if info: + info.state = STATE_FAILED + info.finished_at = time.time() + info.error = f"unhandled:{type(exc).__name__}:{exc}" + finally: + # 清理 asyncio task 引用 + self._asyncio_tasks.pop(request_id, None) + # 定期 GC 已完成任务 + self._cleanup_aged() + + def _task_done( + self, + request_id: str, + result: "PipelineResult", # from pipeline.PipelineResult + ) -> None: + """任务正常完成时由调用方调用,写入结果。""" + info = self._tasks.get(request_id) + if info is None: + return + info.state = STATE_DONE + info.finished_at = time.time() + info.response = result.response + info.status = result.status + info.fast_path = result.fast_path + info.rounds_used = result.rounds_used + info.api_input_tokens = result.api_input_tokens + info.api_output_tokens = result.api_output_tokens + info.cost_est = result.cost_est + info.model_used = result.model_used + info.latency_ms = result.latency_ms + info.workspace_path = result.workspace_path + info.route = list(result.route) if result.route else [] + if result.error: + info.error_detail = result.error + + def _task_failed( + self, + request_id: str, + error: str, + detail: Optional[str] = None, + ) -> None: + """任务异常结束时调用。""" + info = self._tasks.get(request_id) + if info is None: + return + info.state = STATE_FAILED + info.finished_at = time.time() + info.error = error + info.error_detail = detail + + # ───────────────────────────────────────────────────────────────── + # SSE 专用:workspace.json 文件轮询 + # ───────────────────────────────────────────────────────────────── + + async def watch_workspace( + self, + request_id: str, + cancel_event: asyncio.Event, + ) -> AsyncGenerator[Dict[str, Any], None]: + """监视 runs/{id}/workspace.json,yield 单条事件。 + + 事件类型: + - {"type": "status", "value": , "request_id": } + - {"type": "workspace", "version": , "workspace": } + - {"type": "error", "detail": } + + 当任务进入 done/failed 状态或 cancel_event 被 set 时停止。 + """ + ws_path = RUNS_DIR / request_id / "workspace.json" + seen_mtime: float = 0.0 + seen_size: int = 0 + + while not cancel_event.is_set(): + info = self.get(request_id) + # 检查终态 + if info and info.state in (STATE_DONE, STATE_FAILED): + if info.state == STATE_FAILED: + yield {"type": "error", "detail": info.error or "任务失败"} + # result 由 /runs/{id}/status 提供,此处只推送终态 status + yield {"type": "status", "value": info.state, "request_id": request_id} + break + + # 读文件变化(mtime + size 双检) + if ws_path.exists(): + try: + stat = ws_path.stat() + if stat.st_mtime != seen_mtime or stat.st_size != seen_size: + raw = ws_path.read_text(encoding="utf-8") + data = json.loads(raw) + version = int(data.get("meta", {}).get("round", 0)) + seen_mtime = stat.st_mtime + seen_size = stat.st_size + yield { + "type": "workspace", + "version": version, + "state": info.state if info else STATE_RUNNING, + "request_id": request_id, + "workspace": data, + } + except (json.JSONDecodeError, OSError): + pass # 文件正在写入,忽略 + + await asyncio.sleep(self._poll_interval) + + # 连接关闭前最后推一次终态 + info = self.get(request_id) + if info: + yield {"type": "status", "value": info.state, "request_id": request_id} + + # ───────────────────────────────────────────────────────────────── + # 内存清理 + # ───────────────────────────────────────────────────────────────── + + def _cleanup_aged(self) -> None: + """删除超过 max_age_seconds 的已完成任务引用。""" + now = time.time() + to_remove = [ + rid for rid, info in self._tasks.items() + if info.state in (STATE_DONE, STATE_FAILED) + and (now - info.finished_at) > self.max_age_seconds + ] + for rid in to_remove: + self._tasks.pop(rid, None) + + def cancel(self, request_id: str) -> bool: + """取消运行中的任务。返回 True 找到并取消,False 未找到。""" + task = self._asyncio_tasks.get(request_id) + if task is None: + return False + task.cancel() + info = self.get(request_id) + if info: + info.state = STATE_FAILED + info.finished_at = time.time() + info.error = "cancelled_by_user" + return True + + +# ───────────────────────────────────────────────────────────────────────────── +# 全局单例(gateway 进程内共享) +# ───────────────────────────────────────────────────────────────────────────── +_store: Optional[JobStore] = None + + +def get_job_store() -> JobStore: + global _store + if _store is None: + _store = JobStore(max_age_seconds=3600.0, max_running=50) + return _store + + +def reset_job_store() -> None: + """测试用:重置全局注册表。""" + global _store + _store = None diff --git a/gateway/llama_manager.py b/gateway/llama_manager.py new file mode 100644 index 0000000..915fe9f --- /dev/null +++ b/gateway/llama_manager.py @@ -0,0 +1,453 @@ +"""llama-server 进程管理 & 模型下载。 + +职责: +- 启动/停止本地 llama-server 子进程(Windows 兼容) +- 探测已有 .gguf 模型文件 +- 从 HuggingFace URL 下载模型(支持 huggingface.co 路径别名) +- 下载进度可通过 SSE /llama/download/stream 订阅 + +用法: + from gateway.llama_manager import get_llama_manager + lm = get_llama_manager() + await lm.start(model="models/qwen3.5-4b-q4_k_m.gguf") + await lm.stop() +""" +from __future__ import annotations + +import asyncio +import json +import os +import re +import subprocess +import sys +import threading +import time +from dataclasses import dataclass, field +from pathlib import Path +from typing import Any, AsyncGenerator, Optional + +# ───────────────────────────────────────────────────────────────────────────── +# 路径配置(与 config.yaml runtime.llama_server 段保持一致) +# ───────────────────────────────────────────────────────────────────────────── +_ROOT = Path(__file__).resolve().parent.parent # E:\projectAIpopular +BIN_DIR = _ROOT / "bin" +MODELS_DIR = _ROOT / "models" +PID_FILE = _ROOT / "data" / "llama-server.pid" +LOG_FILE = _ROOT / "data" / "llama-server.log" + +# 确保目录存在 +BIN_DIR.mkdir(parents=True, exist_ok=True) +MODELS_DIR.mkdir(parents=True, exist_ok=True) +PID_FILE.parent.mkdir(parents=True, exist_ok=True) + + +# ───────────────────────────────────────────────────────────────────────────── +# 数据模型 +# ───────────────────────────────────────────────────────────────────────────── + +@dataclass +class LlamaServerStatus: + running: bool + pid: Optional[int] = None + model: Optional[str] = None + port: Optional[int] = None + base_url: Optional[str] = None + started_at: Optional[float] = None + error: Optional[str] = None + + +@dataclass +class DownloadProgress: + url: str + dest: str + total_bytes: Optional[int] = None + downloaded_bytes: int = 0 + progress_pct: float = 0.0 + speed: str = "" + eta: str = "" + done: bool = False + error: Optional[str] = None + + +# ───────────────────────────────────────────────────────────────────────────── +# llama_manager 单例 +# ───────────────────────────────────────────────────────────────────────────── + +class LlamaManager: + _instance: Optional["LlamaManager"] = None + + def __init__(self): + self._proc: Optional[subprocess.Popen] = None + self._pid: Optional[int] = None + self._model: Optional[str] = None + self._port: Optional[int] = None + self._started_at: Optional[float] = None + self._downloading: dict[str, DownloadProgress] = {} # url -> progress + self._dl_lock = threading.Lock() + # 加载已有进程 + self._load_pid() + + # ── 进程持久化 ───────────────────────────────────────────────────────── + + def _load_pid(self) -> None: + """从 pid 文件恢复进程引用(进程仍在运行时)。""" + if not PID_FILE.exists(): + return + try: + pid = int(PID_FILE.read_text().strip()) + os.kill(pid, 0) # 检查进程是否存活 + # 进程还在,尝试接管(通过 cmdline 判断是否是 llama-server) + self._pid = pid + self._proc = self._attach_to_process(pid) + except (ValueError, FileNotFoundError, OSError): + PID_FILE.unlink(missing_ok=True) + + def _attach_to_process(self, pid: int) -> Optional[subprocess.Popen]: + """通过 pid 重新关联到 Popen(仅作状态恢复,不拥有 stdout)。""" + try: + return subprocess.Popen( + [sys.executable, "-c", + f"import os; os.kill({pid}, 0)"], # 存活检查 + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL, + ) + except Exception: + return None + + def _write_pid(self, pid: int) -> None: + PID_FILE.write_text(str(pid), encoding="utf-8") + + def _clear_pid(self) -> None: + PID_FILE.unlink(missing_ok=True) + + # ── 进程管理 ───────────────────────────────────────────────────────── + + def find_binary(self) -> Optional[Path]: + """查找 llama-server 可执行文件。""" + candidates = [ + BIN_DIR / "llama-server.exe", + BIN_DIR / "llama-server", + _ROOT / "llama-server.exe", + _ROOT / "llama-server", + ] + for p in candidates: + if p.exists(): + return p + # PATH 中查找 + import shutil + found = shutil.which("llama-server") or shutil.which("llama-server.exe") + if found: + return Path(found) + return None + + def status(self) -> LlamaServerStatus: + """返回当前服务状态。""" + if self._proc is None or self._pid is None: + return LlamaServerStatus(running=False) + try: + # 检查进程是否存活 + os.kill(self._pid, 0) + except OSError: + # 进程已死 + self._proc = None + self._pid = None + self._model = None + self._port = None + self._started_at = None + self._clear_pid() + return LlamaServerStatus(running=False) + return LlamaServerStatus( + running=True, + pid=self._pid, + model=self._model, + port=self._port, + base_url=f"http://127.0.0.1:{self._port}/v1", + started_at=self._started_at, + ) + + async def start( + self, + model: str, + port: int = 8901, + ngl: int = 99, + ctx: int = 4096, + extra_args: Optional[list] = None, + ) -> LlamaServerStatus: + """启动 llama-server,阻塞直到监听就绪或超时。""" + if self.status().running: + s = self.status() + if s.model == model and s.port == port: + return s # 已是同一模型,无需重启 + await self.stop() + + binary = self.find_binary() + if binary is None: + return LlamaServerStatus( + running=False, + error="未找到 llama-server 可执行文件。" + "请将 llama-server.exe 放入 bin/ 目录," + "或从 https://github.com/ggerganov/llama.cpp/releases 下载。", + ) + + model_path = Path(model) + if not model_path.is_absolute(): + model_path = MODELS_DIR / model + if not model_path.exists(): + return LlamaServerStatus( + running=False, + error=f"模型文件不存在:{model_path}。" + "请先下载模型,或在设置页填写 HuggingFace URL 下载。", + ) + + args = [ + str(binary), + "-m", str(model_path), + "-c", str(ctx), + "-ngl", str(ngl), + "--port", str(port), + "--host", "127.0.0.1", + ] + if extra_args: + args.extend(extra_args) + + LOG_FILE.parent.mkdir(parents=True, exist_ok=True) + log_f = open(LOG_FILE, "w", encoding="utf-8", buffering=1) + + try: + self._proc = subprocess.Popen( + args, + stdout=log_f, + stderr=subprocess.STDOUT, + cwd=str(_ROOT), + creationflags=subprocess.CREATE_NEW_PROCESS_GROUP if sys.platform == "win32" else 0, + ) + except OSError as e: + log_f.close() + return LlamaServerStatus(running=False, error=f"启动失败:{e}") + + self._pid = self._proc.pid + self._model = str(model_path) + self._port = port + self._started_at = time.time() + self._write_pid(self._pid) + + # 等待服务就绪 + ok = await self._wait_until_ready(port, timeout=30) + if not ok: + await self.stop() + return LlamaServerStatus( + running=False, + error=f"llama-server 启动后 {port} 端口在 30 秒内未响应", + ) + + return self.status() + + async def stop(self) -> None: + """优雅停止 llama-server。""" + if self._pid is None: + self._proc = None + return + + try: + if sys.platform == "win32": + # Windows: CTRL_BREAK_EVENT 或 taskkill + subprocess.run( + ["taskkill", "/PID", str(self._pid), "/T", "/F"], + capture_output=True, + timeout=10, + ) + else: + os.kill(self._pid, 15) # SIGTERM + time.sleep(1) + try: + os.kill(self._pid, 0) + os.kill(self._pid, 9) + except OSError: + pass + except Exception: + pass + finally: + self._proc = None + self._pid = None + self._model = None + self._port = None + self._started_at = None + self._clear_pid() + + async def _wait_until_ready(self, port: int, timeout: float = 30) -> bool: + """轮询检查端口是否开始监听。""" + import httpx + url = f"http://127.0.0.1:{port}/v1/models" + deadline = time.time() + timeout + async with httpx.AsyncClient(timeout=5.0) as client: + while time.time() < deadline: + try: + r = await client.get(url) + if r.status_code < 500: + return True + except (httpx.ConnectError, httpx.ReadTimeout, OSError): + pass + await asyncio.sleep(0.5) + return False + + # ── 模型列表 ───────────────────────────────────────────────────────── + + def list_local_models(self) -> list[dict[str, str]]: + """列出 models/ 目录下所有 .gguf 文件。""" + models = [] + for p in MODELS_DIR.glob("*.gguf"): + size_mb = p.stat().st_size // (1024 * 1024) + models.append({ + "id": p.name, + "name": p.name, + "size_mb": size_mb, + "path": str(p), + }) + return sorted(models, key=lambda m: m["name"]) + + # ── 模型下载 ───────────────────────────────────────────────────────── + + async def download_model( + self, + url: str, + dest: Optional[str] = None, + ) -> DownloadProgress: + """从 HuggingFace 或直链下载 .gguf 模型文件。 + + HuggingFace 路径别名:用户输入 "Qwen/Qwen3-4B-GGUF/Qwen3-4B-Q4_K_M.gguf" + 自动转换为 "https://huggingface.co//resolve/main/" + + 支持断点续传(Content-Range)。 + + 返回 DownloadProgress 对象(含当前进度),进度通过 get_download_progress() 查询。 + """ + import httpx + + # 路径别名转换 + if not url.startswith("http"): + url = f"https://huggingface.co/{url}/resolve/main" + + # 解析文件名 + filename = url.rstrip("/").split("/")[-1] + if not filename.endswith(".gguf"): + filename += ".gguf" + + if dest: + dest_path = Path(dest) + else: + dest_path = MODELS_DIR / filename + + # 构造 HTTP 头 + headers = {} + resume_bytes = 0 + if dest_path.exists(): + resume_bytes = dest_path.stat().st_size + headers["Range"] = f"bytes={resume_bytes}-" + + # 获取文件大小 + total_bytes: Optional[int] = None + try: + async with httpx.AsyncClient(timeout=httpx.Timeout(30.0, connect=10.0, read=60.0, write=30.0, pool=10.0), follow_redirects=True) as client: + head = await client.head(url, headers={"Range": "bytes=0-0"}) + total_raw = head.headers.get("Content-Length") + if total_raw: + total_bytes = int(total_raw) + # Content-Range 响应时 total_bytes 在 Content-Range 头里 + cr = head.headers.get("Content-Range", "") + m = re.search(r"/(\d+)", cr) + if m: + total_bytes = int(m.group(1)) + except Exception: + pass + + prog = DownloadProgress( + url=url, + dest=str(dest_path), + total_bytes=total_bytes, + downloaded_bytes=resume_bytes, + ) + with self._dl_lock: + self._downloading[url] = prog + + try: + mode = "ab" if resume_bytes > 0 else "wb" + async with httpx.AsyncClient( + timeout=httpx.Timeout(300.0, connect=10.0, read=300.0, write=30.0, pool=10.0), + follow_redirects=True, + ) as client: + t0 = time.time() + last_bytes = resume_bytes + async with client.stream("GET", url, headers=headers) as resp: + if resp.status_code not in (200, 206): + raise RuntimeError(f"HTTP {resp.status_code}") + with open(dest_path, mode) as f: + async for chunk in resp.aiter_bytes(chunk_size=8192): + f.write(chunk) + prog.downloaded_bytes += len(chunk) + + # 速度 & ETA + elapsed = time.time() - t0 + if elapsed > 0.5: + speed_bps = (prog.downloaded_bytes - last_bytes) / elapsed + speed_str = _format_speed(speed_bps) + if prog.total_bytes and speed_bps > 0: + remain = prog.total_bytes - prog.downloaded_bytes + eta_s = remain / speed_bps + prog.eta = _format_eta(eta_s) + else: + prog.eta = "" + prog.speed = speed_str + last_bytes = prog.downloaded_bytes + t0 = time.time() + + if prog.total_bytes: + prog.progress_pct = min(prog.downloaded_bytes / prog.total_bytes * 100, 100) + except Exception as e: + prog.error = str(e) + finally: + prog.done = True + with self._dl_lock: + self._downloading[url] = prog + + return prog + + def get_download_progress(self, url: str) -> Optional[DownloadProgress]: + """查询下载进度。""" + with self._dl_lock: + return self._downloading.get(url) + + def list_downloads(self) -> list[DownloadProgress]: + """列出所有活跃下载。""" + with self._dl_lock: + return list(self._downloading.values()) + + +def _format_speed(bps: float) -> str: + if bps >= 1e9: + return f"{bps/1e9:.1f} GB/s" + if bps >= 1e6: + return f"{bps/1e6:.1f} MB/s" + if bps >= 1e3: + return f"{bps/1e3:.1f} KB/s" + return f"{bps:.0f} B/s" + + +def _format_eta(seconds: float) -> str: + if seconds < 60: + return f"{seconds:.0f}s" + if seconds < 3600: + return f"{seconds/60:.0f}m" + return f"{seconds/3600:.1f}h" + + +# ───────────────────────────────────────────────────────────────────────────── +# 全局单例 +# ───────────────────────────────────────────────────────────────────────────── +_lm: Optional[LlamaManager] = None + + +def get_llama_manager() -> LlamaManager: + global _lm + if _lm is None: + _lm = LlamaManager() + return _lm diff --git a/gateway/static/assets/CollaborationView-BIjnBJHk.js b/gateway/static/assets/CollaborationView-BIjnBJHk.js new file mode 100644 index 0000000..8154538 --- /dev/null +++ b/gateway/static/assets/CollaborationView-BIjnBJHk.js @@ -0,0 +1 @@ +import{A as e,C as t,D as n,E as r,F as i,I as ee,L as a,M as o,R as te,S as s,T as c,_ as ne,j as l,n as re,t as u,w as d,x as f,z as p}from"./index-BnlAnYlB.js";var ie={class:`collab-view`},ae={class:`collab-sidebar`},oe={class:`session-list`},se=[`onClick`],ce={class:`meta`},m={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`},le={class:`section progress-section`},P={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=u(n({__name:`CollaborationView`,setup(n){let u=re(),G=i(u.currentId),K=i(null),q=s(()=>u.sessions.find(e=>e.requestId===G.value)??null),J=s(()=>K.value??q.value?.workspace??null);function Y(e){G.value=e,K.value=null}let X=null;function me(e){X?.close(),X=ne(e),X.subscribe({onWorkspace(t){t.request_id===G.value&&(K.value=t,u.updateWorkspace(e,t))},onStatus(){u.pollStatus(e)},onDone(){u.pollStatus(e)},onError(){}})}o(()=>u.sessions,e=>{G.value&&(e.find(e=>e.requestId===G.value)||(G.value=e[0]?.requestId??null))},{deep:!0}),o(G,e=>{if(!e)return;let t=u.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(n,i)=>(e(),c(`div`,ie,[t(`aside`,ae,[i[6]||=t(`h3`,null,`协作会话`,-1),t(`ul`,oe,[(e(!0),c(f,null,l(ee(u).sessions,n=>(e(),c(`li`,{key:n.requestId,class:a([`session-item`,{active:n.requestId===G.value}]),onClick:e=>Y(n.requestId)},[t(`span`,null,p(n.query.slice(0,24))+p(n.query.length>24?`…`:``),1),t(`span`,ce,p(n.status?.rounds_used??`?`)+` 轮 · `+p(n.status?.model_used??`—`),1)],10,se))),128))]),q.value?(e(),c(`div`,m,[i[4]||=t(`h4`,null,`运行统计`,-1),t(`div`,h,[i[0]||=t(`span`,null,`输入 Token`,-1),t(`b`,null,p(q.value.status?.api_input_tokens??0),1),i[1]||=t(`span`,null,`输出 Token`,-1),t(`b`,null,p(q.value.status?.api_output_tokens??0),1),i[2]||=t(`span`,null,`延迟`,-1),t(`b`,null,p(q.value.status?.latency_ms?.toFixed(0)??`—`)+` ms`,1),i[3]||=t(`span`,null,`快路径`,-1),t(`b`,null,p(q.value.status?.fast_path?`是`:`否`),1)]),i[5]||=t(`h4`,null,`路由路径`,-1),t(`div`,g,[(e(!0),c(f,null,l(q.value.status?.route??[],(t,n)=>(e(),c(`span`,{key:n,class:`route-node`},p(t),1))),128))])])):d(``,!0)]),t(`main`,_,[J.value?(e(),c(f,{key:1},[t(`section`,y,[i[8]||=t(`h2`,{class:`section-title`},`📋 Brief(任务简报)`,-1),t(`div`,b,[t(`div`,x,p(J.value.brief?.goal??`(未生成)`),1),t(`div`,S,[(e(!0),c(f,null,l(J.value.brief?.tags??[],t=>(e(),c(`span`,{key:t,class:`tag`},p(t),1))),128)),J.value.brief?.domain?(e(),c(`span`,C,p(J.value.brief.domain),1)):d(``,!0)]),J.value.brief?.constraints?.length?(e(),c(`ul`,w,[(e(!0),c(f,null,l(J.value.brief.constraints,(t,n)=>(e(),c(`li`,{key:n},p(t),1))),128))])):d(``,!0)])]),t(`section`,T,[i[12]||=t(`h2`,{class:`section-title`},`📌 Plan(执行计划)`,-1),t(`div`,E,[(e(!0),c(f,null,l(J.value.plan??[],n=>(e(),c(`div`,{key:n.id,class:a([`plan-step`,Z(n.id)])},[i[11]||=t(`div`,{class:`step-dot`},null,-1),t(`div`,D,[t(`div`,O,[t(`span`,k,p(n.id),1),t(`span`,A,p(Z(n.id)),1)]),t(`p`,j,p(n.task),1),n.deps.length?(e(),c(`div`,M,[i[9]||=r(` 依赖:`,-1),(e(!0),c(f,null,l(n.deps,t=>(e(),c(`span`,{key:t,class:`dep`},p(t),1))),128))])):d(``,!0),Q(n.id)?(e(),c(`div`,N,[t(`details`,null,[i[10]||=t(`summary`,null,`📄 工件内容`,-1),t(`pre`,null,p(Q(n.id)),1)])])):d(``,!0)])],2))),128))])]),t(`section`,le,[i[13]||=t(`h2`,{class:`section-title`},`🔄 进度(实时)`,-1),t(`div`,P,[t(`div`,F,` 第 `+p(J.value.meta.round)+` / `+p(J.value.meta.rounds_cap)+` 轮 `,1),t(`div`,I,[t(`div`,{class:`progress-fill`,style:te({width:`${J.value.meta.round/J.value.meta.rounds_cap*100}%`})},null,4)])]),t(`div`,L,[(e(!0),c(f,null,l(J.value.progress??[],n=>(e(),c(`div`,{key:n.step,class:a([`pg-step`,n.status])},[t(`span`,R,p(n.status===`done`?`✅`:n.status===`running`?`⏳`:`⭕`),1),t(`span`,null,p(n.step),1),n.note?(e(),c(`span`,z,p(n.note),1)):d(``,!0)],2))),128))])]),J.value.issues?.length?(e(),c(`section`,B,[i[15]||=t(`h2`,{class:`section-title`},`⚠️ Issues & 裁决`,-1),(e(!0),c(f,null,l(J.value.issues,n=>(e(),c(`div`,{key:n.id,class:`issue-card`},[t(`div`,V,[t(`span`,H,p(n.id),1),t(`span`,U,`Step: `+p(n.step),1)]),t(`p`,null,p(n.description),1),$(n.id)?(e(),c(`div`,W,[i[14]||=t(`strong`,null,`Architect 裁决:`,-1),r(p($(n.id)?.reply),1)])):d(``,!0)]))),128))])):d(``,!0),J.value.archive?.length?(e(),c(`section`,ue,[i[16]||=t(`h2`,{class:`section-title`},`📦 Archive(摘要归档)`,-1),t(`ul`,de,[(e(!0),c(f,null,l(J.value.archive,(t,n)=>(e(),c(`li`,{key:n},p(t),1))),128))])])):d(``,!0),J.value.meta.state===`done`?(e(),c(`section`,fe,[i[17]||=t(`h2`,{class:`section-title`},`🎉 交付`,-1),t(`div`,pe,[t(`pre`,null,p(q.value?.status?.response??`(无内容)`),1)])])):d(``,!0)],64)):(e(),c(`div`,v,[...i[7]||=[t(`p`,null,`从左侧选择一个会话,或在「对话」页发起新提问。`,-1),t(`p`,null,`协作过程通过 SSE 实时推送,无需刷新。`,-1)]]))])]))}}),[[`__scopeId`,`data-v-ceac63ce`]]);export{G as default}; \ No newline at end of file diff --git a/gateway/static/assets/CollaborationView-C2afi9cV.css b/gateway/static/assets/CollaborationView-C2afi9cV.css new file mode 100644 index 0000000..baf2ef7 --- /dev/null +++ b/gateway/static/assets/CollaborationView-C2afi9cV.css @@ -0,0 +1 @@ +.collab-view[data-v-ceac63ce]{height:100%;display:flex}.collab-sidebar[data-v-ceac63ce]{background:#f9fafb;border-right:1px solid #e5e7eb;flex-direction:column;width:260px;display:flex;overflow:hidden}.collab-sidebar h3[data-v-ceac63ce]{color:#6b7280;border-bottom:1px solid #e5e7eb;margin:0;padding:12px 16px;font-size:13px;font-weight:600}.session-list[data-v-ceac63ce]{flex:none;max-height:35%;padding:8px;list-style:none;overflow-y:auto}.session-item[data-v-ceac63ce]{cursor:pointer;border-radius:6px;flex-direction:column;gap:2px;margin-bottom:4px;padding:8px 10px;font-size:13px;display:flex}.session-item[data-v-ceac63ce]:hover{background:#e5e7eb}.session-item.active[data-v-ceac63ce]{background:#dbeafe}.meta[data-v-ceac63ce]{color:#9ca3af;font-size:11px}.stats[data-v-ceac63ce]{border-top:1px solid #e5e7eb;padding:12px 16px;font-size:12px;overflow-y:auto}.stats h4[data-v-ceac63ce]{color:#374151;margin:0 0 6px;font-size:12px}.stat-grid[data-v-ceac63ce]{grid-template-columns:1fr 1fr;gap:4px 8px;margin-bottom:12px;display:grid}.stat-grid span[data-v-ceac63ce]{color:#6b7280}.stat-grid b[data-v-ceac63ce]{color:#111;text-align:right}.route-flow[data-v-ceac63ce]{flex-wrap:wrap;gap:4px;display:flex}.route-node[data-v-ceac63ce]{color:#3730a3;background:#e0e7ff;border-radius:4px;padding:2px 6px;font-family:monospace;font-size:11px}.collab-main[data-v-ceac63ce]{flex:1;padding:20px 28px;overflow-y:auto}.empty[data-v-ceac63ce]{color:#9ca3af;flex-direction:column;justify-content:center;align-items:center;gap:8px;height:100%;display:flex}.section[data-v-ceac63ce]{margin-bottom:28px}.section-title[data-v-ceac63ce]{color:#111;border-bottom:2px solid #2563eb;margin:0 0 12px;padding-bottom:6px;font-size:16px}.brief-card[data-v-ceac63ce]{background:#eff6ff;border:1px solid #bfdbfe;border-radius:8px;padding:14px 16px}.goal[data-v-ceac63ce]{color:#1e40af;margin-bottom:8px;font-size:14px;font-weight:600}.tags[data-v-ceac63ce]{flex-wrap:wrap;gap:6px;display:flex}.tag[data-v-ceac63ce]{color:#1d4ed8;background:#dbeafe;border-radius:99px;padding:2px 8px;font-size:12px}.domain[data-v-ceac63ce]{color:#9d174d;background:#fce7f3;border-radius:99px;padding:2px 8px;font-size:12px}.constraints[data-v-ceac63ce]{color:#374151;margin-top:8px;font-size:13px;list-style:inside}.plan-timeline[data-v-ceac63ce]{flex-direction:column;gap:0;display:flex}.plan-step[data-v-ceac63ce]{gap:12px;padding-bottom:16px;display:flex;position:relative}.plan-step[data-v-ceac63ce]:before{content:"";background:#e5e7eb;width:2px;position:absolute;top:16px;bottom:0;left:6px}.plan-step[data-v-ceac63ce]:last-child:before{display:none}.step-dot[data-v-ceac63ce]{z-index:1;background:#fff;border:2px solid #d1d5db;border-radius:50%;flex-shrink:0;width:14px;height:14px;margin-top:2px}.plan-step.done .step-dot[data-v-ceac63ce]{background:#16a34a;border-color:#16a34a}.plan-step.running .step-dot[data-v-ceac63ce]{background:#2563eb;border-color:#2563eb}.plan-step.pending .step-dot[data-v-ceac63ce]{background:#fff}.step-content[data-v-ceac63ce]{flex:1}.step-header[data-v-ceac63ce]{align-items:center;gap:8px;margin-bottom:4px;display:flex}.step-id[data-v-ceac63ce]{color:#6b7280;font-family:monospace;font-size:12px;font-weight:700}.step-status[data-v-ceac63ce]{border-radius:99px;padding:1px 6px;font-size:11px}.plan-step.done .step-status[data-v-ceac63ce]{color:#16a34a;background:#dcfce7}.plan-step.running .step-status[data-v-ceac63ce]{color:#2563eb;background:#dbeafe}.plan-step.pending .step-status[data-v-ceac63ce]{color:#9ca3af;background:#f3f4f6}.step-task[data-v-ceac63ce]{color:#374151;margin:0;font-size:13px}.step-deps[data-v-ceac63ce]{color:#9ca3af;margin-top:2px;font-size:11px}.dep[data-v-ceac63ce]{background:#f3f4f6;border-radius:3px;margin-right:4px;padding:0 4px;font-family:monospace}.step-artifact[data-v-ceac63ce]{margin-top:6px}.step-artifact details[data-v-ceac63ce]{background:#fafafa;border:1px solid #e5e7eb;border-radius:4px}.step-artifact summary[data-v-ceac63ce]{cursor:pointer;color:#6b7280;padding:4px 8px;font-size:12px}.step-artifact pre[data-v-ceac63ce]{white-space:pre-wrap;max-height:120px;margin:0;padding:6px 10px;font-size:12px;overflow-y:auto}.progress-bar-wrap[data-v-ceac63ce]{margin-bottom:10px}.progress-label[data-v-ceac63ce]{color:#6b7280;margin-bottom:4px;font-size:12px}.progress-bar[data-v-ceac63ce]{background:#e5e7eb;border-radius:99px;height:6px;overflow:hidden}.progress-fill[data-v-ceac63ce]{background:#2563eb;border-radius:99px;height:100%;transition:width .4s}.progress-steps[data-v-ceac63ce]{flex-wrap:wrap;gap:8px;display:flex}.pg-step[data-v-ceac63ce]{border:1px solid #e5e7eb;border-radius:6px;align-items:center;gap:4px;padding:4px 10px;font-size:12px;display:flex}.pg-step.done[data-v-ceac63ce]{background:#dcfce7;border-color:#bbf7d0}.pg-step.running[data-v-ceac63ce]{background:#dbeafe;border-color:#bfdbfe}.pg-note[data-v-ceac63ce]{color:#9ca3af;font-size:11px}.issue-card[data-v-ceac63ce]{background:#fff5f5;border:1px solid #fca5a5;border-radius:8px;margin-bottom:10px;padding:12px 14px}.issue-header[data-v-ceac63ce]{gap:8px;margin-bottom:6px;display:flex}.issue-id[data-v-ceac63ce]{color:#dc2626;font-family:monospace;font-size:12px;font-weight:700}.issue-step[data-v-ceac63ce]{color:#6b7280;font-size:11px}.decision[data-v-ceac63ce]{background:#eff6ff;border:1px solid #bfdbfe;border-radius:4px;margin-top:8px;padding:6px 10px;font-size:13px}.archive-list[data-v-ceac63ce]{color:#374151;font-size:13px;list-style:inside}.response-block[data-v-ceac63ce]{background:#f0fdf4;border:1px solid #86efac;border-radius:8px;padding:14px 16px}.response-block pre[data-v-ceac63ce]{white-space:pre-wrap;margin:0;font-size:14px;line-height:1.7} diff --git a/gateway/static/assets/MetricsView-CE-HhrJg.css b/gateway/static/assets/MetricsView-CE-HhrJg.css new file mode 100644 index 0000000..8f626cf --- /dev/null +++ b/gateway/static/assets/MetricsView-CE-HhrJg.css @@ -0,0 +1 @@ +.metrics-view[data-v-51f92136]{height:100%;padding:20px 24px;overflow-y:auto}.metrics-header[data-v-51f92136]{justify-content:space-between;align-items:center;margin-bottom:20px;display:flex}.metrics-header h2[data-v-51f92136]{margin:0;font-size:20px}.refresh[data-v-51f92136]{cursor:pointer;background:#fff;border:1px solid #d1d5db;border-radius:6px;padding:6px 14px}.loading[data-v-51f92136],.error[data-v-51f92136]{text-align:center;color:#9ca3af;padding:40px}.error[data-v-51f92136]{color:#dc2626}.card-grid[data-v-51f92136]{grid-template-columns:repeat(auto-fill,minmax(280px,1fr));gap:16px;margin-bottom:24px;display:grid}.metric-card[data-v-51f92136]{background:#fff;border:1px solid #e5e7eb;border-radius:10px;padding:16px}.metric-card.highlight[data-v-51f92136]{background:#eff6ff;border-color:#2563eb}.metric-card h3[data-v-51f92136]{color:#374151;margin:0 0 12px;font-size:14px}.kv-list[data-v-51f92136]{grid-template-columns:1fr 1fr;gap:6px 12px;font-size:13px;display:grid}.kv-list span[data-v-51f92136]{color:#6b7280}.kv-list b[data-v-51f92136]{color:#111;text-align:right}.review-card[data-v-51f92136]{grid-column:span 2}.review-stats[data-v-51f92136]{gap:24px;margin-bottom:12px;display:flex}.stat-item[data-v-51f92136]{flex-direction:column;align-items:center;display:flex}.stat-num[data-v-51f92136]{color:#2563eb;font-size:28px;font-weight:700}.stat-label[data-v-51f92136]{color:#6b7280;font-size:12px}.progress-wrap[data-v-51f92136]{background:#e5e7eb;border-radius:99px;height:8px;margin-bottom:6px;overflow:hidden}.reviewed-bar[data-v-51f92136]{background:#16a34a;height:100%;transition:width .5s}.review-rate[data-v-51f92136]{color:#6b7280;margin:0;font-size:13px}.raw-json[data-v-51f92136]{background:#f9fafb;border:1px solid #e5e7eb;border-radius:8px}.raw-json summary[data-v-51f92136]{cursor:pointer;color:#6b7280;padding:10px 14px;font-size:13px}.raw-json pre[data-v-51f92136]{white-space:pre-wrap;border-top:1px solid #e5e7eb;margin:0;padding:10px 14px;font-size:12px} diff --git a/gateway/static/assets/MetricsView-rQCk8WKw.js b/gateway/static/assets/MetricsView-rQCk8WKw.js new file mode 100644 index 0000000..4bc8010 --- /dev/null +++ b/gateway/static/assets/MetricsView-rQCk8WKw.js @@ -0,0 +1 @@ +import{A as e,C as t,D as n,F as r,O as i,R as a,T as o,j as s,o as c,t as l,w as u,x as d,z as f}from"./index-BnlAnYlB.js";var p={class:`metrics-view`},m={key:0,class:`loading`},h={key:1,class:`error`},g={class:`card-grid`},_={class:`metric-card`},v={class:`kv-list`},y={class:`metric-card`},b={class:`kv-list`},x={key:0,class:`metric-card highlight`},S={class:`kv-list`},C={key:1,class:`metric-card review-card`},w={class:`review-stats`},T={class:`stat-item`},E={class:`stat-num`},D={class:`stat-item`},O={class:`stat-num`},k={key:0,class:`progress-wrap`},A={class:`review-rate`},j={class:`raw-json`},M=l(n({__name:`MetricsView`,setup(n){let l=r(null),M=r(!1),N=r(``);async function P(){M.value=!0,N.value=``;try{l.value=await c()}catch(e){N.value=e instanceof Error?e.message:`指标加载失败,请检查后端服务`}finally{M.value=!1}}return i(P),(n,r)=>(e(),o(`div`,p,[t(`header`,{class:`metrics-header`},[r[0]||=t(`h2`,null,`系统指标`,-1),t(`button`,{class:`refresh`,onClick:P},`🔄 刷新`)]),M.value?(e(),o(`div`,m,`加载中…`)):N.value?(e(),o(`div`,h,f(N.value),1)):l.value?(e(),o(d,{key:2},[t(`div`,g,[t(`div`,_,[r[1]||=t(`h3`,null,`路由器(v1)`,-1),t(`div`,v,[(e(!0),o(d,null,s(l.value.router,(n,r)=>(e(),o(d,{key:r},[t(`span`,null,f(r),1),t(`b`,null,f(n),1)],64))),128))])]),t(`div`,y,[r[2]||=t(`h3`,null,`缓存`,-1),t(`div`,b,[(e(!0),o(d,null,s(l.value.cache,(n,r)=>(e(),o(d,{key:r},[t(`span`,null,f(r),1),t(`b`,null,f(n),1)],64))),128))])]),l.value.v2?(e(),o(`div`,x,[r[3]||=t(`h3`,null,`协作管线(v2)`,-1),t(`div`,S,[(e(!0),o(d,null,s(l.value.v2,(n,r)=>(e(),o(d,{key:r},[t(`span`,null,f(r),1),t(`b`,null,f(n),1)],64))),128))])])):u(``,!0),l.value.review?(e(),o(`div`,C,[r[6]||=t(`h3`,null,`人工检验`,-1),t(`div`,w,[t(`div`,T,[t(`span`,E,f(l.value.review.pending),1),r[4]||=t(`span`,{class:`stat-label`},`待审核`,-1)]),t(`div`,D,[t(`span`,O,f(l.value.review.total),1),r[5]||=t(`span`,{class:`stat-label`},`总提交`,-1)])]),l.value.review.total>0?(e(),o(`div`,k,[t(`div`,{class:`reviewed-bar`,style:a({width:`${(l.value.review.total-l.value.review.pending)/l.value.review.total*100}%`})},null,4)])):u(``,!0),t(`p`,A,` 通过率: `+f(((l.value.review.total-l.value.review.pending)/l.value.review.total*100).toFixed(1))+`% `,1)])):u(``,!0)]),t(`details`,j,[r[7]||=t(`summary`,null,`原始 JSON`,-1),t(`pre`,null,f(JSON.stringify(l.value,null,2)),1)])],64)):u(``,!0)]))}}),[[`__scopeId`,`data-v-51f92136`]]);export{M as default}; \ No newline at end of file diff --git a/gateway/static/assets/ReviewView-CaOmFJEu.css b/gateway/static/assets/ReviewView-CaOmFJEu.css new file mode 100644 index 0000000..92ce1f3 --- /dev/null +++ b/gateway/static/assets/ReviewView-CaOmFJEu.css @@ -0,0 +1 @@ +.review-view[data-v-19c16eff]{height:100%;padding:20px 24px;overflow-y:auto}.review-header[data-v-19c16eff]{justify-content:space-between;align-items:center;margin-bottom:20px;display:flex}.review-header h2[data-v-19c16eff]{margin:0;font-size:20px}.controls[data-v-19c16eff]{gap:8px;display:flex}button[data-v-19c16eff]{cursor:pointer;background:#fff;border:1px solid #d1d5db;border-radius:6px;padding:6px 14px;font-size:13px}button.active[data-v-19c16eff]{color:#fff;background:#2563eb;border-color:#2563eb}.refresh-btn[data-v-19c16eff]{margin-left:auto}.loading[data-v-19c16eff],.error[data-v-19c16eff],.empty[data-v-19c16eff]{text-align:center;color:#9ca3af;padding:40px}.error[data-v-19c16eff]{color:#dc2626}.queue-list[data-v-19c16eff]{flex-direction:column;gap:16px;display:flex}.review-card[data-v-19c16eff]{background:#fff;border:1px solid #e5e7eb;border-radius:10px;padding:16px}.card-header[data-v-19c16eff]{flex-wrap:wrap;align-items:center;gap:10px;margin-bottom:10px;display:flex}.card-id[data-v-19c16eff]{color:#6b7280;font-family:monospace;font-size:12px}.verdict-badge[data-v-19c16eff]{border-radius:99px;padding:2px 8px;font-size:12px;font-weight:600}.verdict-badge.pending[data-v-19c16eff]{color:#92400e;background:#fef3c7}.verdict-badge.approved[data-v-19c16eff]{color:#16a34a;background:#dcfce7}.verdict-badge.rejected[data-v-19c16eff]{color:#dc2626;background:#fee2e2}.tags[data-v-19c16eff]{gap:4px;display:flex}.tag[data-v-19c16eff]{color:#3730a3;background:#e0e7ff;border-radius:4px;padding:1px 6px;font-size:11px}.date[data-v-19c16eff]{color:#9ca3af;margin-left:auto;font-size:11px}.query-block[data-v-19c16eff],.response-block[data-v-19c16eff]{margin-bottom:8px;font-size:13px;line-height:1.6}.query-block pre[data-v-19c16eff],.response-block pre[data-v-19c16eff]{white-space:pre-wrap;background:#f9fafb;border:1px solid #e5e7eb;border-radius:4px;margin:4px 0 0;padding:6px 10px;font-size:13px}.actions[data-v-19c16eff]{flex-direction:column;gap:8px;margin-top:10px;display:flex}textarea[data-v-19c16eff]{resize:vertical;box-sizing:border-box;border:1px solid #d1d5db;border-radius:6px;width:100%;padding:8px 10px;font-family:inherit;font-size:13px}.btn-row[data-v-19c16eff]{gap:8px;display:flex}.approve[data-v-19c16eff]{color:#16a34a;background:#dcfce7;border-color:#86efac}.reject[data-v-19c16eff]{color:#dc2626;background:#fee2e2;border-color:#fca5a5}.correction[data-v-19c16eff]{background:#fffbeb;border:1px solid #fcd34d;border-radius:4px;margin-top:8px;padding:6px 10px;font-size:13px} diff --git a/gateway/static/assets/ReviewView-nv7jUZLH.js b/gateway/static/assets/ReviewView-nv7jUZLH.js new file mode 100644 index 0000000..4658ac1 --- /dev/null +++ b/gateway/static/assets/ReviewView-nv7jUZLH.js @@ -0,0 +1 @@ +import{A as e,C as t,D as n,E as r,F as i,L as a,N as o,O as s,S as c,T as l,b as u,j as d,l as f,m as p,t as m,w as h,x as g,z as _}from"./index-BnlAnYlB.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=m(n({__name:`ReviewView`,setup(n){let m=i([]),L=i(!1),R=i(``),z=i(`pending`),B=i({}),V=c(()=>z.value===`all`?m.value:m.value.filter(e=>e.verdict===z.value));async function H(){L.value=!0,R.value=``;try{m.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 p(e,t,B.value[e]||void 0),await H()}catch(e){R.value=e instanceof Error?e.message:String(e)}}return s(H),(n,i)=>(e(),l(`div`,v,[t(`header`,y,[i[4]||=t(`h2`,null,`人工检验队列`,-1),t(`div`,b,[t(`button`,{class:a({active:z.value===`all`}),onClick:i[0]||=e=>z.value=`all`},`全部`,2),t(`button`,{class:a({active:z.value===`pending`}),onClick:i[1]||=e=>z.value=`pending`},`待审核`,2),t(`button`,{class:a({active:z.value===`approved`}),onClick:i[2]||=e=>z.value=`approved`},`已通过`,2),t(`button`,{class:a({active:z.value===`rejected`}),onClick:i[3]||=e=>z.value=`rejected`},`已拒绝`,2),t(`button`,{class:`refresh-btn`,onClick:H},`🔄 刷新`)])]),L.value?(e(),l(`div`,x,`加载中…`)):R.value?(e(),l(`div`,S,_(R.value),1)):(e(),l(`div`,C,[V.value.length?h(``,!0):(e(),l(`div`,w,`队列为空。`)),(e(!0),l(g,null,d(V.value,n=>(e(),l(`div`,{key:n.id,class:`review-card`},[t(`div`,T,[t(`span`,E,`#`+_(n.id),1),t(`span`,{class:a([`verdict-badge`,n.verdict])},_(n.verdict),3),t(`span`,D,[(e(!0),l(g,null,d(n.tags,t=>(e(),l(`span`,{key:t,class:`tag`},_(t),1))),128))]),t(`span`,O,_(n.created_at),1)]),t(`div`,k,[i[5]||=t(`strong`,null,`Query:`,-1),r(_(n.query),1)]),t(`div`,A,[i[6]||=t(`strong`,null,`Response:`,-1),t(`pre`,null,_(n.response),1)]),n.verdict===`pending`?(e(),l(`div`,j,[o(t(`textarea`,{"onUpdate:modelValue":e=>B.value[n.id]=e,placeholder:`修正意见(可选)`,rows:`2`},null,8,M),[[u,B.value[n.id]]]),t(`div`,N,[t(`button`,{class:`approve`,onClick:e=>U(n.id,`approved`)},`✅ 通过`,8,P),t(`button`,{class:`reject`,onClick:e=>U(n.id,`rejected`)},`❌ 拒绝`,8,F)])])):n.correction?(e(),l(`div`,I,[i[7]||=t(`strong`,null,`修正:`,-1),r(_(n.correction),1)])):h(``,!0)]))),128))]))]))}}),[[`__scopeId`,`data-v-19c16eff`]]);export{L as default}; \ No newline at end of file diff --git a/gateway/static/assets/SettingsView-Cb0oAlJt.js b/gateway/static/assets/SettingsView-Cb0oAlJt.js new file mode 100644 index 0000000..b77bc9c --- /dev/null +++ b/gateway/static/assets/SettingsView-Cb0oAlJt.js @@ -0,0 +1 @@ +import{A as e,C as t,D as n,E as r,F as i,L as a,N as o,O as ee,P as s,R as te,S as c,T as l,a as ne,b as u,c as d,d as re,f as ie,g as ae,h as oe,i as se,j as f,k as ce,p as le,r as ue,s as de,t as p,u as m,v as fe,w as h,x as g,y as _,z as v}from"./index-BnlAnYlB.js";var pe={class:`settings-view`},me={class:`settings-section`},he={class:`section-title`},ge={class:`title-right`},_e={class:`section-body`},ve={class:`server-control-bar`},ye={class:`server-info`},be={class:`model-chip`},xe={class:`model-chip port-chip`},Se={class:`model-chip pid-chip`},Ce={key:1,class:`server-idle`},we={class:`server-actions`},Te=[`disabled`],Ee=[`disabled`],De={key:0,class:`error-msg llama-error`},Oe={class:`local-models`},ke={class:`local-models-header`},Ae=[`disabled`],je={key:0,class:`no-models`},Me={key:1,class:`model-list`},Ne=[`onClick`],Pe={class:`model-item-name`},Fe={class:`model-item-size`},Ie={class:`download-section`},Le={class:`download-form`},Re=[`disabled`],ze=[`disabled`],Be={key:0,class:`progress-wrap`},Ve={class:`progress-bar`},He={class:`progress-info`},Ue={key:1,class:`error-msg download-error`},We={key:2,class:`done-msg`},Ge={class:`form-grid`,style:{"margin-top":`16px`}},Ke={class:`field full-width`},qe={key:0,class:`field full-width`},Je=[`disabled`],Ye={class:`input-row`},Xe=[`placeholder`],Ze={key:1,class:`field full-width`},Qe=[`disabled`],$e=[`value`],et=[`value`],tt={key:3,class:`field-hint warn`},nt={key:2,class:`field`},rt={class:`field`},it={class:`field`},at={class:`field`},ot={class:`settings-section`},st={class:`section-title`},ct={class:`title-right`},lt={class:`section-body`},ut={class:`form-grid`},dt={class:`field full-width`},ft=[`disabled`],pt={class:`field full-width`},mt={class:`field full-width`},ht=[`disabled`],gt=[`value`],_t={key:0,class:`field-hint warn`},vt={class:`settings-section`},yt={class:`section-body`},bt={class:`form-grid`},xt={class:`field full-width`},St={class:`checkbox-label`},Ct={class:`field`},wt={class:`field`},Tt={class:`field full-width`},Et={class:`actions`},Dt=[`disabled`],Ot=[`disabled`],y=p(n({__name:`SettingsView`,setup(n){let p=i(!1),y=i(``),b=i(null),x=i({running:!1}),S=i(!1),C=i(!1),w=i([]),T=i(!1),E=i(``),D=i(!1),O=i(``),k=i(0),A=i(``),j=i(``),M=i(``),N=i(null),P=i(``),F=i(``),I=i(!1),L=null,R=i([]),z=i([]),B=s({worker:!1,architect:!1}),V=s({worker:``,architect:``}),H=s({worker:!1,architect:!1}),U=s({worker:null,architect:null}),W=s({worker:``,architect:``}),G=[{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`}],K=i(null),q=s({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`}}),kt=c(()=>q.worker.backend===`llama_server`?`http://127.0.0.1:${q.worker.port}/v1`:`http://127.0.0.1:11434/v1`),At=c(()=>K.value?JSON.stringify(q)!==JSON.stringify(K.value):!1);function J(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 jt(){try{let e=await ne();return x.value=e,e}catch{return x.value={running:!1},{running:!1}}}async function Y(){T.value=!0;try{w.value=await de()}catch{w.value=[]}finally{T.value=!1}}async function Mt(){if(!q.worker.model){alert(`请先从上方「本地模型」列表中选择一个模型,再启动服务`);return}S.value=!0;try{let e=await ie(q.worker.model,q.worker.port,99,4096);x.value=e,e.running&&(q.worker.backend=`llama_server`,q.worker.base_url=`http://127.0.0.1:${e.port}/v1`,await Y(),await Z())}catch(e){x.value={running:!1,error:String(e?.message||e)}}finally{S.value=!1}}async function Nt(){C.value=!0;try{await le(),x.value={running:!1}}catch{x.value={running:!1}}finally{C.value=!1}}function Pt(e){E.value=e.id,q.worker.model=e.id}function Ft(){O.value.trim()&&(F.value=``,I.value=!1,k.value=0,A.value=``,j.value=``,M.value=`0 B`,N.value=null,D.value=!0,ue(O.value.trim()).then(e=>{if(e.error){F.value=e.error,D.value=!1;return}if(e.done){X(e);return}L=ae(O.value.trim()),L.subscribe({onProgress:X,onDone:e=>{X(e),L?.close(),L=null}})}))}function X(e){k.value=Math.round(e.progress_pct),A.value=e.speed,j.value=e.eta,M.value=J(e.downloaded_bytes),N.value=e.total_bytes?J(e.total_bytes):null,P.value=e.dest,e.done&&(D.value=!1,e.error?F.value=e.error:(I.value=!0,Y()))}async function It(){V.worker=``,R.value=[],q.worker.backend!==`mock`&&await Z()}async function Lt(){V.architect=``,z.value=[],await Q()}async function Z(){if(q.worker.backend!==`mock`){B.worker=!0,V.worker=``;try{let e=await d(q.worker.backend,q.worker.base_url,``);e.error?q.worker.backend===`llama_server`?V.worker=`💡 llama-server 未启动,请先点击上方「启动服务」`:V.worker=e.error||`连接失败`:R.value=e.models||[]}catch{V.worker=`获取模型列表失败`}finally{B.worker=!1}}}async function Q(){if(B.architect=!0,V.architect=``,!q.architect.base_url){z.value=[...G],B.architect=!1;return}try{let e=await d(`openai`,q.architect.base_url,q.architect.api_key||``);e.error?(V.architect=e.error,z.value=[...G]):(e.models||[]).length>0?z.value=e.models||[]:z.value=[...G]}catch{V.architect=`获取模型列表失败`,z.value=[...G]}finally{B.architect=!1}}async function Rt(){if(q.worker.backend!==`mock`){H.worker=!0,U.worker=null,W.worker=``;try{let e=await m(q.worker.backend,q.worker.base_url,``);U.worker=e.ok,e.ok||(W.worker=e.detail||`连接失败`)}catch(e){U.worker=!1,W.worker=e?.message||String(e)}finally{H.worker=!1}}}async function zt(){if(!q.architect.base_url){U.architect=!1,W.architect=`请先填写 API 端点`;return}H.architect=!0,U.architect=null,W.architect=``;try{let e=await m(`openai`,q.architect.base_url,q.architect.api_key||``);U.architect=e.ok,e.ok||(W.architect=e.detail||`连接失败`)}catch(e){U.architect=!1,W.architect=e?.message||String(e)}finally{H.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 Bt(){y.value=``;try{let[e,t]=await Promise.all([se(),jt()]);x.value=t,e.worker&&$(q.worker,e.worker),e.architect&&$(q.architect,e.architect),e.pipeline&&$(q.pipeline,e.pipeline),K.value=JSON.parse(JSON.stringify(q)),await Y(),z.value=[...G],q.worker.backend!==`mock`&&Z(),q.worker.model&&(E.value=q.worker.model)}catch(e){y.value=`加载配置失败:`+(e?.message||String(e))}}async function Vt(){p.value=!0,b.value=null;try{let e=JSON.parse(JSON.stringify(q));e.architect.api_key||delete e.architect.api_key;let t=await oe(e);$(q.worker,t.worker||{}),$(q.architect,t.architect||{}),$(q.pipeline,t.pipeline||{}),K.value=JSON.parse(JSON.stringify(q)),b.value={ok:!0,msg:`✅ 设置已保存,管线已重建`},setTimeout(()=>b.value=null,3e3)}catch(e){b.value={ok:!1,msg:`❌ 保存失败:`+(e?.message||String(e))}}finally{p.value=!1}}async function Ht(){if(confirm(`确定恢复默认设置?`)){p.value=!0,b.value=null;try{let e=await re();$(q.worker,e.worker||{}),$(q.architect,e.architect||{}),$(q.pipeline,e.pipeline||{}),K.value=JSON.parse(JSON.stringify(q)),R.value=[],z.value=[...G],U.worker=null,U.architect=null,b.value={ok:!0,msg:`✅ 已恢复默认设置`},setTimeout(()=>b.value=null,3e3)}catch(e){b.value={ok:!1,msg:`❌ 重置失败:`+(e?.message||String(e))}}finally{p.value=!1}}}return ee(Bt),ce(()=>{L?.close()}),(n,i)=>(e(),l(`div`,pe,[i[40]||=t(`header`,{class:`settings-header`},[t(`h2`,null,`⚙️ 模型设置`),t(`p`,{class:`subtitle`},`配置小模型(Worker)与大模型(Architect)参数,保存后实时生效`)],-1),t(`section`,me,[t(`div`,he,[i[17]||=t(`span`,null,`🔧 小模型 (Worker · 本地 llama.cpp)`,-1),t(`div`,ge,[t(`span`,{class:a([`server-badge`,x.value.running?`badge-running`:`badge-stopped`])},v(x.value.running?`● 运行中`:`○ 已停止`),3)])]),t(`div`,_e,[t(`div`,ve,[t(`div`,ye,[x.value.running?(e(),l(g,{key:0},[t(`span`,be,v(x.value.model?.split(/[/\\]/).pop()),1),t(`span`,xe,`:`+v(x.value.port),1),t(`span`,Se,`PID `+v(x.value.pid),1)],64)):(e(),l(`span`,Ce,`llama-server 未运行 — 点击「启动服务」即可启用`))]),t(`div`,we,[x.value.running?(e(),l(`button`,{key:1,class:`btn-stop`,disabled:S.value||C.value,onClick:Nt},v(C.value?`停止中…`:`■ 停止服务`),9,Ee)):(e(),l(`button`,{key:0,class:`btn-start`,disabled:S.value||C.value,onClick:Mt},v(S.value?`启动中…`:`▶ 启动服务`),9,Te))])]),x.value.error?(e(),l(`div`,De,v(x.value.error),1)):h(``,!0),t(`div`,Oe,[t(`div`,ke,[t(`span`,null,`📁 本地模型 (`+v(w.value.length)+`)`,1),t(`button`,{class:`btn-refresh`,disabled:T.value,onClick:Y},v(T.value?`…`:`🔄`),9,Ae)]),w.value.length===0&&!T.value?(e(),l(`div`,je,` 暂无本地模型,请下载或放置 .gguf 文件到 models/ 目录 `)):(e(),l(`div`,Me,[(e(!0),l(g,null,f(w.value,n=>(e(),l(`div`,{key:n.id,class:a([`model-item`,E.value===n.id?`selected`:``]),onClick:e=>Pt(n)},[t(`span`,Pe,v(n.name),1),t(`span`,Fe,v(n.size_mb)+` MB`,1)],10,Ne))),128))]))]),t(`div`,Ie,[i[18]||=t(`div`,{class:`download-header`},`⬇️ 下载模型`,-1),t(`div`,Le,[o(t(`input`,{"onUpdate:modelValue":i[0]||=e=>O.value=e,class:`download-input`,placeholder:`HuggingFace 路径或直链,如:Qwen/Qwen3-4B-GGUF/Qwen3-4B-Q4_K_M.gguf`,disabled:D.value},null,8,Re),[[u,O.value]]),t(`button`,{class:`btn-download`,disabled:D.value||!O.value.trim(),onClick:Ft},v(D.value?k.value+`%`:`下载`),9,ze)]),D.value?(e(),l(`div`,Be,[t(`div`,Ve,[t(`div`,{class:`progress-fill`,style:te({width:k.value+`%`})},null,4)]),t(`div`,He,[t(`span`,null,v(A.value),1),t(`span`,null,v(j.value),1),t(`span`,null,v(M.value)+`/`+v(N.value||`?`),1)])])):h(``,!0),F.value?(e(),l(`div`,Ue,v(F.value),1)):h(``,!0),I.value&&!F.value?(e(),l(`div`,We,` ✅ 下载完成:`+v(P.value),1)):h(``,!0)]),t(`div`,Ge,[t(`div`,Ke,[i[20]||=t(`label`,null,`后端类型`,-1),o(t(`select`,{"onUpdate:modelValue":i[1]||=e=>q.worker.backend=e,onChange:It},[...i[19]||=[t(`option`,{value:`mock`},`mock(模拟,不真实调用)`,-1),t(`option`,{value:`llama_server`},`llama_server(内置本地服务,推荐)`,-1),t(`option`,{value:`openai`},`openai / 兼容 API(Ollama 等)`,-1)]],544),[[_,q.worker.backend]])]),q.worker.backend===`mock`?h(``,!0):(e(),l(`div`,qe,[t(`label`,null,[i[21]||=r(` API 地址 `,-1),t(`button`,{class:`btn-refresh btn-inline`,disabled:H.worker,onClick:Rt},v(H.worker?`…`:`🔗 连接测试`),9,Je)]),t(`div`,Ye,[o(t(`input`,{"onUpdate:modelValue":i[2]||=e=>q.worker.base_url=e,placeholder:kt.value,onBlur:It},null,40,Xe),[[u,q.worker.base_url]]),U.worker===null?h(``,!0):(e(),l(`span`,{key:0,class:a([`ping-badge`,U.worker?`ping-ok`:`ping-err`])},v(U.worker?`✅ 正常`:`❌ `+W.worker),3))])])),q.worker.backend===`mock`?h(``,!0):(e(),l(`div`,Ze,[t(`label`,null,[i[22]||=r(` 模型 `,-1),t(`button`,{class:`btn-refresh btn-inline`,disabled:B.worker,onClick:Z},v(B.worker?`…`:`🔄 刷新列表`),9,Qe)]),q.worker.backend===`llama_server`?o((e(),l(`select`,{key:0,"onUpdate:modelValue":i[3]||=e=>q.worker.model=e},[i[23]||=t(`option`,{value:``,disabled:``},`—— 从本地模型中选择 ——`,-1),(e(!0),l(g,null,f(w.value,t=>(e(),l(`option`,{key:t.id,value:t.id},v(t.name),9,$e))),128))],512)),[[_,q.worker.model]]):q.worker.backend===`openai`?o((e(),l(`select`,{key:1,"onUpdate:modelValue":i[4]||=e=>q.worker.model=e},[i[24]||=t(`option`,{value:``,disabled:``},`—— 从 API 获取 ——`,-1),(e(!0),l(g,null,f(R.value,t=>(e(),l(`option`,{key:t.id,value:t.id},v(t.name||t.id),9,et))),128))],512)),[[_,q.worker.model]]):o((e(),l(`input`,{key:2,"onUpdate:modelValue":i[5]||=e=>q.worker.model=e,placeholder:`输入模型名称…`},null,512)),[[u,q.worker.model]]),V.worker?(e(),l(`span`,tt,v(V.worker),1)):h(``,!0)])),q.worker.backend===`llama_server`?(e(),l(`div`,nt,[i[25]||=t(`label`,null,`端口`,-1),o(t(`input`,{"onUpdate:modelValue":i[6]||=e=>q.worker.port=e,type:`number`,min:`1`,max:`65535`},null,512),[[u,q.worker.port,void 0,{number:!0}]])])):h(``,!0),t(`div`,rt,[i[26]||=t(`label`,null,`Temperature`,-1),o(t(`input`,{"onUpdate:modelValue":i[7]||=e=>q.worker.temperature=e,type:`number`,min:`0`,max:`2`,step:`0.05`},null,512),[[u,q.worker.temperature,void 0,{number:!0}]])]),t(`div`,it,[i[27]||=t(`label`,null,`最大修复轮次`,-1),o(t(`input`,{"onUpdate:modelValue":i[8]||=e=>q.worker.max_fix_attempts=e,type:`number`,min:`0`,max:`10`},null,512),[[u,q.worker.max_fix_attempts,void 0,{number:!0}]])]),t(`div`,at,[i[28]||=t(`label`,null,`代码执行超时 (秒)`,-1),o(t(`input`,{"onUpdate:modelValue":i[9]||=e=>q.worker.code_timeout_s=e,type:`number`,min:`1`,max:`300`},null,512),[[u,q.worker.code_timeout_s,void 0,{number:!0}]])])])])]),t(`section`,ot,[t(`div`,st,[i[29]||=t(`span`,null,`🧠 大模型 (Architect · API 云端)`,-1),t(`div`,ct,[U.architect===null?h(``,!0):(e(),l(`span`,{key:0,class:a([`ping-badge`,U.architect?`ping-ok`:`ping-err`])},v(U.architect?`✅ 已连接`:`❌ `+W.architect),3))])]),t(`div`,lt,[t(`div`,ut,[t(`div`,dt,[t(`label`,null,[i[30]||=r(` API 端点 `,-1),t(`button`,{class:`btn-refresh btn-inline`,disabled:H.architect,onClick:zt},v(H.architect?`…`:`🔗 连接测试`),9,ft)]),o(t(`input`,{"onUpdate:modelValue":i[10]||=e=>q.architect.base_url=e,placeholder:`https://api.deepseek.com`,onBlur:Lt},null,544),[[u,q.architect.base_url]])]),t(`div`,pt,[i[31]||=t(`label`,null,`API Key(保存后加密存储)`,-1),o(t(`input`,{"onUpdate:modelValue":i[11]||=e=>q.architect.api_key=e,type:`password`,placeholder:`sk-xxxxxxxxxxxxxxxx`,autocomplete:`off`},null,512),[[u,q.architect.api_key]])]),t(`div`,mt,[t(`label`,null,[i[32]||=r(` 模型 `,-1),t(`button`,{class:`btn-refresh btn-inline`,disabled:B.architect,onClick:Q},v(B.architect?`…`:`🔄 刷新列表`),9,ht)]),o(t(`select`,{"onUpdate:modelValue":i[12]||=e=>q.architect.model=e,class:`model-select`},[i[33]||=t(`option`,{value:``,disabled:``},`—— 从 API 获取模型列表 ——`,-1),(e(!0),l(g,null,f(z.value,t=>(e(),l(`option`,{key:t.id,value:t.id},v(t.name||t.id),9,gt))),128))],512),[[_,q.architect.model]]),V.architect?(e(),l(`span`,_t,v(V.architect),1)):h(``,!0)])])])]),t(`section`,vt,[i[39]||=t(`div`,{class:`section-title`},[t(`span`,null,`⚙️ 协作管线 (Pipeline)`)],-1),t(`div`,yt,[t(`div`,bt,[t(`div`,xt,[t(`label`,St,[o(t(`input`,{type:`checkbox`,"onUpdate:modelValue":i[13]||=e=>q.pipeline.fast_path=e},null,512),[[fe,q.pipeline.fast_path]]),i[34]||=r(` 启用快路径(简单查询跳过本地模型,直接由大模型回答,省 API 费用) `,-1)])]),t(`div`,Ct,[i[35]||=t(`label`,null,`最大协作轮次`,-1),o(t(`input`,{"onUpdate:modelValue":i[14]||=e=>q.pipeline.rounds_cap=e,type:`number`,min:`1`,max:`20`},null,512),[[u,q.pipeline.rounds_cap,void 0,{number:!0}]])]),t(`div`,wt,[i[36]||=t(`label`,null,`API Token 熔断上限`,-1),o(t(`input`,{"onUpdate:modelValue":i[15]||=e=>q.pipeline.api_token_cap=e,type:`number`,min:`100`,max:`100000`},null,512),[[u,q.pipeline.api_token_cap,void 0,{number:!0}]])]),t(`div`,Tt,[i[38]||=t(`label`,null,`超限策略`,-1),o(t(`select`,{"onUpdate:modelValue":i[16]||=e=>q.pipeline.breach_policy=e},[...i[37]||=[t(`option`,{value:`architect_do`},`architect_do(超限交给大模型处理,推荐)`,-1),t(`option`,{value:`local_only`},`local_only(强制留在本地小模型)`,-1)]],512),[[_,q.pipeline.breach_policy]])])])])]),t(`div`,Et,[t(`button`,{class:`btn-primary`,disabled:p.value||!At.value,onClick:Vt},v(p.value?`保存中…`:`💾 保存设置`),9,Dt),t(`button`,{class:`btn-secondary`,disabled:p.value,onClick:Ht},` 🔁 恢复默认 `,8,Ot)]),b.value?(e(),l(`div`,{key:0,class:a([`toast`,b.value.ok?`toast-ok`:`toast-err`])},v(b.value.msg),3)):h(``,!0)]))}}),[[`__scopeId`,`data-v-1b54937b`]]);export{y as default}; \ No newline at end of file diff --git a/gateway/static/assets/SettingsView-yFovudGR.css b/gateway/static/assets/SettingsView-yFovudGR.css new file mode 100644 index 0000000..3b5919a --- /dev/null +++ b/gateway/static/assets/SettingsView-yFovudGR.css @@ -0,0 +1 @@ +.settings-view[data-v-1b54937b]{box-sizing:border-box;background:#f9fafb;height:100%;padding:24px;display:block;overflow-y:auto}.settings-header[data-v-1b54937b]{margin-bottom:20px}.settings-header h2[data-v-1b54937b]{color:#111827;margin-bottom:4px;font-size:20px;font-weight:700}.subtitle[data-v-1b54937b]{color:#6b7280;font-size:13px}.settings-section[data-v-1b54937b]{background:#fff;border:1px solid #e5e7eb;border-radius:10px;margin-bottom:16px;overflow:hidden}.section-title[data-v-1b54937b]{color:#1f2937;-webkit-user-select:none;user-select:none;background:#fff;justify-content:space-between;align-items:center;padding:14px 20px;font-size:15px;font-weight:600;display:flex}.section-title[data-v-1b54937b]:hover{background:#f9fafb}.title-right[data-v-1b54937b]{align-items:center;gap:10px;display:flex}.section-body[data-v-1b54937b]{padding:16px 20px}.server-control-bar[data-v-1b54937b]{background:#f8fafc;border:1px solid #e2e8f0;border-radius:8px;justify-content:space-between;align-items:center;gap:12px;margin-bottom:12px;padding:10px 14px;display:flex}.server-info[data-v-1b54937b]{flex-wrap:wrap;align-items:center;gap:6px;display:flex}.model-chip[data-v-1b54937b]{color:#3730a3;background:#e0e7ff;border-radius:4px;padding:2px 8px;font-size:12px;font-weight:500}.port-chip[data-v-1b54937b]{color:#166534;background:#dcfce7}.pid-chip[data-v-1b54937b]{color:#6b7280;background:#f3f4f6}.server-idle[data-v-1b54937b]{color:#9ca3af;font-size:13px}.server-actions[data-v-1b54937b]{gap:8px;display:flex}.btn-start[data-v-1b54937b],.btn-stop[data-v-1b54937b]{color:#fff;cursor:pointer;border:none;border-radius:6px;padding:6px 14px;font-size:13px}.btn-start[data-v-1b54937b]{background:#16a34a}.btn-start[data-v-1b54937b]:hover:not(:disabled){background:#15803d}.btn-start[data-v-1b54937b]:disabled{cursor:not-allowed;background:#86efac}.btn-stop[data-v-1b54937b]{background:#dc2626}.btn-stop[data-v-1b54937b]:hover:not(:disabled){background:#b91c1c}.btn-stop[data-v-1b54937b]:disabled{cursor:not-allowed;background:#fca5a5}.server-badge[data-v-1b54937b]{border-radius:10px;padding:2px 8px;font-size:12px;font-weight:600}.badge-running[data-v-1b54937b]{color:#16a34a;background:#dcfce7}.badge-stopped[data-v-1b54937b]{color:#9ca3af;background:#f3f4f6}.llama-error[data-v-1b54937b]{margin-top:8px}.local-models[data-v-1b54937b]{border:1px solid #e5e7eb;border-radius:8px;margin-bottom:12px;overflow:hidden}.local-models-header[data-v-1b54937b]{color:#374151;background:#f9fafb;border-bottom:1px solid #f3f4f6;justify-content:space-between;align-items:center;padding:8px 12px;font-size:13px;font-weight:500;display:flex}.no-models[data-v-1b54937b]{color:#9ca3af;text-align:center;padding:12px;font-size:13px}.model-item[data-v-1b54937b]{cursor:pointer;border-bottom:1px solid #f9fafb;justify-content:space-between;align-items:center;padding:7px 12px;font-size:13px;transition:background .1s;display:flex}.model-item[data-v-1b54937b]:last-child{border-bottom:none}.model-item[data-v-1b54937b]:hover{background:#eff6ff}.model-item.selected[data-v-1b54937b]{background:#dbeafe}.model-item-name[data-v-1b54937b]{color:#1f2937;font-weight:500}.model-item-size[data-v-1b54937b]{color:#9ca3af;font-size:12px}.download-section[data-v-1b54937b]{background:#fafafa;border:1px solid #e5e7eb;border-radius:8px;margin-bottom:12px;padding:12px}.download-header[data-v-1b54937b]{color:#374151;margin-bottom:8px;font-size:13px;font-weight:600}.download-form[data-v-1b54937b]{gap:8px;display:flex}.download-input[data-v-1b54937b]{border:1px solid #d1d5db;border-radius:6px;outline:none;flex:1;padding:7px 10px;font-size:13px}.download-input[data-v-1b54937b]:focus{border-color:#2563eb}.btn-download[data-v-1b54937b]{color:#fff;cursor:pointer;background:#2563eb;border:none;border-radius:6px;min-width:70px;padding:7px 16px;font-size:13px}.btn-download[data-v-1b54937b]:disabled{cursor:not-allowed;background:#93c5fd}.progress-wrap[data-v-1b54937b]{margin-top:8px}.progress-bar[data-v-1b54937b]{background:#e5e7eb;border-radius:3px;height:6px;overflow:hidden}.progress-fill[data-v-1b54937b]{background:linear-gradient(90deg,#2563eb,#3b82f6);border-radius:3px;height:100%;transition:width .3s}.progress-info[data-v-1b54937b]{color:#9ca3af;justify-content:space-between;margin-top:4px;font-size:11px;display:flex}.download-error[data-v-1b54937b]{margin-top:6px;font-size:12px}.done-msg[data-v-1b54937b]{color:#16a34a;margin-top:6px;font-size:13px;font-weight:500}.form-grid[data-v-1b54937b]{grid-template-columns:1fr 1fr;gap:14px;display:grid}.field[data-v-1b54937b]{flex-direction:column;gap:5px;display:flex}.field.full-width[data-v-1b54937b]{grid-column:1/-1}.field label[data-v-1b54937b]{color:#374151;align-items:center;gap:6px;font-size:13px;font-weight:500;display:flex}.field input[data-v-1b54937b],.field select[data-v-1b54937b]{color:#111827;background:#fff;border:1px solid #d1d5db;border-radius:6px;outline:none;padding:7px 10px;font-size:13px}.field input[data-v-1b54937b]:focus,.field select[data-v-1b54937b]:focus{border-color:#2563eb;box-shadow:0 0 0 2px #2563eb26}.model-select[data-v-1b54937b]{color:#1f2937;background:#fff;border:1px solid #d1d5db;border-radius:6px;width:100%;padding:8px 12px;font-size:14px}.model-select[data-v-1b54937b]:focus{border-color:#2563eb;outline:none;box-shadow:0 0 0 3px #2563eb26}.input-row[data-v-1b54937b]{align-items:center;gap:8px;display:flex}.input-row input[data-v-1b54937b]{flex:1}.field-hint[data-v-1b54937b]{font-size:11px}.field-hint.warn[data-v-1b54937b]{color:#d97706}.btn-refresh[data-v-1b54937b]{cursor:pointer;background:#f3f4f6;border:1px solid #d1d5db;border-radius:4px;padding:2px 7px;font-size:12px}.btn-refresh[data-v-1b54937b]:hover:not(:disabled){background:#e5e7eb}.btn-refresh[data-v-1b54937b]:disabled{opacity:.5;cursor:not-allowed}.btn-inline[data-v-1b54937b]{font-size:12px}.btn-ping[data-v-1b54937b]{cursor:pointer;white-space:nowrap;background:#f9fafb;border:1px solid #d1d5db;border-radius:6px;padding:6px 10px;font-size:14px;transition:background .1s}.btn-ping[data-v-1b54937b]:hover:not(:disabled){background:#eff6ff}.btn-ping[data-v-1b54937b]:disabled{opacity:.5;cursor:not-allowed}.ping-badge[data-v-1b54937b]{white-space:nowrap;font-size:12px;font-weight:500}.ping-ok[data-v-1b54937b]{color:#16a34a}.ping-err[data-v-1b54937b]{color:#dc2626}.checkbox-label[data-v-1b54937b]{cursor:pointer;color:#374151;align-items:center;gap:8px;font-size:13px;font-weight:500;display:flex}.checkbox-label input[type=checkbox][data-v-1b54937b]{cursor:pointer;width:16px;height:16px}.actions[data-v-1b54937b]{gap:12px;margin-top:8px;display:flex}.btn-primary[data-v-1b54937b]{color:#fff;cursor:pointer;background:#2563eb;border:none;border-radius:7px;padding:9px 20px;font-size:14px;font-weight:500;transition:background .15s}.btn-primary[data-v-1b54937b]:hover:not(:disabled){background:#1d4ed8}.btn-primary[data-v-1b54937b]:disabled{cursor:not-allowed;background:#93c5fd}.btn-secondary[data-v-1b54937b]{color:#374151;cursor:pointer;background:#fff;border:1px solid #d1d5db;border-radius:7px;padding:9px 20px;font-size:14px;font-weight:500;transition:background .15s}.btn-secondary[data-v-1b54937b]:hover:not(:disabled){background:#f9fafb}.btn-secondary[data-v-1b54937b]:disabled{color:#9ca3af;cursor:not-allowed}.toast[data-v-1b54937b]{z-index:999;border-radius:8px;padding:10px 18px;font-size:14px;font-weight:500;animation:.2s slideUp-1b54937b;position:fixed;bottom:24px;right:24px;box-shadow:0 4px 12px #00000026}.toast-ok[data-v-1b54937b]{color:#166534;background:#dcfce7;border:1px solid #86efac}.toast-err[data-v-1b54937b]{color:#991b1b;background:#fee2e2;border:1px solid #fca5a5}@keyframes slideUp-1b54937b{0%{opacity:0;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}} diff --git a/gateway/static/assets/index-BnlAnYlB.js b/gateway/static/assets/index-BnlAnYlB.js new file mode 100644 index 0000000..18f9fa4 --- /dev/null +++ b/gateway/static/assets/index-BnlAnYlB.js @@ -0,0 +1,11 @@ +const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/CollaborationView-BIjnBJHk.js","assets/CollaborationView-C2afi9cV.css","assets/ReviewView-nv7jUZLH.js","assets/ReviewView-CaOmFJEu.css","assets/MetricsView-rQCk8WKw.js","assets/MetricsView-CE-HhrJg.css","assets/SettingsView-Cb0oAlJt.js","assets/SettingsView-yFovudGR.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{Object.defineProperty(e,t,{configurable:!0,enumerable:!1,writable:r,value:n})},A=e=>{let t=parseFloat(e);return isNaN(t)?e:t},j,M=()=>j||=typeof globalThis<`u`?globalThis:typeof self<`u`?self:typeof window<`u`?window:typeof global<`u`?global:{};function ce(e){if(p(e)){let t={};for(let n=0;n{if(e){let n=e.split(ue);n.length>1&&(t[n[0].trim()]=n[1].trim())}}),t}function pe(e){let t=``;if(v(e))t=e;else if(p(e))for(let n=0;nye(e,t))}var xe=e=>!!(e&&e.__v_isRef===!0),N=e=>v(e)?e:e==null?``:p(e)||b(e)&&(e.toString===S||!_(e.toString))?xe(e)?N(e.value):JSON.stringify(e,Se,2):String(e),Se=(e,t)=>xe(t)?Se(e,t.value):m(t)?{[`Map(${t.size})`]:[...t.entries()].reduce((e,[t,n],r)=>(e[Ce(t,r)+` =>`]=n,e),{})}:h(t)?{[`Set(${t.size})`]:[...t.values()].map(e=>Ce(e))}:y(t)?Ce(t):b(t)&&!p(t)&&!ee(t)?String(t):t,Ce=(e,t=``)=>y(e)?`Symbol(${e.description??t})`:e,P,we=class{constructor(e=!1){this.detached=e,this._active=!0,this._on=0,this.effects=[],this.cleanups=[],this._isPaused=!1,this._warnOnRun=!0,this.__v_skip=!0,!e&&P&&(P.active?(this.parent=P,this.index=(P.scopes||(P.scopes=[])).push(this)-1):(this._active=!1,this._warnOnRun=!1))}get active(){return this._active}pause(){if(this._active){this._isPaused=!0;let e,t;if(this.scopes){let n=this.scopes.slice();for(e=0,t=n.length;e0&&--this._on===0){if(P===this)P=this.prevScope;else{let e=P;for(;e;){if(e.prevScope===this){e.prevScope=this.prevScope;break}e=e.prevScope}}this.prevScope=void 0}}stop(e){if(this._active){this._active=!1;let t,n;for(t=0,n=this.effects.length;t0)return;if(Me){let e=Me;for(Me=void 0;e;){let t=e.next;e.next=void 0,e.flags&=-9,e=t}}let e;for(;je;){let t=je;for(je=void 0;t;){let n=t.next;if(t.next=void 0,t.flags&=-9,t.flags&1)try{t.trigger()}catch(t){e||=t}t=n}}if(e)throw e}function Ie(e){for(let t=e.deps;t;t=t.nextDep)t.version=-1,t.prevActiveLink=t.dep.activeLink,t.dep.activeLink=t}function Le(e){let t,n=e.depsTail,r=n;for(;r;){let e=r.prevDep;r.version===-1?(r===n&&(n=e),Be(r),Ve(r)):t=r,r.dep.activeLink=r.prevActiveLink,r.prevActiveLink=void 0,r=e}e.deps=t,e.depsTail=n}function Re(e){for(let t=e.deps;t;t=t.nextDep)if(t.dep.version!==t.version||t.dep.computed&&(ze(t.dep.computed)||t.dep.version!==t.version))return!0;return!!e._dirty}function ze(e){if(e.flags&4&&!(e.flags&16)||(e.flags&=-17,e.globalVersion===qe)||(e.globalVersion=qe,!e.isSSR&&e.flags&128&&(!e.deps&&!e._dirty||!Re(e))))return;e.flags|=2;let t=e.dep,n=F,r=He;F=e,He=!0;try{Ie(e);let n=e.fn(e._value);(t.version===0||k(n,e._value))&&(e.flags|=128,e._value=n,t.version++)}catch(e){throw t.version++,e}finally{F=n,He=r,Le(e),e.flags&=-3}}function Be(e,t=!1){let{dep:n,prevSub:r,nextSub:i}=e;if(r&&(r.nextSub=i,e.prevSub=void 0),i&&(i.prevSub=r,e.nextSub=void 0),n.subs===e&&(n.subs=r,!r&&n.computed)){n.computed.flags&=-5;for(let e=n.computed.deps;e;e=e.nextDep)Be(e,!0)}!t&&!--n.sc&&n.map&&n.map.delete(n.key)}function Ve(e){let{prevDep:t,nextDep:n}=e;t&&(t.nextDep=n,e.prevDep=void 0),n&&(n.prevDep=t,e.nextDep=void 0)}var He=!0,Ue=[];function We(){Ue.push(He),He=!1}function Ge(){let e=Ue.pop();He=e===void 0||e}function Ke(e){let{cleanup:t}=e;if(e.cleanup=void 0,t){let e=F;F=void 0;try{t()}finally{F=e}}}var qe=0,Je=class{constructor(e,t){this.sub=e,this.dep=t,this.version=t.version,this.nextDep=this.prevDep=this.nextSub=this.prevSub=this.prevActiveLink=void 0}},Ye=class{constructor(e){this.computed=e,this.version=0,this.activeLink=void 0,this.subs=void 0,this.map=void 0,this.key=void 0,this.sc=0,this.__v_skip=!0}track(e){if(!F||!He||F===this.computed)return;let t=this.activeLink;if(t===void 0||t.sub!==F)t=this.activeLink=new Je(F,this),F.deps?(t.prevDep=F.depsTail,F.depsTail.nextDep=t,F.depsTail=t):F.deps=F.depsTail=t,Xe(t);else if(t.version===-1&&(t.version=this.version,t.nextDep)){let e=t.nextDep;e.prevDep=t.prevDep,t.prevDep&&(t.prevDep.nextDep=e),t.prevDep=F.depsTail,t.nextDep=void 0,F.depsTail.nextDep=t,F.depsTail=t,F.deps===t&&(F.deps=e)}return t}trigger(e){this.version++,qe++,this.notify(e)}notify(e){Pe();try{for(let e=this.subs;e;e=e.prevSub)e.sub.notify()&&e.sub.dep.notify()}finally{Fe()}}};function Xe(e){if(e.dep.sc++,e.sub.flags&4){let t=e.dep.computed;if(t&&!e.dep.subs){t.flags|=20;for(let e=t.deps;e;e=e.nextDep)Xe(e)}let n=e.dep.subs;n!==e&&(e.prevSub=n,n&&(n.nextSub=e)),e.dep.subs=e}}var Ze=new WeakMap,Qe=Symbol(``),$e=Symbol(``),et=Symbol(``);function I(e,t,n){if(He&&F){let t=Ze.get(e);t||Ze.set(e,t=new Map);let r=t.get(n);r||(t.set(n,r=new Ye),r.map=t,r.key=n),r.track()}}function tt(e,t,n,r,i,a){let o=Ze.get(e);if(!o){qe++;return}let s=e=>{e&&e.trigger()};if(Pe(),t===`clear`)o.forEach(s);else{let i=p(e),a=i&&te(n);if(i&&n===`length`){let e=Number(r);o.forEach((t,n)=>{(n===`length`||n===et||!y(n)&&n>=e)&&s(t)})}else switch((n!==void 0||o.has(void 0))&&s(o.get(n)),a&&s(o.get(et)),t){case`add`:i?a&&s(o.get(`length`)):(s(o.get(Qe)),m(e)&&s(o.get($e)));break;case`delete`:i||(s(o.get(Qe)),m(e)&&s(o.get($e)));break;case`set`:m(e)&&s(o.get(Qe))}}Fe()}function nt(e,t){let n=Ze.get(e);return n&&n.get(t)}function rt(e){let t=L(e);return t===e?t:(I(t,`iterate`,et),Ht(e)?t:t.map(Gt))}function it(e){return I(e=L(e),`iterate`,et),e}function at(e,t){return Vt(e)?Kt(Bt(e)?Gt(t):t):Gt(t)}var ot={__proto__:null,[Symbol.iterator](){return st(this,Symbol.iterator,e=>at(this,e))},concat(...e){return rt(this).concat(...e.map(e=>p(e)?rt(e):e))},entries(){return st(this,`entries`,e=>(e[1]=at(this,e[1]),e))},every(e,t){return lt(this,`every`,e,t,void 0,arguments)},filter(e,t){return lt(this,`filter`,e,t,e=>e.map(e=>at(this,e)),arguments)},find(e,t){return lt(this,`find`,e,t,e=>at(this,e),arguments)},findIndex(e,t){return lt(this,`findIndex`,e,t,void 0,arguments)},findLast(e,t){return lt(this,`findLast`,e,t,e=>at(this,e),arguments)},findLastIndex(e,t){return lt(this,`findLastIndex`,e,t,void 0,arguments)},forEach(e,t){return lt(this,`forEach`,e,t,void 0,arguments)},includes(...e){return dt(this,`includes`,e)},indexOf(...e){return dt(this,`indexOf`,e)},join(e){return rt(this).join(e)},lastIndexOf(...e){return dt(this,`lastIndexOf`,e)},map(e,t){return lt(this,`map`,e,t,void 0,arguments)},pop(){return ft(this,`pop`)},push(...e){return ft(this,`push`,e)},reduce(e,...t){return ut(this,`reduce`,e,t)},reduceRight(e,...t){return ut(this,`reduceRight`,e,t)},shift(){return ft(this,`shift`)},some(e,t){return lt(this,`some`,e,t,void 0,arguments)},splice(...e){return ft(this,`splice`,e)},toReversed(){return rt(this).toReversed()},toSorted(e){return rt(this).toSorted(e)},toSpliced(...e){return rt(this).toSpliced(...e)},unshift(...e){return ft(this,`unshift`,e)},values(){return st(this,`values`,e=>at(this,e))}};function st(e,t,n){let r=it(e),i=r[t]();return r!==e&&!Ht(e)&&(i._next=i.next,i.next=()=>{let e=i._next();return e.done||(e.value=n(e.value)),e}),i}var ct=Array.prototype;function lt(e,t,n,r,i,a){let o=it(e),s=o!==e&&!Ht(e),c=o[t];if(c!==ct[t]){let t=c.apply(e,a);return s?Gt(t):t}let l=n;o!==e&&(s?l=function(t,r){return n.call(this,at(e,t),r,e)}:n.length>2&&(l=function(t,r){return n.call(this,t,r,e)}));let u=c.call(o,l,r);return s&&i?i(u):u}function ut(e,t,n,r){let i=it(e),a=i!==e&&!Ht(e),o=n,s=!1;i!==e&&(a?(s=r.length===0,o=function(t,r,i){return s&&(s=!1,t=at(e,t)),n.call(this,t,at(e,r),i,e)}):n.length>3&&(o=function(t,r,i){return n.call(this,t,r,i,e)}));let c=i[t](o,...r);return s?at(e,c):c}function dt(e,t,n){let r=L(e);I(r,`iterate`,et);let i=r[t](...n);return(i===-1||i===!1)&&Ut(n[0])?(n[0]=L(n[0]),r[t](...n)):i}function ft(e,t,n=[]){We(),Pe();let r=L(e)[t].apply(e,n);return Fe(),Ge(),r}var pt=n(`__proto__,__v_isRef,__isVue`),mt=new Set(Object.getOwnPropertyNames(Symbol).filter(e=>e!==`arguments`&&e!==`caller`).map(e=>Symbol[e]).filter(y));function ht(e){y(e)||(e=String(e));let t=L(this);return I(t,`has`,e),t.hasOwnProperty(e)}var gt=class{constructor(e=!1,t=!1){this._isReadonly=e,this._isShallow=t}get(e,t,n){if(t===`__v_skip`)return e.__v_skip;let r=this._isReadonly,i=this._isShallow;if(t===`__v_isReactive`)return!r;if(t===`__v_isReadonly`)return r;if(t===`__v_isShallow`)return i;if(t===`__v_raw`)return n===(r?i?Pt:Nt:i?Mt:jt).get(e)||Object.getPrototypeOf(e)===Object.getPrototypeOf(n)?e:void 0;let a=p(e);if(!r){let e;if(a&&(e=ot[t]))return e;if(t===`hasOwnProperty`)return ht}let o=Reflect.get(e,t,R(e)?e:n);if((y(t)?mt.has(t):pt(t))||(r||I(e,`get`,t),i))return o;if(R(o)){let e=a&&te(t)?o:o.value;return r&&b(e)?Rt(e):e}return b(o)?r?Rt(o):It(o):o}},_t=class extends gt{constructor(e=!1){super(!1,e)}set(e,t,n,r){let i=e[t],a=p(e)&&te(t);if(!this._isShallow){let e=Vt(i);if(!Ht(n)&&!Vt(n)&&(i=L(i),n=L(n)),!a&&R(i)&&!R(n))return e||(i.value=n),!0}let o=a?Number(t)e,Ct=e=>Reflect.getPrototypeOf(e);function wt(e,t,n){return function(...r){let i=this.__v_raw,a=L(i),o=m(a),s=e===`entries`||e===Symbol.iterator&&o,c=e===`keys`&&o,u=i[e](...r),d=n?St:t?Kt:Gt;return!t&&I(a,`iterate`,c?$e:Qe),l(Object.create(u),{next(){let{value:e,done:t}=u.next();return t?{value:e,done:t}:{value:s?[d(e[0]),d(e[1])]:d(e),done:t}}})}}function Tt(e){return function(...t){return e===`delete`?!1:e===`clear`?void 0:this}}function Et(e,t){let n={get(n){let r=this.__v_raw,i=L(r),a=L(n);e||(k(n,a)&&I(i,`get`,n),I(i,`get`,a));let{has:o}=Ct(i),s=t?St:e?Kt:Gt;if(o.call(i,n))return s(r.get(n));if(o.call(i,a))return s(r.get(a));r!==i&&r.get(n)},get size(){let t=this.__v_raw;return!e&&I(L(t),`iterate`,Qe),t.size},has(t){let n=this.__v_raw,r=L(n),i=L(t);return e||(k(t,i)&&I(r,`has`,t),I(r,`has`,i)),t===i?n.has(t):n.has(t)||n.has(i)},forEach(n,r){let i=this,a=i.__v_raw,o=L(a),s=t?St:e?Kt:Gt;return!e&&I(o,`iterate`,Qe),a.forEach((e,t)=>n.call(r,s(e),s(t),i))}};return l(n,e?{add:Tt(`add`),set:Tt(`set`),delete:Tt(`delete`),clear:Tt(`clear`)}:{add(e){let n=L(this),r=Ct(n),i=L(e),a=!t&&!Ht(e)&&!Vt(e)?i:e;return r.has.call(n,a)||k(e,a)&&r.has.call(n,e)||k(i,a)&&r.has.call(n,i)||(n.add(a),tt(n,`add`,a,a)),this},set(e,n){!t&&!Ht(n)&&!Vt(n)&&(n=L(n));let r=L(this),{has:i,get:a}=Ct(r),o=i.call(r,e);o||=(e=L(e),i.call(r,e));let s=a.call(r,e);return r.set(e,n),o?k(n,s)&&tt(r,`set`,e,n,s):tt(r,`add`,e,n),this},delete(e){let t=L(this),{has:n,get:r}=Ct(t),i=n.call(t,e);i||=(e=L(e),n.call(t,e));let a=r?r.call(t,e):void 0,o=t.delete(e);return i&&tt(t,`delete`,e,void 0,a),o},clear(){let e=L(this),t=e.size!==0,n=e.clear();return t&&tt(e,`clear`,void 0,void 0,void 0),n}}),[`keys`,`values`,`entries`,Symbol.iterator].forEach(r=>{n[r]=wt(r,e,t)}),n}function Dt(e,t){let n=Et(e,t);return(t,r,i)=>r===`__v_isReactive`?!e:r===`__v_isReadonly`?e:r===`__v_raw`?t:Reflect.get(f(n,r)&&r in t?n:t,r,i)}var Ot={get:Dt(!1,!1)},kt={get:Dt(!1,!0)},At={get:Dt(!0,!1)},jt=new WeakMap,Mt=new WeakMap,Nt=new WeakMap,Pt=new WeakMap;function Ft(e){switch(e){case`Object`:case`Array`:return 1;case`Map`:case`Set`:case`WeakMap`:case`WeakSet`:return 2;default:return 0}}function It(e){return Vt(e)?e:zt(e,!1,yt,Ot,jt)}function Lt(e){return zt(e,!1,xt,kt,Mt)}function Rt(e){return zt(e,!0,bt,At,Nt)}function zt(e,t,n,r,i){if(!b(e)||e.__v_raw&&!(t&&e.__v_isReactive)||e.__v_skip||!Object.isExtensible(e))return e;let a=i.get(e);if(a)return a;let o=Ft(w(e));if(o===0)return e;let s=new Proxy(e,o===2?r:n);return i.set(e,s),s}function Bt(e){return Vt(e)?Bt(e.__v_raw):!!(e&&e.__v_isReactive)}function Vt(e){return!!(e&&e.__v_isReadonly)}function Ht(e){return!!(e&&e.__v_isShallow)}function Ut(e){return e?!!e.__v_raw:!1}function L(e){let t=e&&e.__v_raw;return t?L(t):e}function Wt(e){return!f(e,`__v_skip`)&&Object.isExtensible(e)&&se(e,`__v_skip`,!0),e}var Gt=e=>b(e)?It(e):e,Kt=e=>b(e)?Rt(e):e;function R(e){return e?e.__v_isRef===!0:!1}function qt(e){return Yt(e,!1)}function Jt(e){return Yt(e,!0)}function Yt(e,t){return R(e)?e:new Xt(e,t)}var Xt=class{constructor(e,t){this.dep=new Ye,this.__v_isRef=!0,this.__v_isShallow=!1,this._rawValue=t?e:L(e),this._value=t?e:Gt(e),this.__v_isShallow=t}get value(){return this.dep.track(),this._value}set value(e){let t=this._rawValue,n=this.__v_isShallow||Ht(e)||Vt(e);e=n?e:L(e),k(e,t)&&(this._rawValue=e,this._value=n?e:Gt(e),this.dep.trigger())}};function z(e){return R(e)?e.value:e}var Zt={get:(e,t,n)=>t===`__v_raw`?e:z(Reflect.get(e,t,n)),set:(e,t,n,r)=>{let i=e[t];return R(i)&&!R(n)?(i.value=n,!0):Reflect.set(e,t,n,r)}};function Qt(e){return Bt(e)?e:new Proxy(e,Zt)}function $t(e){let t=p(e)?Array(e.length):{};for(let n in e)t[n]=tn(e,n);return t}var en=class{constructor(e,t,n){this._object=e,this._defaultValue=n,this.__v_isRef=!0,this._value=void 0,this._key=y(t)?t:String(t),this._raw=L(e);let r=!0,i=e;if(!p(e)||y(this._key)||!te(this._key))do r=!Ut(i)||Ht(i);while(r&&(i=i.__v_raw));this._shallow=r}get value(){let e=this._object[this._key];return this._shallow&&(e=z(e)),this._value=e===void 0?this._defaultValue:e}set value(e){if(this._shallow&&R(this._raw[this._key])){let t=this._object[this._key];if(R(t)){t.value=e;return}}this._object[this._key]=e}get dep(){return nt(this._raw,this._key)}};function tn(e,t,n){return new en(e,t,n)}var nn=class{constructor(e,t,n){this.fn=e,this.setter=t,this._value=void 0,this.dep=new Ye(this),this.__v_isRef=!0,this.deps=void 0,this.depsTail=void 0,this.flags=16,this.globalVersion=qe-1,this.next=void 0,this.effect=this,this.__v_isReadonly=!t,this.isSSR=n}notify(){if(this.flags|=16,!(this.flags&8)&&F!==this)return Ne(this,!0),!0}get value(){let e=this.dep.track();return ze(this),e&&(e.version=this.dep.version),this._value}set value(e){this.setter&&this.setter(e)}};function rn(e,t,n=!1){let r,i;return _(e)?r=e:(r=e.get,i=e.set),new nn(r,i,n)}var an={},on=new WeakMap,sn=void 0;function cn(e,t=!1,n=sn){if(n){let t=on.get(n);t||on.set(n,t=[]),t.push(e)}}function ln(e,t,n=r){let{immediate:i,deep:o,once:s,scheduler:c,augmentJob:l,call:d}=n,f=e=>o?e:Ht(e)||o===!1||o===0?un(e,1):un(e),m,h,g,v,y=!1,b=!1;if(R(e)?(h=()=>e.value,y=Ht(e)):Bt(e)?(h=()=>f(e),y=!0):p(e)?(b=!0,y=e.some(e=>Bt(e)||Ht(e)),h=()=>e.map(e=>{if(R(e))return e.value;if(Bt(e))return f(e);if(_(e))return d?d(e,2):e()})):h=_(e)?t?d?()=>d(e,2):e:()=>{if(g){We();try{g()}finally{Ge()}}let t=sn;sn=m;try{return d?d(e,3,[v]):e(v)}finally{sn=t}}:a,t&&o){let e=h,t=o===!0?1/0:o;h=()=>un(e(),t)}let x=Ee(),S=()=>{m.stop(),x&&x.active&&u(x.effects,m)};if(s&&t){let e=t;t=(...t)=>{let n=e(...t);return S(),n}}let C=b?Array(e.length).fill(an):an,w=e=>{if(!(!(m.flags&1)||!m.dirty&&!e)){if(t){let n=m.run();if(e||o||y||(b?n.some((e,t)=>k(e,C[t])):k(n,C))){g&&g();let e=sn;sn=m;try{let e=[n,C===an?void 0:b&&C[0]===an?[]:C,v];C=n,d?d(t,3,e):t(...e)}finally{sn=e}}}else m.run()}};return l&&l(w),m=new ke(h),m.scheduler=c?()=>c(w,!1):w,v=e=>cn(e,!1,m),g=m.onStop=()=>{let e=on.get(m);if(e){if(d)d(e,4);else for(let t of e)t();on.delete(m)}},t?i?w(!0):C=m.run():c?c(w.bind(null,!0),!0):m.run(),S.pause=m.pause.bind(m),S.resume=m.resume.bind(m),S.stop=S,S}function un(e,t=1/0,n){if(t<=0||!b(e)||e.__v_skip||(n||=new Map,(n.get(e)||0)>=t))return e;if(n.set(e,t),t--,R(e))un(e.value,t,n);else if(p(e))for(let r=0;r{un(e,t,n)});else if(ee(e)){for(let r in e)un(e[r],t,n);for(let r of Object.getOwnPropertySymbols(e))Object.prototype.propertyIsEnumerable.call(e,r)&&un(e[r],t,n)}return e}function dn(e,t,n,r){try{return r?e(...r):e()}catch(e){pn(e,t,n)}}function fn(e,t,n,r){if(_(e)){let i=dn(e,t,n,r);return i&&x(i)&&i.catch(e=>{pn(e,t,n)}),i}if(p(e)){let i=[];for(let a=0;a>>1,i=hn[r],a=kn(i);a=kn(n)?hn.push(e):hn.splice(Cn(t),0,e),e.flags|=1,Tn()}}function Tn(){xn||=bn.then(An)}function En(e){if(!p(e))vn&&e.id===-1?vn.splice(yn+1,0,e):e.flags&1||(_n.push(e),e.flags|=1);else for(let t=0;tkn(e)-kn(t));if(_n.length=0,vn){for(let t=0;te.id==null?e.flags&2?-1:1/0:e.id;function An(e){try{for(gn=0;gn{r._d&&Yi(-1);let i=Nn(t),a=Gi.length,o;try{o=e(...n)}finally{for(let e=Gi.length;e>a;e--)qi();Nn(i),r._d&&Yi(1)}return o};return r._n=!0,r._c=!0,r._d=!0,r}function Fn(e,t){if(jn===null)return e;let n=Oa(jn),i=e.dirs||=[];for(let e=0;e1)return n&&_(t)?t.call(r&&r.proxy):t}}function zn(){return!!(ha()||Zr)}var Bn=Symbol.for(`v-scx`),Vn=()=>Rn(Bn);function Hn(e,t,n){return Un(e,t,n)}function Un(e,t,n=r){let{immediate:i,deep:o,flush:s,once:c}=n,u=l({},n),d=t&&i||!t&&s!==`post`,f;if(xa){if(s===`sync`){let e=Vn();f=e.__watcherHandles||=[]}else if(!d){let e=()=>{};return e.stop=a,e.resume=a,e.pause=a,e}}let p=W;u.call=(e,t,n)=>fn(e,p,t,n);let m=!1;s===`post`?u.scheduler=e=>{Oi(e,p&&p.suspense)}:s!==`sync`&&(m=!0,u.scheduler=(e,t)=>{t?e():wn(e)}),u.augmentJob=e=>{t&&(e.flags|=4),m&&(e.flags|=2,p&&(e.id=p.uid,e.i=p))};let h=ln(e,t,u);return xa&&(f?f.push(h):d&&h()),h}function Wn(e,t,n){let r=this.proxy,i=v(e)?e.includes(`.`)?Gn(r,e):()=>r[e]:e.bind(r,r),a;_(t)?a=t:(a=t.handler,n=t);let o=va(this),s=Un(i,a.bind(r),n);return o(),s}function Gn(e,t){let n=t.split(`.`);return()=>{let t=e;for(let e=0;ee.__isTeleport,Jn=Symbol(`_leaveCb`);function Yn(e){let t=e[0];if(e.length>1){for(let n of e)if(n.type!==Ui){t=n;break}}return t}function Xn(e){if(!ar(e))return qn(e.type)&&e.children?Yn(e.children):e;if(e.component)return e.component.subTree;let{shapeFlag:t,children:n}=e;if(n){if(t&16)return n[0];if(t&32&&_(n.default))return n.default()}}function Zn(e,t){if(e.shapeFlag&6&&e.component){e.transition=t;let n=e.component.subTree;Zn(qn(n.type)&&Xn(n)||n,t)}else e.shapeFlag&128?(e.ssContent.transition=t.clone(e.ssContent),e.ssFallback.transition=t.clone(e.ssFallback)):e.transition=t}function Qn(e,t){return _(e)?l({name:e.name},t,{setup:e}):e}function $n(e){e.ids=[e.ids[0]+e.ids[2]+++`-`,0,0]}function er(e,t){let n;return!!((n=Object.getOwnPropertyDescriptor(e,t))&&!n.configurable)}var tr=new WeakMap;function nr(e,t,n,i,a=!1){if(p(e)){e.forEach((e,r)=>nr(e,t&&(p(t)?t[r]:t),n,i,a));return}if(ir(i)&&!a){i.shapeFlag&512&&i.type.__asyncResolved&&i.component.subTree.component&&nr(e,t,n,i.component.subTree);return}let s=i.shapeFlag&4?Oa(i.component):i.el,c=a?null:s,{i:l,r:d}=e,m=t&&t.r,h=l.refs===r?l.refs={}:l.refs,g=l.setupState,y=L(g),b=g===r?o:e=>!er(h,e)&&f(y,e),x=(e,t)=>!(t&&er(h,t));if(m!=null&&m!==d){if(rr(t),v(m))h[m]=null,b(m)&&(g[m]=null);else if(R(m)){let e=t;x(m,e.k)&&(m.value=null),e.k&&(h[e.k]=null)}}if(_(d))dn(d,l,12,[c,h]);else{let t=v(d),r=R(d);if(t||r){let i=()=>{if(e.f){let n=t?b(d)?g[d]:h[d]:x(d)||!e.k?d.value:h[e.k];if(a)p(n)&&u(n,s);else if(p(n))n.includes(s)||n.push(s);else if(t)h[d]=[s],b(d)&&(g[d]=h[d]);else{let t=[s];x(d,e.k)&&(d.value=t),e.k&&(h[e.k]=t)}}else t?(h[d]=c,b(d)&&(g[d]=c)):r&&(x(d,e.k)&&(d.value=c),e.k&&(h[e.k]=c))};if(c){let t=()=>{i(),tr.delete(e)};t.id=-1,tr.set(e,t),Oi(t,n)}else rr(e),i()}}}function rr(e){let t=tr.get(e);t&&(t.flags|=8,tr.delete(e))}M().requestIdleCallback,M().cancelIdleCallback;var ir=e=>!!e.type.__asyncLoader,ar=e=>e.type.__isKeepAlive;function or(e,t){cr(e,`a`,t)}function sr(e,t){cr(e,`da`,t)}function cr(e,t,n=W){let r=e.__wdc||=()=>{let t=n;for(;t;){if(t.isDeactivated)return;t=t.parent}return e()};if(ur(t,r,n),n){let e=n.parent;for(;e&&e.parent;)ar(e.parent.vnode)&&lr(r,t,n,e),e=e.parent}}function lr(e,t,n,r){let i=ur(t,e,r,!0);_r(()=>{u(r[t],i)},n)}function ur(e,t,n=W,r=!1){if(n){let i=n[e]||(n[e]=[]),a=t.__weh||=(...r)=>{We();let i=va(n),a=fn(t,n,e,r);return i(),Ge(),a};return r?i.unshift(a):i.push(a),a}}var dr=e=>(t,n=W)=>{(!xa||e===`sp`)&&ur(e,(...e)=>t(...e),n)},fr=dr(`bm`),pr=dr(`m`),mr=dr(`bu`),hr=dr(`u`),gr=dr(`bum`),_r=dr(`um`),vr=dr(`sp`),yr=dr(`rtg`),br=dr(`rtc`);function xr(e,t=W){ur(`ec`,e,t)}var Sr=`components`;function Cr(e,t){return Tr(Sr,e,!0,t)||e}var wr=Symbol.for(`v-ndc`);function Tr(e,t,n=!0,r=!1){let i=jn||W;if(i){let n=i.type;if(e===Sr){let e=ka(n,!1);if(e&&(e===t||e===T(t)||e===O(T(t))))return n}let a=Er(i[e]||n[e],t)||Er(i.appContext[e],t);return!a&&r?n:a}}function Er(e,t){return e&&(e[t]||e[T(t)]||e[O(T(t))])}function Dr(e,t,n,r){let i,a=n&&n[r],o=p(e);if(o||v(e)){let n=o&&Bt(e),r=!1,s=!1;n&&(r=!Ht(e),s=Vt(e),e=it(e)),i=Array(e.length);for(let n=0,o=e.length;nt(e,n,void 0,a&&a[n]));else{let n=Object.keys(e);i=Array(n.length);for(let r=0,o=n.length;re?ba(e)?Oa(e):Or(e.parent):null,kr=l(Object.create(null),{$:e=>e,$el:e=>e.vnode.el,$data:e=>e.data,$props:e=>e.props,$attrs:e=>e.attrs,$slots:e=>e.slots,$refs:e=>e.refs,$parent:e=>Or(e.parent),$root:e=>Or(e.root),$host:e=>e.ce,$emit:e=>e.emit,$options:e=>Rr(e),$forceUpdate:e=>e.f||=()=>{wn(e.update)},$nextTick:e=>e.n||=Sn.bind(e.proxy),$watch:e=>Wn.bind(e)}),Ar=(e,t)=>e!==r&&!e.__isScriptSetup&&f(e,t),jr={get({_:e},t){if(t===`__v_skip`)return!0;let{ctx:n,setupState:i,data:a,props:o,accessCache:s,type:c,appContext:l}=e;if(t[0]!==`$`){let e=s[t];if(e!==void 0)switch(e){case 1:return i[t];case 2:return a[t];case 4:return n[t];case 3:return o[t]}else if(Ar(i,t))return s[t]=1,i[t];else if(a!==r&&f(a,t))return s[t]=2,a[t];else if(f(o,t))return s[t]=3,o[t];else if(n!==r&&f(n,t))return s[t]=4,n[t];else Nr&&(s[t]=0)}let u=kr[t],d,p;if(u)return t===`$attrs`&&I(e.attrs,`get`,``),u(e);if((d=c.__cssModules)&&(d=d[t]))return d;if(n!==r&&f(n,t))return s[t]=4,n[t];if(p=l.config.globalProperties,f(p,t))return p[t]},set({_:e},t,n){let{data:i,setupState:a,ctx:o}=e;return Ar(a,t)?(a[t]=n,!0):i!==r&&f(i,t)?(i[t]=n,!0):f(e.props,t)||t[0]===`$`&&t.slice(1)in e?!1:(o[t]=n,!0)},has({_:{data:e,setupState:t,accessCache:n,ctx:i,appContext:a,props:o,type:s}},c){let l;return!!(n[c]||e!==r&&c[0]!==`$`&&f(e,c)||Ar(t,c)||f(o,c)||f(i,c)||f(kr,c)||f(a.config.globalProperties,c)||(l=s.__cssModules)&&l[c])},defineProperty(e,t,n){return n.get==null?f(n,`value`)&&this.set(e,t,n.value,null):e._.accessCache[t]=0,Reflect.defineProperty(e,t,n)}};function Mr(e){return p(e)?e.reduce((e,t)=>(e[t]=null,e),{}):e}var Nr=!0;function Pr(e){let t=Rr(e),n=e.proxy,r=e.ctx;Nr=!1,t.beforeCreate&&Ir(t.beforeCreate,e,`bc`);let{data:i,computed:o,methods:s,watch:c,provide:l,inject:u,created:d,beforeMount:f,mounted:m,beforeUpdate:h,updated:g,activated:v,deactivated:y,beforeDestroy:x,beforeUnmount:S,destroyed:C,unmounted:w,render:ee,renderTracked:te,renderTriggered:ne,errorCaptured:re,serverPrefetch:ie,expose:T,inheritAttrs:E,components:D,directives:O,filters:ae}=t;if(u&&Fr(u,r,null),s)for(let e in s){let t=s[e];_(t)&&(r[e]=t.bind(n))}if(i){let t=i.call(n,n);b(t)&&(e.data=It(t))}if(Nr=!0,o)for(let e in o){let t=o[e],i=G({get:_(t)?t.bind(n,n):_(t.get)?t.get.bind(n,n):a,set:!_(t)&&_(t.set)?t.set.bind(n):a});Object.defineProperty(r,e,{enumerable:!0,configurable:!0,get:()=>i.value,set:e=>i.value=e})}if(c)for(let e in c)Lr(c[e],r,n,e);if(l){let e=_(l)?l.call(n):l;Reflect.ownKeys(e).forEach(t=>{Ln(t,e[t])})}d&&Ir(d,e,`c`);function k(e,t){p(t)?t.forEach(t=>e(t.bind(n))):t&&e(t.bind(n))}if(k(fr,f),k(pr,m),k(mr,h),k(hr,g),k(or,v),k(sr,y),k(xr,re),k(br,te),k(yr,ne),k(gr,S),k(_r,w),k(vr,ie),p(T)){if(T.length){let t=e.exposed||={};T.forEach(e=>{Object.defineProperty(t,e,{get:()=>n[e],set:t=>n[e]=t,enumerable:!0})})}else e.exposed||={}}ee&&e.render===a&&(e.render=ee),E!=null&&(e.inheritAttrs=E),D&&(e.components=D),O&&(e.directives=O),ie&&$n(e)}function Fr(e,t,n=a){p(e)&&(e=Ur(e));for(let n in e){let r=e[n],i;i=b(r)?`default`in r?Rn(r.from||n,r.default,!0):Rn(r.from||n):Rn(r),R(i)?Object.defineProperty(t,n,{enumerable:!0,configurable:!0,get:()=>i.value,set:e=>i.value=e}):t[n]=i}}function Ir(e,t,n){fn(p(e)?e.map(e=>e.bind(t.proxy)):e.bind(t.proxy),t,n)}function Lr(e,t,n,r){let i=r.includes(`.`)?Gn(n,r):()=>n[r];if(v(e)){let n=t[e];_(n)&&Hn(i,n)}else if(_(e))Hn(i,e.bind(n));else if(b(e)){if(p(e))e.forEach(e=>Lr(e,t,n,r));else{let r=_(e.handler)?e.handler.bind(n):t[e.handler];_(r)&&Hn(i,r,e)}}}function Rr(e){let t=e.type,{mixins:n,extends:r}=t,{mixins:i,optionsCache:a,config:{optionMergeStrategies:o}}=e.appContext,s=a.get(t),c;return s?c=s:!i.length&&!n&&!r?c=t:(c={},i.length&&i.forEach(e=>zr(c,e,o,!0)),zr(c,t,o)),b(t)&&a.set(t,c),c}function zr(e,t,n,r=!1){let{mixins:i,extends:a}=t;a&&zr(e,a,n,!0),i&&i.forEach(t=>zr(e,t,n,!0));for(let i in t)if(!(r&&i===`expose`)){let r=Br[i]||n&&n[i];e[i]=r?r(e[i],t[i]):t[i]}return e}var Br={data:Vr,props:Kr,emits:Kr,methods:Gr,computed:Gr,beforeCreate:Wr,created:Wr,beforeMount:Wr,mounted:Wr,beforeUpdate:Wr,updated:Wr,beforeDestroy:Wr,beforeUnmount:Wr,destroyed:Wr,unmounted:Wr,activated:Wr,deactivated:Wr,errorCaptured:Wr,serverPrefetch:Wr,components:Gr,directives:Gr,watch:qr,provide:Vr,inject:Hr};function Vr(e,t){return t?e?function(){return l(_(e)?e.call(this,this):e,_(t)?t.call(this,this):t)}:t:e}function Hr(e,t){return Gr(Ur(e),Ur(t))}function Ur(e){if(p(e)){let t={};for(let n=0;nt===`modelValue`||t===`model-value`?e.modelModifiers:e[`${t}Modifiers`]||e[`${T(t)}Modifiers`]||e[`${D(t)}Modifiers`];function $r(e,t,...n){if(e.isUnmounted)return;let i=e.vnode.props||r,a=n,o=t.startsWith(`update:`),s=o&&Qr(i,t.slice(7));s&&(s.trim&&(a=n.map(e=>v(e)?e.trim():e)),s.number&&(a=a.map(A)));let c,l=i[c=ae(t)]||i[c=ae(T(t))];!l&&o&&(l=i[c=ae(D(t))]),l&&fn(l,e,6,a);let u=i[c+`Once`];if(u){if(!e.emitted)e.emitted={};else if(e.emitted[c])return;e.emitted[c]=!0,fn(u,e,6,a)}}var ei=new WeakMap;function ti(e,t,n=!1){let r=n?ei:t.emitsCache,i=r.get(e);if(i!==void 0)return i;let a=e.emits,o={},s=!1;if(!_(e)){let r=e=>{let n=ti(e,t,!0);n&&(s=!0,l(o,n))};!n&&t.mixins.length&&t.mixins.forEach(r),e.extends&&r(e.extends),e.mixins&&e.mixins.forEach(r)}return!a&&!s?(b(e)&&r.set(e,null),null):(p(a)?a.forEach(e=>o[e]=null):l(o,a),b(e)&&r.set(e,o),o)}function ni(e,t){return!e||!s(t)?!1:(t=t.slice(2),t=t===`Once`?t:t.replace(/Once$/,``),f(e,t[0].toLowerCase()+t.slice(1))||f(e,D(t))||f(e,t))}function ri(e){let{type:t,vnode:n,proxy:r,withProxy:i,propsOptions:[a],slots:o,attrs:s,emit:l,render:u,renderCache:d,props:f,data:p,setupState:m,ctx:h,inheritAttrs:g}=e,_=Nn(e),v,y;try{if(n.shapeFlag&4){let e=i||r,t=e;v=sa(u.call(t,e,d,f,m,p,h)),y=s}else{let e=t;v=sa(e.length>1?e(f,{attrs:s,slots:o,emit:l}):e(f,null)),y=t.props?s:ii(s)}}catch(t){Gi.length=0,pn(t,e,1),v=U(Ui)}let b=v;if(y&&g!==!1){let e=Object.keys(y),{shapeFlag:t}=b;e.length&&t&7&&(a&&e.some(c)&&(y=ai(y,a)),b=ia(b,y,!1,!0))}return n.dirs&&(b=ia(b,null,!1,!0),b.dirs=b.dirs?b.dirs.concat(n.dirs):n.dirs),n.transition&&Zn(qn(b.type)&&Xn(b)||b,n.transition),v=b,Nn(_),v}var ii=e=>{let t;for(let n in e)(n===`class`||n===`style`||s(n))&&((t||={})[n]=e[n]);return t},ai=(e,t)=>{let n={};for(let r in e)(!c(r)||!(r.slice(9)in t))&&(n[r]=e[r]);return n};function oi(e,t,n){let{props:r,children:i,component:a}=e,{props:o,children:s,patchFlag:c}=t,l=a.emitsOptions;if(t.dirs||t.transition)return!0;if(n&&c>=0){if(c&1024)return!0;if(c&16)return r?si(r,o,l):!!o;if(c&8){let e=t.dynamicProps;for(let t=0;tObject.create(ui),fi=e=>Object.getPrototypeOf(e)===ui;function pi(e,t,n,r=!1){let i={},a=di();e.propsDefaults=Object.create(null),hi(e,t,i,a);for(let t in e.propsOptions[0])t in i||(i[t]=void 0);e.props=n?r?i:Lt(i):e.type.props?i:a,e.attrs=a}function mi(e,t,n,r){let{props:i,attrs:a,vnode:{patchFlag:o}}=e,s=L(i),[c]=e.propsOptions,l=!1;if((r||o>0)&&!(o&16)){if(o&8){let n=e.vnode.dynamicProps;for(let r=0;r{d=!0;let[n,r]=vi(e,t,!0);l(c,n),r&&u.push(...r)};!n&&t.mixins.length&&t.mixins.forEach(r),e.extends&&r(e.extends),e.mixins&&e.mixins.forEach(r)}if(!s&&!d)return b(e)&&a.set(e,i),i;if(p(s))for(let e=0;ee===`_`||e===`_ctx`||e===`$stable`,xi=e=>p(e)?e.map(sa):[sa(e)],Si=(e,t,n)=>{if(t._n)return t;let r=Pn((...e)=>xi(t(...e)),n);return r._c=!1,r},Ci=(e,t,n)=>{let r=e._ctx;for(let n in e){if(bi(n))continue;let i=e[n];if(_(i))t[n]=Si(n,i,r);else if(i!=null){let e=xi(i);t[n]=()=>e}}},wi=(e,t)=>{let n=xi(t);e.slots.default=()=>n},Ti=(e,t,n)=>{for(let r in t)(n||!bi(r))&&(e[r]=t[r])},Ei=(e,t,n)=>{let r=e.slots=di();if(e.vnode.shapeFlag&32){let e=t._;e?(Ti(r,t,n),n&&se(r,`_`,e,!0)):Ci(t,r)}else t&&wi(e,t)},Di=(e,t,n)=>{let{vnode:i,slots:a}=e,o=!0,s=r;if(i.shapeFlag&32){let e=t._;e?n&&e===1?o=!1:Ti(a,t,n):(o=!t.$stable,Ci(t,a)),s=t}else t&&(wi(e,t),s={default:1});if(o)for(let e in a)!bi(e)&&s[e]==null&&delete a[e]},Oi=Bi;function ki(e){return Ai(e)}function Ai(e,t){let n=M();n.__VUE__=!0;let{insert:o,remove:s,patchProp:c,createElement:l,createText:u,createComment:d,setText:f,setElementText:p,parentNode:m,nextSibling:h,setScopeId:g=a,insertStaticContent:_}=e,v=(e,t,n,r=null,i=null,a=null,o=void 0,s=null,c=!!t.dynamicChildren)=>{if(e===t)return;e&&!$i(e,t)&&(r=ge(e),de(e,i,a,!0),e=null),t.patchFlag===-2&&(c=!1,t.dynamicChildren=null);let{type:l,ref:u,shapeFlag:d}=t;switch(l){case Hi:y(e,t,n,r);break;case Ui:b(e,t,n,r);break;case Wi:e??x(t,n,r,o);break;case Vi:D(e,t,n,r,i,a,o,s,c);break;default:d&1?w(e,t,n,r,i,a,o,s,c):d&6?O(e,t,n,r,i,a,o,s,c):(d&64||d&128)&&l.process(e,t,n,r,i,a,o,s,c,ye)}u!=null&&i?nr(u,e&&e.ref,a,t||e,!t):u==null&&e&&e.ref!=null&&nr(e.ref,null,a,e,!0)},y=(e,t,n,r)=>{if(e==null)o(t.el=u(t.children),n,r);else{let n=t.el=e.el;t.children!==e.children&&f(n,t.children)}},b=(e,t,n,r)=>{e==null?o(t.el=d(t.children||``),n,r):t.el=e.el},x=(e,t,n,r)=>{[e.el,e.anchor]=_(e.children,t,n,r,e.el,e.anchor)},S=({el:e,anchor:t},n,r)=>{let i;for(;e&&e!==t;)i=h(e),o(e,n,r),e=i;o(t,n,r)},C=({el:e,anchor:t})=>{let n;for(;e&&e!==t;)n=h(e),s(e),e=n;s(t)},w=(e,t,n,r,i,a,o,s,c)=>{if(t.type===`svg`?o=`svg`:t.type===`math`&&(o=`mathml`),e==null)ee(t,n,r,i,a,o,s,c);else{let n=e.el&&e.el._isVueCE?e.el:null;try{n&&n._beginPatch(),ie(e,t,i,a,o,s,c)}finally{n&&n._endPatch()}}},ee=(e,t,n,r,i,a,s,u)=>{let d,f,{props:m,shapeFlag:h,transition:g,dirs:_}=e;if(d=e.el=l(e.type,a,m&&m.is,m),h&8?p(d,e.children):h&16&&re(e.children,d,null,r,i,ji(e,a),s,u),_&&In(e,null,r,`created`),te(d,e,e.scopeId,s,r),m){for(let e in m)e!==`value`&&!ne(e)&&c(d,e,null,m[e],a,r);`value`in m&&c(d,`value`,null,m.value,a),(f=m.onVnodeBeforeMount)&&da(f,r,e)}_&&In(e,null,r,`beforeMount`);let v=Ni(i,g);v&&g.beforeEnter(d),o(d,t,n),((f=m&&m.onVnodeMounted)||v||_)&&Oi(()=>{try{f&&da(f,r,e),v&&g.enter(d),_&&In(e,null,r,`mounted`)}finally{}},i)},te=(e,t,n,r,i)=>{if(n&&g(e,n),r)for(let t=0;t{for(let l=c;l{let l=t.el=e.el,{patchFlag:u,dynamicChildren:d,dirs:f}=t;u|=e.patchFlag&16;let m=e.props||r,h=t.props||r,g;if(n&&Mi(n,!1),(g=h.onVnodeBeforeUpdate)&&da(g,n,t,e),f&&In(t,e,n,`beforeUpdate`),n&&Mi(n,!0),d&&(!e.dynamicChildren||e.dynamicChildren.length!==d.length)&&(u=0,s=!1,d=null),(m.innerHTML&&h.innerHTML==null||m.textContent&&h.textContent==null)&&p(l,``),d?T(e.dynamicChildren,d,l,n,i,ji(t,a),o):s||j(e,t,l,null,n,i,ji(t,a),o,!1),u>0){if(u&16)E(l,m,h,n,a);else if(u&2&&m.class!==h.class&&c(l,`class`,null,h.class,a),u&4&&c(l,`style`,m.style,h.style,a),u&8){let e=t.dynamicProps;for(let t=0;t{g&&da(g,n,t,e),f&&In(t,e,n,`updated`)},i)},T=(e,t,n,r,i,a,o)=>{for(let s=0;s{if(t!==n){if(t!==r)for(let r in t)!ne(r)&&!(r in n)&&c(e,r,t[r],null,a,i);for(let r in n){if(ne(r))continue;let o=n[r],s=t[r];o!==s&&r!==`value`&&c(e,r,s,o,a,i)}`value`in n&&c(e,`value`,t.value,n.value,a)}},D=(e,t,n,r,i,a,s,c,l)=>{let d=t.el=e?e.el:u(``),f=t.anchor=e?e.anchor:u(``),{patchFlag:p,dynamicChildren:m,slotScopeIds:h}=t;h&&(c=c?c.concat(h):h),e==null?(o(d,n,r),o(f,n,r),re(t.children||[],n,f,i,a,s,c,l)):p>0&&p&64&&m&&e.dynamicChildren&&e.dynamicChildren.length===m.length?(T(e.dynamicChildren,m,n,i,a,s,c),(t.key!=null||i&&t===i.subTree)&&Pi(e,t,!0)):j(e,t,n,f,i,a,s,c,l)},O=(e,t,n,r,i,a,o,s,c)=>{t.slotScopeIds=s,e==null?t.shapeFlag&512?i.ctx.activate(t,n,r,o,c):ae(t,n,r,i,a,o,c):k(e,t,c)},ae=(e,t,n,r,i,a,o)=>{let s=e.component=ma(e,r,i);if(ar(e)&&(s.ctx.renderer=ye),Sa(s,!1,o),s.asyncDep){if(i&&i.registerDep(s,se,o),!e.el){let r=s.subTree=U(Ui);b(null,r,t,n),e.placeholder=r.el}}else se(s,e,t,n,i,a,o)},k=(e,t,n)=>{let r=t.component=e.component;if(oi(e,t,n)){if(r.asyncDep&&!r.asyncResolved){A(r,t,n);return}r.next=t,r.update()}else t.el=e.el,r.vnode=t},se=(e,t,n,r,i,a,o)=>{let s=()=>{if(e.isMounted){let{next:t,bu:n,u:r,parent:s,vnode:c}=e;{let n=Ii(e);if(n){t&&(t.el=c.el,A(e,t,o)),n.asyncDep.then(()=>{Oi(()=>{e.isUnmounted||l()},i)});return}}let u=t,d;Mi(e,!1),t?(t.el=c.el,A(e,t,o)):t=c,n&&oe(n),(d=t.props&&t.props.onVnodeBeforeUpdate)&&da(d,s,t,c),Mi(e,!0);let f=ri(e),p=e.subTree;e.subTree=f,v(p,f,m(p.el),ge(p),e,i,a),t.el=f.el,u===null&&li(e,f.el),r&&Oi(r,i),(d=t.props&&t.props.onVnodeUpdated)&&Oi(()=>da(d,s,t,c),i)}else{let o,{el:s,props:c}=t,{bm:l,m:u,parent:d,root:f,type:p}=e,m=ir(t);if(Mi(e,!1),l&&oe(l),!m&&(o=c&&c.onVnodeBeforeMount)&&da(o,d,t),Mi(e,!0),s&&xe){let t=()=>{e.subTree=ri(e),xe(s,e.subTree,e,i,null)};m&&p.__asyncHydrate?p.__asyncHydrate(s,e,t):t()}else{f.ce&&f.ce._hasShadowRoot()&&f.ce._injectChildStyle(p,e.parent?e.parent.type:void 0);let o=e.subTree=ri(e);v(null,o,n,r,e,i,a),t.el=o.el}if(u&&Oi(u,i),!m&&(o=c&&c.onVnodeMounted)){let e=t;Oi(()=>da(o,d,e),i)}(t.shapeFlag&256||d&&ir(d.vnode)&&d.vnode.shapeFlag&256)&&e.a&&Oi(e.a,i),e.isMounted=!0,t=n=r=null}};e.scope.on();let c=e.effect=new ke(s);e.scope.off();let l=e.update=c.run.bind(c),u=e.job=c.runIfDirty.bind(c);u.i=e,u.id=e.uid,c.scheduler=()=>wn(u),Mi(e,!0),l()},A=(e,t,n)=>{t.component=e;let r=e.vnode.props;e.vnode=t,e.next=null,mi(e,t.props,r,n),Di(e,t.children,n),We(),Dn(e),Ge()},j=(e,t,n,r,i,a,o,s,c=!1)=>{let l=e&&e.children,u=e?e.shapeFlag:0,d=t.children,{patchFlag:f,shapeFlag:m}=t;if(f>0){if(f&128){le(l,d,n,r,i,a,o,s,c);return}if(f&256){ce(l,d,n,r,i,a,o,s,c);return}}m&8?(u&16&&he(l,i,a),d!==l&&p(n,d)):u&16?m&16?le(l,d,n,r,i,a,o,s,c):he(l,i,a,!0):(u&8&&p(n,``),m&16&&re(d,n,r,i,a,o,s,c))},ce=(e,t,n,r,a,o,s,c,l)=>{e||=i,t||=i;let u=e.length,d=t.length,f=Math.min(u,d),p;for(p=0;pd?he(e,a,o,!0,!1,f):re(t,n,r,a,o,s,c,l,f)},le=(e,t,n,r,a,o,s,c,l)=>{let u=0,d=t.length,f=e.length-1,p=d-1;for(;u<=f&&u<=p;){let r=e[u],i=t[u]=l?ca(t[u]):sa(t[u]);if($i(r,i))v(r,i,n,null,a,o,s,c,l);else break;u++}for(;u<=f&&u<=p;){let r=e[f],i=t[p]=l?ca(t[p]):sa(t[p]);if($i(r,i))v(r,i,n,null,a,o,s,c,l);else break;f--,p--}if(u>f){if(u<=p){let e=p+1,i=ep)for(;u<=f;)de(e[u],a,o,!0),u++;else{let m=u,h=u,g=new Map;for(u=h;u<=p;u++){let e=t[u]=l?ca(t[u]):sa(t[u]);e.key!=null&&g.set(e.key,u)}let _,y=0,b=p-h+1,x=!1,S=0,C=Array(b);for(u=0;u=b){de(r,a,o,!0);continue}let i;if(r.key!=null)i=g.get(r.key);else for(_=h;_<=p;_++)if(C[_-h]===0&&$i(r,t[_])){i=_;break}i===void 0?de(r,a,o,!0):(C[i-h]=u+1,i>=S?S=i:x=!0,v(r,t[i],n,null,a,o,s,c,l),y++)}let w=x?Fi(C):i;for(_=w.length-1,u=b-1;u>=0;u--){let e=h+u,i=t[e],f=t[e+1],p=e+1{let{el:a,type:c,transition:l,children:u,shapeFlag:d}=e;if(d&6){ue(e.component.subTree,t,n,r);return}if(d&128){e.suspense.move(t,n,r);return}if(d&64){c.move(e,t,n,ye);return}if(c===Vi){o(a,t,n);for(let e=0;el.enter(a),i));else{let{leave:r,delayLeave:i,afterLeave:c}=l,u=()=>{e.ctx.isUnmounted?s(a):o(a,t,n)},d=()=>{let e=a._isLeaving||!!a[Jn];a._isLeaving&&a[Jn](!0),l.persisted&&!e?u():r(a,()=>{u(),c&&c()})};i?i(a,u,d):d()}}else o(a,t,n)},de=(e,t,n,r=!1,i=!1)=>{let{type:a,props:o,ref:s,children:c,dynamicChildren:l,shapeFlag:u,patchFlag:d,dirs:f,cacheIndex:p,memo:m}=e;if(d===-2&&(i=!1),s!=null&&(We(),nr(s,null,n,e,!0),Ge()),p!=null&&(t.renderCache[p]=void 0),u&256){t.ctx.deactivate(e);return}let h=u&1&&f,g=!ir(e),_;if(g&&(_=o&&o.onVnodeBeforeUnmount)&&da(_,t,e),u&6)me(e.component,n,r);else{if(u&128){e.suspense.unmount(n,r);return}h&&In(e,null,t,`beforeUnmount`),u&64?e.type.remove(e,t,n,ye,r):l&&!l.hasOnce&&(a!==Vi||d>0&&d&64)?he(l,t,n,!1,!0):(a===Vi&&d&384||!i&&u&16)&&he(c,t,n),r&&fe(e)}let v=m!=null&&p==null;(g&&(_=o&&o.onVnodeUnmounted)||h||v)&&Oi(()=>{_&&da(_,t,e),h&&In(e,null,t,`unmounted`),v&&(e.el=null)},n)},fe=e=>{let{type:t,el:n,anchor:r,transition:i}=e;if(t===Vi){pe(n,r);return}if(t===Wi){C(e);return}let a=()=>{s(n),i&&!i.persisted&&i.afterLeave&&i.afterLeave()};if(e.shapeFlag&1&&i&&!i.persisted){let{leave:t,delayLeave:r}=i,o=()=>t(n,a);r?r(e.el,a,o):o()}else a()},pe=(e,t)=>{let n;for(;e!==t;)n=h(e),s(e),e=n;s(t)},me=(e,t,n)=>{let{bum:r,scope:i,job:a,subTree:o,um:s,m:c,a:l}=e;Li(c),Li(l),r&&oe(r),i.stop(),a&&(a.flags|=8,de(o,e,t,n)),s&&Oi(s,t),Oi(()=>{e.isUnmounted=!0},t)},he=(e,t,n,r=!1,i=!1,a=0)=>{for(let o=a;o{if(e.shapeFlag&6)return ge(e.component.subTree);if(e.shapeFlag&128)return e.suspense.next();let t=h(e.anchor||e.el),n=t&&t[Kn];return n?h(n):t},_e=!1,ve=(e,t,n)=>{let r;e==null?t._vnode&&(de(t._vnode,null,null,!0),r=t._vnode.component):v(t._vnode||null,e,t,null,null,null,n),t._vnode=e,_e||=(_e=!0,Dn(r),On(),!1)},ye={p:v,um:de,m:ue,r:fe,mt:ae,mc:re,pc:j,pbc:T,n:ge,o:e},be,xe;return t&&([be,xe]=t(ye)),{render:ve,hydrate:be,createApp:Xr(ve,be)}}function ji({type:e,props:t},n){return n===`svg`&&e===`foreignObject`||n===`mathml`&&e===`annotation-xml`&&t&&t.encoding&&t.encoding.includes(`html`)?void 0:n}function Mi({effect:e,job:t},n){n?(e.flags|=32,t.flags|=4):(e.flags&=-33,t.flags&=-5)}function Ni(e,t){return(!e||e&&!e.pendingBranch)&&t&&!t.persisted}function Pi(e,t,n=!1){let r=e.children,i=t.children;if(p(r)&&p(i))for(let e=0;e>1,e[n[s]]0&&(t[r]=n[a-1]),n[a]=r)}}for(a=n.length,o=n[a-1];a-->0;)n[a]=o,o=t[o];return n}function Ii(e){let t=e.subTree.component;if(t)return t.asyncDep&&!t.asyncResolved?t:Ii(t)}function Li(e){if(e)for(let t=0;te.__isSuspense;function Bi(e,t){t&&t.pendingBranch?p(e)?t.effects.push(...e):t.effects.push(e):En(e)}var Vi=Symbol.for(`v-fgt`),Hi=Symbol.for(`v-txt`),Ui=Symbol.for(`v-cmt`),Wi=Symbol.for(`v-stc`),Gi=[],Ki=null;function B(e=!1){Gi.push(Ki=e?null:[])}function qi(){Gi.pop(),Ki=Gi[Gi.length-1]||null}var Ji=1;function Yi(e,t=!1){Ji+=e,e<0&&Ki&&t&&(Ki.hasOnce=!0)}function Xi(e){return e.dynamicChildren=Ji>0?Ki||i:null,qi(),Ji>0&&Ki&&Ki.push(e),e}function V(e,t,n,r,i,a){return Xi(H(e,t,n,r,i,a,!0))}function Zi(e,t,n,r,i){return Xi(U(e,t,n,r,i,!0))}function Qi(e){return e?e.__v_isVNode===!0:!1}function $i(e,t){return e.type===t.type&&e.key===t.key}var ea=({key:e})=>e??null,ta=({ref:e,ref_key:t,ref_for:n})=>(typeof e==`number`&&(e=``+e),e==null?null:v(e)||R(e)||_(e)?{i:jn,r:e,k:t,f:!!n}:e);function H(e,t=null,n=null,r=0,i=null,a=e===Vi?0:1,o=!1,s=!1){let c={__v_isVNode:!0,__v_skip:!0,type:e,props:t,key:t&&ea(t),ref:t&&ta(t),scopeId:Mn,slotScopeIds:null,children:n,component:null,suspense:null,ssContent:null,ssFallback:null,dirs:null,transition:null,el:null,anchor:null,target:null,targetStart:null,targetAnchor:null,staticCount:0,shapeFlag:a,patchFlag:r,dynamicProps:i,dynamicChildren:null,appContext:null,ctx:jn};return s?(la(c,n),a&128&&e.normalize(c)):n&&(c.shapeFlag|=v(n)?8:16),Ji>0&&!o&&Ki&&(c.patchFlag>0||a&6)&&c.patchFlag!==32&&Ki.push(c),c}var U=na;function na(e,t=null,n=null,r=0,i=null,a=!1){if((!e||e===wr)&&(e=Ui),Qi(e)){let r=ia(e,t,!0);return n&&la(r,n),Ji>0&&!a&&Ki&&(r.shapeFlag&6?Ki[Ki.indexOf(e)]=r:Ki.push(r)),r.patchFlag=-2,r}if(Aa(e)&&(e=e.__vccOpts),t){t=ra(t);let{class:e,style:n}=t;e&&!v(e)&&(t.class=pe(e)),b(n)&&(Ut(n)&&!p(n)&&(n=l({},n)),t.style=ce(n))}let o=v(e)?1:zi(e)?128:qn(e)?64:b(e)?4:_(e)?2:0;return H(e,t,n,r,i,o,a,!0)}function ra(e){return e?Ut(e)||fi(e)?l({},e):e:null}function ia(e,t,n=!1,r=!1){let{props:i,ref:a,patchFlag:o,children:s,transition:c}=e,l=t?ua(i||{},t):i,u={__v_isVNode:!0,__v_skip:!0,type:e.type,props:l,key:l&&ea(l),ref:t&&t.ref?n&&a?p(a)?a.concat(ta(t)):[a,ta(t)]:ta(t):a,scopeId:e.scopeId,slotScopeIds:e.slotScopeIds,children:s,target:e.target,targetStart:e.targetStart,targetAnchor:e.targetAnchor,staticCount:e.staticCount,shapeFlag:e.shapeFlag,patchFlag:t&&e.type!==Vi?o===-1?16:o|16:o,dynamicProps:e.dynamicProps,dynamicChildren:e.dynamicChildren,appContext:e.appContext,dirs:e.dirs,transition:c,component:e.component,suspense:e.suspense,ssContent:e.ssContent&&ia(e.ssContent),ssFallback:e.ssFallback&&ia(e.ssFallback),placeholder:e.placeholder,el:e.el,anchor:e.anchor,ctx:e.ctx,ce:e.ce};return c&&r&&Zn(u,c.clone(u)),u}function aa(e=` `,t=0){return U(Hi,null,e,t)}function oa(e=``,t=!1){return t?(B(),Zi(Ui,null,e)):U(Ui,null,e)}function sa(e){return e==null||typeof e==`boolean`?U(Ui):p(e)?U(Vi,null,e.slice()):Qi(e)?ca(e):U(Hi,null,String(e))}function ca(e){return e.el===null&&e.patchFlag!==-1||e.memo?e:ia(e)}function la(e,t){let n=0,{shapeFlag:r}=e;if(t==null)t=null;else if(p(t))n=16;else if(typeof t==`object`){if(r&65){let n=t.default;n&&(n._c&&(n._d=!1),la(e,n()),n._c&&(n._d=!0));return}{n=32;let r=t._;!r&&!fi(t)?t._ctx=jn:r===3&&jn&&(jn.slots._===1?t._=1:(t._=2,e.patchFlag|=1024))}}else if(_(t)){if(r&65){la(e,{default:t});return}t={default:t,_ctx:jn},n=32}else t=String(t),r&64?(n=16,t=[aa(t)]):n=8;e.children=t,e.shapeFlag|=n}function ua(...e){let t={};for(let n=0;nW||jn,ga,_a;{let e=M(),t=(t,n)=>{let r;return(r=e[t])||(r=e[t]=[]),r.push(n),e=>{r.length>1?r.forEach(t=>t(e)):r[0](e)}};ga=t(`__VUE_INSTANCE_SETTERS__`,e=>W=e),_a=t(`__VUE_SSR_SETTERS__`,e=>xa=e)}var va=e=>{let t=W;return ga(e),e.scope.on(),()=>{e.scope.off(),ga(t)}},ya=()=>{W&&W.scope.off(),ga(null)};function ba(e){return e.vnode.shapeFlag&4}var xa=!1;function Sa(e,t=!1,n=!1){t&&_a(t);let{props:r,children:i}=e.vnode,a=ba(e);pi(e,r,a,t),Ei(e,i,n||t);let o=a?Ca(e,t):void 0;return t&&_a(!1),o}function Ca(e,t){let n=e.type;e.accessCache=Object.create(null),e.proxy=new Proxy(e.ctx,jr);let{setup:r}=n;if(r){We();let n=e.setupContext=r.length>1?Da(e):null,i=va(e),a=dn(r,e,0,[e.props,n]),o=x(a);if(Ge(),i(),(o||e.sp)&&!ir(e)&&$n(e),o){if(a.then(ya,ya),t)return a.then(n=>{_a(!0);try{wa(e,n,t)}finally{_a(!1)}}).catch(t=>{pn(t,e,0)});e.asyncDep=a}else wa(e,a,t)}else Ta(e,t)}function wa(e,t,n){_(t)?e.type.__ssrInlineRender?e.ssrRender=t:e.render=t:b(t)&&(e.setupState=Qt(t)),Ta(e,n)}function Ta(e,t,n){let r=e.type;e.render||=r.render||a;{let t=va(e);We();try{Pr(e)}finally{Ge(),t()}}}var Ea={get(e,t){return I(e,`get`,``),e[t]}};function Da(e){return{attrs:new Proxy(e.attrs,Ea),slots:e.slots,emit:e.emit,expose:t=>{e.exposed=t||{}}}}function Oa(e){return e.exposed?e.exposeProxy||=new Proxy(Qt(Wt(e.exposed)),{get(t,n){if(n in t)return t[n];if(n in kr)return kr[n](e)},has(e,t){return t in e||t in kr}}):e.proxy}function ka(e,t=!0){return _(e)?e.displayName||e.name:e.name||t&&e.__name}function Aa(e){return _(e)&&`__vccOpts`in e}var G=(e,t)=>rn(e,t,xa);function ja(e,t,n){try{Yi(-1);let r=arguments.length;return r===2?b(t)&&!p(t)?Qi(t)?U(e,null,[t]):U(e,t):U(e,null,t):(r>3?n=Array.prototype.slice.call(arguments,2):r===3&&Qi(n)&&(n=[n]),U(e,t,n))}finally{Yi(1)}}var Ma=`3.5.42`,Na=void 0,Pa=typeof window<`u`&&window.trustedTypes;if(Pa)try{Na=Pa.createPolicy(`vue`,{createHTML:e=>e})}catch{}var Fa=Na?e=>Na.createHTML(e):e=>e,Ia=`http://www.w3.org/2000/svg`,La=`http://www.w3.org/1998/Math/MathML`,Ra=typeof document<`u`?document:null,za=Ra&&Ra.createElement(`template`),Ba={insert:(e,t,n)=>{t.insertBefore(e,n||null)},remove:e=>{let t=e.parentNode;t&&t.removeChild(e)},createElement:(e,t,n,r)=>{let i=t===`svg`?Ra.createElementNS(Ia,e):t===`mathml`?Ra.createElementNS(La,e):n?Ra.createElement(e,{is:n}):Ra.createElement(e);return e===`select`&&r&&r.multiple!=null&&i.setAttribute(`multiple`,r.multiple),i},createText:e=>Ra.createTextNode(e),createComment:e=>Ra.createComment(e),setText:(e,t)=>{e.nodeValue=t},setElementText:(e,t)=>{e.textContent=t},parentNode:e=>e.parentNode,nextSibling:e=>e.nextSibling,querySelector:e=>Ra.querySelector(e),setScopeId(e,t){e.setAttribute(t,``)},insertStaticContent(e,t,n,r,i,a){let o=n?n.previousSibling:t.lastChild;if(i&&(i===a||i.nextSibling))for(;t.insertBefore(i.cloneNode(!0),n),!(i===a||!(i=i.nextSibling)););else{za.innerHTML=Fa(r===`svg`?`${e}`:r===`mathml`?`${e}`:e);let i=za.content;if(r===`svg`||r===`mathml`){let e=i.firstChild;for(;e.firstChild;)i.appendChild(e.firstChild);i.removeChild(e)}t.insertBefore(i,n)}return[o?o.nextSibling:t.firstChild,n?n.previousSibling:t.lastChild]}},Va=Symbol(`_vtc`);function Ha(e,t,n){let r=e[Va];r&&(t=(t?[t,...r]:[...r]).join(` `)),t==null?e.removeAttribute(`class`):n?e.setAttribute(`class`,t):e.className=t}var Ua=Symbol(`_vod`),Wa=Symbol(`_vsh`),Ga=Symbol(``),Ka=/(?:^|;)\s*display\s*:/;function qa(e,t,n){let r=e.style,i=v(n),a=!1;if(n&&!i){if(t){if(v(t))for(let e of t.split(`;`)){let t=e.slice(0,e.indexOf(`:`)).trim();n[t]??Ya(r,t,``)}else for(let e in t)n[e]??Ya(r,e,``)}for(let i in n){i===`display`&&(a=!0);let o=n[i];o==null?Ya(r,i,``):$a(e,i,!v(t)&&t?t[i]:void 0,o)||Ya(r,i,o)}}else if(i){if(t!==n){let e=r[Ga];e&&(n+=`;`+e),r.cssText=n,a=Ka.test(n)}}else t&&e.removeAttribute(`style`);Ua in e&&(e[Ua]=a?r.display:``,e[Wa]&&(r.display=`none`))}var Ja=/\s*!important$/;function Ya(e,t,n){if(p(n))n.forEach(n=>Ya(e,t,n));else if(n??=``,t.startsWith(`--`))Ja.test(n)?e.setProperty(t,n.replace(Ja,``),`important`):e.setProperty(t,n);else{let r=Qa(e,t);Ja.test(n)?e.setProperty(D(r),n.replace(Ja,``),`important`):e[r]=n}}var Xa=[`Webkit`,`Moz`,`ms`],Za={};function Qa(e,t){let n=Za[t];if(n)return n;let r=T(t);if(r!==`filter`&&r in e)return Za[t]=r;r=O(r);for(let n=0;nuo||=(fo.then(()=>uo=0),Date.now());function mo(e,t){let n=e=>{if(!e._vts)e._vts=Date.now();else if(e._vts<=n.attached)return;let r=n.value;if(p(r)){let n=e.stopImmediatePropagation;e.stopImmediatePropagation=()=>{n.call(e),e._stopped=!0};let i=r.slice(),a=[e];for(let n=0;ne.charCodeAt(0)===111&&e.charCodeAt(1)===110&&e.charCodeAt(2)>96&&e.charCodeAt(2)<123,go=(e,t,n,r,i,a)=>{let o=i===`svg`;t===`class`?Ha(e,r,o):t===`style`?qa(e,n,r):s(t)?c(t)||oo(e,t,n,r,a):(t[0]===`.`?(t=t.slice(1),!0):t[0]===`^`?(t=t.slice(1),!1):_o(e,t,r,o))?(no(e,t,r),!e.tagName.includes(`-`)&&(t===`value`||t===`checked`||t===`selected`)&&to(e,t,r,o,a,t!==`value`)):e._isVueCE&&(vo(e,t)||e._def.__asyncLoader&&(/[A-Z]/.test(t)||!v(r)))?no(e,T(t),r,a,t):(t===`true-value`?e._trueValue=r:t===`false-value`&&(e._falseValue=r),to(e,t,r,o))};function _o(e,t,n,r){if(r)return!!(t===`innerHTML`||t===`textContent`||t in e&&ho(t)&&_(n));if(t===`spellcheck`||t===`draggable`||t===`translate`||t===`autocorrect`||t===`sandbox`&&e.tagName===`IFRAME`||t===`form`||t===`list`&&e.tagName===`INPUT`||t===`type`&&e.tagName===`TEXTAREA`)return!1;if(t===`width`||t===`height`){let t=e.tagName;if(t===`IMG`||t===`VIDEO`||t===`CANVAS`||t===`SOURCE`)return!1}return ho(t)&&v(n)?!1:t in e}function vo(e,t){let n=e._def.props;if(!n)return!1;let r=T(t);return Array.isArray(n)?n.some(e=>T(e)===r):Object.keys(n).some(e=>T(e)===r)}var yo=e=>{let t=e.props[`onUpdate:modelValue`]||!1;return p(t)?e=>oe(t,e):t};function bo(e){e.target.composing=!0}function xo(e){let t=e.target;t.composing&&(t.composing=!1,t.dispatchEvent(new Event(`input`)))}var So=Symbol(`_assign`),Co=Symbol(`_initialValue`);function wo(e,t,n){return t&&(e=e.trim()),n&&(e=A(e)),e}var To={created(e,{modifiers:{lazy:t,trim:n,number:r}},i){e.parentNode&&(e.type===`text`?e[Co]=e.defaultValue.replace(/[\r\n]/g,``):e.type===`textarea`&&(e[Co]=e.defaultValue.replace(/\r\n?/g,` +`))),e[So]=yo(i);let a=r||i.props&&i.props.type===`number`;ro(e,t?`change`:`input`,t=>{t.target.composing||e[So](wo(e.value,n,a))}),(n||a)&&ro(e,`change`,()=>{e.value=wo(e.value,n,a)}),t||(ro(e,`compositionstart`,bo),ro(e,`compositionend`,xo),ro(e,`change`,xo))},mounted(e,{value:t,modifiers:{trim:n,number:r}}){let i=t??``,a=e[Co];delete e[Co],a!==void 0&&(e.type===`text`||e.type===`textarea`)&&e.value!==a?e[So](wo(e.value,n,r)):e.value=i},beforeUpdate(e,{value:t,oldValue:n,modifiers:{lazy:r,trim:i,number:a}},o){if(e[So]=yo(o),e.composing)return;let s=(a||e.type===`number`)&&!/^0\d/.test(e.value)?A(e.value):e.value,c=t??``;if(s===c)return;let l=e.getRootNode();(l instanceof Document||l instanceof ShadowRoot)&&l.activeElement===e&&e.type!==`range`&&(r&&t===n||i&&e.value.trim()===c)||(e.value=c)}},Eo={deep:!0,created(e,t,n){e[So]=yo(n),ro(e,`change`,()=>{let t=e._modelValue,n=jo(e),r=e.checked,i=e[So];if(p(t)){let e=be(t,n),a=e!==-1;if(r&&!a)i(t.concat(n));else if(!r&&a){let n=[...t];n.splice(e,1),i(n)}}else if(h(t)){let e=new Set(t);r?e.add(n):e.delete(n),i(e)}else i(Mo(e,r))})},mounted:Do,beforeUpdate(e,t,n){e[So]=yo(n),Do(e,t,n)}};function Do(e,{value:t,oldValue:n},r){e._modelValue=t;let i;if(p(t))i=be(t,r.props.value)>-1;else if(h(t))i=t.has(r.props.value);else{if(t===n)return;i=ye(t,Mo(e,!0))}e.checked!==i&&(e.checked=i)}var Oo={deep:!0,created(e,{value:t,modifiers:{number:n}},r){e._modelValue=t,ro(e,`change`,()=>{let t=Array.prototype.filter.call(e.options,e=>e.selected).map(e=>n?A(jo(e)):jo(e)),r=e.multiple,i=r?h(e._modelValue)?new Set(t):t:t[0],a=e._pendingValue=[r,r?p(i)?t.slice():t:i];try{e[So](i)}finally{Sn(()=>{e._pendingValue===a&&(e._pendingValue=void 0)})}}),e[So]=yo(r)},mounted(e,{value:t}){Ao(e,t)},beforeUpdate(e,{value:t},n){e._modelValue=t,e[So]=yo(n)},updated(e,{value:t}){let n=e._pendingValue;e._pendingValue=void 0,(!n||n[0]!==e.multiple||!ko(t,n[1],n[0]))&&Ao(e,t)}};function ko(e,t,n){if(!n||p(e))return ye(e,t);if(h(e)){if(e.size!==t.length)return!1;for(let n of t)if(!e.has(n))return!1;return!0}return!1}function Ao(e,t){let n=e.multiple,r=p(t);if(!(n&&!r&&!h(t))){for(let i=0,a=e.options.length;iString(e)===String(o)):be(t,o)>-1}else a.selected=t.has(o)}else if(ye(jo(a),t)){e.selectedIndex!==i&&(e.selectedIndex=i);return}}!n&&e.selectedIndex!==-1&&(e.selectedIndex=-1)}}function jo(e){return`_value`in e?e._value:e.value}function Mo(e,t){let n=t?`_trueValue`:`_falseValue`;return n in e?e[n]:t}var No=[`ctrl`,`shift`,`alt`,`meta`],Po={stop:e=>e.stopPropagation(),prevent:e=>e.preventDefault(),self:e=>e.target!==e.currentTarget,ctrl:e=>!e.ctrlKey,shift:e=>!e.shiftKey,alt:e=>!e.altKey,meta:e=>!e.metaKey,left:e=>`button`in e&&e.button!==0,middle:e=>`button`in e&&e.button!==1,right:e=>`button`in e&&e.button!==2,exact:(e,t)=>No.some(n=>e[`${n}Key`]&&!t.includes(n))},Fo=(e,t)=>{if(!e)return e;let n=e._withMods||={},r=t.join(`.`);return n[r]||(n[r]=((n,...r)=>{for(let e=0;e{let t=Ro().createApp(...e),{mount:n}=t;return t.mount=e=>{let r=Vo(e);if(!r)return;let i=t._component;!_(i)&&!i.render&&!i.template&&(i.template=r.innerHTML),r.nodeType===1&&(r.textContent=``);let a=n(r,!1,Bo(r));return r instanceof Element&&(r.removeAttribute(`v-cloak`),r.setAttribute(`data-v-app`,``)),a},t});function Bo(e){if(e instanceof SVGElement)return`svg`;if(typeof MathMLElement==`function`&&e instanceof MathMLElement)return`mathml`}function Vo(e){return v(e)?document.querySelector(e):e}var Ho=typeof window<`u`,Uo,Wo=e=>Uo=e,Go=Symbol();function Ko(e){return e&&typeof e==`object`&&Object.prototype.toString.call(e)===`[object Object]`&&typeof e.toJSON!=`function`}var qo=typeof window==`object`&&window.window===window?window:typeof self==`object`&&self.self===self?self:typeof global==`object`&&global.global===global?global:typeof globalThis==`object`?globalThis:{HTMLElement:null};function Jo(e,{autoBom:t=!1}={}){return t&&/^\s*(?:text\/\S*|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i.test(e.type)?new Blob([``,e],{type:e.type}):e}function Yo(e,t,n){let r=new XMLHttpRequest;r.open(`GET`,e),r.responseType=`blob`,r.onload=function(){es(r.response,t,n)},r.onerror=function(){console.error(`could not download file`)},r.send()}function Xo(e){let t=new XMLHttpRequest;t.open(`HEAD`,e,!1);try{t.send()}catch{}return t.status>=200&&t.status<=299}function Zo(e){try{e.dispatchEvent(new MouseEvent(`click`))}catch{let t=new MouseEvent(`click`,{bubbles:!0,cancelable:!0,view:window,detail:0,screenX:80,screenY:20,clientX:80,clientY:20,ctrlKey:!1,altKey:!1,shiftKey:!1,metaKey:!1,button:0,relatedTarget:null});e.dispatchEvent(t)}}var Qo=typeof navigator==`object`?navigator:{userAgent:``},$o=/Macintosh/.test(Qo.userAgent)&&/AppleWebKit/.test(Qo.userAgent)&&!/Safari/.test(Qo.userAgent),es=Ho?typeof HTMLAnchorElement<`u`&&`download`in HTMLAnchorElement.prototype&&!$o?ts:`msSaveOrOpenBlob`in Qo?ns:rs:()=>{};function ts(e,t=`download`,n){let r=document.createElement(`a`);r.download=t,r.rel=`noopener`,typeof e==`string`?(r.href=e,r.origin===location.origin?Zo(r):Xo(r.href)?Yo(e,t,n):(r.target=`_blank`,Zo(r))):(r.href=URL.createObjectURL(e),setTimeout(function(){URL.revokeObjectURL(r.href)},4e4),setTimeout(function(){Zo(r)},0))}function ns(e,t=`download`,n){if(typeof e==`string`){if(Xo(e))Yo(e,t,n);else{let t=document.createElement(`a`);t.href=e,t.target=`_blank`,setTimeout(function(){Zo(t)})}}else navigator.msSaveOrOpenBlob(Jo(e,n),t)}function rs(e,t,n,r){if(r||=open(``,`_blank`),r&&(r.document.title=r.document.body.innerText=`downloading...`),typeof e==`string`)return Yo(e,t,n);let i=e.type===`application/octet-stream`,a=/constructor/i.test(String(qo.HTMLElement))||`safari`in qo,o=/CriOS\/[\d]+/.test(navigator.userAgent);if((o||i&&a||$o)&&typeof FileReader<`u`){let t=new FileReader;t.onloadend=function(){let e=t.result;if(typeof e!=`string`)throw r=null,Error(`Wrong reader.result type`);e=o?e:e.replace(/^data:[^;]*;/,`data:attachment/file;`),r?r.location.href=e:location.assign(e),r=null},t.readAsDataURL(e)}else{let t=URL.createObjectURL(e);r?r.location.assign(t):location.href=t,r=null,setTimeout(function(){URL.revokeObjectURL(t)},4e4)}}var{assign:is}=Object;function as(){let e=Te(!0),t=e.run(()=>qt({})),n=[],r=[],i=Wt({install(e){Wo(i),i._a=e,e.provide(Go,i),e.config.globalProperties.$pinia=i,r.forEach(e=>n.push(e)),r=[]},use(e){return this._a?n.push(e):r.push(e),this},_p:n,_a:null,_e:e,_s:new Map,state:t});return i}var os=()=>{};function ss(e,t,n,r=os){e.add(t);let i=()=>{e.delete(t)&&r()};return!n&&Ee()&&De(i),i}function cs(e,...t){e.forEach(e=>{e(...t)})}var ls=e=>e(),us=Symbol(),ds=Symbol();function fs(e,t){e instanceof Map&&t instanceof Map?t.forEach((t,n)=>e.set(n,t)):e instanceof Set&&t instanceof Set&&t.forEach(e.add,e);for(let n in t){if(!Object.hasOwn(t,n))continue;let r=t[n],i=e[n];e[n]=Ko(i)&&Ko(r)&&Object.hasOwn(e,n)&&!R(r)&&!Bt(r)?fs(i,r):r}return e}var ps=Symbol();function ms(e){return!e||typeof e!=`object`||!Object.hasOwn(e,ps)}var{assign:hs}=Object;function gs(e){return!!(R(e)&&e.effect)}function _s(e,t,n,r){let{state:i,actions:a,getters:o}=t,s=n.state.value[e],c;function l(){return s||(n.state.value[e]=i?i():{}),hs($t(n.state.value[e]),a,Object.keys(o||{}).reduce((t,r)=>(t[r]=Wt(G(()=>{Wo(n);let t=n._s.get(e);return o[r].call(t,t)})),t),{}))}return c=vs(e,l,t,n,r,!0),c}function vs(e,t,n={},r,i,a){let o,s=hs({actions:{}},n),c={deep:!0},l,u,d=new Set,f=new Set,p,m=r.state.value[e];!a&&!m&&(r.state.value[e]={});let h;function g(t){let n;l=u=!1,typeof t==`function`?(t(r.state.value[e]),n={type:`patch function`,storeId:e,events:p}):(fs(r.state.value[e],t),n={type:`patch object`,payload:t,storeId:e,events:p});let i=h=Symbol();Sn().then(()=>{h===i&&(l=!0)}),u=!0,cs(d,n,r.state.value[e])}let _=a?function(){let{state:e}=n,t=e?e():{};this.$patch(e=>{hs(e,t)})}:os;function v(){o.stop(),d.clear(),f.clear(),r._s.delete(e)}let y=(t,n=``)=>{if(us in t)return t[ds]=n,t;let i=function(){Wo(r);let n=Array.from(arguments),a=new Set,o=new Set;function s(e){a.add(e)}function c(e){o.add(e)}cs(f,{args:n,name:i[ds],store:b,after:s,onError:c});let l;try{l=t.apply(this&&this.$id===e?this:b,n)}catch(e){throw cs(o,e),e}return l instanceof Promise?l.then(e=>(cs(a,e),e)).catch(e=>(cs(o,e),Promise.reject(e))):(cs(a,l),l)};return i[us]=!0,i[ds]=n,i},b=It({_p:r,$id:e,$onAction:ss.bind(null,f),$patch:g,$reset:_,$subscribe(t,n={}){if(d.has(t))return os;let i=ss(d,t,n.detached,()=>a()),a=o.run(()=>Hn(()=>r.state.value[e],r=>{(n.flush===`sync`?u:l)&&t({storeId:e,type:`direct`,events:p},r)},hs({},c,n)));return i},$dispose:v});r._s.set(e,b);let x=(r._a&&r._a.runWithContext||ls)(()=>r._e.run(()=>(o=Te()).run(()=>t({action:y}))));for(let t in x){let n=x[t];R(n)&&!gs(n)||Bt(n)?a||(m&&ms(n)&&(R(n)?n.value=m[t]:((n instanceof Set||n instanceof Map)&&n.clear(),fs(n,m[t]))),r.state.value[e][t]=n):typeof n==`function`&&(x[t]=y(n,t),s.actions[t]=n)}return hs(b,x),hs(L(b),x),Object.defineProperty(b,"$state",{get:()=>r.state.value[e],set:e=>{g(t=>{hs(t,e)})}}),r._p.forEach(e=>{let t=o.run(()=>e({store:b,app:r._a,pinia:r,options:s}));hs(b,t)}),m&&a&&n.hydrate&&n.hydrate(b.$state,m),l=!0,u=!0,b}function ys(e,t,n){let r,i=typeof t==`function`;r=i?n:t;function a(n,a){let o=zn();return n||=o?Rn(Go,null):null,n&&Wo(n),n=Uo,n._s.has(e)||(i?vs(e,t,r,n):_s(e,r,n)),n._s.get(e)}return a.$id=e,a}var bs={class:`app`},xs={class:`topbar`},Ss={class:`nav-links`},Cs=Qn({__name:`App`,setup(e){return(e,t)=>{let n=Cr(`router-link`),r=Cr(`router-view`);return B(),V(`div`,bs,[H(`nav`,xs,[t[5]||=H(`span`,{class:`brand`},`🤖 端云协同 LLM 系统`,-1),H(`div`,Ss,[U(n,{to:`/chat`},{default:Pn(()=>[...t[0]||=[aa(`💬 对话`,-1)]]),_:1}),U(n,{to:`/collaboration`},{default:Pn(()=>[...t[1]||=[aa(`🔄 协作`,-1)]]),_:1}),U(n,{to:`/review`},{default:Pn(()=>[...t[2]||=[aa(`🔍 检验`,-1)]]),_:1}),U(n,{to:`/metrics`},{default:Pn(()=>[...t[3]||=[aa(`📊 指标`,-1)]]),_:1}),U(n,{to:`/settings`},{default:Pn(()=>[...t[4]||=[aa(`⚙️ 设置`,-1)]]),_:1})])]),U(r,{class:`content`})])}}}),ws=typeof document<`u`;function Ts(e){return typeof e==`object`||`displayName`in e||`props`in e||`__vccOpts`in e}function Es(e){return e.__esModule||e[Symbol.toStringTag]===`Module`||e.default&&Ts(e.default)}var K=Object.assign;function Ds(e,t){let n={};for(let r in t){let i=t[r];n[r]=ks(i)?i.map(e):e(i)}return n}var Os=()=>{},ks=Array.isArray;function As(e,t){let n={};for(let r in e)n[r]=r in t?t[r]:e[r];return n}var js=/#/g,Ms=/&/g,Ns=/\//g,Ps=/=/g,Fs=/\?/g,Is=/\+/g,Ls=/%5B/g,Rs=/%5D/g,zs=/%5E/g,Bs=/%60/g,Vs=/%7B/g,Hs=/%7C/g,Us=/%7D/g,Ws=/%20/g;function Gs(e){return e==null?``:encodeURI(``+e).replace(Hs,`|`).replace(Ls,`[`).replace(Rs,`]`)}function Ks(e){return Gs(e).replace(Vs,`{`).replace(Us,`}`).replace(zs,`^`)}function qs(e){return Gs(e).replace(Is,`%2B`).replace(Ws,`+`).replace(js,`%23`).replace(Ms,`%26`).replace(Bs,"`").replace(Vs,`{`).replace(Us,`}`).replace(zs,`^`)}function Js(e){return qs(e).replace(Ps,`%3D`)}function Ys(e){return Gs(e).replace(js,`%23`).replace(Fs,`%3F`)}function Xs(e){return Ys(e).replace(Ns,`%2F`)}function Zs(e){if(e==null)return null;try{return decodeURIComponent(``+e)}catch{}return``+e}var Qs=/\/$/,$s=e=>e.replace(Qs,``);function ec(e,t,n=`/`){let r,i={},a=``,o=``,s=t.indexOf(`#`),c=t.indexOf(`?`);return c=s>=0&&c>s?-1:c,c>=0&&(r=t.slice(0,c),a=t.slice(c,s>0?s:t.length),i=e(a.slice(1))),s>=0&&(r||=t.slice(0,s),o=t.slice(s,t.length)),r=cc(r??t,n),{fullPath:r+a+o,path:r,query:i,hash:Zs(o)}}function tc(e,t){let n=t.query?e(t.query):``;return t.path+(n&&`?`)+n+(t.hash||``)}function nc(e,t){return!t||!e.toLowerCase().startsWith(t.toLowerCase())?e:e.slice(t.length)||`/`}function rc(e,t,n){let r=t.matched.length-1,i=n.matched.length-1;return r>-1&&r===i&&ic(t.matched[r],n.matched[i])&&ac(t.params,n.params)&&e(t.query)===e(n.query)&&t.hash===n.hash}function ic(e,t){return(e.aliasOf||e)===(t.aliasOf||t)}function ac(e,t){if(Object.keys(e).length!==Object.keys(t).length)return!1;for(var n in e)if(!oc(e[n],t[n]))return!1;return!0}function oc(e,t){return ks(e)?sc(e,t):ks(t)?sc(t,e):e?.valueOf()===t?.valueOf()}function sc(e,t){return ks(t)?e.length===t.length&&e.every((e,n)=>e===t[n]):e.length===1&&e[0]===t}function cc(e,t){if(e.startsWith(`/`))return e;if(!e)return t;let n=t.split(`/`),r=e.split(`/`),i=r[r.length-1];(i===`..`||i===`.`)&&r.push(``);let a=n.length-1,o,s;for(o=0;o1&&a--;else break}return n.slice(0,a).join(`/`)+`/`+r.slice(o).join(`/`)}var lc={path:`/`,name:void 0,params:{},query:{},hash:``,fullPath:`/`,matched:[],meta:{},redirectedFrom:void 0},uc=function(e){return e.pop=`pop`,e.push=`push`,e}({}),dc=function(e){return e.back=`back`,e.forward=`forward`,e.unknown=``,e}({});function fc(e){if(!e){if(ws){let t=document.querySelector(`base`);e=t&&t.getAttribute(`href`)||`/`,e=e.replace(/^\w+:\/\/[^\/]+/,``)}else e=`/`}return e[0]!==`/`&&e[0]!==`#`&&(e=`/`+e),$s(e)}var pc=/^[^#]+#/;function mc(e,t){return e.replace(pc,`#`)+t}function hc(e,t){let n=document.documentElement.getBoundingClientRect(),r=e.getBoundingClientRect();return{behavior:t.behavior,left:r.left-n.left-(t.left||0),top:r.top-n.top-(t.top||0)}}var gc=()=>({left:window.scrollX,top:window.scrollY});function _c(e){let t;if(`el`in e){let n=e.el,r=typeof n==`string`&&n.startsWith(`#`),i=typeof n==`string`?r?document.getElementById(n.slice(1)):document.querySelector(n):n;if(!i)return;t=hc(i,e)}else t=e;`scrollBehavior`in document.documentElement.style?window.scrollTo(t):window.scrollTo(t.left==null?window.scrollX:t.left,t.top==null?window.scrollY:t.top)}function vc(e,t){return(history.state?history.state.position-t:-1)+e}var yc=new Map;function bc(e,t){yc.set(e,t)}function xc(e){let t=yc.get(e);return yc.delete(e),t}function Sc(e){return typeof e==`string`||e&&typeof e==`object`}function Cc(e){return typeof e==`string`||typeof e==`symbol`}var q=function(e){return e[e.MATCHER_NOT_FOUND=1]=`MATCHER_NOT_FOUND`,e[e.NAVIGATION_GUARD_REDIRECT=2]=`NAVIGATION_GUARD_REDIRECT`,e[e.NAVIGATION_ABORTED=4]=`NAVIGATION_ABORTED`,e[e.NAVIGATION_CANCELLED=8]=`NAVIGATION_CANCELLED`,e[e.NAVIGATION_DUPLICATED=16]=`NAVIGATION_DUPLICATED`,e}({}),wc=Symbol(``);q.MATCHER_NOT_FOUND,q.NAVIGATION_GUARD_REDIRECT,q.NAVIGATION_ABORTED,q.NAVIGATION_CANCELLED,q.NAVIGATION_DUPLICATED;function Tc(e,t){return K(Error(),{type:e,[wc]:!0},t)}function Ec(e,t){return e instanceof Error&&wc in e&&(t==null||!!(e.type&t))}function Dc(e){let t={};if(e===``||e===`?`)return t;let n=(e[0]===`?`?e.slice(1):e).split(`&`);for(let e=0;ee&&qs(e)):[r&&qs(r)]).forEach(e=>{e!==void 0&&(t+=(t.length?`&`:``)+n,e!=null&&(t+=`=`+e))})}return t}function kc(e){let t={};for(let n in e){let r=e[n];r!==void 0&&(t[n]=ks(r)?r.map(e=>e==null?null:``+e):r==null?r:``+r)}return t}var Ac=Symbol(``),jc=Symbol(``),Mc=Symbol(``),Nc=Symbol(``),Pc=Symbol(``);function Fc(){let e=[];function t(t){return e.push(t),()=>{let n=e.indexOf(t);n>-1&&e.splice(n,1)}}function n(){e=[]}return{add:t,list:()=>e.slice(),reset:n}}function Ic(e,t,n,r,i,a=e=>e()){let o=r&&(r.enterCallbacks[i]=r.enterCallbacks[i]||[]);return()=>new Promise((s,c)=>{let l=e=>{e===!1?c(Tc(q.NAVIGATION_ABORTED,{from:n,to:t})):e instanceof Error?c(e):Sc(e)?c(Tc(q.NAVIGATION_GUARD_REDIRECT,{from:t,to:e})):(o&&r.enterCallbacks[i]===o&&typeof e==`function`&&o.push(e),s())},u=a(()=>e.call(r&&r.instances[i],t,n,l)),d=Promise.resolve(u);e.length<3&&(d=d.then(l)),d.catch(e=>c(e))})}function Lc(e,t,n,r,i=e=>e()){let a=[];for(let o of e)for(let e in o.components){let s=o.components[e];if(!(t!==`beforeRouteEnter`&&!o.instances[e])){if(Ts(s)){let c=(s.__vccOpts||s)[t];c&&a.push(Ic(c,n,r,o,e,i))}else{let c=s();a.push(()=>c.then(a=>{if(!a)throw Error(`Couldn't resolve component "${e}" at "${o.path}"`);let s=Es(a)?a.default:a;o.mods[e]=a,o.components[e]=s;let c=(s.__vccOpts||s)[t];return c&&Ic(c,n,r,o,e,i)()}))}}}return a}function Rc(e,t){let n=[],r=[],i=[],a=Math.max(t.matched.length,e.matched.length);for(let o=0;oic(e,a))?r.push(a):n.push(a));let s=e.matched[o];s&&(t.matched.find(e=>ic(e,s))||i.push(s))}return[n,r,i]}var zc=()=>location.protocol+`//`+location.host;function Bc(e,t){let{pathname:n,search:r,hash:i}=t,a=e.indexOf(`#`);if(a>-1){let t=i.includes(e.slice(a))?e.slice(a).length:1,n=i.slice(t);return n[0]!==`/`&&(n=`/`+n),nc(n,``)}return nc(n,e)+r+i}function Vc(e,t,n,r){let i=[],a=[],o=null,s=({state:a})=>{let s=Bc(e,location),c=n.value,l=t.value,u=0;if(a){if(n.value=s,t.value=a,o&&o===c){o=null;return}u=l?a.position-l.position:0}else r(s);i.forEach(e=>{e(n.value,c,{delta:u,type:uc.pop,direction:u?u>0?dc.forward:dc.back:dc.unknown})})};function c(){o=n.value}function l(e){i.push(e);let t=()=>{let t=i.indexOf(e);t>-1&&i.splice(t,1)};return a.push(t),t}function u(){if(document.visibilityState===`hidden`){let{history:e}=window;if(!e.state)return;e.replaceState(K({},e.state,{scroll:gc()}),``)}}function d(){for(let e of a)e();a=[],window.removeEventListener(`popstate`,s),window.removeEventListener(`pagehide`,u),document.removeEventListener(`visibilitychange`,u)}return window.addEventListener(`popstate`,s),window.addEventListener(`pagehide`,u),document.addEventListener(`visibilitychange`,u),{pauseListeners:c,listen:l,destroy:d}}function Hc(e,t,n,r=!1,i=!1){return{back:e,current:t,forward:n,replaced:r,position:window.history.length,scroll:i?gc():null}}function Uc(e){let{history:t,location:n}=window,r={value:Bc(e,n)},i={value:t.state};i.value||a(r.value,{back:null,current:r.value,forward:null,position:t.length-1,replaced:!0,scroll:null},!0);function a(r,a,o){let s=e.indexOf(`#`),c=s>-1?(n.host&&document.querySelector(`base`)?e:e.slice(s))+r:zc()+e+r;try{t[o?`replaceState`:`pushState`](a,``,c),i.value=a}catch(e){console.error(e),n[o?`replace`:`assign`](c)}}function o(e,n){a(e,K({},t.state,Hc(i.value.back,e,i.value.forward,!0),n,{position:i.value.position}),!0),r.value=e}function s(e,n){let o=K({},i.value,t.state,{forward:e,scroll:gc()});a(o.current,o,!0),a(e,K({},Hc(r.value,e,null),{position:o.position+1},n),!1),r.value=e}return{location:r,state:i,push:s,replace:o}}function Wc(e){e=fc(e);let t=Uc(e),n=Vc(e,t.state,t.location,t.replace);function r(e,t=!0){t||n.pauseListeners(),history.go(e)}let i=K({location:``,base:e,go:r,createHref:mc.bind(null,e)},t,n);return Object.defineProperty(i,"location",{enumerable:!0,get:()=>t.location.value}),Object.defineProperty(i,"state",{enumerable:!0,get:()=>t.state.value}),i}var Gc=function(e){return e[e.Static=0]=`Static`,e[e.Param=1]=`Param`,e[e.Group=2]=`Group`,e}({}),J=function(e){return e[e.Static=0]=`Static`,e[e.Param=1]=`Param`,e[e.ParamRegExp=2]=`ParamRegExp`,e[e.ParamRegExpEnd=3]=`ParamRegExpEnd`,e[e.EscapeNext=4]=`EscapeNext`,e}(J||{}),Kc={type:Gc.Static,value:``},qc=/[a-zA-Z0-9_]/;function Jc(e){if(!e)return[[]];if(e===`/`)return[[Kc]];if(!e.startsWith(`/`))throw Error(`Invalid path "${e}"`);function t(e){throw Error(`ERR (${n})/"${l}": ${e}`)}let n=J.Static,r=n,i=[],a;function o(){a&&i.push(a),a=[]}let s=0,c,l=``,u=``;function d(){l&&=(n===J.Static?a.push({type:Gc.Static,value:l}):n===J.Param||n===J.ParamRegExp||n===J.ParamRegExpEnd?(a.length>1&&(c===`*`||c===`+`)&&t(`A repeatable param (${l}) must be alone in its segment. eg: '/:ids+.`),a.push({type:Gc.Param,value:l,regexp:u,repeatable:c===`*`||c===`+`,optional:c===`*`||c===`?`})):t(`Invalid state to consume buffer`),``)}function f(){l+=c}for(;st.length?t.length===1&&t[0]===Zc.Static+Zc.Segment?1:-1:0}function tl(e,t){let n=0,r=e.score,i=t.score;for(;n0&&t[t.length-1]<0}var rl={strict:!1,end:!0,sensitive:!1};function il(e,t,n){let r=K($c(Jc(e.path),n),{record:e,parent:t,children:[],alias:[]});return t&&!r.record.aliasOf==!t.record.aliasOf&&t.children.push(r),r}function al(e,t){let n=[],r=new Map;t=As(rl,t);function i(e){return r.get(e)}function a(e,n,r){let i=!r,s=sl(e);s.aliasOf=r&&r.record;let l=As(t,e),u=[s];if(`alias`in e){let t=typeof e.alias==`string`?[e.alias]:e.alias;for(let e of t)u.push(sl(K({},s,{components:r?r.record.components:s.components,path:e,aliasOf:r?r.record:s})))}let d,f;for(let t of u){let{path:u}=t;if(n&&u[0]!==`/`){let e=n.record.path,r=e[e.length-1]===`/`?``:`/`;t.path=n.record.path+(u&&r+u)}if(d=il(t,n,l),r?r.alias.push(d):(f||=d,f!==d&&f.alias.push(d),i&&e.name&&!ll(d)&&o(e.name)),pl(d)&&c(d),s.children){let e=s.children;for(let t=0;t{o(f)}:Os}function o(e){if(Cc(e)){let t=r.get(e);t&&(r.delete(e),n.splice(n.indexOf(t),1),t.children.forEach(o),t.alias.forEach(o))}else{let t=n.indexOf(e);t>-1&&(n.splice(t,1),e.record.name&&r.delete(e.record.name),e.children.forEach(o),e.alias.forEach(o))}}function s(){return n}function c(e){let t=dl(e,n);n.splice(t,0,e),e.record.name&&!ll(e)&&r.set(e.record.name,e)}function l(e,t){let i,a={},o,s;if(`name`in e&&e.name){if(i=r.get(e.name),!i)throw Tc(q.MATCHER_NOT_FOUND,{location:e});s=i.record.name,a=K(ol(t.params,i.keys.filter(e=>!e.optional).concat(i.parent?i.parent.keys.filter(e=>e.optional):[]).map(e=>e.name)),e.params&&ol(e.params,i.keys.map(e=>e.name))),o=i.stringify(a)}else if(e.path!=null)o=e.path,i=n.find(e=>e.re.test(o)),i&&(a=i.parse(o),s=i.record.name);else{if(i=t.name?r.get(t.name):n.find(e=>e.re.test(t.path)),!i)throw Tc(q.MATCHER_NOT_FOUND,{location:e,currentLocation:t});s=i.record.name,a=K({},t.params,e.params),o=i.stringify(a)}let c=[],l=i;for(;l;)c.unshift(l.record),l=l.parent;return{name:s,path:o,params:a,matched:c,meta:ul(c)}}e.forEach(e=>a(e));function u(){n.length=0,r.clear()}return{addRoute:a,resolve:l,removeRoute:o,clearRoutes:u,getRoutes:s,getRecordMatcher:i}}function ol(e,t){let n={};for(let r of t)r in e&&(n[r]=e[r]);return n}function sl(e){let t={path:e.path,redirect:e.redirect,name:e.name,meta:e.meta||{},aliasOf:e.aliasOf,beforeEnter:e.beforeEnter,props:cl(e),children:e.children||[],instances:{},leaveGuards:new Set,updateGuards:new Set,enterCallbacks:{},components:`components`in e?e.components||null:e.component&&{default:e.component}};return Object.defineProperty(t,"mods",{value:{}}),t}function cl(e){let t={},n=e.props||!1;if(`component`in e)t.default=n;else for(let r in e.components)t[r]=typeof n==`object`?n[r]:n;return t}function ll(e){for(;e;){if(e.record.aliasOf)return!0;e=e.parent}return!1}function ul(e){return e.reduce((e,t)=>K(e,t.meta),{})}function dl(e,t){let n=0,r=t.length;for(;n!==r;){let i=n+r>>1;tl(e,t[i])<0?r=i:n=i+1}let i=fl(e);return i&&(r=t.lastIndexOf(i,r-1)),r}function fl(e){let t=e;for(;t=t.parent;)if(pl(t)&&tl(e,t)===0)return t}function pl({record:e}){return!!(e.name||e.components&&Object.keys(e.components).length||e.redirect)}function ml(e){let t=Rn(Mc),n=Rn(Nc),r=G(()=>{let n=z(e.to);return t.resolve(n)}),i=G(()=>{let{matched:e}=r.value,{length:t}=e,i=e[t-1],a=n.matched;if(!i||!a.length)return-1;let o=a.findIndex(ic.bind(null,i));if(o>-1)return o;let s=yl(e[t-2]);return t>1&&yl(i)===s&&a[a.length-1].path!==s?a.findIndex(ic.bind(null,e[t-2])):o}),a=G(()=>i.value>-1&&vl(n.params,r.value.params)),o=G(()=>i.value>-1&&i.value===n.matched.length-1&&ac(n.params,r.value.params));function s(n={}){if(_l(n)){let n=t[z(e.replace)?`replace`:`push`](z(e.to)).catch(Os);return e.viewTransition&&typeof document<`u`&&`startViewTransition`in document&&document.startViewTransition(()=>n),n}return Promise.resolve()}return{route:r,href:G(()=>r.value.href),isActive:a,isExactActive:o,navigate:s}}function hl(e){return e.length===1?e[0]:e}var gl=Qn({name:`RouterLink`,compatConfig:{MODE:3},props:{to:{type:[String,Object],required:!0},replace:Boolean,activeClass:String,exactActiveClass:String,custom:Boolean,ariaCurrentValue:{type:String,default:`page`},viewTransition:Boolean},useLink:ml,setup(e,{slots:t}){let n=It(ml(e)),{options:r}=Rn(Mc),i=G(()=>({[bl(e.activeClass,r.linkActiveClass,`router-link-active`)]:n.isActive,[bl(e.exactActiveClass,r.linkExactActiveClass,`router-link-exact-active`)]:n.isExactActive}));return()=>{let r=t.default&&hl(t.default(n));return e.custom?r:ja(`a`,{"aria-current":n.isExactActive?e.ariaCurrentValue:null,href:n.href,onClick:n.navigate,class:i.value},r)}}});function _l(e){if(!(e.metaKey||e.altKey||e.ctrlKey||e.shiftKey)&&!e.defaultPrevented&&(e.button===void 0||e.button===0)){if(e.currentTarget&&e.currentTarget.getAttribute){let t=e.currentTarget.getAttribute(`target`);if(/\b_blank\b/i.test(t))return}return e.preventDefault&&e.preventDefault(),!0}}function vl(e,t){for(let n in t){let r=t[n],i=e[n];if(typeof r==`string`){if(r!==i)return!1}else if(!ks(i)||i.length!==r.length||r.some((e,t)=>e.valueOf()!==i[t].valueOf()))return!1}return!0}function yl(e){return e?e.aliasOf?e.aliasOf.path:e.path:``}var bl=(e,t,n)=>e??t??n,xl=Qn({name:`RouterView`,inheritAttrs:!1,props:{name:{type:String,default:`default`},route:Object},compatConfig:{MODE:3},setup(e,{attrs:t,slots:n}){let r=Rn(Pc),i=G(()=>e.route||r.value),a=Rn(jc,0),o=G(()=>{let e=z(a),{matched:t}=i.value,n;for(;(n=t[e])&&!n.components;)e++;return e}),s=G(()=>i.value.matched[o.value]);Ln(jc,G(()=>o.value+1)),Ln(Ac,s),Ln(Pc,i);let c=qt();return Hn(()=>[c.value,s.value,e.name],([e,t,n],[r,i,a])=>{t&&(t.instances[n]=e,i&&i!==t&&e&&e===r&&(t.leaveGuards.size||(t.leaveGuards=i.leaveGuards),t.updateGuards.size||(t.updateGuards=i.updateGuards))),e&&t&&(!i||!ic(t,i)||!r)&&(t.enterCallbacks[n]||[]).forEach(t=>t(e))},{flush:`post`}),()=>{let r=i.value,a=e.name,o=s.value,l=o&&o.components[a];if(!l)return Sl(n.default,{Component:l,route:r});let u=o.props[a],d=ja(l,K({},u?u===!0?r.params:typeof u==`function`?u(r):u:null,t,{onVnodeUnmounted:e=>{e.component.isUnmounted&&(o.instances[a]=null)},ref:c}));return Sl(n.default,{Component:d,route:r})||d}}});function Sl(e,t){if(!e)return null;let n=e(t);return n.length===1?n[0]:n}var Cl=xl;function wl(e){let t=al(e.routes,e),n=e.parseQuery||Dc,r=e.stringifyQuery||Oc,i=e.history,a=Fc(),o=Fc(),s=Fc(),c=Jt(lc),l=lc;ws&&e.scrollBehavior&&`scrollRestoration`in history&&(history.scrollRestoration=`manual`);let u=Ds.bind(null,e=>``+e),d=Ds.bind(null,Xs),f=Ds.bind(null,Zs);function p(e,n){let r,i;return Cc(e)?(r=t.getRecordMatcher(e),i=n):i=e,t.addRoute(i,r)}function m(e){let n=t.getRecordMatcher(e);n&&t.removeRoute(n)}function h(){return t.getRoutes().map(e=>e.record)}function g(e){return!!t.getRecordMatcher(e)}function _(e,a){if(a=K({},a||c.value),typeof e==`string`){let r=ec(n,e,a.path),o=t.resolve({path:r.path},a),s=i.createHref(r.fullPath);return K(r,o,{params:f(o.params),hash:Zs(r.hash),redirectedFrom:void 0,href:s})}let o;if(e.path!=null)o=K({},e,{path:ec(n,e.path,a.path).path});else{let t=K({},e.params);for(let e in t)t[e]??delete t[e];o=K({},e,{params:d(t)}),a.params=d(a.params)}let s=t.resolve(o,a),l=e.hash||``;s.params=u(f(s.params));let p=tc(r,K({},e,{hash:Ks(l),path:s.path})),m=i.createHref(p);return K({fullPath:p,hash:l,query:r===Oc?kc(e.query):e.query||{}},s,{redirectedFrom:void 0,href:m})}function v(e){return typeof e==`string`?ec(n,e,c.value.path):K({},e)}function y(e,t){if(l!==e)return Tc(q.NAVIGATION_CANCELLED,{from:t,to:e})}function b(e){return C(e)}function x(e){return b(K(v(e),{replace:!0}))}function S(e,t){let n=e.matched[e.matched.length-1];if(n&&n.redirect){let{redirect:r}=n,i=typeof r==`function`?r(e,t):r;return typeof i==`string`&&(i=i.includes(`?`)||i.includes(`#`)?i=v(i):{path:i},i.params={}),K({query:e.query,hash:e.hash,params:i.path==null?e.params:{}},i)}}function C(e,t){let n=l=_(e),i=c.value,a=e.state,o=e.force,s=e.replace===!0,u=S(n,i);if(u)return C(K(v(u),{state:typeof u==`object`?K({},a,u.state):a,force:o,replace:s}),t||n);let d=n;d.redirectedFrom=t;let f;return!o&&rc(r,i,n)&&(f=Tc(q.NAVIGATION_DUPLICATED,{to:d,from:i}),se(i,i,!0,!1)),(f?Promise.resolve(f):te(d,i)).catch(e=>Ec(e)?Ec(e,q.NAVIGATION_GUARD_REDIRECT)?e:oe(e):ae(e,d,i)).then(e=>{if(e){if(Ec(e,q.NAVIGATION_GUARD_REDIRECT))return C(K({replace:s},v(e.to),{state:typeof e.to==`object`?K({},a,e.to.state):a,force:o}),t||d)}else e=re(d,i,!0,s,a);return ne(d,i,e),e})}function w(e,t){let n=y(e,t);return n?Promise.reject(n):Promise.resolve()}function ee(e){let t=M.values().next().value;return t&&typeof t.runWithContext==`function`?t.runWithContext(e):e()}function te(e,t){let n,[r,i,s]=Rc(e,t);n=Lc(r.reverse(),`beforeRouteLeave`,e,t);for(let i of r)i.leaveGuards.forEach(r=>{n.push(Ic(r,e,t))});let c=w.bind(null,e,t);return n.push(c),le(n).then(()=>{n=[];for(let r of a.list())n.push(Ic(r,e,t));return n.push(c),le(n)}).then(()=>{n=Lc(i,`beforeRouteUpdate`,e,t);for(let r of i)r.updateGuards.forEach(r=>{n.push(Ic(r,e,t))});return n.push(c),le(n)}).then(()=>{n=[];for(let r of s)if(r.beforeEnter){if(ks(r.beforeEnter))for(let i of r.beforeEnter)n.push(Ic(i,e,t));else n.push(Ic(r.beforeEnter,e,t))}return n.push(c),le(n)}).then(()=>(e.matched.forEach(e=>e.enterCallbacks={}),n=Lc(s,`beforeRouteEnter`,e,t,ee),n.push(c),le(n))).then(()=>{n=[];for(let r of o.list())n.push(Ic(r,e,t));return n.push(c),le(n)}).catch(e=>Ec(e,q.NAVIGATION_CANCELLED)?e:Promise.reject(e))}function ne(e,t,n){s.list().forEach(r=>ee(()=>r(e,t,n)))}function re(e,t,n,r,a){let o=y(e,t);if(o)return o;let s=t===lc,l=ws?history.state:{};n&&(r||s?i.replace(e.fullPath,K({scroll:s&&l&&l.scroll},a)):i.push(e.fullPath,a)),c.value=e,se(e,t,n,s),oe()}let ie;function T(){ie||=i.listen((e,t,n)=>{if(!ce.listening)return;let r=_(e),a=S(r,ce.currentRoute.value);if(a){C(K(a,{replace:!0,force:!0}),r).catch(Os);return}l=r;let o=c.value;ws&&bc(vc(o.fullPath,n.delta),gc()),te(r,o).catch(e=>Ec(e,q.NAVIGATION_ABORTED|q.NAVIGATION_CANCELLED)?e:Ec(e,q.NAVIGATION_GUARD_REDIRECT)?(C(K(v(e.to),{force:!0}),r).then(e=>{Ec(e,q.NAVIGATION_ABORTED|q.NAVIGATION_DUPLICATED)&&!n.delta&&n.type===uc.pop&&i.go(-1,!1)}).catch(Os),Promise.reject()):(n.delta&&i.go(-n.delta,!1),ae(e,r,o))).then(e=>{e||=re(r,o,!1),e&&(n.delta&&!Ec(e,q.NAVIGATION_CANCELLED)?i.go(-n.delta,!1):n.type===uc.pop&&Ec(e,q.NAVIGATION_ABORTED|q.NAVIGATION_DUPLICATED)&&i.go(-1,!1)),ne(r,o,e)}).catch(Os)})}let E=Fc(),D=Fc(),O;function ae(e,t,n){oe(e);let r=D.list();return r.length?r.forEach(r=>r(e,t,n)):console.error(e),Promise.reject(e)}function k(){return O&&c.value!==lc?Promise.resolve():new Promise((e,t)=>{E.add([e,t])})}function oe(e){return O||(O=!e,T(),E.list().forEach(([t,n])=>e?n(e):t()),E.reset()),e}function se(t,n,r,i){let{scrollBehavior:a}=e;if(!ws||!a)return Promise.resolve();let o=!r&&xc(vc(t.fullPath,0))||(i||!r)&&history.state&&history.state.scroll||null;return Sn().then(()=>a(t,n,o)).then(e=>e&&_c(e)).catch(e=>ae(e,t,n))}let A=e=>i.go(e),j,M=new Set,ce={currentRoute:c,listening:!0,addRoute:p,removeRoute:m,clearRoutes:t.clearRoutes,hasRoute:g,getRoutes:h,resolve:_,options:e,push:b,replace:x,go:A,back:()=>A(-1),forward:()=>A(1),beforeEach:a.add,beforeResolve:o.add,afterEach:s.add,onError:D.add,isReady:k,install(e){e.component(`RouterLink`,gl),e.component(`RouterView`,Cl),e.config.globalProperties.$router=ce,Object.defineProperty(e.config.globalProperties,"$route",{enumerable:!0,get:()=>z(c)}),ws&&!j&&c.value===lc&&(j=!0,b(i.location).catch(e=>{}));let t={};for(let e in lc)Object.defineProperty(t,e,{get:()=>c.value[e],enumerable:!0});e.provide(Mc,ce),e.provide(Nc,Lt(t)),e.provide(Pc,c);let n=e.unmount;M.add(e),e.unmount=function(){M.delete(e),M.size<1&&(l=lc,ie&&ie(),ie=null,c.value=lc,j=!1,O=!1),n()}}};function le(e){return e.reduce((e,t)=>e.then(()=>ee(t)),Promise.resolve())}return ce}function Tl(e,t){return function(){return e.apply(t,arguments)}}var{toString:El}=Object.prototype,{getPrototypeOf:Dl}=Object,{iterator:Ol,toStringTag:kl}=Symbol,Al=(({hasOwnProperty:e})=>(t,n)=>e.call(t,n))(Object.prototype),jl=e=>typeof e==`string`&&(e===`__proto__`||e===`constructor`||e===`prototype`),Ml=(e,t,n)=>e===Object.prototype||!n&&t===null,Nl=e=>{if(!Object.isExtensible(e))return!1;let t=Object.getOwnPropertyNames(e);return Object.getOwnPropertySymbols&&t.push(...Object.getOwnPropertySymbols(e)),t.every(t=>{if(jl(t))return!1;let n=Object.getOwnPropertyDescriptor(e,t);return!!n&&n.configurable&&n.writable===!0})},Pl=(e,t)=>{let n=e,r=[];for(;n!=null;){if(r.indexOf(n)!==-1)return!1;r.push(n);let i=Dl(n);if(Ml(n,i,n===e))return!1;if(Al(n,t))return!0;n=i}return!1},Fl=(e,t)=>e!=null&&Pl(e,t)?e[t]:void 0,Il=e=>{if(e==null||typeof e!=`object`&&typeof e!=`function`)return e;let t=Dl(e);if(t===null&&Nl(e))return e;let n=Object.create(null),r=Object.create(null),i=[],a=e;for(;a!=null&&i.indexOf(a)===-1;){i.push(a);let o=a===e?t:Dl(a);if(Ml(a,o,a===e))break;let s=Object.getOwnPropertyNames(a);Object.getOwnPropertySymbols&&s.push(...Object.getOwnPropertySymbols(a));for(let t of s)jl(t)||Al(r,t)||(n[t]=e[t],r[t]=!0);a=o}return n},Ll=(e=>t=>{let n=El.call(t);return e[n]||(e[n]=n.slice(8,-1).toLowerCase())})(Object.create(null)),Rl=e=>(e=e.toLowerCase(),t=>Ll(t)===e),zl=e=>t=>typeof t===e,{isArray:Bl}=Array,Vl=zl(`undefined`);function Hl(e){return e!==null&&!Vl(e)&&e.constructor!==null&&!Vl(e.constructor)&&Kl(e.constructor.isBuffer)&&e.constructor.isBuffer(e)}var Ul=Rl(`ArrayBuffer`);function Wl(e){let t;return t=typeof ArrayBuffer<`u`&&ArrayBuffer.isView?ArrayBuffer.isView(e):e&&e.buffer&&Ul(e.buffer),t}var Gl=zl(`string`),Kl=zl(`function`),ql=zl(`number`),Jl=e=>typeof e==`object`&&!!e,Yl=e=>e===!0||e===!1,Xl=e=>{if(!Jl(e))return!1;let t=Dl(e);return(t===null||t===Object.prototype||Dl(t)===null)&&!Pl(e,kl)&&!Pl(e,Ol)},Zl=e=>{if(!Jl(e)||Hl(e))return!1;try{return Object.keys(e).length===0&&Object.getPrototypeOf(e)===Object.prototype}catch{return!1}},Ql=Rl(`Date`),$l=Rl(`File`),eu=e=>!!(e&&e.uri!==void 0),tu=e=>e&&e.getParts!==void 0,nu=Rl(`Blob`),ru=Rl(`FileList`),iu=Rl(`Set`),au=e=>Jl(e)&&Kl(e.pipe);function ou(){return typeof globalThis<`u`?globalThis:typeof self<`u`?self:typeof window<`u`?window:typeof global<`u`?global:{}}var su=ou(),cu=su.FormData===void 0?void 0:su.FormData,lu=e=>{if(!e)return!1;if(cu&&e instanceof cu)return!0;let t=Dl(e);if(!t||t===Object.prototype||!Kl(e.append))return!1;let n=Ll(e);return n===`formdata`||n===`object`&&Kl(e.toString)&&e.toString()===`[object FormData]`},uu=Rl(`URLSearchParams`),[du,fu,pu,mu]=[`ReadableStream`,`Request`,`Response`,`Headers`].map(Rl),hu=e=>e.trim?e.trim():e.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,``);function gu(e,t,{allOwnKeys:n=!1}={}){if(e==null)return;let r,i;if(typeof e!=`object`&&(e=[e]),Bl(e))for(r=0,i=e.length;r0;)if(i=n[r],t===i.toLowerCase())return i;return null}var vu=typeof globalThis<`u`?globalThis:typeof self<`u`?self:typeof window<`u`?window:global,yu=e=>!Vl(e)&&e!==vu;function bu(...e){let{caseless:t,skipUndefined:n}=yu(this)&&this||{},r={},i=(e,i)=>{if(i===`__proto__`||i===`constructor`||i===`prototype`)return;let a=t&&typeof i==`string`&&_u(r,i)||i,o=Al(r,a)?r[a]:void 0;Xl(o)&&Xl(e)?r[a]=bu(o,e):Xl(e)?r[a]=bu({},e):Bl(e)?r[a]=e.slice():(!n||!Vl(e))&&(r[a]=e)};for(let t=0,n=e.length;t(gu(t,(t,r)=>{n&&Kl(t)?Object.defineProperty(e,r,{__proto__:null,value:Tl(t,n),writable:!0,enumerable:!0,configurable:!0}):Object.defineProperty(e,r,{__proto__:null,value:t,writable:!0,enumerable:!0,configurable:!0})},{allOwnKeys:r}),e),Su=e=>(e.charCodeAt(0)===65279&&(e=e.slice(1)),e),Cu=(e,t,n,r)=>{e.prototype=Object.create(t.prototype,r),Object.defineProperty(e.prototype,"constructor",{__proto__:null,value:e,writable:!0,enumerable:!1,configurable:!0}),Object.defineProperty(e,"super",{__proto__:null,value:t.prototype}),n&&Object.assign(e.prototype,n)},wu=(e,t,n,r)=>{let i,a,o,s={};if(t||={},e==null)return t;do{for(i=Object.getOwnPropertyNames(e),a=i.length;a-->0;)o=i[a],(!r||r(o,e,t))&&!s[o]&&(t[o]=e[o],s[o]=!0);e=n!==!1&&Dl(e)}while(e&&(!n||n(e,t))&&e!==Object.prototype);return t},Tu=(e,t,n)=>{e=String(e),(n===void 0||n>e.length)&&(n=e.length),n-=t.length;let r=e.indexOf(t,n);return r!==-1&&r===n},Eu=e=>{if(!e)return null;if(Bl(e))return e;let t=e.length;if(!ql(t))return null;let n=Array(t);for(;t-->0;)n[t]=e[t];return n},Du=(e=>t=>e&&t instanceof e)(typeof Uint8Array<`u`&&Dl(Uint8Array)),Ou=(e,t)=>{let n=(e&&e[Ol]).call(e),r;for(;(r=n.next())&&!r.done;){let n=r.value;t.call(e,n[0],n[1])}},ku=(e,t)=>{let n,r=[];for(;(n=e.exec(t))!==null;)r.push(n);return r},Au=Rl(`HTMLFormElement`),ju=e=>e.toLowerCase().replace(/[-_\s]([a-z\d])(\w*)/g,function(e,t,n){return t.toUpperCase()+n}),{propertyIsEnumerable:Mu}=Object.prototype,Nu=Rl(`RegExp`),Pu=(e,t)=>{let n=Object.getOwnPropertyDescriptors(e),r={};gu(n,(n,i)=>{let a;(a=t(n,i,e))!==!1&&(r[i]=a||n)}),Object.defineProperties(e,r)},Fu=e=>{Pu(e,(t,n)=>{if(Kl(e)&&[`arguments`,`caller`,`callee`].includes(n))return!1;let r=e[n];if(Kl(r)){if(t.enumerable=!1,`writable`in t){t.writable=!1;return}t.set||=()=>{throw Error(`Can not rewrite read-only method '`+n+`'`)}}})},Iu=(e,t)=>{let n={},r=e=>{e.forEach(e=>{n[e]=!0})};return Bl(e)?r(e):r(String(e).split(t)),n},Lu=()=>{},Ru=(e,t)=>e!=null&&Number.isFinite(e=+e)?e:t;function zu(e){return!!(e&&Kl(e.append)&&e[kl]===`FormData`&&e[Ol])}var Bu=e=>{let t=new WeakSet,n=e=>{if(Jl(e)){if(t.has(e))return;if(Hl(e))return e;if(!(`toJSON`in e)){t.add(e);let r;if(iu(e)){r=[];for(let t of e){let e=n(t);!Vl(e)&&r.push(e)}}else r=Bl(e)?[]:{},gu(e,(e,t)=>{let i=n(e);!Vl(i)&&(r[t]=i)});return t.delete(e),r}}return e};return n(e)},Vu=Rl(`AsyncFunction`),Hu=e=>e&&(Jl(e)||Kl(e))&&Kl(e.then)&&Kl(e.catch),Uu=((e,t)=>e?setImmediate:t?((e,t)=>(vu.addEventListener(`message`,({source:n,data:r})=>{n===vu&&r===e&&t.length&&t.shift()()},!1),n=>{t.push(n),vu.postMessage(e,`*`)}))(`axios@${Math.random()}`,[]):e=>setTimeout(e))(typeof setImmediate==`function`,Kl(vu.postMessage)),Wu=typeof queueMicrotask<`u`?queueMicrotask.bind(vu):typeof process<`u`&&process.nextTick||Uu,Gu=e=>e!=null&&Kl(e[Ol]),Y={isArray:Bl,isArrayBuffer:Ul,isBuffer:Hl,isFormData:lu,isArrayBufferView:Wl,isString:Gl,isNumber:ql,isBoolean:Yl,isObject:Jl,isPlainObject:Xl,isEmptyObject:Zl,isReadableStream:du,isRequest:fu,isResponse:pu,isHeaders:mu,isUndefined:Vl,isDate:Ql,isFile:$l,isReactNativeBlob:eu,isReactNative:tu,isBlob:nu,isRegExp:Nu,isFunction:Kl,isStream:au,isURLSearchParams:uu,isTypedArray:Du,isFileList:ru,forEach:gu,merge:bu,extend:xu,trim:hu,stripBOM:Su,inherits:Cu,toFlatObject:wu,kindOf:Ll,kindOfTest:Rl,endsWith:Tu,toArray:Eu,forEachEntry:Ou,matchAll:ku,isHTMLForm:Au,hasOwnProperty:Al,hasOwnProp:Al,hasOwnInPrototypeChain:Pl,getSafeProp:Fl,toSafeFlatObject:Il,reduceDescriptors:Pu,freezeMethods:Fu,toObjectSet:Iu,toCamelCase:ju,noop:Lu,toFiniteNumber:Ru,findKey:_u,global:vu,isContextDefined:yu,isSpecCompliantForm:zu,toJSONObject:Bu,isAsyncFn:Vu,isThenable:Hu,setImmediate:Uu,asap:Wu,isIterable:Gu,isSafeIterable:e=>e!=null&&Pl(e,Ol)&&Gu(e)},Ku=Y.toObjectSet([`age`,`authorization`,`content-length`,`content-type`,`etag`,`expires`,`from`,`host`,`if-modified-since`,`if-unmodified-since`,`last-modified`,`location`,`max-forwards`,`proxy-authorization`,`referer`,`retry-after`,`user-agent`]),qu=e=>{let t={},n,r,i;return e&&e.split(` +`).forEach(function(e){i=e.indexOf(`:`),n=e.substring(0,i).trim().toLowerCase(),r=e.substring(i+1).trim();let a=Y.hasOwnProp(t,n);!n||a&&Y.hasOwnProp(Ku,n)||(n===`set-cookie`?a?t[n].push(r):t[n]=[r]:t[n]=a?t[n]+`, `+r:r)}),t};function Ju(e){let t=0,n=e.length;for(;tt;){let t=e.charCodeAt(n-1);if(t!==9&&t!==32)break;--n}return t===0&&n===e.length?e:e.slice(t,n)}var Yu=RegExp(`[\\u0000-\\u0008\\u000a-\\u001f\\u007f]+`,`g`),Xu=RegExp(`[^\\u0009\\u0020-\\u007e\\u0080-\\u00ff]+`,`g`);function Zu(e,t){return Y.isArray(e)?e.map(e=>Zu(e,t)):Ju(String(e).replace(t,``))}var Qu=e=>Zu(e,Yu),$u=e=>Zu(e,Xu);function ed(e){let t=Object.create(null);return Y.forEach(e.toJSON(),(e,n)=>{t[n]=$u(e)}),t}var td=Symbol(`internals`);function nd(e){return e&&String(e).trim().toLowerCase()}function rd(e){return e===!1||e==null?e:Y.isArray(e)?e.map(rd):Qu(String(e))}function id(e){let t=Object.create(null),n=/([^\s,;=]+)\s*(?:=\s*([^,;]+))?/g,r;for(;r=n.exec(e);)t[r[1]]=r[2];return t}var ad=/^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$/;function od(e){let t=0,n=e.length;for(;tt;){let t=e.charCodeAt(n-1);if(t!==9&&t!==32)break;--n}return t===0&&n===e.length?e:e.slice(t,n)}function sd(e){let t=e.length-1;if(t<1||e.charCodeAt(0)!==34||e.charCodeAt(t)!==34)return e;let n=``;for(let r=1;r=t))return e;n+=e[r]}return n}function cd(e){let t=Object.create(null),n=String(e),r=0,i=!1,a=!1;function o(e){let i=od(n.slice(r,e)),a=i.indexOf(`=`);if(a<1)return;let o=od(i.slice(0,a));if(!ad.test(o))return;let s=o.toLowerCase();if(s===`__proto__`||s===`constructor`||s===`prototype`)return;let c=od(i.slice(a+1));t[s]=sd(c)}for(let e=0;e/^[-_a-zA-Z0-9^`|~,!#$%&'*+.]+$/.test(e.trim());function ud(e,t,n,r,i){if(Y.isFunction(r))return r.call(this,t,n);if(i&&(t=n),Y.isString(t)){if(Y.isString(r))return t.indexOf(r)!==-1;if(Y.isRegExp(r))return r.test(t)}}function dd(e){return e.trim().toLowerCase().replace(/([a-z\d])(\w*)/g,(e,t,n)=>t.toUpperCase()+n)}function fd(e,t){let n=Y.toCamelCase(` `+t);[`get`,`set`,`has`].forEach(r=>{Object.defineProperty(e,r+n,{__proto__:null,value:function(e,n,i){return this[r].call(this,t,e,n,i)},configurable:!0})})}var X=class{constructor(e){e&&this.set(e)}set(e,t,n){let r=this;function i(e,t,n){let i=nd(t);if(!i)return;let a=Y.findKey(r,i);(!a||r[a]===void 0||n===!0||n===void 0&&r[a]!==!1)&&(r[a||t]=rd(e))}let a=(e,t)=>Y.forEach(e,(e,n)=>i(e,n,t));if(Y.isPlainObject(e)||e instanceof this.constructor)a(e,t);else if(Y.isString(e)&&(e=e.trim())&&!ld(e))a(qu(e),t);else if(Y.isObject(e)&&Y.isSafeIterable(e)){let n=Object.create(null),r,i;for(let t of e){if(!Y.isArray(t))throw TypeError(`Object iterator must return a key-value pair`);i=t[0],Y.hasOwnProp(n,i)?(r=n[i],n[i]=Y.isArray(r)?[...r,t[1]]:[r,t[1]]):n[i]=t[1]}a(n,t)}else e!=null&&i(t,e,n);return this}get(e,t){if(e=nd(e),e){let n=Y.findKey(this,e);if(n){let e=this[n];if(!t)return e;if(t===!0)return id(e);if(Y.isFunction(t))return t.call(this,e,n);if(Y.isRegExp(t))return t.exec(e);throw TypeError(`parser must be boolean|regexp|function`)}}}has(e,t){if(e=nd(e),e){let n=Y.findKey(this,e);return!!(n&&this[n]!==void 0&&(!t||ud(this,this[n],n,t)))}return!1}delete(e,t){let n=this,r=!1;function i(e){if(e=nd(e),e){let i=Y.findKey(n,e);i&&(!t||ud(n,n[i],i,t))&&(delete n[i],r=!0)}}return Y.isArray(e)?e.forEach(i):i(e),r}clear(e){let t=Object.keys(this),n=t.length,r=!1;for(;n--;){let i=t[n];(!e||ud(this,this[i],i,e,!0))&&(delete this[i],r=!0)}return r}normalize(e){let t=this,n={};return Y.forEach(this,(r,i)=>{let a=Y.findKey(n,i);if(a){t[a]=rd(r),delete t[i];return}let o=e?dd(i):String(i).trim();o!==i&&delete t[i],t[o]=rd(r),n[o]=!0}),this}concat(...e){return this.constructor.concat(this,...e)}toJSON(e){let t=Object.create(null);return Y.forEach(this,(n,r)=>{n!=null&&n!==!1&&(t[r]=e&&Y.isArray(n)?n.join(`, `):n)}),t}[Symbol.iterator](){return Object.entries(this.toJSON())[Symbol.iterator]()}toString(){return Object.entries(this.toJSON()).map(([e,t])=>e+`: `+t).join(` +`)}getSetCookie(){let e=this.get(`set-cookie`);return Y.isArray(e)?e:e==null||e===!1?[]:[e]}get[Symbol.toStringTag](){return`AxiosHeaders`}static from(e){return e instanceof this?e:new this(e)}static parseParameters(e){return cd(e)}static concat(e,...t){let n=new this(e);return t.forEach(e=>n.set(e)),n}static accessor(e){let t=(this[td]=this[td]={accessors:{}}).accessors,n=this.prototype;function r(e){let r=nd(e);t[r]||(fd(n,e),t[r]=!0)}return Y.isArray(e)?e.forEach(r):r(e),this}};X.accessor([`Content-Type`,`Content-Length`,`Accept`,`Accept-Encoding`,`User-Agent`,`Authorization`]),Y.reduceDescriptors(X.prototype,({value:e},t)=>{let n=t[0].toUpperCase()+t.slice(1);return{get:()=>e,set(e){this[n]=e}}}),Y.freezeMethods(X);var pd=`[REDACTED ****]`;function md(e){if(Y.hasOwnProp(e,`toJSON`))return!0;let t=Object.getPrototypeOf(e);for(;t&&t!==Object.prototype;){if(Y.hasOwnProp(t,`toJSON`))return!0;t=Object.getPrototypeOf(t)}return!1}function hd(e,t){let n=new Set(t.map(e=>String(e).toLowerCase())),r=[],i=e=>{if(typeof e!=`object`||!e||Y.isBuffer(e))return e;if(r.indexOf(e)!==-1)return;e instanceof X&&(e=e.toJSON()),r.push(e);let t;if(Y.isArray(e))t=[],e.forEach((e,n)=>{let r=i(e);Y.isUndefined(r)||(t[n]=r)});else{if(!Y.isPlainObject(e)&&md(e))return r.pop(),e;t=Object.create(null);for(let[r,a]of Object.entries(e)){let e=n.has(r.toLowerCase())?pd:i(a);Y.isUndefined(e)||(t[r]=e)}}return r.pop(),t};return i(e)}function gd(e){try{return String(e)}catch{return``}}function _d(e){return e.errors.map(e=>{try{return e&&e.message?gd(e.message):gd(e)}catch{return``}}).filter(Boolean).join(`; `)||e.name||`AggregateError`}var Z=class e extends Error{static from(t,n,r,i,a,o){let s=t.message;!s&&Y.isArray(t.errors)&&t.errors.length&&(s=_d(t));let c=new e(s,n||t.code,r,i,a);return Object.defineProperty(c,"cause",{__proto__:null,value:t,writable:!0,enumerable:!1,configurable:!0}),c.name=t.name,t.status!=null&&c.status==null&&(c.status=t.status),o&&Object.assign(c,o),c}constructor(e,t,n,r,i){super(e),Object.defineProperty(this,"message",{__proto__:null,value:e,enumerable:!0,writable:!0,configurable:!0}),this.name=`AxiosError`,this.isAxiosError=!0,t&&(this.code=t),n&&(this.config=n),r&&(this.request=r),i&&(this.response=i,this.status=i.status)}toJSON(){let e=this.config,t=e&&Y.hasOwnProp(e,`redact`)?e.redact:void 0,n=Y.isArray(t)&&t.length>0?hd(e,t):Y.toJSONObject(e);return{message:this.message,name:this.name,description:this.description,number:this.number,fileName:this.fileName,lineNumber:this.lineNumber,columnNumber:this.columnNumber,stack:this.stack,config:n,code:this.code,status:this.status}}};Z.ERR_BAD_OPTION_VALUE=`ERR_BAD_OPTION_VALUE`,Z.ERR_BAD_OPTION=`ERR_BAD_OPTION`,Z.ECONNABORTED=`ECONNABORTED`,Z.ETIMEDOUT=`ETIMEDOUT`,Z.ECONNREFUSED=`ECONNREFUSED`,Z.ERR_NETWORK=`ERR_NETWORK`,Z.ERR_FR_TOO_MANY_REDIRECTS=`ERR_FR_TOO_MANY_REDIRECTS`,Z.ERR_DEPRECATED=`ERR_DEPRECATED`,Z.ERR_BAD_RESPONSE=`ERR_BAD_RESPONSE`,Z.ERR_BAD_REQUEST=`ERR_BAD_REQUEST`,Z.ERR_CANCELED=`ERR_CANCELED`,Z.ERR_NOT_SUPPORT=`ERR_NOT_SUPPORT`,Z.ERR_INVALID_URL=`ERR_INVALID_URL`,Z.ERR_FORM_DATA_DEPTH_EXCEEDED=`ERR_FORM_DATA_DEPTH_EXCEEDED`;function vd(e){return Y.isPlainObject(e)||Y.isArray(e)}function yd(e){return Y.endsWith(e,`[]`)?e.slice(0,-2):e}function bd(e,t,n){return e?e.concat(t).map(function(e,t){return e=yd(e),!n&&t?`[`+e+`]`:e}).join(n?`.`:``):t}function xd(e){return Y.isArray(e)&&!e.some(vd)}var Sd=Y.toFlatObject(Y,{},null,function(e){return/^is[A-Z]/.test(e)});function Cd(e,t,n){if(!Y.isObject(e))throw TypeError(`target must be an object`);t||=new FormData;let r=(e,t)=>{let r=Y.getSafeProp(n,e);return Y.isUndefined(r)?t:r},i=r(`metaTokens`,!0),a=r(`visitor`)||h,o=r(`dots`,!1),s=r(`indexes`,!1),c=r(`Blob`)||typeof Blob<`u`&&Blob,l=r(`maxDepth`,100),u=c&&Y.isSpecCompliantForm(t),d=[];if(!Y.isFunction(a))throw TypeError(`visitor must be a function`);function f(e){if(e===null)return``;if(Y.isDate(e))return e.toISOString();if(Y.isBoolean(e))return e.toString();if(!u&&Y.isBlob(e))throw new Z(`Blob is not supported. Use a Buffer instead.`);if(Y.isArrayBuffer(e)||Y.isTypedArray(e)){if(u&&typeof c==`function`)return new c([e]);throw new Z(`Blob is not supported. Use a Buffer instead.`,Z.ERR_NOT_SUPPORT)}return e}function p(e){if(e>l)throw new Z(`Object is too deeply nested (`+e+` levels). Max depth: `+l,Z.ERR_FORM_DATA_DEPTH_EXCEEDED)}function m(e,t){if(l===1/0)return JSON.stringify(e);let n=[];return JSON.stringify(e,function(e,r){if(!Y.isObject(r))return r;for(;n.length&&n[n.length-1]!==this;)n.pop();return n.push(r),p(t+n.length-1),r})}function h(e,n,r){let a=e;if(Y.isReactNative(t)&&Y.isReactNativeBlob(e))return t.append(bd(r,n,o),f(e)),!1;if(e&&!r&&typeof e==`object`){if(Y.endsWith(n,`{}`))n=i?n:n.slice(0,-2),e=m(e,1);else if(Y.isArray(e)&&xd(e)||(Y.isFileList(e)||Y.endsWith(n,`[]`))&&(a=Y.toArray(e)))return n=yd(n),a.forEach(function(e,r){!(Y.isUndefined(e)||e===null)&&t.append(s===!0?bd([n],r,o):s===null?n:n+`[]`,f(e))}),!1}return vd(e)?!0:(t.append(bd(r,n,o),f(e)),!1)}let g=Object.assign(Sd,{defaultVisitor:h,convertValue:f,isVisitable:vd});function _(e,n,r=0){if(!Y.isUndefined(e)){if(p(r),d.indexOf(e)!==-1)throw Error(`Circular reference detected in `+n.join(`.`));d.push(e),Y.forEach(e,function(e,i){(!(Y.isUndefined(e)||e===null)&&a.call(t,e,Y.isString(i)?i.trim():i,n,g))===!0&&_(e,n?n.concat(i):[i],r+1)}),d.pop()}}if(!Y.isObject(e))throw TypeError(`data must be an object`);return _(e),t}function wd(e){let t={"!":`%21`,"'":`%27`,"(":`%28`,")":`%29`,"~":`%7E`,"%20":`+`};return encodeURIComponent(e).replace(/[!'()~]|%20/g,function(e){return t[e]})}function Td(e,t){this._pairs=[],e&&Cd(e,this,t)}var Ed=Td.prototype;Ed.append=function(e,t){this._pairs.push([e,t])},Ed.toString=function(e){let t=e?t=>e.call(this,t,wd):wd;return this._pairs.map(function(e){return t(e[0])+`=`+t(e[1])},``).join(`&`)};function Dd(e){return encodeURIComponent(e).replace(/%3A/gi,`:`).replace(/%24/g,`$`).replace(/%2C/gi,`,`).replace(/%20/g,`+`)}function Od(e,t,n){if(!t)return e;e||=``;let r=Y.isFunction(n)?{serialize:n}:n,i=Y.getSafeProp(r,`encode`)||Dd,a=Y.getSafeProp(r,`serialize`),o;if(o=a?a(t,r):Y.isURLSearchParams(t)?t.toString():new Td(t,r).toString(i),o){let t=e.indexOf(`#`);t!==-1&&(e=e.slice(0,t)),e+=(e.indexOf(`?`)===-1?`?`:`&`)+o}return e}var kd=Symbol(`internals`);function Ad(e){return e?e.length:0}function jd(e){if(e)for(;e.length&&e[e.length-1]===null;)e.pop()}function Md(e,t){let n=e.handlers,r=Ad(n);n===t.handlersRef?r!==t.handlersLength&&(r?t.handlerEntries.forEach(function(e,r){n[e.index]!==e.handler&&t.handlerEntries.delete(r)}):t.handlerEntries.clear()):(t.handlersRef=n,t.handlerEntries.clear()),t.handlersLength=r}var Nd=class{constructor(){this.handlers=[],this[kd]={handlersRef:this.handlers,handlersLength:this.handlers.length,handlerEntries:new Map,iterationDepth:0,nextId:0}}use(e,t,n){let r={fulfilled:e,rejected:t,synchronous:n?n.synchronous:!1,runWhen:n?n.runWhen:null},i=this[kd];this.handlers??=[],Md(this,i);let a=i.nextId++;return this.handlers.push(r),i.handlerEntries.set(a,{handler:r,index:this.handlers.length-1}),i.handlersLength=this.handlers.length,a}eject(e){let t=this[kd];Md(this,t);let n=t.handlerEntries.get(e);if(n){if(t.handlerEntries.delete(e),this.handlers[n.index]!==n.handler)return;this.handlers[n.index]=null,t.iterationDepth||(jd(this.handlers),t.handlersLength=this.handlers.length)}}clear(){this.handlers&&(this.handlers=[],Md(this,this[kd]))}forEach(e){let t=this[kd];Md(this,t),t.iterationDepth++;try{Y.forEach(this.handlers,function(t){t!==null&&e(t)})}finally{--t.iterationDepth||(Md(this,t),jd(this.handlers),t.handlersLength=Ad(this.handlers))}}},Pd={silentJSONParsing:!0,forcedJSONParsing:!0,clarifyTimeoutError:!1,legacyInterceptorReqResOrdering:!0,advertiseZstdAcceptEncoding:!1,validateStatusUndefinedResolves:!0},Fd={isBrowser:!0,classes:{URLSearchParams:typeof URLSearchParams<`u`?URLSearchParams:Td,FormData:typeof FormData<`u`?FormData:null,Blob:typeof Blob<`u`?Blob:null},protocols:[`http`,`https`,`file`,`blob`,`url`,`data`]},Id=t({hasBrowserEnv:()=>Ld,hasStandardBrowserEnv:()=>zd,hasStandardBrowserWebWorkerEnv:()=>Bd,navigator:()=>Rd,origin:()=>Vd}),Ld=typeof window<`u`&&typeof document<`u`,Rd=typeof navigator==`object`&&navigator||void 0,zd=Ld&&(!Rd||[`ReactNative`,`NativeScript`,`NS`].indexOf(Rd.product)<0),Bd=typeof WorkerGlobalScope<`u`&&self instanceof WorkerGlobalScope&&typeof self.importScripts==`function`,Vd=Ld&&window.location.href||`http://localhost`,Q={...Id,...Fd};function Hd(e,t){return Cd(e,new Q.classes.URLSearchParams,{visitor:function(e,t,n,r){return Q.isNode&&Y.isBuffer(e)?(this.append(t,e.toString(`base64`)),!1):r.defaultVisitor.apply(this,arguments)},...t})}var Ud=100;function Wd(e){if(e>Ud)throw new Z(`FormData field is too deeply nested (`+e+` levels). Max depth: `+Ud,Z.ERR_FORM_DATA_DEPTH_EXCEEDED)}function Gd(e){let t=[],n=/[^.[\]]+|\[([^.[\]]*)]/g,r;for(;(r=n.exec(e))!==null;)Wd(t.length),t.push(r[0]===`[]`?``:r[1]||r[0]);return t}function Kd(e){let t={},n=Object.keys(e),r,i=n.length,a;for(r=0;r=e.length;return a=!a&&Y.isArray(r)?r.length:a,s?(Y.hasOwnProp(r,a)?r[a]=Y.isArray(r[a])?r[a].concat(n):[r[a],n]:r[a]=n,!o):((!Y.hasOwnProp(r,a)||!Y.isObject(r[a]))&&(r[a]=[]),t(e,n,r[a],i)&&Y.isArray(r[a])&&(r[a]=Kd(r[a])),!o)}if(Y.isFormData(e)&&Y.isFunction(e.entries)){let n={};return Y.forEachEntry(e,(e,r)=>{t(Gd(e),r,n,0)}),n}return null}var Jd=Object.freeze([`get`,`delete`,`head`,`options`,`post`,`put`,`patch`,`purge`,`link`,`unlink`,`query`]),Yd=(e,t)=>e!=null&&Y.hasOwnProp(e,t)?e[t]:void 0;function Xd(e,t,n){if(Y.isString(e))try{return(t||JSON.parse)(e),Y.trim(e)}catch(e){if(e.name!==`SyntaxError`)throw e}return(n||JSON.stringify)(e)}var Zd={transitional:Pd,adapter:[`xhr`,`http`,`fetch`],transformRequest:[function(e,t){let n=t.getContentType()||``,r=n.indexOf(`application/json`)>-1,i=Y.isObject(e);if(i&&Y.isHTMLForm(e)&&(e=new FormData(e)),Y.isFormData(e))return r?JSON.stringify(qd(e)):e;if(Y.isArrayBuffer(e)||Y.isBuffer(e)||Y.isStream(e)||Y.isFile(e)||Y.isBlob(e)||Y.isReadableStream(e))return e;if(Y.isArrayBufferView(e))return e.buffer;if(Y.isURLSearchParams(e))return t.setContentType(`application/x-www-form-urlencoded;charset=utf-8`,!1),e.toString();let a;if(i){let t=Yd(this,`formSerializer`);if(n.indexOf(`application/x-www-form-urlencoded`)>-1)return Hd(e,t).toString();if((a=Y.isFileList(e))||n.indexOf(`multipart/form-data`)>-1){let n=Yd(this,`env`),r=n&&n.FormData;return Cd(a?{"files[]":e}:e,r&&new r,t)}}return i||r?(t.setContentType(`application/json`,!1),Xd(e)):e}],transformResponse:[function(e){let t=Yd(this,`transitional`)||Zd.transitional,n=t&&t.forcedJSONParsing,r=Yd(this,`responseType`),i=r===`json`;if(Y.isResponse(e)||Y.isReadableStream(e))return e;if(e&&Y.isString(e)&&(n&&!r||i)){let n=!(t&&t.silentJSONParsing)&&i;try{return JSON.parse(e,Yd(this,`parseReviver`))}catch(e){if(n)throw e.name===`SyntaxError`?Z.from(e,Z.ERR_BAD_RESPONSE,this,null,Yd(this,`response`)):e}}return e}],timeout:0,xsrfCookieName:`XSRF-TOKEN`,xsrfHeaderName:`X-XSRF-TOKEN`,maxContentLength:-1,maxBodyLength:-1,env:{FormData:Q.classes.FormData,Blob:Q.classes.Blob},validateStatus:function(e){return e>=200&&e<300},headers:{common:{Accept:`application/json, text/plain, */*`,"Content-Type":void 0}}};Y.forEach(Jd,e=>{Zd.headers[e]={}});function Qd(e,t){let n=this||Zd,r=t||n,i=X.from(r.headers),a=r.data;return Y.forEach(e,function(e){a=e.call(n,a,i.normalize(),t?t.status:void 0)}),i.normalize(),a}function $d(e){return!!(e&&e.__CANCEL__)}var ef=class extends Z{constructor(e,t,n){super(e??`canceled`,Z.ERR_CANCELED,t,n),this.name=`CanceledError`,this.__CANCEL__=!0}};function tf(e,t,n){let r=n.config.validateStatus;!n.status||!r||r(n.status)?e(n):t(new Z(`Request failed with status code `+n.status,n.status>=400&&n.status<500?Z.ERR_BAD_REQUEST:Z.ERR_BAD_RESPONSE,n.config,n.request,n))}var nf=/[\t\n\r]/g;function rf(e){if(typeof e!=`string`)return e;let t=0;for(;t{n=r,i=null,a&&=(clearTimeout(a),null),e(...t)};return[(...e)=>{let t=Date.now(),s=t-n;s>=r?o(e,t):(i=e,a||=setTimeout(()=>{a=null,o(i)},r-s))},()=>i&&o(i),(...e)=>o(e)]}var cf=(e,t,n=3)=>{let r=0,i=of(50,250);return sf(n=>{if(!n||!Y.isNumber(n.loaded))return;let a=n.loaded,o=n.lengthComputable?n.total:void 0,s=Math.max(0,o==null?a:Math.min(a,o)),c=Math.max(0,s-r),l=i(c);r=Math.max(r,s),e({loaded:s,total:o,progress:o?s/o:void 0,bytes:c,rate:l||void 0,estimated:l&&o?(o-s)/l:void 0,event:n,lengthComputable:o!=null,[t?`download`:`upload`]:!0})},n)},lf=(e,t)=>{let n=e!=null;return[r=>t[0]({lengthComputable:n,total:e,loaded:r}),t[1]]},uf=(e,t=Y.asap)=>(...n)=>t(()=>e(...n)),df=Q.hasStandardBrowserEnv?((e,t)=>n=>(n=new URL(n,Q.origin),e.protocol===n.protocol&&e.host===n.host&&(t||e.port===n.port)))(new URL(Q.origin),Q.navigator&&/(msie|trident)/i.test(Q.navigator.userAgent)):()=>!0,ff=Q.hasStandardBrowserEnv?{write(e,t,n,r,i,a,o){if(typeof document>`u`)return;let s=[`${e}=${encodeURIComponent(t)}`];Y.isNumber(n)&&s.push(`expires=${new Date(n).toUTCString()}`),Y.isString(r)&&s.push(`path=${r}`),Y.isString(i)&&s.push(`domain=${i}`),a===!0&&s.push(`secure`),Y.isString(o)&&s.push(`SameSite=${o}`),document.cookie=s.join(`; `)},read(e){if(typeof document>`u`)return null;let t=document.cookie.split(`;`);for(let n=0;n0&&e.charCodeAt(n-1)===47;)n--;return e.slice(0,n)+`/`+t.replace(/^\/+/,``)}var hf=/^https?:(?!\/\/)/i;function gf(e){return e&&e.replace(/(^|&)([^=&]*=)?[^&]+/g,(e,t,n=``)=>`${t}${n}${pd}`)}function _f(e){let t=e.replace(/^(https?:\/{0,2})[^/?#]*@/i,`$1${pd}@`),n=t.indexOf(`#`),r=(n===-1?t:t.slice(0,n)).replace(/([?&][^=&#]*=)[^&#]*/g,`$1${pd}`);return n===-1?r:`${r}#${gf(t.slice(n+1))}`}function vf(e,t){if(typeof e==`string`){let n=rf(e);if(hf.test(n))throw new Z(`Invalid URL ${JSON.stringify(_f(n))}: missing "//" after protocol`,Z.ERR_INVALID_URL,t)}}function yf(e,t,n,r){vf(t,r);let i=!pf(t);return e&&(i||n===!1)?(vf(e,r),mf(e,t)):t}var bf=e=>e instanceof X?{...e}:e,xf=e=>Object.getOwnPropertySymbols&&Object.getOwnPropertyDescriptor?Object.keys(e).concat(Object.getOwnPropertySymbols(e).filter(t=>Object.getOwnPropertyDescriptor(e,t).enumerable)):Object.keys(e);function Sf(e,t){e||={},t||={};let n=Object.create(null);Object.defineProperty(n,"hasOwnProperty",{__proto__:null,value:Object.prototype.hasOwnProperty,enumerable:!1,writable:!0,configurable:!0});function r(e,t,n,r){return Y.isPlainObject(e)&&Y.isPlainObject(t)?Y.merge.call({caseless:r},e,t):Y.isPlainObject(t)?Y.merge({},t):Y.isArray(t)?t.slice():t}function i(e,t,n,i){if(!Y.isUndefined(t))return r(e,t,n,i);if(!Y.isUndefined(e))return r(void 0,e,n,i)}function a(e,t){if(!Y.isUndefined(t))return r(void 0,t)}function o(e,t){if(!Y.isUndefined(t))return r(void 0,t);if(!Y.isUndefined(e))return r(void 0,e)}function s(n){let r=Y.hasOwnProp(t,`transitional`)?t.transitional:void 0;if(!Y.isUndefined(r)){if(Y.isPlainObject(r)){if(Y.hasOwnProp(r,n))return r[n]}else return}let i=Y.hasOwnProp(e,`transitional`)?e.transitional:void 0;if(Y.isPlainObject(i)&&Y.hasOwnProp(i,n))return i[n]}function c(n,i,a){if(Y.hasOwnProp(t,a))return r(n,i);if(Y.hasOwnProp(e,a))return r(void 0,n)}let l={url:a,method:a,data:a,baseURL:o,transformRequest:o,transformResponse:o,paramsSerializer:o,timeout:o,timeoutErrorMessage:o,withCredentials:o,withXSRFToken:o,adapter:o,responseType:o,xsrfCookieName:o,xsrfHeaderName:o,onUploadProgress:o,onDownloadProgress:o,decompress:o,maxContentLength:o,maxBodyLength:o,beforeRedirect:o,transport:o,httpAgent:o,httpsAgent:o,cancelToken:o,socketPath:o,allowedSocketPaths:o,responseEncoding:o,validateStatus:c,headers:(e,t,n)=>i(bf(e),bf(t),n,!0)};return Y.forEach(xf({...e,...t}),function(r){if(r===`__proto__`||r===`constructor`||r===`prototype`)return;let a=Y.hasOwnProp(l,r)?l[r]:i,o=a(Y.hasOwnProp(e,r)?e[r]:void 0,Y.hasOwnProp(t,r)?t[r]:void 0,r);Y.isUndefined(o)&&a!==c||(n[r]=o)}),Y.hasOwnProp(t,`validateStatus`)&&Y.isUndefined(t.validateStatus)&&s(`validateStatusUndefinedResolves`)===!1&&(Y.hasOwnProp(e,`validateStatus`)?n.validateStatus=r(void 0,e.validateStatus):delete n.validateStatus),n}var Cf=[`content-type`,`content-length`];function wf(e,t,n){if(n!==`content-only`){e.set(t);return}Object.entries(t||{}).forEach(([t,n])=>{Cf.includes(t.toLowerCase())&&e.set(t,n)})}var Tf=e=>encodeURIComponent(e).replace(/%([0-9A-F]{2})/gi,(e,t)=>String.fromCharCode(parseInt(t,16)));function Ef(e){let t=Sf({},e),n=e=>Y.hasOwnProp(t,e)?t[e]:void 0,r=n(`data`),i=n(`withXSRFToken`),a=n(`xsrfHeaderName`),o=n(`xsrfCookieName`),s=n(`headers`),c=n(`auth`),l=n(`baseURL`),u=n(`allowAbsoluteUrls`),d=n(`url`);if(t.headers=s=X.from(s),t.url=Od(yf(l,d,u,t),n(`params`),n(`paramsSerializer`)),c){let t=Y.getSafeProp(c,`username`)||``,n=Y.getSafeProp(c,`password`)||``;try{s.set(`Authorization`,`Basic `+btoa(t+`:`+(n?Tf(n):``)))}catch(t){throw Z.from(t,Z.ERR_BAD_OPTION_VALUE,e)}}if(Y.isFormData(r)){let e=Y.getSafeProp(r,`getHeaders`);Q.hasStandardBrowserEnv||Q.hasStandardBrowserWebWorkerEnv||Y.isReactNative(r)?s.setContentType(void 0):Y.isFunction(e)&&wf(s,e.call(r),n(`formDataHeaderPolicy`))}if(Q.hasStandardBrowserEnv&&(Y.isFunction(i)&&(i=i(t)),i===!0||i==null&&df(t.url))){let e=a&&o&&ff.read(o);e&&s.set(a,e)}return t}var Df=typeof XMLHttpRequest<`u`&&function(e){return new Promise(function(t,n){let r=Ef(e),i=r.data,a=X.from(r.headers).normalize(),{responseType:o,onUploadProgress:s,onDownloadProgress:c}=r,l,u,d,f,p,m;function h(){f&&f(),p&&p(),r.cancelToken&&r.cancelToken.unsubscribe(l),r.signal&&r.signal.removeEventListener(`abort`,l)}let g=new XMLHttpRequest;g.open(r.method.toUpperCase(),r.url,!0),g.timeout=r.timeout;function _(i){if(!g)return;if(g.status===0&&(af(rf(r.url))||af(Q.origin))!==`file`&&!(g.responseURL&&g.responseURL.startsWith(`file:`))){n(new Z(`Request aborted`,Z.ECONNABORTED,e,g)),h(),g=null;return}try{i?m&&m(i):p&&p()}catch(e){setTimeout(()=>{throw e})}if(!g)return;let a=X.from(`getAllResponseHeaders`in g&&g.getAllResponseHeaders());tf(function(e){t(e),h()},function(e){n(e),h()},{data:!o||o===`text`||o===`json`?g.responseText:g.response,status:g.status,statusText:g.statusText,headers:a,config:e,request:g}),g=null}`onloadend`in g?g.onloadend=_:g.onreadystatechange=function(){!g||g.readyState!==4||g.status===0&&!(g.responseURL&&g.responseURL.startsWith(`file:`))||setTimeout(_)},g.onabort=function(){g&&=(n(new Z(`Request aborted`,Z.ECONNABORTED,e,g)),h(),null)},g.onerror=function(t){let r=new Z(t&&t.message?t.message:`Network Error`,Z.ERR_NETWORK,e,g);r.event=t||null,n(r),h(),g=null},g.ontimeout=function(){let t=r.timeout?`timeout of `+r.timeout+`ms exceeded`:`timeout exceeded`,i=r.transitional||Pd;r.timeoutErrorMessage&&(t=r.timeoutErrorMessage),n(new Z(t,i.clarifyTimeoutError?Z.ETIMEDOUT:Z.ECONNABORTED,e,g)),h(),g=null},i===void 0&&a.setContentType(null),`setRequestHeader`in g&&Y.forEach(ed(a),function(e,t){g.setRequestHeader(t,e)}),Y.isUndefined(r.withCredentials)||(g.withCredentials=!!r.withCredentials),o&&o!==`json`&&(g.responseType=r.responseType),c&&([d,p,m]=cf(c,!0),g.addEventListener(`progress`,d)),s&&g.upload&&([u,f]=cf(s),g.upload.addEventListener(`progress`,u),g.upload.addEventListener(`loadend`,f)),(r.cancelToken||r.signal)&&(l=t=>{g&&=(n(!t||t.type?new ef(null,e,g):t),g.abort(),h(),null)},r.cancelToken&&r.cancelToken.subscribe(l),r.signal&&(r.signal.aborted?l():r.signal.addEventListener(`abort`,l)));let v=af(r.url);if(v&&!Q.protocols.includes(v)){n(new Z(`Unsupported protocol `+v+`:`,Z.ERR_BAD_REQUEST,e)),h();return}g.send(i||null)})},Of=(e,t)=>{if(e=e?e.filter(Boolean):[],!t&&!e.length)return;let n=new AbortController,r=!1,i=function(e){if(!r){r=!0,o();let t=e instanceof Error?e:this.reason;n.abort(t instanceof Z?t:new ef(t instanceof Error?t.message:t))}},a=t&&setTimeout(()=>{a=null,i(new Z(`timeout of ${t}ms exceeded`,Z.ETIMEDOUT))},t),o=()=>{e&&=(a&&clearTimeout(a),a=null,e.forEach(e=>{e.unsubscribe?e.unsubscribe(i):e.removeEventListener(`abort`,i)}),null)};e.forEach(e=>{if(!r){if(e.aborted){i.call(e);return}e.addEventListener(`abort`,i,{once:!0})}});let{signal:s}=n;return s.unsubscribe=()=>Y.asap(o),s},kf=function*(e,t){let n=e.byteLength;if(!t||n{let i=Af(e,t),a=0,o,s=e=>{o||(o=!0,r&&r(e))};return new ReadableStream({async pull(e){try{let{done:t,value:r}=await i.next();if(t){s(),e.close();return}let o=r.byteLength;n&&n(a+=o),e.enqueue(new Uint8Array(r))}catch(e){throw s(e),e}},cancel(e){return s(e),i.return()}},{highWaterMark:2})},Nf=e=>e>=48&&e<=57||e>=65&&e<=70||e>=97&&e<=102,Pf=(e,t,n)=>t+2e<=57?e-48:(e&223)-55,If=e=>e>=65&&e<=90||e>=97&&e<=122||e>=48&&e<=57||e===43||e===47||e===45||e===95,Lf=e=>e===9||e===10||e===12||e===13||e===32,Rf=e=>{let t=Math.floor(e/4),n=e%4;return t*3+(n===2?1:n===3?2:0)},zf=e=>{let t=e.length,n=0;return t>0&&e.charCodeAt(t-1)===61&&(n++,t>1&&e.charCodeAt(t-2)===61&&n++),Math.floor((t-n)*3/4)},Bf=e=>{let t=e.length,n=0,r=0,i=!1;for(let a=0;a0){i=!0;continue}n++}}return i||r>2||r>0&&(n+r)%4!=0||n%4==1?zf(e):Rf(n)},Vf=(e,t)=>{if(!e||typeof e!=`string`||!e.startsWith(`data:`))return 0;let n=e.indexOf(`,`);if(n<0)return 0;let r=e.slice(5,n),i=e.slice(n+1);if(/;base64/i.test(r))return t(i);let a=0;for(let e=0,t=i.length;e=55296&&n<=56319&&e+1=56320&&t<=57343?(a+=4,e++):a+=3}else a+=3}return a};function Hf(e){let t=typeof e==`string`?e.indexOf(`#`):-1;return Vf(t===-1?e:e.slice(0,t),Bf)}var Uf=`1.20.0`,Wf=65536,Gf={cache:`default`,redirect:`follow`,referrer:`about:client`,referrerPolicy:``,mode:`cors`,integrity:``,keepalive:!1,priority:`auto`,window:null},{isFunction:Kf}=Y,qf=e=>encodeURIComponent(e).replace(/%([0-9A-F]{2})/gi,(e,t)=>String.fromCharCode(parseInt(t,16))),Jf=e=>{if(!Y.isString(e))return e;try{return decodeURIComponent(e)}catch{return e}},Yf=(e,...t)=>{try{return!!e(...t)}catch{return!1}},Xf=e=>{let t=e.indexOf(`://`),n=e;return t!==-1&&(n=n.slice(t+3)),n.includes(`@`)||n.includes(`:`)},Zf=e=>{let t=Y.global!==void 0&&Y.global!==null?Y.global:globalThis,{ReadableStream:n,TextEncoder:r}=t;e=Y.merge.call({skipUndefined:!0},{Request:t.Request,Response:t.Response},e);let{fetch:i,Request:a,Response:o}=e,s=i?Kf(i):typeof fetch==`function`,c=Kf(a),l=Kf(o);if(!s)return!1;let u=s&&Kf(n),d=s&&(typeof r==`function`?(e=>t=>e.encode(t))(new r):async e=>new Uint8Array(await new a(e).arrayBuffer())),f=c&&u&&Yf(()=>{let e=!1,t=new a(Q.origin,{body:new n,method:`POST`,get duplex(){return e=!0,`half`}}),r=t.headers.has(`Content-Type`);return t.body!=null&&t.body.cancel(),e&&!r}),p=l&&u&&Yf(()=>Y.isReadableStream(new o(``).body)),m={stream:p&&(e=>e.body)};s&&[`text`,`arrayBuffer`,`blob`,`formData`,`stream`].forEach(e=>{!m[e]&&(m[e]=(t,n)=>{let r=t&&t[e];if(r)return r.call(t);throw new Z(`Response type '${e}' is not supported`,Z.ERR_NOT_SUPPORT,n)})});let h=async e=>{if(e==null)return 0;if(Y.isBlob(e))return e.size;if(Y.isSpecCompliantForm(e))return(await new a(Q.origin,{method:`POST`,body:e}).arrayBuffer()).byteLength;if(Y.isArrayBufferView(e)||Y.isArrayBuffer(e))return e.byteLength;if(Y.isURLSearchParams(e)&&(e+=``),Y.isString(e))return(await d(e)).byteLength},g=async(e,t)=>Y.toFiniteNumber(e.getContentLength())??h(t);return async e=>{let{url:t,method:n,data:s,signal:l,cancelToken:d,timeout:_,onDownloadProgress:v,onUploadProgress:y,responseType:b,headers:x,withCredentials:S=`same-origin`,fetchOptions:C,maxContentLength:w,maxBodyLength:ee,maxRedirects:te}=Ef(e),ne=Y.isNumber(w)&&w>-1,re=Y.isNumber(ee)&&ee>-1,ie=t=>Y.hasOwnProp(e,t)?e[t]:void 0,T=i||fetch;b=b?(b+``).toLowerCase():`text`;let E=Of([l,d&&d.toAbortSignal()],_),D=null,O=E&&E.unsubscribe&&(()=>{E.unsubscribe()}),ae,k=null,oe=()=>new Z(`Request body larger than maxBodyLength limit`,Z.ERR_BAD_REQUEST,e,D);try{let i,l=ie(`auth`);if(l&&(i={username:Y.getSafeProp(l,`username`)||``,password:Y.getSafeProp(l,`password`)||``}),Xf(t)){let e=new URL(t,Q.origin);!i&&(e.username||e.password)&&(i={username:Jf(e.username),password:Jf(e.password)}),(e.username||e.password)&&(e.username=``,e.password=``,t=e.href)}if(i&&(x.delete(`authorization`),x.set(`Authorization`,`Basic `+btoa(qf((i.username||``)+`:`+(i.password||``))))),ne&&typeof t==`string`&&t.startsWith(`data:`)&&Hf(t)>w)throw new Z(`maxContentLength size of `+w+` exceeded`,Z.ERR_BAD_RESPONSE,e,D);if(re&&n!==`get`&&n!==`head`){let e=await h(s);if(typeof e==`number`&&isFinite(e)&&(ae=e,e>ee))throw oe()}let d=re&&(Y.isReadableStream(s)||Y.isStream(s)),_=(e,t,n)=>Mf(e,Wf,e=>{if(re&&e>ee)throw k=oe();t&&t(e)},n);if(f&&n!==`get`&&n!==`head`&&(y||d)){if(ae??=await g(x,s),ae!==0||d){let e=new a(t,{method:`POST`,body:s,duplex:`half`}),n;if(Y.isFormData(s)&&(n=e.headers.get(`content-type`))&&x.setContentType(n),e.body){let[t,n]=y&&lf(ae,cf(uf(y)))||[];s=_(e.body,t,n)}}}else if(d&&!c&&u&&n!==`get`&&n!==`head`)s=_(s);else if(d&&c&&!f&&n!==`get`&&n!==`head`)throw new Z(`Stream request bodies are not supported by the current fetch implementation`,Z.ERR_NOT_SUPPORT,e,D);Y.isString(S)||(S=S?`include`:`omit`);let se=c&&`credentials`in a.prototype;if(Y.isFormData(s)){let e=x.getContentType();e&&/^multipart\/form-data/i.test(e)&&!/boundary=/i.test(e)&&x.delete(`content-type`)}x.set(`User-Agent`,`axios/`+Uf,!1);let A=C==null?C:Object.assign(Object.create(null),C);A&&(delete A.body,delete A.headers,delete A.method,delete A.signal,delete A.duplex,delete A.credentials);let j=Object.assign(Object.create(null),A,{signal:E,method:n.toUpperCase(),headers:ed(x.normalize()),body:s,duplex:`half`,credentials:se?S:void 0});c&&(Y.forEach(Gf,(e,t)=>{j[t]===void 0&&(j[t]=e)}),j.signal===void 0&&(j.signal=null),j.body===void 0&&(j.body=null)),te===0&&(j.redirect=`manual`,A&&(A.redirect=`manual`)),D=c&&new a(t,j);let M=await(c?T(D,A):T(t,j)),ce=X.from(M.headers);if(ne){let t=Y.toFiniteNumber(ce.getContentLength());if(t!=null&&t>w)throw new Z(`maxContentLength size of `+w+` exceeded`,Z.ERR_BAD_RESPONSE,e,D)}let le=p&&(b===`stream`||b===`response`);if(p&&M.body&&(v||ne||le&&O)){let t={};[`status`,`statusText`,`headers`].forEach(e=>{t[e]=M[e]});let n=Y.toFiniteNumber(ce.getContentLength()),[r,i]=v&&lf(n,cf(uf(v),!0))||[],a=0;M=new o(Mf(M.body,Wf,t=>{if(ne&&(a=t,a>w))throw new Z(`maxContentLength size of `+w+` exceeded`,Z.ERR_BAD_RESPONSE,e,D);r&&r(t)},()=>{i&&i(),O&&O()}),t)}b||=`text`;let ue=await m[Y.findKey(m,b)||`text`](M,e);if(ne&&!p&&!le){let t;if(ue!=null&&(typeof ue.byteLength==`number`?t=ue.byteLength:typeof ue.size==`number`?t=ue.size:typeof ue==`string`&&(t=typeof r==`function`?new r().encode(ue).byteLength:ue.length)),typeof t==`number`&&t>w)throw new Z(`maxContentLength size of `+w+` exceeded`,Z.ERR_BAD_RESPONSE,e,D)}return!le&&O&&O(),await new Promise((t,n)=>{tf(t,n,{data:ue,headers:X.from(M.headers),status:M.status,statusText:M.statusText,config:e,request:D})})}catch(t){if(O&&O(),E&&E.aborted&&E.reason instanceof Z){let n=E.reason;throw n.config=e,D&&(n.request=D),t!==n&&Object.defineProperty(n,"cause",{__proto__:null,value:t,writable:!0,enumerable:!1,configurable:!0}),n}if(k)throw D&&!k.request&&(k.request=D),k;if(t instanceof Z)throw D&&!t.request&&(t.request=D),t;if(t&&t.name===`TypeError`&&/Load failed|fetch/i.test(t.message)){let n=new Z(`Network Error`,Z.ERR_NETWORK,e,D,t&&t.response);throw Object.defineProperty(n,"cause",{__proto__:null,value:t.cause||t,writable:!0,enumerable:!1,configurable:!0}),n}throw Z.from(t,t&&t.code,e,D,t&&t.response)}}},Qf=new Map,$f=e=>{let t=e&&e.env||{},{fetch:n,Request:r,Response:i}=t,a=[r,i,n],o=a.length,s,c,l=Qf;for(;o--;)s=a[o],c=l.get(s),c===void 0&&l.set(s,c=o?new Map:Zf(t)),l=c;return c};$f();var ep={http:null,xhr:Df,fetch:{get:$f}};Y.forEach(ep,(e,t)=>{if(e){try{Object.defineProperty(e,"name",{__proto__:null,value:t})}catch{}Object.defineProperty(e,"adapterName",{__proto__:null,value:t})}});var tp=e=>`- ${e}`,np=e=>Y.isFunction(e)||e===null||e===!1;function rp(e,t){e=Y.isArray(e)?e:[e];let{length:n}=e,r,i,a={};for(let o=0;o`adapter ${e} `+(t===!1?`is not supported by the environment`:`is not available in the build`));throw new Z(`There is no suitable adapter to dispatch the request `+(n?e.length>1?`since : +`+e.map(tp).join(` +`):` `+tp(e[0]):`as no adapter specified`),Z.ERR_NOT_SUPPORT)}return i}var ip={getAdapter:rp,adapters:ep};function ap(e){if(e.cancelToken&&e.cancelToken.throwIfRequested(),e.signal&&e.signal.aborted)throw new ef(null,e)}function op(e){let t=Y.toSafeFlatObject(e);return ap(t),t.headers=X.from(Y.getSafeProp(t,`headers`)),t.data=Qd.call(t,t.transformRequest),[`post`,`put`,`patch`].indexOf(t.method)!==-1&&t.headers.setContentType(`application/x-www-form-urlencoded`,!1),ip.getAdapter(t.adapter||Zd.adapter,t)(t).then(function(e){ap(t),t.response=e;try{e.data=Qd.call(t,t.transformResponse,e)}finally{delete t.response}return e.headers=X.from(e.headers),e},function(e){if(!$d(e)&&(ap(t),e&&e.response)){t.response=e.response;try{e.response.data=Qd.call(t,t.transformResponse,e.response)}finally{delete t.response}e.response.headers=X.from(e.response.headers)}return Promise.reject(e)})}var sp={};[`object`,`boolean`,`number`,`function`,`string`,`symbol`].forEach((e,t)=>{sp[e]=function(n){return typeof n===e||`a`+(t<1?`n `:` `)+e}});var cp={};sp.transitional=function(e,t,n){function r(e,t){return`[Axios v`+Uf+`] Transitional option '`+e+`'`+t+(n?`. `+n:``)}return(n,i,a)=>{if(e===!1)throw new Z(r(i,` has been removed`+(t?` in `+t:``)),Z.ERR_DEPRECATED);return t&&!cp[i]&&(cp[i]=!0,console.warn(r(i,` has been deprecated since v`+t+` and will be removed in the near future`))),!e||e(n,i,a)}},sp.spelling=function(e){return(t,n)=>(console.warn(`${n} is likely a misspelling of ${e}`),!0)};function lp(e,t,n){if(typeof e!=`object`||!e)throw new Z(`options must be an object`,Z.ERR_BAD_OPTION_VALUE);let r=Object.keys(e),i=r.length;for(;i-->0;){let a=r[i],o=Object.prototype.hasOwnProperty.call(t,a)?t[a]:void 0;if(o){let t=e[a],n=t===void 0||o(t,a,e);if(n!==!0)throw new Z(`option `+a+` must be `+n,Z.ERR_BAD_OPTION_VALUE);continue}if(n!==!0)throw new Z(`Unknown option `+a,Z.ERR_BAD_OPTION)}}var up={assertOptions:lp,validators:sp},dp=up.validators,fp=class{constructor(e){this.defaults=e||{},this.interceptors={request:new Nd,response:new Nd}}async request(e,t){try{return await this._request(e,t)}catch(e){if(e instanceof Error)try{let t={};Error.captureStackTrace?Error.captureStackTrace(t):t=Error();let n=t.stack,r=``;if(typeof n==`string`){let e=n.indexOf(` +`);r=e===-1?``:n.slice(e+1)}if(!e.stack)e.stack=r;else if(r){let t=r.indexOf(` +`),n=t===-1?-1:r.indexOf(` +`,t+1),i=n===-1?``:r.slice(n+1);String(e.stack).endsWith(i)||(e.stack+=` +`+r)}}catch{}throw e}}_request(e,t){typeof e==`string`?(t||={},t.url=e):t=e||{},t=Sf(this.defaults,t);let{transitional:n,paramsSerializer:r,headers:i}=t;n!==void 0&&up.assertOptions(n,{silentJSONParsing:dp.transitional(dp.boolean),forcedJSONParsing:dp.transitional(dp.boolean),clarifyTimeoutError:dp.transitional(dp.boolean),legacyInterceptorReqResOrdering:dp.transitional(dp.boolean),advertiseZstdAcceptEncoding:dp.transitional(dp.boolean),validateStatusUndefinedResolves:dp.transitional(dp.boolean)},!1),r!=null&&(Y.isFunction(r)?t.paramsSerializer={serialize:r}:up.assertOptions(r,{encode:dp.function,serialize:dp.function},!0)),t.allowAbsoluteUrls!==void 0||(this.defaults.allowAbsoluteUrls===void 0?t.allowAbsoluteUrls=!0:t.allowAbsoluteUrls=this.defaults.allowAbsoluteUrls),up.assertOptions(t,{baseUrl:dp.spelling(`baseURL`),withXsrfToken:dp.spelling(`withXSRFToken`)},!0),t.method=(Y.getSafeProp(t,`method`)||Y.getSafeProp(this.defaults,`method`)||`get`).toLowerCase();let a=i&&Y.merge(i.common,i[t.method]);i&&Y.forEach(Jd.concat(`common`),e=>{delete i[e]}),t.headers=X.concat(a,i);let o=[],s=!0;this.interceptors.request.forEach(function(e){if(typeof e.runWhen==`function`&&e.runWhen(t)===!1)return;s&&=e.synchronous;let n=t.transitional||Pd;n&&n.legacyInterceptorReqResOrdering?o.unshift(e.fulfilled,e.rejected):o.push(e.fulfilled,e.rejected)});let c=[];this.interceptors.response.forEach(function(e){c.push(e.fulfilled,e.rejected)});let l,u=0,d;if(!s){let e=[op.bind(this),void 0];for(e.unshift(...o),e.push(...c),d=e.length,l=Promise.resolve(t);uop.call(this,f)))}catch(e){l=Promise.reject(e)}break}}if(!l)try{l=op.call(this,f)}catch(e){l=Promise.reject(e)}for(u=0,d=c.length;u{if(!n._listeners)return;let t=n._listeners.length;for(;t-->0;)n._listeners[t](e);n._listeners=null}),this.promise.then=e=>{let t,r=new Promise(e=>{n.subscribe(e),t=e}).then(e);return r.cancel=function(){n.unsubscribe(t)},r},e(function(e,r,i){n.reason||(n.reason=new ef(e,r,i),t(n.reason))})}throwIfRequested(){if(this.reason)throw this.reason}subscribe(e){if(this.reason){e(this.reason);return}this._listeners?this._listeners.push(e):this._listeners=[e]}unsubscribe(e){if(!this._listeners)return;let t=this._listeners.indexOf(e);t!==-1&&this._listeners.splice(t,1)}toAbortSignal(){let e=new AbortController,t=t=>{e.abort(t)};return this.subscribe(t),e.signal.unsubscribe=()=>this.unsubscribe(t),e.signal}static source(){let t;return{token:new e(function(e){t=e}),cancel:t}}};function mp(e){return function(t){return e.apply(null,t)}}function hp(e){return Y.isObject(e)&&e.isAxiosError===!0}var gp={Continue:100,SwitchingProtocols:101,Processing:102,EarlyHints:103,Ok:200,Created:201,Accepted:202,NonAuthoritativeInformation:203,NoContent:204,ResetContent:205,PartialContent:206,MultiStatus:207,AlreadyReported:208,ImUsed:226,MultipleChoices:300,MovedPermanently:301,Found:302,SeeOther:303,NotModified:304,UseProxy:305,Unused:306,TemporaryRedirect:307,PermanentRedirect:308,BadRequest:400,Unauthorized:401,PaymentRequired:402,Forbidden:403,NotFound:404,MethodNotAllowed:405,NotAcceptable:406,ProxyAuthenticationRequired:407,RequestTimeout:408,Conflict:409,Gone:410,LengthRequired:411,PreconditionFailed:412,PayloadTooLarge:413,ContentTooLarge:413,UriTooLong:414,UnsupportedMediaType:415,RangeNotSatisfiable:416,ExpectationFailed:417,ImATeapot:418,MisdirectedRequest:421,UnprocessableEntity:422,UnprocessableContent:422,Locked:423,FailedDependency:424,TooEarly:425,UpgradeRequired:426,PreconditionRequired:428,TooManyRequests:429,RequestHeaderFieldsTooLarge:431,UnavailableForLegalReasons:451,InternalServerError:500,NotImplemented:501,BadGateway:502,ServiceUnavailable:503,GatewayTimeout:504,HttpVersionNotSupported:505,VariantAlsoNegotiates:506,InsufficientStorage:507,LoopDetected:508,NotExtended:510,NetworkAuthenticationRequired:511,WebServerReturnsAnUnknownError:520,WebServerIsDown:521,ConnectionTimedOut:522,OriginIsUnreachable:523,TimeoutOccurred:524,SslHandshakeFailed:525,InvalidSslCertificate:526};Object.entries(gp).forEach(([e,t])=>{gp[t]===void 0&&(gp[t]=e)});function _p(e){let t=new fp(e),n=Tl(fp.prototype.request,t);return Y.extend(n,fp.prototype,t,{allOwnKeys:!0}),Y.extend(n,t,null,{allOwnKeys:!0}),n.create=function(t){return _p(Sf(e,t))},n}var $=_p(Zd);$.Axios=fp,$.CanceledError=ef,$.CancelToken=pp,$.isCancel=$d,$.VERSION=Uf,$.toFormData=Cd,$.AxiosError=Z,$.Cancel=$.CanceledError,$.all=function(e){return Promise.all(e)},$.spread=mp,$.isAxiosError=hp,$.mergeConfig=Sf,$.AxiosHeaders=X,$.formToJSON=e=>qd(Y.isHTMLForm(e)?new FormData(e):e),$.getAdapter=ip.getAdapter,$.HttpStatusCode=gp,$.default=$;var vp=$.create({timeout:1e4});async function yp(e,t){let{data:n}=await vp.post(`/chat`,{query:e,domain_group:t});return n}async function bp(e){let{data:t}=await vp.get(`/runs/${e}/status`);return t}function xp(e){let t=new EventSource(`/runs/${e}/stream`);return{subscribe(e){t.addEventListener(`message`,t=>{let n=JSON.parse(t.data);n.type===`workspace`?e.onWorkspace(n.workspace):n.type===`status`?(e.onStatus(n.value),(n.value===`done`||n.value===`failed`)&&e.onDone()):n.type===`error`&&e.onError(n.detail)}),t.addEventListener(`error`,()=>{})},close(){t.close()}}}async function Sp(e){let t=e?{status:e}:{},{data:n}=await vp.get(`/review/queue`,{params:t});return n}async function Cp(e,t,n){let{data:r}=await vp.post(`/review/${e}`,null,{params:{verdict:t,correction:n}});return r}async function wp(){let{data:e}=await vp.get(`/api/metrics`);return e}async function Tp(){let{data:e}=await vp.get(`/config`);return e}async function Ep(e){let{data:t}=await vp.put(`/config`,e);return t}async function Dp(){let{data:e}=await vp.post(`/config/reset`);return e}async function Op(e,t,n){let r={backend:e};t&&(r.base_url=t),n&&(r.api_key=n);let{data:i}=await vp.get(`/config/models`,{params:r});return i}async function kp(e,t,n){let r={backend:e};t&&(r.base_url=t),n&&(r.api_key=n);let{data:i}=await vp.get(`/config/ping`,{params:r});return i}async function Ap(){let{data:e}=await vp.get(`/llama/status`);return e}async function jp(){let{data:e}=await vp.get(`/llama/models`);return e.models}async function Mp(e,t=8901,n=99,r=4096){let{data:i}=await vp.post(`/llama/start`,null,{params:{model:e,port:t,ngl:n,ctx:r}});return i}async function Np(){let{data:e}=await vp.post(`/llama/stop`);return e}async function Pp(e,t){let n={url:e};t&&(n.dest=t);let{data:r}=await vp.post(`/llama/download`,null,{params:n});return r}function Fp(e){let t=new EventSource(`/llama/download/stream?url=${encodeURIComponent(e)}`);return{subscribe(e){t.addEventListener(`message`,t=>{let n=JSON.parse(t.data);e.onProgress(n),n.done&&e.onDone(n)}),t.addEventListener(`error`,()=>{t.close()})},close(){t.close()}}}async function Ip(e,{timeout:t=6e4,interval:n=500}={}){let r=Date.now();for(;Date.now()-rsetTimeout(e,n))}throw Error(`poll timeout`)}var Lp=ys(`chat`,()=>{let e=qt([]),t=qt(null),n=()=>e.value.find(e=>e.requestId===t.value)??null;async function r(n){let r=await yp(n),i={requestId:r.request_id,query:n,init:r,status:null,workspace:null,error:null};return e.value.unshift(i),t.value=r.request_id,i}async function i(t){let n=await bp(t),r=e.value.find(e=>e.requestId===t);return r&&(r.status=n),n}async function a(t){let n=await Ip(t),r=e.value.find(e=>e.requestId===t);return r&&(r.status=n),n}function o(t,n){let r=e.value.find(e=>e.requestId===t);r&&(r.workspace=n)}function s(e){t.value=e}return{sessions:e,currentId:t,current:n,sendQuery:r,pollStatus:i,waitForDone:a,updateWorkspace:o,setCurrent:s}}),Rp={class:`chat-view`},zp={class:`sidebar`},Bp={class:`session-list`},Vp=[`onClick`],Hp={class:`s-query`},Up={class:`main`},Wp={key:0,class:`empty`},Gp={class:`user-msg`},Kp={class:`status-bar`},qp={key:0,class:`badge pending`},Jp={key:1,class:`badge running`},Yp={key:2,class:`badge done`},Xp={key:3,class:`badge failed`},Zp={key:4,class:`route-path`},Qp={key:0,class:`ws-meta`},$p={key:1,class:`assistant-msg`},em={key:2,class:`ws-preview`},tm={key:0,class:`brief-block`},nm={class:`tags`},rm={key:1,class:`plan-list`},im={class:`step-id`},am={key:0,class:`deps`},om={key:2,class:`progress-list`},sm={key:3,class:`error-msg`},cm=[`disabled`],lm=[`disabled`],um=Qn({__name:`ChatView`,setup(e){let t=Lp(),n=qt(``),r=qt(!1),i=G(()=>t.current()),a=G(()=>i.value?.status?.status??`pending`),o=G(()=>i.value?.workspace),s=G(()=>i.value?.status?.response??null),c=null;function l(e){c?.close(),c=xp(e),c.subscribe({onWorkspace(n){t.updateWorkspace(e,n)},onStatus(n){t.pollStatus(e)},onError(n){let r=t.sessions.find(t=>t.requestId===e);r&&(r.error=n)},onDone(){t.pollStatus(e)}})}Hn(()=>t.currentId,e=>{e&&(l(e),i.value?.status?.status===`done`&&t.pollStatus(e))},{immediate:!0});async function u(){if(!n.value.trim()||r.value)return;let e=n.value.trim();n.value=``,r.value=!0;try{l((await t.sendQuery(e)).requestId)}finally{r.value=!1}}return(e,i)=>(B(),V(`div`,Rp,[H(`aside`,zp,[i[1]||=H(`h3`,null,`会话记录`,-1),H(`ul`,Bp,[(B(!0),V(Vi,null,Dr(z(t).sessions,e=>(B(),V(`li`,{key:e.requestId,class:pe([`session-item`,{active:e.requestId===z(t).currentId}]),onClick:n=>z(t).setCurrent(e.requestId)},[H(`span`,Hp,N(e.query.slice(0,28))+N(e.query.length>28?`…`:``),1),H(`span`,{class:pe([`s-status`,e.status?.status??`pending`])},N(e.status?.status??`pending`),3)],10,Vp))),128))])]),H(`main`,Up,[z(t).current()?(B(),V(Vi,{key:1},[H(`div`,Gp,[i[3]||=H(`span`,{class:`role-label`},`你`,-1),H(`p`,null,N(z(t).current().query),1)]),H(`div`,Kp,[a.value===`pending`?(B(),V(`span`,qp,`⏳ 排队中…`)):a.value===`running`?(B(),V(`span`,Jp,` 🔄 协作中 (`+N(o.value?.meta.round??0)+`/`+N(o.value?.meta.rounds_cap??6)+`) `,1)):a.value===`done`?(B(),V(`span`,Yp,`✅ 完成`)):a.value===`failed`?(B(),V(`span`,Xp,`❌ 失败`)):oa(``,!0),o.value?(B(),V(`span`,Zp,` 路由:`+N(z(t).current().status?.route?.join(` → `)??`—`),1)):oa(``,!0)]),o.value?(B(),V(`div`,Qp,[H(`span`,null,`tokens: `+N(o.value.meta.api_input_tokens)+` in / `+N(o.value.meta.api_output_tokens)+` out`,1),H(`span`,null,`延迟: `+N(z(t).current().status?.latency_ms?.toFixed(0))+` ms`,1),H(`span`,null,`模型: `+N(z(t).current().status?.model_used??`—`),1)])):oa(``,!0),s.value?(B(),V(`div`,$p,[i[4]||=H(`span`,{class:`role-label`},`系统`,-1),H(`pre`,null,N(s.value),1)])):oa(``,!0),o.value?(B(),V(`details`,em,[i[6]||=H(`summary`,null,`📄 交流文本预览`,-1),o.value.brief?(B(),V(`div`,tm,[i[5]||=H(`strong`,null,`Brief:`,-1),aa(` `+N(o.value.brief.goal)+` `,1),H(`span`,nm,N(o.value.brief.tags.join(`, `)),1)])):oa(``,!0),o.value.plan?.length?(B(),V(`ul`,rm,[(B(!0),V(Vi,null,Dr(o.value.plan,e=>(B(),V(`li`,{key:e.id,class:pe(e.status)},[H(`span`,im,N(e.id),1),H(`span`,null,N(e.task),1),e.deps.length?(B(),V(`span`,am,`←`+N(e.deps.join(`,`)),1)):oa(``,!0)],2))),128))])):oa(``,!0),o.value.progress?.length?(B(),V(`ul`,om,[(B(!0),V(Vi,null,Dr(o.value.progress,e=>(B(),V(`li`,{key:e.step,class:pe(e.status)},N(e.step)+`: `+N(e.status),3))),128))])):oa(``,!0)])):oa(``,!0),z(t).current().error?(B(),V(`div`,sm,N(z(t).current().error),1)):oa(``,!0)],64)):(B(),V(`div`,Wp,[...i[2]||=[H(`p`,null,`输入问题,开启端云协同协作之旅。`,-1),H(`p`,{class:`hint`},`结果通过 SSE 实时推送,可切换「协作」页面查看交流文本可视化。`,-1)]])),H(`form`,{class:`input-bar`,onSubmit:Fo(u,[`prevent`])},[Fn(H(`input`,{"onUpdate:modelValue":i[0]||=e=>n.value=e,placeholder:`输入你的问题…`,disabled:r.value,autofocus:``},null,8,cm),[[To,n.value]]),H(`button`,{type:`submit`,disabled:r.value||!n.value.trim()},N(r.value?`发送中…`:`发送`),9,lm)],32)])]))}}),dm=(e,t)=>{let n=e.__vccOpts||e;for(let[e,r]of t)n[e]=r;return n},fm=dm(um,[[`__scopeId`,`data-v-6692005c`]]),pm=`modulepreload`,mm=function(e){return`/static/`+e},hm={},gm=function(e,t,n){let r=Promise.resolve();if(t&&t.length>0){let e=document.getElementsByTagName(`link`),i=document.querySelector(`meta[property=csp-nonce]`),a=i?.nonce||i?.getAttribute(`nonce`);function o(e){return Promise.all(e.map(e=>Promise.resolve(e).then(e=>({status:`fulfilled`,value:e}),e=>({status:`rejected`,reason:e}))))}function s(e){return import.meta.resolve?import.meta.resolve(e):new URL(e,import.meta.url).href}r=o(t.map(t=>{if(t=mm(t,n),t=s(t),t in hm)return;hm[t]=!0;let r=t.endsWith(`.css`);for(let n=e.length-1;n>=0;n--){let i=e[n];if(i.href===t&&(!r||i.rel===`stylesheet`))return}let i=document.createElement(`link`);if(i.rel=r?`stylesheet`:pm,r||(i.as=`script`),i.crossOrigin=``,i.href=t,a&&i.setAttribute(`nonce`,a),document.head.appendChild(i),r)return new Promise((e,n)=>{i.addEventListener(`load`,e),i.addEventListener(`error`,()=>n(Error(`Unable to preload CSS for ${t}`)))})}))}function i(e){let t=new Event(`vite:preloadError`,{cancelable:!0});if(t.payload=e,window.dispatchEvent(t),!t.defaultPrevented)throw e}return r.then(t=>{for(let e of t||[])e.status===`rejected`&&i(e.reason);return e().catch(i)})},_m=wl({history:Wc(`/`),routes:[{path:`/`,redirect:`/chat`},{path:`/chat`,name:`chat`,component:fm},{path:`/collaboration`,name:`collaboration`,component:()=>gm(()=>import(`./CollaborationView-BIjnBJHk.js`),__vite__mapDeps([0,1]))},{path:`/review`,name:`review`,component:()=>gm(()=>import(`./ReviewView-nv7jUZLH.js`),__vite__mapDeps([2,3]))},{path:`/metrics`,name:`metrics`,component:()=>gm(()=>import(`./MetricsView-rQCk8WKw.js`),__vite__mapDeps([4,5]))},{path:`/settings`,name:`settings`,component:()=>gm(()=>import(`./SettingsView-Cb0oAlJt.js`),__vite__mapDeps([6,7]))}]}),vm=zo(Cs);vm.use(as()),vm.use(_m),vm.mount(`#app`);export{B as A,H as C,Qn as D,aa as E,qt as F,z as I,pe as L,Hn as M,Fn as N,pr as O,It as P,ce as R,G as S,V as T,xp as _,Ap as a,To as b,Op as c,Dp as d,Mp as f,Fp as g,Ep as h,Tp as i,Dr as j,_r as k,Sp as l,Cp as m,Lp as n,wp as o,Np as p,Pp as r,jp as s,dm as t,kp as u,Eo as v,oa as w,Vi as x,Oo as y,N as z}; \ No newline at end of file diff --git a/gateway/static/assets/index-G8RWfJ2Y.css b/gateway/static/assets/index-G8RWfJ2Y.css new file mode 100644 index 0000000..fea0b2e --- /dev/null +++ b/gateway/static/assets/index-G8RWfJ2Y.css @@ -0,0 +1 @@ +*{box-sizing:border-box;margin:0;padding:0}html,body,#app{color:#111;background:#fff;height:100%;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,sans-serif;font-size:14px}.app{flex-direction:column;height:100%;display:flex}.topbar{background:#fff;border-bottom:1px solid #e5e7eb;flex-shrink:0;align-items:center;gap:24px;height:52px;padding:0 24px;display:flex}.brand{color:#1e40af;white-space:nowrap;font-size:16px;font-weight:700}.nav-links{gap:4px;display:flex}.nav-links a{color:#374151;border-radius:6px;padding:6px 14px;font-size:14px;font-weight:500;text-decoration:none;transition:background .15s}.nav-links a:hover{background:#f3f4f6}.nav-links a.router-link-active{color:#2563eb;background:#eff6ff}.content{flex-direction:column;flex:1;min-height:0;display:flex;overflow:hidden}.chat-view[data-v-6692005c]{gap:0;height:100%;display:flex}.sidebar[data-v-6692005c]{background:#f9fafb;border-right:1px solid #e5e7eb;flex-direction:column;width:240px;display:flex;overflow:hidden}.sidebar h3[data-v-6692005c]{color:#6b7280;border-bottom:1px solid #e5e7eb;margin:0;padding:12px 16px;font-size:14px}.session-list[data-v-6692005c]{flex:1;margin:0;padding:8px;list-style:none;overflow-y:auto}.session-item[data-v-6692005c]{cursor:pointer;border-radius:6px;flex-direction:column;gap:2px;margin-bottom:4px;padding:8px 10px;font-size:13px;display:flex}.session-item[data-v-6692005c]:hover{background:#e5e7eb}.session-item.active[data-v-6692005c]{background:#dbeafe}.s-query[data-v-6692005c]{color:#111}.s-status[data-v-6692005c]{color:#9ca3af;font-size:11px}.s-status.done[data-v-6692005c]{color:#16a34a}.s-status.failed[data-v-6692005c]{color:#dc2626}.s-status.running[data-v-6692005c]{color:#2563eb}.main[data-v-6692005c]{flex-direction:column;flex:1;gap:12px;padding:20px 24px;display:flex;overflow:hidden}.empty[data-v-6692005c]{color:#9ca3af;flex-direction:column;flex:1;justify-content:center;align-items:center;gap:8px;display:flex}.hint[data-v-6692005c]{font-size:13px}.user-msg[data-v-6692005c],.assistant-msg[data-v-6692005c]{background:#f3f4f6;border-radius:8px;gap:12px;padding:12px 16px;font-size:14px;line-height:1.6;display:flex}.assistant-msg[data-v-6692005c]{background:#eff6ff}.role-label[data-v-6692005c]{color:#6b7280;min-width:32px;font-size:12px;font-weight:700}.user-msg pre[data-v-6692005c],.assistant-msg pre[data-v-6692005c]{white-space:pre-wrap;margin:0;font-family:inherit}.status-bar[data-v-6692005c]{flex-wrap:wrap;align-items:center;gap:12px;display:flex}.badge[data-v-6692005c]{border-radius:99px;padding:3px 10px;font-size:12px;font-weight:600}.badge.pending[data-v-6692005c]{color:#6b7280;background:#f3f4f6}.badge.running[data-v-6692005c]{color:#2563eb;background:#dbeafe}.badge.done[data-v-6692005c]{color:#16a34a;background:#dcfce7}.badge.failed[data-v-6692005c]{color:#dc2626;background:#fee2e2}.route-path[data-v-6692005c]{color:#9ca3af;font-size:12px}.ws-meta[data-v-6692005c]{color:#9ca3af;gap:16px;font-size:12px;display:flex}.ws-preview[data-v-6692005c]{background:#fafafa;border:1px solid #e5e7eb;border-radius:8px;padding:12px;font-size:13px}.brief-block[data-v-6692005c]{margin-bottom:8px}.tags[data-v-6692005c]{color:#6b7280;margin-left:8px;font-size:12px}.plan-list[data-v-6692005c],.progress-list[data-v-6692005c]{margin:4px 0 0;padding:0;list-style:none}.plan-list li[data-v-6692005c],.progress-list li[data-v-6692005c]{gap:6px;padding:2px 0;display:flex}.step-id[data-v-6692005c]{color:#6b7280;min-width:48px;font-family:monospace}.deps[data-v-6692005c]{color:#9ca3af;font-size:12px}.done[data-v-6692005c]{color:#16a34a}.pending[data-v-6692005c]{color:#9ca3af}.running[data-v-6692005c]{color:#2563eb}.error-msg[data-v-6692005c]{color:#dc2626;background:#fee2e2;border-radius:6px;padding:8px 12px;font-size:13px}.input-bar[data-v-6692005c]{border-top:1px solid #e5e7eb;gap:8px;margin-top:auto;padding-top:12px;display:flex}.input-bar input[data-v-6692005c]{border:1px solid #d1d5db;border-radius:8px;outline:none;flex:1;padding:10px 14px;font-size:14px}.input-bar input[data-v-6692005c]:focus{border-color:#2563eb}.input-bar button[data-v-6692005c]{color:#fff;cursor:pointer;background:#2563eb;border:none;border-radius:8px;padding:10px 20px;font-size:14px}.input-bar button[data-v-6692005c]:disabled{cursor:not-allowed;background:#9ca3af} diff --git a/gateway/static/favicon.svg b/gateway/static/favicon.svg new file mode 100644 index 0000000..6893eb1 --- /dev/null +++ b/gateway/static/favicon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gateway/static/icons.svg b/gateway/static/icons.svg new file mode 100644 index 0000000..e952219 --- /dev/null +++ b/gateway/static/icons.svg @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/gateway/static/index.html b/gateway/static/index.html index df4000d..1127e4a 100644 --- a/gateway/static/index.html +++ b/gateway/static/index.html @@ -1,224 +1,14 @@ - - - - - -端云协同 LLM 协作系统 - - - -
-

⚙️ 端云协同 LLM 协作系统

- Architect(大模型 API)+ Worker(本地小模型)+ 交流文本协议 - -
- -
- -
-
-
-
- - -
-
- - -
-
-
-
-
-
-
- -
-
- - -
-
-
- -
-
- - -
-
-
- -
-
-
-
- -
-
-

⚙️ 内置小模型(Worker)

-
- - - - - - -
-
-
-

🧠 大模型(Architect / API)

-
- - - -
-
-
-

🔀 管线 pipeline

-
- - - - -
-
-
- - - -
-
-
-
端云协同 LLM 协作系统 · v2 · 交流文本协议 · 北极星:token 下降 ≥80%
- - - \ No newline at end of file + + + + + + + webapp + + + + +
+ + diff --git a/router_system/architect.py b/router_system/architect.py index 750aa88..001ff46 100644 --- a/router_system/architect.py +++ b/router_system/architect.py @@ -59,7 +59,7 @@ class ArchitectClient: def __init__( self, model: str, - base_url: str = "https://api.deepseek.com/v1", + base_url: str = "https://api.deepseek.com", api_key: Optional[str] = None, temperature: float = 0.2, timeout_s: float = 60.0, @@ -220,8 +220,8 @@ def build_architect(cfg: Dict[str, Any], _env = get_env or os.environ.get key = cfg.get("api_key") or _env(cfg.get("api_key_env", "DEEPSEEK_API_KEY")) return ArchitectClient( - model=cfg.get("model", "deepseek-chat"), - base_url=cfg.get("base_url", "https://api.deepseek.com/v1"), + model=cfg.get("model", "deepseek-v4-flash"), + base_url=cfg.get("base_url", "https://api.deepseek.com"), api_key=key, temperature=float(cfg.get("temperature", 0.2)), timeout_s=float(cfg.get("timeout_s", 60)), diff --git a/router_system/classifier.py b/router_system/classifier.py index 2eb7437..1d10372 100644 --- a/router_system/classifier.py +++ b/router_system/classifier.py @@ -10,7 +10,7 @@ confidence = 1 - exp(-s),保证 s=1 -> 0.63,s=2 -> 0.86,s=3 -> 0.95。 from __future__ import annotations import math -from typing import Dict, List, Tuple +from typing import Dict, List, Optional, Tuple from .difficulty import estimate_difficulty from .models import Classification @@ -51,6 +51,14 @@ DOMAIN_RULES: Dict[str, List[Tuple[str, float]]] = { ("知识产权", 1.1), ("版权", 0.9), ("专利", 0.9), ("违约", 0.9), ("赔偿", 0.8), ("仲裁", 0.9), ("劳动法", 1.0), ("刑法", 1.0), ("民法典", 1.0), ("法规", 0.8), ("条款", 0.7), ("律师", 0.8), ("起诉", 0.9), ("判决", 0.9), + # 劳动法 + ("加班", 0.9), ("加班费", 1.0), ("工资", 0.8), ("辞退", 0.9), ("裁员", 0.9), + ("试用期", 0.9), ("社保", 0.8), ("公积金", 0.8), ("年假", 0.9), ("离职", 0.8), + ("解除劳动合同", 1.1), ("经济补偿", 1.0), ("竞业", 1.0), + # 房产/婚姻/消费者 + ("租房", 0.9), ("买房", 0.9), ("购房", 0.9), ("押金", 0.8), ("房贷", 0.9), + ("离婚", 1.0), ("继承", 0.9), ("遗产", 0.9), ("抚养权", 0.9), ("遗嘱", 0.9), + ("退款", 0.9), ("退货", 0.8), ("消费者", 0.8), ("七天无理由", 1.0), ("维权", 0.8), ("law", 1.0), ("legal", 1.1), ("contract", 1.0), ("compliance", 1.0), ("litigation", 1.0), ("copyright", 0.9), ("patent", 0.9), ("trademark", 0.9), ("liability", 0.9), ("regulatory", 0.8), ("jurisdiction", 0.9), @@ -61,11 +69,17 @@ DOMAIN_RULES: Dict[str, List[Tuple[str, float]]] = { ("医生", 0.9), ("血压", 0.9), ("高血压", 1.0), ("糖尿病", 1.0), ("感冒", 0.9), ("剂量", 0.9), ("副作用", 0.9), ("手术", 0.9), ("患者", 0.9), ("吃药", 0.9), ("发烧", 1.0), ("疫苗", 0.9), ("感染", 0.9), ("体检", 0.7), + # 急救/消化/心理/营养/儿科 + ("烫伤", 1.0), ("烧伤", 1.0), ("止血", 0.9), ("扭伤", 0.9), ("中暑", 1.0), + ("急救", 0.9), ("腹泻", 0.9), ("拉肚子", 0.9), ("便秘", 0.9), ("胃", 0.7), + ("失眠", 0.9), ("焦虑", 0.9), ("抑郁", 0.9), ("压力", 0.6), ("睡眠", 0.7), + ("减肥", 0.8), ("营养", 0.7), ("卡路里", 0.9), ("儿童", 0.8), ("婴儿", 0.9), + ("宝宝", 0.8), ("抗生素", 0.9), ("止咳", 0.9), ("medical", 1.0), ("patient", 0.9), ("symptom", 1.0), ("disease", 0.9), ("diagnosis", 1.0), ("treatment", 0.8), ("prescription", 1.0), ("dosage", 0.9), ("side effect", 0.9), ("hypertension", 1.0), ("diabetes", 1.0), ("surgery", 0.8), ("clinic", 0.7), ("vaccine", 0.9), - ("infection", 0.9), + ("infection", 0.9), ("first aid", 0.9), ("insomnia", 0.9), ], "general": [ ("总结", 0.4), ("翻译", 0.4), ("介绍", 0.4), ("解释", 0.3), @@ -74,6 +88,40 @@ DOMAIN_RULES: Dict[str, List[Tuple[str, float]]] = { ("write an essay", 0.4), ("邮件", 0.4), ("email", 0.3), ("推荐", 0.3), ("评价", 0.3), ], + "finance": [ + ("理财", 1.0), ("投资", 1.0), ("基金", 1.0), ("股票", 1.0), ("债券", 0.9), + ("存款", 0.9), ("储蓄", 0.8), ("利率", 0.8), ("利息", 0.8), ("贷款", 1.0), + ("房贷", 1.0), ("月供", 0.9), ("保险", 0.9), ("理赔", 0.9), ("保费", 0.8), + ("信用卡", 1.0), ("征信", 0.9), ("逾期", 0.9), ("分期", 0.8), ("记账", 0.7), + ("预算", 0.7), ("理财规划", 1.0), ("收益率", 0.9), ("定投", 0.9), + ("invest", 0.8), ("fund", 0.8), ("stock", 0.9), ("loan", 0.9), + ("mortgage", 0.9), ("insurance", 0.9), ("credit card", 0.9), + ("finance", 0.8), ("money", 0.6), ("lpr", 0.9), ("投资理财", 1.1), + ], + "life": [ + ("菜谱", 0.9), ("做饭", 0.8), ("烹饪", 0.9), ("美食", 0.8), ("做法", 0.7), + ("旅行", 0.9), ("旅游", 0.9), ("攻略", 0.8), ("机票", 0.8), ("酒店", 0.7), + ("签证", 0.9), ("景点", 0.8), ("自驾", 0.8), + ("装修", 0.9), ("收纳", 0.8), ("家居", 0.7), ("清洁", 0.7), ("打扫", 0.7), + ("宠物", 0.9), ("猫", 0.7), ("狗", 0.7), ("猫粮", 0.9), ("驱虫", 0.9), + ("健身", 0.9), ("锻炼", 0.8), ("跑步", 0.8), ("增肌", 0.9), ("减脂", 0.9), + ("瑜伽", 0.8), ("天气", 0.7), ("气温", 0.7), + ("recipe", 0.8), ("travel", 0.9), ("trip", 0.8), ("pet", 0.8), + ("workout", 0.9), ("gym", 0.8), ("weather", 0.7), ("cook", 0.8), + ], + "education": [ + ("学习方法", 1.0), ("怎么学", 0.7), ("高效学习", 1.0), ("记忆", 0.6), ("复习", 0.7), + ("预习", 0.7), ("笔记", 0.6), ("专注", 0.6), ("拖延", 0.7), ("学习效率", 0.9), + ("考试", 0.9), ("备考", 1.0), ("刷题", 0.9), ("模拟考", 0.9), ("中考", 0.9), + ("高考", 0.9), ("考研", 0.9), ("考前", 0.7), + ("英语", 0.8), ("单词", 0.7), ("口语", 0.8), ("听力", 0.7), ("雅思", 1.0), + ("托福", 1.0), ("四级", 0.9), ("六级", 0.9), ("背单词", 0.9), + ("选课", 0.9), ("课程", 0.6), ("专业选择", 0.9), ("报班", 0.8), ("网课", 0.7), + ("自学", 0.7), ("职业规划", 1.0), ("求职", 0.9), ("面试", 0.8), ("简历", 0.8), + ("实习", 0.7), ("跳槽", 0.8), ("转行", 0.9), + ("study", 0.8), ("exam", 0.9), ("language", 0.7), ("career", 0.8), + ("interview", 0.8), ("education", 0.7), ("learn", 0.6), + ], } _STOPWORDS = { @@ -92,11 +140,21 @@ class BaseClassifier: class RuleClassifier(BaseClassifier): - """基于关键词规则的分类器(零依赖)。""" + """基于关键词规则的分类器(零依赖)。 - def __init__(self, confidence_floor: float = 0.55): + domains 参数(可选):限定只对部分领域打分 —— 两级路由中, + 每个大领域的组内路由模型用 RuleClassifier(domains=组内领域), + 只认识本组领域,体积与匹配开销约为统一分类器的 1/4。 + """ + + def __init__(self, confidence_floor: float = 0.55, + domains: Optional[List[str]] = None): self.confidence_floor = confidence_floor - self.rules = DOMAIN_RULES + if domains is None: + self.rules = DOMAIN_RULES + else: + self.rules = {d: DOMAIN_RULES[d] for d in domains if d in DOMAIN_RULES} + self.domains = list(self.rules.keys()) def _score(self, query: str) -> Tuple[Dict[str, float], Dict[str, List[str]]]: q = query.lower() @@ -159,7 +217,7 @@ class HuggingFaceClassifier(BaseClassifier): 仅当安装 torch+transformers 且模型可加载时可用;否则抛错提示。 """ - def __init__(self, model_name: str, num_labels: int = 5, confidence_floor: float = 0.55): + def __init__(self, model_name: str, num_labels: int = 8, confidence_floor: float = 0.55): try: from transformers import AutoModelForSequenceClassification, AutoTokenizer except ImportError as e: @@ -170,7 +228,8 @@ class HuggingFaceClassifier(BaseClassifier): self.model = AutoModelForSequenceClassification.from_pretrained( model_name, num_labels=num_labels ) - self.labels = ["code", "math", "legal", "medical", "general"] + self.labels = ["code", "math", "legal", "medical", "general", + "finance", "life", "education"] self.confidence_floor = confidence_floor def classify(self, query: str) -> Classification: diff --git a/router_system/config.py b/router_system/config.py index f15b331..3d0cea8 100644 --- a/router_system/config.py +++ b/router_system/config.py @@ -1,100 +1,118 @@ -"""配置加载:优先 YAML(若安装了 pyyaml),否则回退 JSON。 - -设计原则:router_system 核心零依赖,因此 pyyaml 是"可选"的。 -默认 config/config.yaml 存在;若 pyyaml 不可用,可提供同名 .json。 -""" -from __future__ import annotations - -import json -import os -from pathlib import Path -from typing import Any, Dict, Optional - -DEFAULT_CONFIG_PATH = Path(__file__).resolve().parent.parent / "config" / "config.yaml" - -_DEFAULTS: Dict[str, Any] = { - "system": {"name": "multi-expert-router", "version": "0.1.0"}, - "router": { - "low_confidence_threshold": 0.60, # 分类置信度低于此值 -> 直接走大模型 - "judge_fallback_threshold": 0.70, # Judge 质量分低于此值 -> 升级大模型 - "default_temperature": 0.2, - }, - "classifier": {"type": "rule", "model": "Qwen/Qwen3-0.6B", "confidence_floor": 0.55}, - "domains": ["code", "math", "legal", "medical", "general"], - "experts": { - "code": {"type": "mock", "model": "Qwen/Qwen2.5-Coder-7B-Instruct"}, - "math": {"type": "mock", "model": "Qwen/Qwen3-4B-Instruct"}, - "legal": {"type": "mock", "model": "Qwen/Qwen3-4B-Instruct"}, - "medical": {"type": "mock", "model": "Qwen/Qwen3-4B-Instruct"}, - "general": {"type": "mock", "model": "Qwen/Qwen3-1.7B-Instruct"}, - }, - "fallback": { - "type": "mock", - "model": "deepseek-chat", - "base_url": "https://api.deepseek.com/v1", - "api_key_env": "DEEPSEEK_API_KEY", - }, - "judge": {"type": "rule", "model": "Qwen/Qwen3-1.7B-Instruct"}, - "cache": { - "enabled": True, - "semantic_enabled": True, - "similarity_threshold": 0.88, - "promote_frequency": 5, - }, -} - - -def load_defaults() -> Dict[str, Any]: - return _DEFAULTS - - -def _try_load_yaml(path: Path) -> Optional[Dict[str, Any]]: - try: - import yaml # type: ignore - except ImportError: - return None - try: - with open(path, "r", encoding="utf-8") as f: - data = yaml.safe_load(f) - return data if isinstance(data, dict) else None - except Exception: - return None - - -def _try_load_json(path: Path) -> Optional[Dict[str, Any]]: - json_path = path.with_suffix(".json") - if not json_path.exists(): - return None - try: - with open(json_path, "r", encoding="utf-8") as f: - data = json.load(f) - return data if isinstance(data, dict) else None - except Exception: - return None - - -def _merge_defaults(data: Dict[str, Any]) -> Dict[str, Any]: - """将用户配置与内置默认配置做一层合并(用户优先)。""" - merged = dict(_DEFAULTS) - for k, v in data.items(): - if isinstance(v, dict) and isinstance(merged.get(k), dict): - merged[k] = {**merged[k], **v} - else: - merged[k] = v - return merged - - -def load_config(path: Optional[Path | str] = None) -> Dict[str, Any]: - """加载配置,返回 dict。文件不存在或解析失败时返回内置默认配置。""" - cfg_path = Path(path) if path else DEFAULT_CONFIG_PATH - if cfg_path.exists(): - data = _try_load_yaml(cfg_path) or _try_load_json(cfg_path) - if data is not None: - return _merge_defaults(data) - return dict(_DEFAULTS) - - -def get_api_key(cfg: Dict[str, Any]) -> Optional[str]: - """从环境变量读取 API Key(用于 api 类型后端)。""" - env_name = cfg.get("api_key_env") or "API_KEY" - return os.environ.get(env_name) or None +"""配置加载:优先 YAML(若安装了 pyyaml),否则回退 JSON。 + +设计原则:router_system 核心零依赖,因此 pyyaml 是"可选"的。 +默认 config/config.yaml 存在;若 pyyaml 不可用,可提供同名 .json。 +""" +from __future__ import annotations + +import json +import os +from pathlib import Path +from typing import Any, Dict, Optional + +DEFAULT_CONFIG_PATH = Path(__file__).resolve().parent.parent / "config" / "config.yaml" + +_DEFAULTS: Dict[str, Any] = { + "system": {"name": "multi-expert-router", "version": "0.1.0"}, + "router": { + "low_confidence_threshold": 0.60, # 分类置信度低于此值 -> 直接走最后处理者 + "judge_fallback_threshold": 0.70, # Judge 质量分低于此值 -> 升级最后处理者 + "default_temperature": 0.2, + }, + "execution": { + "mode": "rule", # rule(L0 零参数)| hybrid + "planner": "rule", # rule | hf + "expert_backend": "rule", # rule(规则执行器)| hf | api + "model_level": "L0", # L0 | L1 | L2 + "max_plan_depth": 3, + }, + "classifier": {"type": "rule", "model": "Qwen/Qwen3-0.6B", "confidence_floor": 0.55}, + "domains": ["code", "math", "legal", "medical", "finance", "life", "education", "general"], + # 两级路由:大领域分组(用户接口指定 group → 组内路由模型 → 组内专业小模型) + # 组内路由模型只识别本组领域,体积约为统一路由模型的 1/4 + "domain_groups": { + "tech": ["code", "math"], + "professional": ["legal", "medical", "finance"], + "lifestyle": ["life", "education"], + "general": ["general"], + }, + "experts": { + "code": {"type": "mock", "model": "Qwen/Qwen2.5-Coder-7B-Instruct"}, + "math": {"type": "mock", "model": "Qwen/Qwen3-4B-Instruct"}, + "legal": {"type": "mock", "model": "Qwen/Qwen3-4B-Instruct"}, + "medical": {"type": "mock", "model": "Qwen/Qwen3-4B-Instruct"}, + "finance": {"type": "mock", "model": "Qwen/Qwen3-4B-Instruct"}, + "life": {"type": "mock", "model": "Qwen/Qwen3-1.7B-Instruct"}, + "education": {"type": "mock", "model": "Qwen/Qwen3-1.7B-Instruct"}, + "general": {"type": "mock", "model": "Qwen/Qwen3-1.7B-Instruct"}, + }, + "fallback": { + "type": "mock", + "model": "deepseek-v4-flash", + "base_url": "https://api.deepseek.com", + "api_key_env": "DEEPSEEK_API_KEY", + }, + "judge": {"type": "rule", "model": "Qwen/Qwen3-1.7B-Instruct"}, + "cache": { + "enabled": True, + "semantic_enabled": True, + "similarity_threshold": 0.88, + "promote_frequency": 5, + }, +} + + +def load_defaults() -> Dict[str, Any]: + return _DEFAULTS + + +def _try_load_yaml(path: Path) -> Optional[Dict[str, Any]]: + try: + import yaml # type: ignore + except ImportError: + return None + try: + with open(path, "r", encoding="utf-8") as f: + data = yaml.safe_load(f) + return data if isinstance(data, dict) else None + except Exception: + return None + + +def _try_load_json(path: Path) -> Optional[Dict[str, Any]]: + json_path = path.with_suffix(".json") + if not json_path.exists(): + return None + try: + with open(json_path, "r", encoding="utf-8") as f: + data = json.load(f) + return data if isinstance(data, dict) else None + except Exception: + return None + + +def _merge_defaults(data: Dict[str, Any]) -> Dict[str, Any]: + """将用户配置与内置默认配置做一层合并(用户优先)。""" + merged = dict(_DEFAULTS) + for k, v in data.items(): + if isinstance(v, dict) and isinstance(merged.get(k), dict): + merged[k] = {**merged[k], **v} + else: + merged[k] = v + return merged + + +def load_config(path: Optional[Path | str] = None) -> Dict[str, Any]: + """加载配置,返回 dict。文件不存在或解析失败时返回内置默认配置。""" + cfg_path = Path(path) if path else DEFAULT_CONFIG_PATH + if cfg_path.exists(): + data = _try_load_yaml(cfg_path) or _try_load_json(cfg_path) + if data is not None: + return _merge_defaults(data) + return dict(_DEFAULTS) + + +def get_api_key(cfg: Dict[str, Any]) -> Optional[str]: + """从环境变量读取 API Key(用于 api 类型后端)。""" + env_name = cfg.get("api_key_env") or "API_KEY" + return os.environ.get(env_name) or None diff --git a/router_system/fallback.py b/router_system/fallback.py index f02106c..0c54115 100644 --- a/router_system/fallback.py +++ b/router_system/fallback.py @@ -1,99 +1,185 @@ -"""大模型回退层:Mock 与 OpenAI 兼容 API 两种后端。""" -from __future__ import annotations - -import asyncio -from typing import Dict, Optional - -from .models import ExpertResponse - - -class FallbackProvider: - name: str = "fallback" - - async def generate(self, query: str) -> ExpertResponse: - raise NotImplementedError - - -class MockFallback(FallbackProvider): - """确定性 mock 大模型:标识为 fallback,便于测试升级路径。""" - - def __init__(self, model: str = "mock-large"): - self.model = model - self.name = f"fallback-{model}" - - async def generate(self, query: str) -> ExpertResponse: - await asyncio.sleep(0.002) - body = ( - f"(大模型回退)「{query}」\n\n" - "这是一条来自大模型回退路径的完整回答。\n" - "要点:\n" - "1. 对复杂/跨域任务给出综合推理\n" - "2. 补充领域专家未覆盖的上下文\n" - "3. 给出可执行的后续建议\n" - ) - return ExpertResponse( - text=body, - model_used=self.model, - latency_ms=2.0, - tokens=120, - cost_est=2.0 * 120 / 1_000_000, - ) - - -class APIFallback(FallbackProvider): - """OpenAI 兼容大模型 API(如 DeepSeek / OpenAI / 本地 vLLM)。""" - - def __init__(self, model: str, base_url: str, api_key: str): - self.model = model - self.base_url = base_url.rstrip("/") - self.api_key = api_key - self.name = f"fallback-{model}" - self._client = None - - def _get_client(self): - if self._client is None: - import httpx - self._client = httpx.AsyncClient(timeout=90.0) - return self._client - - async def generate(self, query: str) -> ExpertResponse: - client = self._get_client() - resp = await client.post( - f"{self.base_url}/chat/completions", - headers={"Authorization": f"Bearer {self.api_key}"}, - json={ - "model": self.model, - "messages": [{"role": "user", "content": query}], - "temperature": 0.3, - "max_tokens": 2048, - }, - ) - resp.raise_for_status() - data = resp.json() - body = data["choices"][0]["message"]["content"] - usage = data.get("usage", {}) - tokens = usage.get("completion_tokens", int(len(body) / 2.2)) - return ExpertResponse( - text=body, - model_used=self.model, - latency_ms=0.0, - tokens=tokens, - cost_est=2.0 * tokens / 1_000_000, - ) - - -def build_fallback(cfg: Dict) -> FallbackProvider: - """cfg 为 fallback 段配置。""" - ftype = cfg.get("type", "mock") - model = cfg.get("model", "deepseek-chat") - if ftype == "mock": - return MockFallback(model=model) - if ftype == "api": - import os - api_key = cfg.get("api_key") or os.environ.get(cfg.get("api_key_env", "API_KEY")) - if not api_key: - raise RuntimeError( - f"APIFallback 缺少 API Key:请设置环境变量 {cfg.get('api_key_env')} 或配置 api_key" - ) - return APIFallback(model, cfg.get("base_url", "https://api.deepseek.com/v1"), api_key) - raise ValueError(f"未知 fallback 类型: {ftype}(支持 mock | api)") +"""大模型回退层(最后处理者):Mock / 降级模板 / 本地模型 / OpenAI 兼容 API。 + +- NoneFallback :降级模板(零参数,明确告知超出知识库范围)—— L0 最小可用 +- MockFallback :确定性 mock 大模型(零参数,测试升级路径用) +- LocalFallback :本地小模型(≤8B,Q4 量化,OpenAI 兼容端点如 Ollama/vLLM)—— 按需加载 +- APIFallback :远程 OpenAI 兼容 API(可选,默认关闭) +""" +from __future__ import annotations + +import asyncio +from typing import Dict, Optional + +from .models import ExpertResponse + + +class FallbackProvider: + name: str = "fallback" + + async def generate(self, query: str) -> ExpertResponse: + raise NotImplementedError + + +class NoneFallback(FallbackProvider): + """降级模板:零参数兜底,明确告知查询超出知识库范围。""" + + def __init__(self, model: str = "none"): + self.model = model + self.name = "fallback-none" + + async def generate(self, query: str) -> ExpertResponse: + body = ( + f"(降级响应)「{query}」\n\n" + "当前查询超出本地知识库可处理范围(低置信度或质量校验未通过)。\n" + "可选处理:\n" + "1. 换个更明确的问法重试\n" + "2. 启用 L2 本地小模型或配置最后处理者(fallback.type: local)\n" + ) + return ExpertResponse( + text=body, + model_used=self.model, + latency_ms=0.0, + tokens=80, + cost_est=0.0, + ) + + +class MockFallback(FallbackProvider): + """确定性 mock 大模型:标识为 fallback,便于测试升级路径。""" + + def __init__(self, model: str = "mock-large"): + self.model = model + self.name = f"fallback-{model}" + + async def generate(self, query: str) -> ExpertResponse: + await asyncio.sleep(0.002) + body = ( + f"(大模型回退)「{query}」\n\n" + "这是一条来自大模型回退路径的完整回答。\n" + "要点:\n" + "1. 对复杂/跨域任务给出综合推理\n" + "2. 补充领域专家未覆盖的上下文\n" + "3. 给出可执行的后续建议\n" + ) + return ExpertResponse( + text=body, + model_used=self.model, + latency_ms=2.0, + tokens=120, + cost_est=2.0 * 120 / 1_000_000, + ) + + +class LocalFallback(FallbackProvider): + """本地小模型最后处理者(≤8B,如 DeepSeek-R1-Distill-Qwen-7B Q4)。 + + 通过 OpenAI 兼容端点调用(Ollama 默认 11434/v1,vLLM 默认 8001/v1), + 模型按需加载、用完即卸载(由本地推理服务管理),不常驻显存。 + """ + + def __init__(self, model: str, base_url: str = "http://127.0.0.1:11434/v1", + api_key: str = ""): + self.model = model + self.base_url = base_url.rstrip("/") + self.api_key = api_key + self.name = f"fallback-local-{model}" + self._client = None + + def _get_client(self): + if self._client is None: + import httpx + self._client = httpx.AsyncClient(timeout=120.0) + return self._client + + async def generate(self, query: str) -> ExpertResponse: + client = self._get_client() + headers = {"Authorization": f"Bearer {self.api_key}"} if self.api_key else {} + resp = await client.post( + f"{self.base_url}/chat/completions", + headers=headers, + json={ + "model": self.model, + "messages": [{"role": "user", "content": query}], + "temperature": 0.3, + "max_tokens": 2048, + }, + ) + resp.raise_for_status() + data = resp.json() + body = data["choices"][0]["message"]["content"] + usage = data.get("usage", {}) + tokens = usage.get("completion_tokens", int(len(body) / 2.2)) + return ExpertResponse( + text=body, + model_used=self.model, + latency_ms=0.0, + tokens=tokens, + cost_est=0.0, # 本地推理成本按电费计,模型层成本记为 0(相对 API) + ) + + +class APIFallback(FallbackProvider): + """OpenAI 兼容大模型 API(如 DeepSeek / OpenAI / 本地 vLLM)。""" + + def __init__(self, model: str, base_url: str, api_key: str): + self.model = model + self.base_url = base_url.rstrip("/") + self.api_key = api_key + self.name = f"fallback-{model}" + self._client = None + + def _get_client(self): + if self._client is None: + import httpx + self._client = httpx.AsyncClient(timeout=90.0) + return self._client + + async def generate(self, query: str) -> ExpertResponse: + client = self._get_client() + resp = await client.post( + f"{self.base_url}/chat/completions", + headers={"Authorization": f"Bearer {self.api_key}"}, + json={ + "model": self.model, + "messages": [{"role": "user", "content": query}], + "temperature": 0.3, + "max_tokens": 2048, + }, + ) + resp.raise_for_status() + data = resp.json() + body = data["choices"][0]["message"]["content"] + usage = data.get("usage", {}) + tokens = usage.get("completion_tokens", int(len(body) / 2.2)) + return ExpertResponse( + text=body, + model_used=self.model, + latency_ms=0.0, + tokens=tokens, + cost_est=2.0 * tokens / 1_000_000, + ) + + +def build_fallback(cfg: Dict) -> FallbackProvider: + """cfg 为 fallback 段配置。""" + ftype = cfg.get("type", "mock") + model = cfg.get("model", "deepseek-v4-flash") + if ftype == "none": + return NoneFallback(model=model) + if ftype == "mock": + return MockFallback(model=model) + if ftype == "local": + return LocalFallback( + model, + cfg.get("base_url", "http://127.0.0.1:11434/v1"), + cfg.get("api_key", ""), + ) + if ftype == "api": + import os + api_key = cfg.get("api_key") or os.environ.get(cfg.get("api_key_env", "API_KEY")) + if not api_key: + raise RuntimeError( + f"APIFallback 缺少 API Key:请设置环境变量 {cfg.get('api_key_env')} 或配置 api_key" + ) + return APIFallback(model, cfg.get("base_url", "https://api.deepseek.com"), api_key) + raise ValueError(f"未知 fallback 类型: {ftype}(支持 none | mock | local | api)") diff --git a/router_system/judge.py b/router_system/judge.py index a0dc2c5..0c52c8d 100644 --- a/router_system/judge.py +++ b/router_system/judge.py @@ -1,174 +1,193 @@ -"""质量控制器(Judge):评估专家输出,决定是否升级大模型。 - -- RuleJudge:零依赖启发式(内容覆盖度 / 长度充分性 / 领域格式 / 安全提示), - 稳定可测,适合 MVP 与离线演示。 -- LLMJudge:可选,基于 transformers 小模型或 API 的 LLM-as-Judge。 - -设计对齐实现方案:overall_score < judge_fallback_threshold -> 升级大模型。 -""" -from __future__ import annotations - -from dataclasses import dataclass, field -from typing import Dict, List - -from .experts import extract_content_terms - -# 各领域期望的响应长度范围(字符数) -_EXPECTED_LEN = { - "code": (60, 2000), - "math": (60, 2000), - "legal": (80, 3000), - "medical": (80, 3000), - "general": (40, 2000), -} - -# 领域格式检查:响应应包含的标记 -_DOMAIN_FORMAT_HINTS = { - "code": ["```", "def ", "function", "class "], - "math": ["步骤", "推导", "=", "解", "step"], - "legal": ["⚠", "法律", "意见", "合规", "contract", "law"], - "medical": ["⚠", "就医", "医生", "症状", "诊断", "symptom"], - "general": [], -} - - -@dataclass -class QualityEvaluation: - overall_score: float - scores: Dict[str, float] = field(default_factory=dict) - needs_fallback: bool = False - reasons: List[str] = field(default_factory=list) - - def to_dict(self) -> Dict: - return { - "overall_score": round(self.overall_score, 4), - "scores": {k: round(v, 4) for k, v in self.scores.items()}, - "needs_fallback": self.needs_fallback, - "reasons": self.reasons, - } - - -class BaseJudge: - async def evaluate(self, query: str, response: str, domain: str) -> QualityEvaluation: - raise NotImplementedError - - -class RuleJudge(BaseJudge): - """启发式质量评估(零依赖)。""" - - def __init__(self, fallback_threshold: float = 0.70): - self.fallback_threshold = fallback_threshold - - async def evaluate(self, query: str, response: str, domain: str) -> QualityEvaluation: - scores: Dict[str, float] = {} - reasons: List[str] = [] - - # 1) 内容覆盖度:查询中的内容词有多少出现在响应里 - terms = extract_content_terms(query) - if terms: - hit = sum(1 for t in terms if t in response.lower()) - coverage = hit / len(terms) - scores["coverage"] = coverage - if coverage < 0.4: - reasons.append(f"内容覆盖度低 ({coverage:.0%})") - else: - scores["coverage"] = 1.0 - - # 2) 长度充分性 - lo, hi = _EXPECTED_LEN.get(domain, (40, 2000)) - n = len(response) - if n < lo: - scores["length"] = max(0.0, n / lo) - reasons.append(f"响应过短 ({n} 字符)") - elif n > hi: - scores["length"] = 0.8 - reasons.append(f"响应过长 ({n} 字符)") - else: - scores["length"] = 1.0 - - # 3) 领域格式检查 - hints = _DOMAIN_FORMAT_HINTS.get(domain, []) - if hints: - hit_hints = sum(1 for h in hints if h in response) - scores["format"] = min(1.0, 0.4 + 0.2 * hit_hints) - if hit_hints == 0: - reasons.append("缺少领域格式特征") - else: - scores["format"] = 1.0 - - # 4) 安全/免责提示(法律、医疗领域应有警示语) - if domain in ("legal", "medical") and ("⚠" not in response and "提示" not in response): - scores["safety"] = 0.6 - reasons.append("缺少免责提示") - else: - scores["safety"] = 1.0 - - weights = {"coverage": 0.4, "length": 0.2, "format": 0.2, "safety": 0.2} - overall = sum(scores.get(k, 0.0) * w for k, w in weights.items()) - needs = overall < self.fallback_threshold - if needs: - reasons.append("质量分低于阈值,建议升级大模型") - return QualityEvaluation( - overall_score=round(overall, 4), - scores=scores, - needs_fallback=needs, - reasons=reasons, - ) - - -class LLMJudge(BaseJudge): - """可选:LLM-as-Judge(API 后端)。""" - - def __init__(self, model: str, base_url: str, api_key: str, fallback_threshold: float = 0.70): - self.model = model - self.base_url = base_url.rstrip("/") - self.api_key = api_key - self.fallback_threshold = fallback_threshold - self._client = None - - def _get_client(self): - if self._client is None: - import httpx - self._client = httpx.AsyncClient(timeout=60.0) - return self._client - - async def evaluate(self, query: str, response: str, domain: str) -> QualityEvaluation: - client = self._get_client() - prompt = ( - f"你是质量评审员。评估以下回答对查询的满足程度,输出 0-1 分(相关性/正确性/完整性)。\n" - f"查询: {query}\n领域: {domain}\n回答: {response[:2000]}\n" - f"只输出一个 0 到 1 之间的数字。" - ) - resp = await client.post( - f"{self.base_url}/chat/completions", - headers={"Authorization": f"Bearer {self.api_key}"}, - json={"model": self.model, "messages": [{"role": "user", "content": prompt}]}, - ) - resp.raise_for_status() - try: - score = float(resp.json()["choices"][0]["message"]["content"].strip()) - score = max(0.0, min(1.0, score)) - except Exception: - score = 0.5 - return QualityEvaluation( - overall_score=score, - scores={"llm_judge": score}, - needs_fallback=score < self.fallback_threshold, - ) - - -def build_judge(cfg: Dict, fallback_threshold: float = 0.70) -> BaseJudge: - """cfg 为 judge 段配置。""" - jtype = cfg.get("type", "rule") - if jtype == "rule": - return RuleJudge(fallback_threshold=fallback_threshold) - if jtype == "llm": - import os - api_key = cfg.get("api_key") or os.environ.get(cfg.get("api_key_env", "API_KEY")) - return LLMJudge( - cfg.get("model", "deepseek-chat"), - cfg.get("base_url", "https://api.deepseek.com/v1"), - api_key or "", - fallback_threshold=fallback_threshold, - ) - raise ValueError(f"未知 judge 类型: {jtype}(支持 rule | llm)") +"""质量控制器(Judge):评估专家输出,决定是否升级大模型。 + +- RuleJudge:零依赖启发式(内容覆盖度 / 长度充分性 / 领域格式 / 安全提示), + 稳定可测,适合 MVP 与离线演示。 +- LLMJudge:可选,基于 transformers 小模型或 API 的 LLM-as-Judge。 + +设计对齐实现方案:overall_score < judge_fallback_threshold -> 升级大模型。 +""" +from __future__ import annotations + +from dataclasses import dataclass, field +from typing import Dict, List + +from .experts import extract_content_terms + +# 各领域期望的响应长度范围(字符数) +_EXPECTED_LEN = { + "code": (60, 2000), + "math": (60, 2000), + "legal": (80, 3000), + "medical": (80, 3000), + "general": (40, 2000), +} + +# 领域格式检查:响应应包含的标记 +_DOMAIN_FORMAT_HINTS = { + "code": ["```", "def ", "function", "class "], + "math": ["步骤", "推导", "=", "解", "step"], + "legal": ["⚠", "法律", "意见", "合规", "contract", "law"], + "medical": ["⚠", "就医", "医生", "症状", "诊断", "symptom"], + "general": [], +} + + +@dataclass +class QualityEvaluation: + overall_score: float + scores: Dict[str, float] = field(default_factory=dict) + needs_fallback: bool = False + reasons: List[str] = field(default_factory=list) + + def to_dict(self) -> Dict: + return { + "overall_score": round(self.overall_score, 4), + "scores": {k: round(v, 4) for k, v in self.scores.items()}, + "needs_fallback": self.needs_fallback, + "reasons": self.reasons, + } + + +class BaseJudge: + async def evaluate(self, query: str, response: str, domain: str) -> QualityEvaluation: + raise NotImplementedError + + +class RuleJudge(BaseJudge): + """启发式质量评估(零依赖)。""" + + def __init__(self, fallback_threshold: float = 0.70, kb=None): + self.fallback_threshold = fallback_threshold + self.kb = kb # 可选知识库:facts 维度校验用 + + async def evaluate(self, query: str, response: str, domain: str) -> QualityEvaluation: + scores: Dict[str, float] = {} + reasons: List[str] = [] + + # 1) 内容覆盖度:查询中的内容词有多少出现在响应里 + terms = extract_content_terms(query) + if terms: + hit = sum(1 for t in terms if t in response.lower()) + coverage = hit / len(terms) + scores["coverage"] = coverage + if coverage < 0.4: + reasons.append(f"内容覆盖度低 ({coverage:.0%})") + else: + scores["coverage"] = 1.0 + + # 2) 长度充分性 + lo, hi = _EXPECTED_LEN.get(domain, (40, 2000)) + n = len(response) + if n < lo: + scores["length"] = max(0.0, n / lo) + reasons.append(f"响应过短 ({n} 字符)") + elif n > hi: + scores["length"] = 0.8 + reasons.append(f"响应过长 ({n} 字符)") + else: + scores["length"] = 1.0 + + # 3) 领域格式检查 + hints = _DOMAIN_FORMAT_HINTS.get(domain, []) + if hints: + hit_hints = sum(1 for h in hints if h in response) + scores["format"] = min(1.0, 0.4 + 0.2 * hit_hints) + if hit_hints == 0: + reasons.append("缺少领域格式特征") + else: + scores["format"] = 1.0 + + # 4) 安全/免责提示(法律、医疗领域应有警示语) + if domain in ("legal", "medical") and ("⚠" not in response and "提示" not in response): + scores["safety"] = 0.6 + reasons.append("缺少免责提示") + else: + scores["safety"] = 1.0 + + # 5) 领域知识引用(facts 维度):法律/医疗响应应覆盖查询命中的知识条目 + if domain in ("legal", "medical") and self.kb is not None: + facts = self.kb.facts(domain) + hit_facts = [f for f in facts if any(k in query for k in f.get("keywords", []))] + if hit_facts: + ok = sum( + 1 for f in hit_facts + if any(k in response for k in f.get("keywords", [])) + ) + scores["facts"] = ok / len(hit_facts) + if scores["facts"] < 0.6: + reasons.append(f"领域知识引用不足 ({scores['facts']:.0%})") + else: + scores["facts"] = 1.0 + else: + scores["facts"] = 1.0 + + weights = {"coverage": 0.35, "length": 0.15, "format": 0.15, + "safety": 0.15, "facts": 0.2} + overall = sum(scores.get(k, 0.0) * w for k, w in weights.items()) + needs = overall < self.fallback_threshold + if needs: + reasons.append("质量分低于阈值,建议升级大模型") + return QualityEvaluation( + overall_score=round(overall, 4), + scores=scores, + needs_fallback=needs, + reasons=reasons, + ) + + +class LLMJudge(BaseJudge): + """可选:LLM-as-Judge(API 后端)。""" + + def __init__(self, model: str, base_url: str, api_key: str, fallback_threshold: float = 0.70): + self.model = model + self.base_url = base_url.rstrip("/") + self.api_key = api_key + self.fallback_threshold = fallback_threshold + self._client = None + + def _get_client(self): + if self._client is None: + import httpx + self._client = httpx.AsyncClient(timeout=60.0) + return self._client + + async def evaluate(self, query: str, response: str, domain: str) -> QualityEvaluation: + client = self._get_client() + prompt = ( + f"你是质量评审员。评估以下回答对查询的满足程度,输出 0-1 分(相关性/正确性/完整性)。\n" + f"查询: {query}\n领域: {domain}\n回答: {response[:2000]}\n" + f"只输出一个 0 到 1 之间的数字。" + ) + resp = await client.post( + f"{self.base_url}/chat/completions", + headers={"Authorization": f"Bearer {self.api_key}"}, + json={"model": self.model, "messages": [{"role": "user", "content": prompt}]}, + ) + resp.raise_for_status() + try: + score = float(resp.json()["choices"][0]["message"]["content"].strip()) + score = max(0.0, min(1.0, score)) + except Exception: + score = 0.5 + return QualityEvaluation( + overall_score=score, + scores={"llm_judge": score}, + needs_fallback=score < self.fallback_threshold, + ) + + +def build_judge(cfg: Dict, fallback_threshold: float = 0.70, kb=None) -> BaseJudge: + """cfg 为 judge 段配置。""" + jtype = cfg.get("type", "rule") + if jtype == "rule": + return RuleJudge(fallback_threshold=fallback_threshold, kb=kb) + if jtype == "llm": + import os + api_key = cfg.get("api_key") or os.environ.get(cfg.get("api_key_env", "API_KEY")) + return LLMJudge( + cfg.get("model", "deepseek-v4-flash"), + cfg.get("base_url", "https://api.deepseek.com"), + api_key or "", + fallback_threshold=fallback_threshold, + ) + raise ValueError(f"未知 judge 类型: {jtype}(支持 rule | llm)") diff --git a/router_system/models.py b/router_system/models.py index e925f5e..c84fe26 100644 --- a/router_system/models.py +++ b/router_system/models.py @@ -1,68 +1,76 @@ -"""核心数据模型(纯标准库,无外部依赖)""" -from __future__ import annotations - -import time -from dataclasses import dataclass, field -from typing import Any, Dict, List, Optional - - -@dataclass -class Classification: - """分类器输出:领域 + 置信度 + 难度""" - domain: str - confidence: float - difficulty: str # easy | medium | hard - difficulty_score: float = 0.5 - raw_scores: Dict[str, float] = field(default_factory=dict) - matched_rules: List[str] = field(default_factory=list) - - -@dataclass -class ExpertResponse: - """专家模型输出""" - text: str - model_used: str - latency_ms: float = 0.0 - tokens: int = 0 - cost_est: float = 0.0 # 相对成本估计(美元,近似) - - -@dataclass -class RouterResult: - """一次路由的完整结果""" - query: str - response: str - domain: str - difficulty: str - confidence: float - upgraded: bool # 是否升级到大模型 - quality_score: float - model_used: str - route: List[str] = field(default_factory=list) # 路由决策轨迹 - latency_ms: float = 0.0 - cache_hit: bool = False - cache_level: Optional[str] = None # exact | semantic - cost_est: float = 0.0 - error: Optional[str] = None - - def to_dict(self) -> Dict[str, Any]: - return { - "query": self.query, - "response": self.response, - "domain": self.domain, - "difficulty": self.difficulty, - "confidence": round(self.confidence, 4), - "upgraded": self.upgraded, - "quality_score": round(self.quality_score, 4), - "model_used": self.model_used, - "route": self.route, - "latency_ms": round(self.latency_ms, 2), - "cache_hit": self.cache_hit, - "cache_level": self.cache_level, - "cost_est": round(self.cost_est, 6), - "error": self.error, - } - - -def now_ms() -> float: - return time.perf_counter() * 1000.0 +"""核心数据模型(纯标准库,无外部依赖)""" +from __future__ import annotations + +import time +from dataclasses import dataclass, field +from typing import Any, Dict, List, Optional + + +@dataclass +class Classification: + """分类器输出:领域 + 置信度 + 难度""" + domain: str + confidence: float + difficulty: str # easy | medium | hard + difficulty_score: float = 0.5 + raw_scores: Dict[str, float] = field(default_factory=dict) + matched_rules: List[str] = field(default_factory=list) + + +@dataclass +class ExpertResponse: + """专家模型输出""" + text: str + model_used: str + latency_ms: float = 0.0 + tokens: int = 0 + cost_est: float = 0.0 # 相对成本估计(美元,近似) + + +@dataclass +class RouterResult: + """一次路由的完整结果""" + query: str + response: str + domain: str + difficulty: str + confidence: float + upgraded: bool # 是否升级到大模型 + quality_score: float + model_used: str + route: List[str] = field(default_factory=list) # 路由决策轨迹 + latency_ms: float = 0.0 + cache_hit: bool = False + cache_level: Optional[str] = None # exact | semantic + cost_est: float = 0.0 + error: Optional[str] = None + subdomain: Optional[str] = None # 二级子领域(如 investing/labor) + subdomain2: Optional[str] = None # 三级子领域(如 fund/overtime) + domain_group: Optional[str] = None # 大领域组(两级路由第一级:tech/professional/...) + request_id: Optional[str] = None # 请求 ID(配合 /traces/{id} 查询完整推理链) + + def to_dict(self) -> Dict[str, Any]: + return { + "query": self.query, + "response": self.response, + "domain": self.domain, + "difficulty": self.difficulty, + "confidence": round(self.confidence, 4), + "upgraded": self.upgraded, + "quality_score": round(self.quality_score, 4), + "model_used": self.model_used, + "route": self.route, + "latency_ms": round(self.latency_ms, 2), + "cache_hit": self.cache_hit, + "cache_level": self.cache_level, + "cost_est": round(self.cost_est, 6), + "error": self.error, + "subdomain": self.subdomain, + "subdomain2": self.subdomain2, + "domain_group": self.domain_group, + "request_id": self.request_id, + } + + +def now_ms() -> float: + return time.perf_counter() * 1000.0 diff --git a/router_system/pipeline.py b/router_system/pipeline.py index e4d030a..742d009 100644 --- a/router_system/pipeline.py +++ b/router_system/pipeline.py @@ -68,8 +68,9 @@ class CollaborativePipeline: # --------------------------------------------------------------- # 入口 # --------------------------------------------------------------- - async def run(self, query: str) -> PipelineResult: - request_id = uuid.uuid4().hex[:12] + async def run(self, query: str, request_id: Optional[str] = None) -> PipelineResult: + if request_id is None: + request_id = uuid.uuid4().hex[:12] ws = Workspace.new(request_id, query, self.api_token_cap, self.rounds_cap) route: List[str] = ["v2"] t0 = time.perf_counter() * 1000.0 @@ -100,11 +101,15 @@ class CollaborativePipeline: route.append("loop") plan = brief.get("plan") or [] pending = [p.get("id") for p in plan] + plan_ids = {p.get("id") for p in plan} # 用于 _deps_done 过滤 while pending and not ws.exhausted(): progressed = False for sid in list(pending): step = next((p for p in plan if p.get("id") == sid), {}) - if not self._deps_done(ws, step.get("deps") or []): + deps = step.get("deps") or [] + # 只检查在 plan 中的依赖;不在 plan 的 ID 视为"不存在"→自动满足 + relevant = [d for d in deps if d in plan_ids] + if not self._deps_done(ws, relevant): continue existing = self._read_artifact(request_id, self._artifact_name(sid, ws)) outcome = await self.worker.run_step(ws, sid, existing_artifact=existing, @@ -115,6 +120,8 @@ class CollaborativePipeline: ws.rollup() route.append(f"step:{sid}:done") progressed = True + if not pending: # 所有步骤完成,退出循环 + break else: # issue -> Architect 裁决 route.append(f"step:{sid}:issue") try: @@ -227,7 +234,17 @@ class CollaborativePipeline: return artifact_name_for(sid, domain) def _deps_done(self, ws: Workspace, deps: List[str]) -> bool: + # progress 中 done 的条目;done 条目 rollup 后移到 archive(字符串格式如 "s1: ...") done = {p["step"] for p in ws.get("progress", []) if p.get("status") == "done"} + for entry in ws.get("archive", []): + if isinstance(entry, dict): + sid = entry.get("step", "") + elif isinstance(entry, str): + sid = entry.split(":")[0].strip() if ":" in entry else "" + else: + sid = "" + if sid in deps: + done.add(sid) return all(d in done for d in deps) def _last_decision_for(self, ws: Workspace, sid: str) -> str: diff --git a/router_system/router.py b/router_system/router.py index b061cd4..bf5cab4 100644 --- a/router_system/router.py +++ b/router_system/router.py @@ -1,217 +1,415 @@ -"""主路由器:协调 缓存 -> 分类 -> 专家 -> Judge -> 大模型回退 的完整链路。 - -流程(对齐实现方案): -1. 检查缓存(L1 精确 / L2 语义) -2. 低置信度查询直接走大模型(should_fallback) -3. 分类器输出领域 + 难度 -4. 选择专家模型生成 -5. Judge 评估质量 -6. 质量不达标 -> 升级大模型 -7. 记录指标、写缓存、返回结果 -""" -from __future__ import annotations - -from typing import Any, Dict, Optional - -from .cache import RouterCache -from .classifier import BaseClassifier, build_classifier -from .config import load_config -from .experts import Expert, build_expert_pool -from .fallback import FallbackProvider, build_fallback -from .judge import BaseJudge, build_judge -from .models import Classification, ExpertResponse, RouterResult, now_ms -from .stats import Stats - - -class Router: - def __init__( - self, - classifier: BaseClassifier, - experts: Dict[str, Expert], - judge: BaseJudge, - fallback: FallbackProvider, - cache: Optional[RouterCache] = None, - stats: Optional[Stats] = None, - config: Optional[Dict[str, Any]] = None, - ): - self.classifier = classifier - self.experts = experts - self.judge = judge - self.fallback = fallback - self.cache = cache or RouterCache() - self.stats = stats or Stats() - cfg = config or {} - rcfg = cfg.get("router", {}) - self.low_confidence_threshold = rcfg.get("low_confidence_threshold", 0.60) - self.judge_fallback_threshold = rcfg.get("judge_fallback_threshold", 0.70) - self.cache_enabled = cfg.get("cache", {}).get("enabled", True) - - # --------------------------------------------------------------- - async def route(self, query: str) -> RouterResult: - start = now_ms() - route: list = [] - - # ---- Step 1: 缓存 ---- - if self.cache_enabled: - hit = self.cache.get(query) - if hit is not None: - level, cached = hit - latency = now_ms() - start - result = RouterResult( - query=query, - response=cached.get("response", ""), - domain=cached.get("domain", "general"), - difficulty=cached.get("difficulty", "medium"), - confidence=cached.get("confidence", 0.0), - upgraded=False, - quality_score=cached.get("quality_score", 0.0), - model_used=cached.get("model_used", ""), - route=["cache:" + level], - latency_ms=latency, - cache_hit=True, - cache_level=level, - cost_est=0.0, - ) - self.stats.record(latency, result.domain, result.difficulty, False, True, level, 0.0, result.model_used) - return result - route.append("cache:miss") - - # ---- Step 2: 分类 ---- - classification = self.classifier.classify(query) - route.append(f"classify:{classification.domain}@{classification.confidence:.2f}/{classification.difficulty}") - - # 低置信度 -> 直接走大模型 - if self.classifier.should_fallback(classification, self.low_confidence_threshold): - route.append("direct_fallback") - fb = await self._call_fallback(query) - latency = now_ms() - start - result = self._finalize(query, classification, fb, quality_score=0.0, - upgraded=True, route=route, latency_ms=latency, - model_used=fb.model_used, cost_est=fb.cost_est) - self._record(result, latency) - return result - - # ---- Step 3: 选择专家 ---- - domain = classification.domain - expert = self.experts.get(domain) - if expert is None: - expert = self.experts.get("general") - route.append("expert:fallback-to-general") - else: - route.append(f"expert:{expert.name}") - - # ---- Step 4: 生成 ---- - try: - expert_resp = await expert.generate(query, classification.difficulty) - except Exception as e: - self.stats.record_error() - route.append(f"expert_error:{type(e).__name__}") - fb = await self._call_fallback(query) - latency = now_ms() - start - result = self._finalize(query, classification, fb, quality_score=0.0, - upgraded=True, route=route, latency_ms=latency, - model_used=fb.model_used, cost_est=fb.cost_est, - error=str(e)) - self._record(result, latency) - return result - - # ---- Step 5: Judge 评估 ---- - try: - evaluation = await self.judge.evaluate(query, expert_resp.text, domain) - except Exception: - evaluation = None - route.append("judge_error") - - quality_score = evaluation.overall_score if evaluation else 0.0 - route.append(f"judge:{quality_score:.2f}") - - upgraded = False - final_resp = expert_resp - if evaluation is not None and evaluation.needs_fallback: - route.append("upgrade") - final_resp = await self._call_fallback(query) - upgraded = True - - latency = now_ms() - start - result = self._finalize(query, classification, final_resp, quality_score=quality_score, - upgraded=upgraded, route=route, latency_ms=latency, - model_used=final_resp.model_used, cost_est=final_resp.cost_est) - self._record(result, latency) - - # 未升级的结果写缓存 - if self.cache_enabled and not upgraded and result.response: - self.cache.put(query, result.to_dict()) - - return result - - # --------------------------------------------------------------- - async def _call_fallback(self, query: str) -> ExpertResponse: - try: - return await self.fallback.generate(query) - except Exception as e: - # 回退也失败:返回错误占位响应 - return ExpertResponse( - text=f"[系统错误] 专家与大模型回退均失败:{type(e).__name__}: {e}", - model_used=f"error:{self.fallback.name}", - cost_est=0.0, - ) - - @staticmethod - def _finalize(query: str, classification: Classification, resp: ExpertResponse, - quality_score: float, upgraded: bool, route: list, - latency_ms: float, model_used: str, cost_est: float, - error: Optional[str] = None) -> RouterResult: - return RouterResult( - query=query, - response=resp.text, - domain=classification.domain, - difficulty=classification.difficulty, - confidence=classification.confidence, - upgraded=upgraded, - quality_score=quality_score, - model_used=model_used, - route=route, - latency_ms=latency_ms, - cache_hit=False, - cost_est=cost_est, - error=error, - ) - - def _record(self, result: RouterResult, latency_ms: float): - self.stats.record( - latency_ms, - result.domain, - result.difficulty, - result.upgraded, - result.cache_hit, - result.cache_level, - result.cost_est, - result.model_used, - ) - - # --------------------------------------------------------------- - def health(self) -> Dict[str, Any]: - return { - "status": "ok", - "domains": list(self.experts.keys()), - "classifier": type(self.classifier).__name__, - "judge": type(self.judge).__name__, - "fallback": type(self.fallback).__name__, - } - - -def build_router(config_path: Optional[str] = None) -> Router: - """从配置构建完整 Router(默认 mock 全链路,零依赖可跑)。""" - config = load_config(config_path) - classifier = build_classifier(config.get("classifier", {})) - experts = build_expert_pool(config.get("experts", {}), config.get("domains", [])) - judge = build_judge(config.get("judge", {}), config.get("router", {}).get("judge_fallback_threshold", 0.70)) - fallback = build_fallback(config.get("fallback", {})) - cache_cfg = config.get("cache", {}) - cache = RouterCache( - semantic_enabled=cache_cfg.get("semantic_enabled", True), - similarity_threshold=cache_cfg.get("similarity_threshold", 0.88), - promote_frequency=cache_cfg.get("promote_frequency", 5), - ) - stats = Stats() - return Router(classifier, experts, judge, fallback, cache, stats, config) +"""主路由器:两级路由(大领域组 → 组内路由模型 → 专业执行器)专家系统编排。 + +两级体系(对齐用户架构决策): + 第一级:用户通过接口指定大领域组(domain_group: tech/professional/lifestyle/general), + 或系统自动检测(8 领域分类 → 映射到组) + 第二级:组内路由模型(RuleClassifier(domains=组内领域) + 组内知识/模板)识别具体 + 领域、子领域、拆解子任务 → 组内专业小模型/规则执行器 + 组内路由模型只认识本组领域:体积与匹配开销约为统一路由模型的 1/4, + 且未来 L2 模型层可每组一个更小的路由模型,按需加载不常驻。 + +链路:缓存 → 组路由(分类/子领域/拆解) → 黑板+前向链 → DAG 执行 → 合并 + → Judge 校验 → (不达标)最后处理者升级 → 缓存/指标 + +L0 模式(默认):规则分类 + 规则拆解 + 规则执行器 + 规则 Judge —— 零模型参数、零 API。 +L2 模式(可选):execution.expert_backend = hf/api 时,子任务改由专家池小模型执行 +(≤8B,按需加载),其余流程不变。 +""" +from __future__ import annotations + +import uuid +from typing import Any, Dict, List, Optional + +from .cache import RouterCache +from .classifier import BaseClassifier, RuleClassifier, build_classifier +from .config import load_config +from .executors import NodeExecutor, build_node_executor +from .experts import Expert, build_expert_pool +from .fallback import FallbackProvider, build_fallback +from .inference import InferenceEngine +from .judge import BaseJudge, build_judge +from .knowledge import KnowledgeBase +from .memory import TaskGraph, TaskNode, WorkingMemory +from .models import Classification, ExpertResponse, RouterResult, now_ms +from .planner import Planner +from .stats import Stats +from .trace import TraceStore + + +class Router: + def __init__( + self, + classifier: BaseClassifier, + experts: Dict[str, Expert], + judge: BaseJudge, + fallback: FallbackProvider, + cache: Optional[RouterCache] = None, + stats: Optional[Stats] = None, + config: Optional[Dict[str, Any]] = None, + kb: Optional[KnowledgeBase] = None, + planner: Optional[Planner] = None, + ): + self.classifier = classifier + self.experts = experts + self.judge = judge + self.fallback = fallback + self.cache = cache or RouterCache() + self.stats = stats or Stats() + cfg = config or {} + rcfg = cfg.get("router", {}) + self.low_confidence_threshold = rcfg.get("low_confidence_threshold", 0.60) + self.judge_fallback_threshold = rcfg.get("judge_fallback_threshold", 0.70) + self.cache_enabled = cfg.get("cache", {}).get("enabled", True) + # ---- 专家系统内核 ---- + self.kb = kb or KnowledgeBase() + self.planner = planner or Planner(self.kb) + self.inference = InferenceEngine(self.kb) + ecfg = cfg.get("execution", {}) + self.expert_backend = ecfg.get("expert_backend", "rule") # rule | hf | api + # 子任务执行后端(T1 抽象:NodeExecutor 工厂,新增后端无需改 Router) + self.node_executor: NodeExecutor = build_node_executor( + self.expert_backend, kb=self.kb, experts=experts) + # 推理链轨迹存储(T3:可解释性产品化) + self.trace_store = TraceStore() + # ---- 两级路由:大领域分组 + 组内路由模型(更小更专) ---- + self.domain_groups: Dict[str, List[str]] = cfg.get("domain_groups", {}) or {} + if not self.domain_groups: + # 兜底:未配置时按单组(全部领域)处理,行为退化为一级路由 + self.domain_groups = {"all": list(self.experts.keys())} + self._group_of_domain: Dict[str, str] = {} + for g, domains in self.domain_groups.items(): + for d in domains: + self._group_of_domain[d] = g + # 组内路由模型:每组一个轻量分类器(只认识组内领域) + self._group_classifiers: Dict[str, RuleClassifier] = { + g: RuleClassifier(domains=domains) + for g, domains in self.domain_groups.items() + } + + # --------------------------------------------------------------- + async def route(self, query: str, domain_group: Optional[str] = None) -> RouterResult: + """两级路由入口。 + + domain_group 指定时:跳过 8 领域统一分类器,直接用组内路由模型 + (RuleClassifier(domains=组内领域))识别组内领域 —— 更小更专。 + 未指定时:统一分类器识别领域 → 自动映射到大领域组(向后兼容)。 + """ + start = now_ms() + route: list = [] + request_id = uuid.uuid4().hex[:12] + + # ---- Step 1: 缓存 ---- + if self.cache_enabled: + hit = self.cache.get(query) + if hit is not None: + level, cached = hit + latency = now_ms() - start + result = RouterResult( + query=query, + response=cached.get("response", ""), + domain=cached.get("domain", "general"), + difficulty=cached.get("difficulty", "medium"), + confidence=cached.get("confidence", 0.0), + upgraded=False, + quality_score=cached.get("quality_score", 0.0), + model_used=cached.get("model_used", ""), + route=["cache:" + level], + latency_ms=latency, + cache_hit=True, + cache_level=level, + cost_est=0.0, + subdomain=cached.get("subdomain"), + subdomain2=cached.get("subdomain2"), + domain_group=cached.get("domain_group"), + request_id=request_id, + ) + self._store_trace( + request_id=request_id, query=query, group=cached.get("domain_group"), + domain=result.domain, difficulty=result.difficulty, + confidence=result.confidence, subdomain=result.subdomain, + subdomain2=result.subdomain2, route=route, quality=result.quality_score, + upgraded=False, model=result.model_used, latency=latency, + cache_hit=True, cache_level=level, + ) + self.stats.record(latency, result.domain, result.difficulty, False, True, level, 0.0, result.model_used) + return result + route.append("cache:miss") + + # ---- Step 2: 组路由(两级第一级)→ 组内分类(两级第二级) ---- + classifier = self.classifier + group = domain_group + if group is not None: + # 用户指定大领域:校验 + 使用组内路由模型 + if group not in self.domain_groups: + raise ValueError( + f"未知大领域组: {group}(可用: {sorted(self.domain_groups)})" + ) + classifier = self._group_classifiers[group] + route.append(f"group:{group}@explicit") + classification = classifier.classify(query) + if group is None: + # 自动检测:8 领域分类 → 映射大领域组 + group = self._group_of_domain.get(classification.domain, "general") + route.append(f"group:{group}@auto") + route.append(f"classify:{classification.domain}@{classification.confidence:.2f}/{classification.difficulty}") + subdomain, subdomain2 = self._detect_subdomain(query, classification.domain) + if subdomain: + route.append(f"subdomain:{subdomain}") + if subdomain2: + route.append(f"subdomain2:{subdomain2}") + + # ---- Step 3: 低置信度 -> 直接走最后处理者 ---- + if classifier.should_fallback(classification, self.low_confidence_threshold): + route.append("direct_fallback") + fb = await self._call_fallback(query) + latency = now_ms() - start + result = self._finalize(query, classification, fb, quality_score=0.0, + upgraded=True, route=route, latency_ms=latency, + model_used=fb.model_used, cost_est=fb.cost_est, + subdomain=subdomain, subdomain2=subdomain2, + domain_group=group) + result.request_id = request_id + self._store_trace( + request_id=request_id, query=query, group=group, + domain=result.domain, difficulty=result.difficulty, + confidence=result.confidence, subdomain=subdomain, + subdomain2=subdomain2, route=route, quality=0.0, + upgraded=True, model=fb.model_used, latency=latency, + ) + self._record(result, latency) + return result + + # ---- Step 4: Planner 任务拆解(DAG) ---- + graph = self.planner.plan(query, classification) + route.extend(self.planner.explain_plan(graph)) + + # ---- Step 5: 黑板初始化 + 前向链(规则轨迹) ---- + memory = WorkingMemory() + self.inference.initialize( + query, classification.domain, classification.difficulty, + classification.confidence, memory, + ) + fired = self.inference.run(query, classification.domain, memory) + if fired: + route.append(f"rules:{','.join(fired[:5])}") + + # ---- Step 6: DAG 顺序执行(拓扑序) ---- + order = graph.topo_order() + last_model = f"rule:{classification.domain}" + for node in order: + last_model = await self._execute_node(graph, node, classification, memory, route) or last_model + + # ---- Step 7: 黑板合并(节点输出 + 推理机规则产出) ---- + response = memory.merge([n.id for n in order]) + # 追加推理机规则产出的部分解(带 output 的知识规则,如 git/docker/常识条目) + node_ids = {n.id for n in order} + extra_sections = [sid for sid in memory.sections if sid not in node_ids] + extras = [memory.section(s) for s in extra_sections if memory.section(s)] + if extras: + extra_text = "\n\n".join(extras) + response = (response + "\n\n" + extra_text) if response.strip() else extra_text + if not response.strip(): + response = "(规则执行器)未能生成有效回答:任务均未产出内容。" + route.append("merge:empty") + + # ---- Step 8: Judge 校验 ---- + try: + evaluation = await self.judge.evaluate(query, response, classification.domain) + except Exception: + evaluation = None + route.append("judge_error") + quality_score = evaluation.overall_score if evaluation else 0.0 + route.append(f"judge:{quality_score:.2f}") + + upgraded = False + if evaluation is not None and evaluation.needs_fallback: + route.append("upgrade") + fb = await self._call_fallback(query) + response = fb.text + last_model = fb.model_used + upgraded = True + + latency = now_ms() - start + result = self._finalize(query, classification, ExpertResponse( + text=response, model_used=last_model, latency_ms=latency, + tokens=max(8, int(len(response) / 2.2)), cost_est=0.0, + ), quality_score=quality_score, upgraded=upgraded, route=route, + latency_ms=latency, model_used=last_model, cost_est=0.0, + subdomain=subdomain, subdomain2=subdomain2, domain_group=group) + result.request_id = request_id + self._store_trace( + request_id=request_id, query=query, group=group, + domain=result.domain, difficulty=result.difficulty, + confidence=result.confidence, subdomain=subdomain, + subdomain2=subdomain2, route=route, quality=quality_score, + upgraded=upgraded, model=last_model, latency=latency, + ) + self._record(result, latency) + + # 未升级的结果写缓存 + if self.cache_enabled and not upgraded and result.response: + self.cache.put(query, result.to_dict()) + + return result + + # --------------------------------------------------------------- + def _store_trace(self, request_id: str, query: str, group: Optional[str], + domain: str, difficulty: str, confidence: float, + subdomain: Optional[str], subdomain2: Optional[str], + route: list, quality: float, upgraded: bool, + model: str, latency: float, + cache_hit: bool = False, cache_level: Optional[str] = None) -> None: + """记录完整推理链到轨迹存储(T3:可解释性产品化)。""" + self.trace_store.put(request_id, { + "request_id": request_id, + "query": query, + "domain_group": group, + "domain": domain, + "difficulty": difficulty, + "confidence": round(confidence, 4), + "subdomain": subdomain, + "subdomain2": subdomain2, + "route": list(route), + "quality_score": round(quality, 4), + "upgraded": upgraded, + "model_used": model, + "latency_ms": round(latency, 2), + "cache_hit": cache_hit, + "cache_level": cache_level, + }) + + # --------------------------------------------------------------- + def _detect_subdomain(self, query: str, domain: str) -> tuple: + """子领域识别:返回 (二级 subdomain, 三级 subdomain2)。 + + 二级取领域内最高优先级带 subdomain 的命中规则; + 三级取最高优先级带 subdomain2 的命中规则(可与二级来自不同规则)。 + """ + hits = self.kb.match(query, domain=domain) + sub = None + sub2 = None + for h in hits: + if sub is None and h.subdomain: + sub = h.subdomain + if sub2 is None and h.subdomain2: + sub2 = h.subdomain2 + if sub is not None and sub2 is not None: + break + return sub, sub2 + + # --------------------------------------------------------------- + async def _execute_node(self, graph: TaskGraph, node: TaskNode, + classification: Classification, memory: WorkingMemory, + route: list) -> Optional[str]: + """执行一个子任务节点;返回使用的 model_used(失败返回 None)。""" + # 依赖检查:依赖失败/跳过 → 本节点跳过 + for dep_id in node.deps: + dep = graph.get(dep_id) + if dep is not None and dep.status in ("failed", "skipped"): + node.status = "skipped" + route.append(f"{node.id}:{node.kind}:skip") + return None + node.status = "running" + try: + # NodeExecutor 后端执行(rule 零参数 / model 专家池 ≤8B) + resp = await self.node_executor.execute( + node, classification.domain, classification.difficulty, memory) + node.output = resp.text + node.status = "done" + memory.write_section(node.id, resp.text) + route.append(f"{node.id}:{node.kind}") + return resp.model_used + except Exception as e: + node.status = "failed" + node.error = str(e) + self.stats.record_error() + route.append(f"{node.id}:{node.kind}:error:{type(e).__name__}") + return None + + # --------------------------------------------------------------- + async def _call_fallback(self, query: str) -> ExpertResponse: + try: + return await self.fallback.generate(query) + except Exception as e: + # 回退也失败:返回错误占位响应 + return ExpertResponse( + text=f"[系统错误] 专家与最后处理者均失败:{type(e).__name__}: {e}", + model_used=f"error:{self.fallback.name}", + cost_est=0.0, + ) + + @staticmethod + def _finalize(query: str, classification: Classification, resp: ExpertResponse, + quality_score: float, upgraded: bool, route: list, + latency_ms: float, model_used: str, cost_est: float, + error: Optional[str] = None, + subdomain: Optional[str] = None, + subdomain2: Optional[str] = None, + domain_group: Optional[str] = None) -> RouterResult: + return RouterResult( + query=query, + response=resp.text, + domain=classification.domain, + difficulty=classification.difficulty, + confidence=classification.confidence, + upgraded=upgraded, + quality_score=quality_score, + model_used=model_used, + route=route, + latency_ms=latency_ms, + cache_hit=False, + cost_est=cost_est, + error=error, + subdomain=subdomain, + subdomain2=subdomain2, + domain_group=domain_group, + ) + + def _record(self, result: RouterResult, latency_ms: float): + self.stats.record( + latency_ms, + result.domain, + result.difficulty, + result.upgraded, + result.cache_hit, + result.cache_level, + result.cost_est, + result.model_used, + ) + + # --------------------------------------------------------------- + def health(self) -> Dict[str, Any]: + return { + "status": "ok", + "domains": list(self.experts.keys()), + "domain_groups": self.domain_groups, + "classifier": type(self.classifier).__name__, + "judge": type(self.judge).__name__, + "fallback": type(self.fallback).__name__, + "planner": type(self.planner).__name__, + "execution_mode": self.expert_backend, + "rules": self.kb.rules_count(), + } + + +def build_router(config_path: Optional[str] = None) -> Router: + """从配置构建完整 Router(默认 L0 专家系统模式:零参数可跑)。""" + config = load_config(config_path) + kb = KnowledgeBase() + classifier = build_classifier(config.get("classifier", {})) + experts = build_expert_pool(config.get("experts", {}), config.get("domains", [])) + judge = build_judge(config.get("judge", {}), + config.get("router", {}).get("judge_fallback_threshold", 0.70), + kb=kb) + fallback = build_fallback(config.get("fallback", {})) + cache_cfg = config.get("cache", {}) + cache = RouterCache( + semantic_enabled=cache_cfg.get("semantic_enabled", True), + similarity_threshold=cache_cfg.get("similarity_threshold", 0.88), + promote_frequency=cache_cfg.get("promote_frequency", 5), + ) + ecfg = config.get("execution", {}) + planner = Planner(kb, max_depth=ecfg.get("max_plan_depth", 3)) + stats = Stats() + return Router(classifier, experts, judge, fallback, cache, stats, config, + kb=kb, planner=planner) diff --git a/router_system/workspace.py b/router_system/workspace.py index 6514324..83f93f0 100644 --- a/router_system/workspace.py +++ b/router_system/workspace.py @@ -39,7 +39,7 @@ LIMITS = { # 允许的领域标签(4.2 brief.tags;仅用于安全标记与验证接地,不做路由 D3) ALLOWED_TAGS = {"code", "math", "legal", "medical", "finance", - "life", "education", "general", "safety"} + "life", "education", "general", "safety", "science"} STATUS_FLOW = { "draft": {"in_progress"}, diff --git a/scripts/demo.py b/scripts/demo.py index 796f560..236a9b8 100644 --- a/scripts/demo.py +++ b/scripts/demo.py @@ -1,65 +1,75 @@ -"""CLI 演示:构建 mock 全链路路由系统,跑一组样例查询并打印结果。 - -用法: - python scripts/demo.py [--query "自定义查询"] [--batch] -""" -from __future__ import annotations - -import argparse -import asyncio -import sys -from pathlib import Path - -sys.path.insert(0, str(Path(__file__).resolve().parent.parent)) - -from router_system.router import build_router - -SAMPLE_QUERIES = [ - "用 Python 写一个快速排序函数,并解释时间复杂度", - "求解方程 x^2 - 5x + 6 = 0", - "劳动合同里约定离职后两年内不得从事同行业,是否有效?", - "高血压患者日常饮食需要注意什么?", - "给我总结一下深度学习中注意力机制的优缺点", - "为什么天空是蓝色的?", - "帮我调试这段代码:def f(x): return x + 1 报 TypeError", - "求 ∫ x^2 dx 从 0 到 1 的定积分是多少?", -] - - -async def run_demo(router, queries, verbose: bool = False): - for q in queries: - r = await router.route(q) - print("=" * 72) - print(f"Q: {q}") - print(f" domain={r.domain} difficulty={r.difficulty} conf={r.confidence:.2f} " - f"upgraded={r.upgraded} quality={r.quality_score:.2f} model={r.model_used} " - f"latency={r.latency_ms:.1f}ms cache={r.cache_hit}({r.cache_level}) cost=${r.cost_est:.6f}") - print(f" route: {' -> '.join(r.route)}") - if verbose: - print(f" --- response ---\n{r.response[:400]}") - - -async def main(): - parser = argparse.ArgumentParser(description="多专家路由系统 CLI 演示") - parser.add_argument("--query", type=str, default=None, help="单条查询(覆盖默认样例)") - parser.add_argument("--batch", action="store_true", help="批量模式(打印全部响应)") - parser.add_argument("--verbose", action="store_true", help="打印响应正文") - args = parser.parse_args() - - router = build_router() - print("系统组件:", router.health()) - print() - - if args.query: - await run_demo(router, [args.query], verbose=True) - else: - await run_demo(router, SAMPLE_QUERIES, verbose=args.verbose) - - print() - print("=" * 72) - print("运行指标:", router.stats.summary()) - print("缓存统计:", router.cache.stats()) - - -if __name__ == "__main__": - asyncio.run(main()) +"""CLI 演示:构建专家系统内核路由(默认 L0 零参数模式),跑样例查询并打印推理链。 + +用法: + python scripts/demo.py [--query "自定义查询"] [--batch] [--trace] [--verbose] +--trace 打印完整推理链(分类 → 拆解 DAG → 规则轨迹 → 子任务执行 → Judge) +""" +from __future__ import annotations + +import argparse +import asyncio +import sys +from pathlib import Path + +if hasattr(sys.stdout, "reconfigure"): + sys.stdout.reconfigure(encoding="utf-8") + sys.stderr.reconfigure(encoding="utf-8") + +sys.path.insert(0, str(Path(__file__).resolve().parent.parent)) + +from router_system.router import build_router + +SAMPLE_QUERIES = [ + "用 Python 写一个快速排序函数,并解释时间复杂度", + "求解方程 x^2 - 5x + 6 = 0", + "劳动合同里约定离职后两年内不得从事同行业,是否有效?", + "高血压患者日常饮食需要注意什么?", + "给我总结一下深度学习中注意力机制的优缺点", + "为什么天空是蓝色的?", + "帮我调试这段代码:def f(x): return x + 1 报 TypeError", + "求 ∫ x^2 dx 从 0 到 1 的定积分是多少?", +] + + +async def run_demo(router, queries, verbose: bool = False, trace: bool = False): + for q in queries: + r = await router.route(q) + print("=" * 72) + print(f"Q: {q}") + print(f" domain={r.domain} difficulty={r.difficulty} conf={r.confidence:.2f} " + f"upgraded={r.upgraded} quality={r.quality_score:.2f} model={r.model_used} " + f"latency={r.latency_ms:.1f}ms cache={r.cache_hit}({r.cache_level}) cost=${r.cost_est:.6f}") + print(f" route: {' -> '.join(r.route)}") + if trace: + # 拆解轨迹从 route 中展开为更可读的形式 + plan_steps = [s for s in r.route if s.startswith("plan:") or ":" in s] + print(" 推理链: " + " -> ".join(r.route)) + if verbose: + print(f" --- response ---\n{r.response[:600]}") + + +async def main(): + parser = argparse.ArgumentParser(description="多专家路由系统(专家系统内核)CLI 演示") + parser.add_argument("--query", type=str, default=None, help="单条查询(覆盖默认样例)") + parser.add_argument("--batch", action="store_true", help="批量模式(打印全部响应)") + parser.add_argument("--verbose", action="store_true", help="打印响应正文") + parser.add_argument("--trace", action="store_true", help="打印完整推理链") + args = parser.parse_args() + + router = build_router() + print("系统组件:", router.health()) + print() + + if args.query: + await run_demo(router, [args.query], verbose=True, trace=True) + else: + await run_demo(router, SAMPLE_QUERIES, verbose=args.verbose, trace=args.trace) + + print() + print("=" * 72) + print("运行指标:", router.stats.summary()) + print("缓存统计:", router.cache.stats()) + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/scripts/eval.py b/scripts/eval.py index 6f27f23..3dc3443 100644 --- a/scripts/eval.py +++ b/scripts/eval.py @@ -1,80 +1,121 @@ -"""迷你评估:对带标注的样例查询评估分类准确率、升级率、成本。 - -用法: - python scripts/eval.py [--repeat 2] [--config path] ---repeat 用于把样例跑 N 遍,验证语义缓存命中与降本效果。 -""" -from __future__ import annotations - -import argparse -import asyncio -import sys -from collections import Counter -from pathlib import Path - -sys.path.insert(0, str(Path(__file__).resolve().parent.parent)) - -from router_system.router import build_router - -# (query, 期望领域) -BENCH = [ - ("用 Python 实现二分查找", "code"), - ("这段 JavaScript 为什么报错:undefined is not a function", "code"), - ("帮我优化这个 SQL 查询的索引", "code"), - ("求解一元二次方程 ax^2+bx+c=0 的求根公式", "math"), - ("证明勾股定理", "math"), - ("计算 3x + 5 = 20,x 等于多少", "math"), - ("劳动合同到期不续签,公司需要支付经济补偿吗", "legal"), - ("在合同中约定违约金上限 30%,是否合规", "legal"), - ("专利申请的流程和费用大概是多少", "legal"), - ("高血压患者可以吃哪些降压药,副作用是什么", "medical"), - ("感冒发烧 38.5 度,需要吃退烧药吗", "medical"), - ("糖尿病患者的日常饮食建议", "medical"), - ("介绍一下 Transformer 架构", "general"), - ("写一封请假邮件", "general"), - ("为什么天空是蓝色的", "general"), -] - - -async def main(): - parser = argparse.ArgumentParser() - parser.add_argument("--repeat", type=int, default=2, help="重复轮数(验证缓存)") - parser.add_argument("--config", type=str, default=None) - args = parser.parse_args() - - router = build_router(args.config) - correct = Counter() - total = 0 - upgraded = 0 - cache_hits = 0 - - for round_i in range(args.repeat): - for q, expected in BENCH: - r = await router.route(q) - total += 1 - if r.domain == expected: - correct["total"] += 1 - else: - correct[f"misclass->{r.domain}"] += 1 - if r.upgraded: - upgraded += 1 - if r.cache_hit: - cache_hits += 1 - - acc = correct["total"] / total - print(f"样例数: {len(BENCH)} x {args.repeat} 轮 = {total} 次请求") - print(f"分类准确率: {acc:.1%} ({correct['total']}/{total})") - print(f"升级率: {upgraded/total:.1%} ({upgraded}/{total})") - print(f"缓存命中率: {cache_hits/total:.1%} ({cache_hits}/{total})") - print() - print("运行指标:", router.stats.summary()) - print("缓存统计:", router.cache.stats()) - print() - if acc < 0.8: - print("⚠️ 准确率低于 80%,请检查分类规则。") - else: - print("✅ 分类准确率达标(≥80%)。") - - -if __name__ == "__main__": - asyncio.run(main()) +"""迷你评估:对带标注的样例查询评估分类准确率、升级率、成本。 + +用法: + python scripts/eval.py [--repeat 2] [--config path] +--repeat 用于把样例跑 N 遍,验证语义缓存命中与降本效果。 +""" +from __future__ import annotations + +import argparse +import asyncio +import sys +from collections import Counter +from pathlib import Path + +# 兼容 GBK 控制台(中文 Windows 默认编码),避免打印 ✅/⚠️ 时 UnicodeEncodeError +if hasattr(sys.stdout, "reconfigure"): + sys.stdout.reconfigure(encoding="utf-8") + sys.stderr.reconfigure(encoding="utf-8") + +sys.path.insert(0, str(Path(__file__).resolve().parent.parent)) + +from router_system.router import build_router + +# (query, 期望领域, 期望大领域组, 期望子领域) +# 覆盖 8 领域 × 3 条 = 24 条 + 两级路由/三级子领域指标 +BENCH = [ + # ---- tech:code / math ---- + ("用 Python 实现二分查找", "code", "tech", "algorithm"), + ("这段 JavaScript 为什么报错:undefined is not a function", "code", "tech", "debugging"), + ("帮我优化这个 SQL 查询的索引", "code", "tech", "database"), + ("求解一元二次方程 ax^2+bx+c=0 的求根公式", "math", "tech", "algebra"), + ("证明勾股定理", "math", "tech", "geometry"), + ("计算 3x + 5 = 20,x 等于多少", "math", "tech", "algebra"), + # ---- professional:legal / medical / finance ---- + ("劳动合同到期不续签,公司需要支付经济补偿吗", "legal", "professional", "labor"), + ("在合同中约定违约金上限 30%,是否合规", "legal", "professional", "contract"), + ("加班费怎么计算", "legal", "professional", "labor"), + ("高血压患者可以吃哪些降压药,副作用是什么", "medical", "professional", "medication"), + ("感冒发烧 38.5 度,需要吃退烧药吗", "medical", "professional", "medication"), + ("烫伤后怎么处理", "medical", "professional", "firstaid"), + ("基金定投的收益率怎么计算", "finance", "professional", "investing"), + ("信用卡逾期了怎么办", "finance", "professional", "credit"), + ("房贷利率是 LPR 加多少", "finance", "professional", "loan"), + # ---- lifestyle:life / education ---- + ("日本旅行攻略", "life", "lifestyle", "travel"), + ("健身增肌计划怎么安排", "life", "lifestyle", "fitness"), + ("家常菜谱推荐", "life", "lifestyle", "food"), + ("考研英语怎么备考", "education", "lifestyle", "exam"), + ("高效学习方法", "education", "lifestyle", "study"), + ("面试技巧有哪些", "education", "lifestyle", "career"), + # ---- general ---- + ("介绍一下 Transformer 架构", "general", "general", "explain"), + ("写一封请假邮件", "general", "general", "writing"), + ("为什么天空是蓝色的", "general", "general", "explain"), +] + + +async def main(): + parser = argparse.ArgumentParser() + parser.add_argument("--repeat", type=int, default=2, help="重复轮数(验证缓存)") + parser.add_argument("--config", type=str, default=None) + parser.add_argument("--group", type=str, default=None, + help="指定大领域组测试两级路由(如 tech);默认自动检测") + args = parser.parse_args() + + router = build_router(args.config) + # 指定组时只评测组内样例(组路由只认识本组领域) + bench = BENCH + if args.group is not None: + bench = [b for b in BENCH if b[2] == args.group] + if not bench: + print(f"组 {args.group} 无评测样例,可用组: {sorted({b[2] for b in BENCH})}") + return + correct = Counter() + group_correct = 0 + subdomain_correct = 0 + total = 0 + upgraded = 0 + cache_hits = 0 + decomposed = 0 # 被 Planner 拆解为多子任务的请求数 + + for round_i in range(args.repeat): + for q, expected, expected_group, expected_sub in bench: + r = await router.route(q, domain_group=args.group) + total += 1 + if r.domain == expected: + correct["total"] += 1 + else: + correct[f"misclass->{r.domain}"] += 1 + if args.group is None and r.domain_group == expected_group: + group_correct += 1 + if r.subdomain == expected_sub: + subdomain_correct += 1 + if r.upgraded: + upgraded += 1 + if r.cache_hit: + cache_hits += 1 + if any("plan:multi" in s for s in r.route): + decomposed += 1 + + acc = correct["total"] / total + print(f"样例数: {len(bench)} x {args.repeat} 轮 = {total} 次请求") + print(f"分类准确率: {acc:.1%} ({correct['total']}/{total})") + if args.group is None: + print(f"大领域组识别准确率: {group_correct/total:.1%} ({group_correct}/{total})") + print(f"子领域识别准确率: {subdomain_correct/total:.1%} ({subdomain_correct}/{total})") + print(f"升级率: {upgraded/total:.1%} ({upgraded}/{total})") + print(f"缓存命中率: {cache_hits/total:.1%} ({cache_hits}/{total})") + print(f"任务拆解率: {decomposed/total:.1%} ({decomposed}/{total})") + print() + print("运行指标:", router.stats.summary()) + print("缓存统计:", router.cache.stats()) + print() + if acc < 0.8: + print("⚠️ 准确率低于 80%,请检查分类规则。") + else: + print("✅ 分类准确率达标(≥80%)。") + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/scripts/serve.py b/scripts/serve.py index 76445d8..d9ff253 100644 --- a/scripts/serve.py +++ b/scripts/serve.py @@ -1,4 +1,4 @@ -"""启动路由网关服务(后台、无窗口)。 +"""启动路由网关服务(后台、无窗口)。 用法: python scripts/serve.py [--port 8000] [--stop] @@ -41,11 +41,16 @@ def stop(): return pid = int(PID_FILE.read_text().strip()) try: - import signal - os.kill(pid, signal.SIGTERM) - print(f"已发送终止信号 pid={pid}") - except ProcessLookupError: - print(f"进程 {pid} 不存在,清理 pid 文件。") + # Windows 下 SIGTERM 对 detached 进程不可靠,改用 taskkill 强制结束进程树 + subprocess.run( + ["taskkill", "/PID", str(pid), "/T", "/F"], + capture_output=True, text=True, timeout=15, + ) + print(f"已终止服务 pid={pid}") + except subprocess.TimeoutExpired: + print(f"终止超时 pid={pid},请手动结束进程。") + except Exception as e: + print(f"终止失败(进程可能不存在): {e}") PID_FILE.unlink(missing_ok=True) diff --git a/tests/test_cache.py b/tests/test_cache.py index 29bb74e..329ebed 100644 --- a/tests/test_cache.py +++ b/tests/test_cache.py @@ -13,9 +13,9 @@ def test_exact_hit(): def test_semantic_hit(): c = RouterCache(semantic_enabled=True, similarity_threshold=0.5) - c.put("?python?????", {"response": "code", "domain": "code"}) - # ?????????? L2 - hit = c.get("?python????????") + c.put("用python写一个快速排序", {"response": "code", "domain": "code"}) + # 相似改写查询命中 L2 语义缓存 + hit = c.get("用python写一个快速排序算法") assert hit is not None assert hit[0] == "semantic" @@ -24,7 +24,7 @@ def test_promote_to_exact(): c = RouterCache(promote_frequency=3) result = {"response": "x", "domain": "general"} c.put("query", result) - # ?????? 3 ? ? ??????? + # 语义命中 3 次后提升为精确缓存 for _ in range(3): hit = c.get("query") assert hit is not None diff --git a/tests/test_classifier.py b/tests/test_classifier.py index d882199..cfb6b84 100644 --- a/tests/test_classifier.py +++ b/tests/test_classifier.py @@ -1,44 +1,44 @@ -"""分类器单元测试。""" -from router_system.classifier import RuleClassifier - - -def test_code_classification(): - clf = RuleClassifier() - r = clf.classify("用 Python 写一个快速排序函数") - assert r.domain == "code" - assert r.confidence > 0.7 - - -def test_math_classification(): - clf = RuleClassifier() - r = clf.classify("求解方程 x^2 - 5x + 6 = 0") - assert r.domain == "math" - assert r.confidence > 0.7 - - -def test_legal_classification(): - clf = RuleClassifier() - r = clf.classify("劳动合同到期不续签需要支付经济补偿吗") - assert r.domain == "legal" - - -def test_medical_classification(): - clf = RuleClassifier() - r = clf.classify("高血压患者日常饮食需要注意什么") - assert r.domain == "medical" - - -def test_general_low_confidence(): - clf = RuleClassifier() - r = clf.classify("今天天气怎么样") - # 未命中任何领域 -> 低置信度,触发 should_fallback - assert r.domain == "general" - assert clf.should_fallback(r, 0.6) is True - - -def test_difficulty_estimation(): - clf = RuleClassifier() - easy = clf.classify("1 + 1 = ?") - hard = clf.classify("证明费马大定理并推导其推论,给出详细步骤") - assert hard.difficulty in ("medium", "hard") - assert easy.difficulty == "easy" +"""分类器单元测试。""" +from router_system.classifier import RuleClassifier + + +def test_code_classification(): + clf = RuleClassifier() + r = clf.classify("用 Python 写一个快速排序函数") + assert r.domain == "code" + assert r.confidence > 0.7 + + +def test_math_classification(): + clf = RuleClassifier() + r = clf.classify("求解方程 x^2 - 5x + 6 = 0") + assert r.domain == "math" + assert r.confidence > 0.7 + + +def test_legal_classification(): + clf = RuleClassifier() + r = clf.classify("劳动合同到期不续签需要支付经济补偿吗") + assert r.domain == "legal" + + +def test_medical_classification(): + clf = RuleClassifier() + r = clf.classify("高血压患者日常饮食需要注意什么") + assert r.domain == "medical" + + +def test_general_low_confidence(): + clf = RuleClassifier() + r = clf.classify("你好呀") + # 未命中任何领域 -> general,低置信度,触发 should_fallback + assert r.domain == "general" + assert clf.should_fallback(r, 0.6) is True + + +def test_difficulty_estimation(): + clf = RuleClassifier() + easy = clf.classify("1 + 1 = ?") + hard = clf.classify("证明费马大定理并推导其推论,给出详细步骤") + assert hard.difficulty in ("medium", "hard") + assert easy.difficulty == "easy" diff --git a/tests/test_gateway.py b/tests/test_gateway.py index 348c6a8..1022d45 100644 --- a/tests/test_gateway.py +++ b/tests/test_gateway.py @@ -41,14 +41,30 @@ def test_chat_legacy(client): def test_chat_v2(v2_client): + # POST /chat 立即返回 request_id(异步协议) resp = v2_client.post("/chat", json={"query": "请介绍快速排序算法"}) assert resp.status_code == 200 data = resp.json() - assert data["response"] - assert data["request_id"] - assert "fast_path" in data - assert "route" in data - assert data["status"] in ("fast_path", "done", "escalated", "failed") + assert "request_id" in data + assert data["status"] == "pending" + + # 轮询 /runs/{id}/status 直到完成 + import time + for _ in range(50): # 最多 5s + time.sleep(0.1) + status_resp = v2_client.get(f"/runs/{data['request_id']}/status") + assert status_resp.status_code == 200 + s = status_resp.json() + if s["status"] in ("done", "failed"): + break + + assert s["status"] == "done", f"期望 done,实际 {s['status']},error={s.get('error')}" + assert s["response"] + assert "pipeline_status" in s + assert s["pipeline_status"] in ("fast_path", "done", "escalated") + # fast_path 不写 workspace.json,所以 workspace_path 可能为 None + if s["pipeline_status"] != "fast_path": + assert s["workspace_path"] is not None def test_chat_empty_query(client): @@ -93,8 +109,9 @@ def test_web_ui_served(client): resp = client.get("/") assert resp.status_code == 200 assert resp.headers["content-type"].startswith("text/html") - assert "端云协同" in resp.text - assert "发送" in resp.text + # Vue SPA:由 Vite 生成,特征是
和 /static/assets/ 引用 + assert '
' in resp.text + assert '/static/assets/' in resp.text def test_review_flow(client): diff --git a/tests/test_router.py b/tests/test_router.py index 341b964..b06c5d3 100644 --- a/tests/test_router.py +++ b/tests/test_router.py @@ -1,56 +1,56 @@ -"""路由主流程单元测试。""" -import pytest - -from router_system.router import build_router - - -@pytest.mark.asyncio -async def test_normal_flow_code(router): - r = await router.route("用 Python 写一个快速排序函数") - assert r.domain == "code" - assert r.response - assert r.model_used - assert r.latency_ms >= 0 - assert "expert" in r.route[1] or "classify" in r.route[1] - - -@pytest.mark.asyncio -async def test_low_confidence_direct_fallback(router): - r = await router.route("今天天气怎么样") - assert r.upgraded is True - assert "direct_fallback" in r.route - assert r.model_used == router.fallback.model - - -@pytest.mark.asyncio -async def test_cache_second_round(router): - q = "用 Python 写一个快速排序函数" - r1 = await router.route(q) - assert r1.cache_hit is False - r2 = await router.route(q) - assert r2.cache_hit is True - assert r2.cache_level in ("exact", "semantic") - assert r2.response == r1.response - - -@pytest.mark.asyncio -async def test_judge_route_present(router): - r = await router.route("高血压患者日常饮食需要注意什么") - assert any(step.startswith("judge:") for step in r.route) - - -@pytest.mark.asyncio -async def test_stats_recorded(router): - await router.route("写一个 python 函数") - await router.route("写一个 python 函数") - s = router.stats.summary() - assert s["total_requests"] == 2 - assert s["domain_distribution"]["code"] == 2 - assert s["cache_hit_rate"] > 0 - - -def test_health(router): - h = router.health() - assert h["status"] == "ok" - assert "code" in h["domains"] - assert "math" in h["domains"] +"""路由主流程单元测试。""" +import pytest + +from router_system.router import build_router + + +@pytest.mark.asyncio +async def test_normal_flow_code(router): + r = await router.route("用 Python 写一个快速排序函数") + assert r.domain == "code" + assert r.response + assert r.model_used + assert r.latency_ms >= 0 + assert any("classify:" in s for s in r.route) + + +@pytest.mark.asyncio +async def test_low_confidence_direct_fallback(router): + r = await router.route("今天天气怎么样") + assert r.upgraded is True + assert "direct_fallback" in r.route + assert r.model_used == router.fallback.model + + +@pytest.mark.asyncio +async def test_cache_second_round(router): + q = "用 Python 写一个快速排序函数" + r1 = await router.route(q) + assert r1.cache_hit is False + r2 = await router.route(q) + assert r2.cache_hit is True + assert r2.cache_level in ("exact", "semantic") + assert r2.response == r1.response + + +@pytest.mark.asyncio +async def test_judge_route_present(router): + r = await router.route("高血压患者日常饮食需要注意什么") + assert any(step.startswith("judge:") for step in r.route) + + +@pytest.mark.asyncio +async def test_stats_recorded(router): + await router.route("写一个 python 函数") + await router.route("写一个 python 函数") + s = router.stats.summary() + assert s["total_requests"] == 2 + assert s["domain_distribution"]["code"] == 2 + assert s["cache_hit_rate"] > 0 + + +def test_health(router): + h = router.health() + assert h["status"] == "ok" + assert "code" in h["domains"] + assert "math" in h["domains"] diff --git a/webapp/.gitignore b/webapp/.gitignore new file mode 100644 index 0000000..a547bf3 --- /dev/null +++ b/webapp/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/webapp/README.md b/webapp/README.md new file mode 100644 index 0000000..33895ab --- /dev/null +++ b/webapp/README.md @@ -0,0 +1,5 @@ +# Vue 3 + TypeScript + Vite + +This template should help get you started developing with Vue 3 and TypeScript in Vite. The template uses Vue 3 ` + + diff --git a/webapp/package-lock.json b/webapp/package-lock.json new file mode 100644 index 0000000..11d7433 --- /dev/null +++ b/webapp/package-lock.json @@ -0,0 +1,1648 @@ +{ + "name": "webapp", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "webapp", + "version": "0.0.0", + "dependencies": { + "@vueuse/core": "^14.4.0", + "axios": "^1.20.0", + "pinia": "^4.0.3", + "vue": "^3.5.41", + "vue-router": "^4.6.4" + }, + "devDependencies": { + "@types/node": "^24.13.3", + "@vitejs/plugin-vue": "^6.0.8", + "@vue/tsconfig": "^0.9.1", + "typescript": "~6.0.2", + "vite": "^8.2.2", + "vue-tsc": "^3.3.11" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", + "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", + "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.8.tgz", + "integrity": "sha512-E8lTAYNB1KW+FH+VGJuZM1ioAx2E6oVlvQFRrf5P8ZZmsiJXYAD9vTFV7yyEURNzgh1dFqMZuO6tUwcARbqFCA==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.8" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.8.tgz", + "integrity": "sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==", + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.6.0.tgz", + "integrity": "sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==", + "license": "MIT" + }, + "node_modules/@oxc-project/types": { + "version": "0.147.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.147.0.tgz", + "integrity": "sha512-IJ3s6ltHLp45S0bh7phkX+gJO7A1Wuz2EaqpAhb8WjqDwbzMiWKHhyyT42tskaWjEYXtHtVCPpnBJVT9+dcRLg==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/Boshen" + } + }, + "node_modules/@rolldown/binding-android-arm-eabi": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm-eabi/-/binding-android-arm-eabi-1.2.6.tgz", + "integrity": "sha512-b+jTcARdTiFLI6jB4a5XjTm0RWd6KcRfQj/I2356fxUZemiho9zQLxo0RtCuMDAyKcLo6cEltkgbQp6d1+sjjQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-android-arm64": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.2.6.tgz", + "integrity": "sha512-lkWU8ZJaRk9q3CIEY1Tc7vIFALp3Xw5NfGJo2hQg5oIqNgxWi1zI+IiDEK3r70BF5Dzol1tcXsnzsRc8NLhG+Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-arm64": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.2.6.tgz", + "integrity": "sha512-dgR56NYnvAszm7Ob1B2/Vn0e8bUQYZH2UjVaMMtMVOCKFSfjhfLmuA/9+O+F+ajUdG6B/bSssrKW6JJYASa8jA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-x64": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.2.6.tgz", + "integrity": "sha512-vpVxFvUCFioJqug7OTvqptkc4yb8UX0AwfDmJpaR/0sWz+BUmqSVAf7c8JkUgnN8YLspb4a/N6NhTyMAmdyQ7Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-freebsd-x64": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.2.6.tgz", + "integrity": "sha512-h1wG6Y6K3JlRswxsI64qQJqBAy4vrLuHgRbc8CZMGSWTOFRY6ghMApM1NKzB2I0n5xV1fjkE18SuVl2QpLeNpA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm-gnueabihf": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.2.6.tgz", + "integrity": "sha512-tbCiqub0q2MVWJKgF5PoAlNWCtQydiOYSLIkd8sByqK/6MMYLJRcSXSYodqYtd0O+Fw7QaVmKKlS4oL94YRZ0w==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-gnu": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.2.6.tgz", + "integrity": "sha512-oxK9+baEBPhZG5HB4URY+uU04zJWeZlH6Tb9rB5DK4DF9XR1uXNLXt5Q5ZsugTKayNCNLhkcwz/ye74hRI98dg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-musl": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.2.6.tgz", + "integrity": "sha512-muWCk27FVBEZtv0MsK8gnfSmgczA8KQ0uRVJbTABKhkRfQc38aUrcb7fhi3BNiyseFmgcRsoMfQsSNJ+DbZdSw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-ppc64-gnu": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.2.6.tgz", + "integrity": "sha512-eWDoSfU7Co2qj3vgB3Dt4lj1mG6CoWbcJQkRMP3XJplyCMtuaq3LHvPFjS9QIPvMGWVadJC04Xiy0IdcVPtnwQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-s390x-gnu": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.2.6.tgz", + "integrity": "sha512-2bWNjRSIayvupRKxXUY2tWG9fYdoUlTqWywHRvE8Eq3GvuQ+f2HeIkve697fIt+IQs/PV8yFsdWuhp1aJ1PdnA==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-gnu": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.2.6.tgz", + "integrity": "sha512-KekI0gS0wLxe1UBSQSjenBVwou/JkcQPDzBPICGZjxUv9k3RteHDPBQaiOicZUFKRIH2wKEimGwVpnJsbPzu7w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-musl": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.2.6.tgz", + "integrity": "sha512-TvtPnfVr+HtyGiDmPK4VWmlNm7QhNNAcK5Q9A7aOXsI8545yCyaoMaicXrFZ72JzeYjaUVk7yT243zT0jzjFKQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-openharmony-arm64": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.2.6.tgz", + "integrity": "sha512-iOo0VEay2XFhaCcH0sps5XIimkSuOnNaZrf6+ZkoSOQBJPKNU48RkmJv0/lSpipexu5P+ouFgafe5IGr/DiQfg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-arm64-msvc": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.2.6.tgz", + "integrity": "sha512-y5NTmmasMS455JlOCO4ZM9krIchv3Mvm1crL1iUPGOPgEzSkves9n0SdC5Sjz6+qWDFhd8/JpfWMH8NSWNHe+A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-x64-msvc": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.2.6.tgz", + "integrity": "sha512-np8iZSLfXlAD4kWhiyq/u0Yt8oZDtRQ8lGhQaCXo2rl37KNjeU0GjJuwr4P3oeZ++ROfofsKNBqR5LTO8aXyWQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz", + "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "24.13.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.13.3.tgz", + "integrity": "sha512-Dh8vAsV36ig5wa9OX4pXvMc9D3Veibfw2wix0CUwYODLD8nkj9UsLjASr49nPg+2eKzxhBV+v7L8pXvT4e639Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~7.18.0" + } + }, + "node_modules/@types/web-bluetooth": { + "version": "0.0.21", + "resolved": "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.21.tgz", + "integrity": "sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA==", + "license": "MIT" + }, + "node_modules/@vitejs/plugin-vue": { + "version": "6.0.8", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-6.0.8.tgz", + "integrity": "sha512-0ZjgOg7oO6farnNGup7yvoM/YXZV84OZxHAwtflItNa/6zzQyVb5LNxyea3FEKEX2XlagIKzrlH7wwxkKgtiew==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rolldown/pluginutils": "^1.0.1" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0", + "vue": "^3.2.25" + } + }, + "node_modules/@volar/language-core": { + "version": "2.4.28", + "resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-2.4.28.tgz", + "integrity": "sha512-w4qhIJ8ZSitgLAkVay6AbcnC7gP3glYM3fYwKV3srj8m494E3xtrCv6E+bWviiK/8hs6e6t1ij1s2Endql7vzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/source-map": "2.4.28" + } + }, + "node_modules/@volar/source-map": { + "version": "2.4.28", + "resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-2.4.28.tgz", + "integrity": "sha512-yX2BDBqJkRXfKw8my8VarTyjv48QwxdJtvRgUpNE5erCsgEUdI2DsLbpa+rOQVAJYshY99szEcRDmyHbF10ggQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@volar/typescript": { + "version": "2.4.28", + "resolved": "https://registry.npmjs.org/@volar/typescript/-/typescript-2.4.28.tgz", + "integrity": "sha512-Ja6yvWrbis2QtN4ClAKreeUZPVYMARDYZl9LMEv1iQ1QdepB6wn0jTRxA9MftYmYa4DQ4k/DaSZpFPUfxl8giw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/language-core": "2.4.28", + "path-browserify": "^1.0.1", + "vscode-uri": "^3.0.8" + } + }, + "node_modules/@vue/compiler-core": { + "version": "3.5.42", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.42.tgz", + "integrity": "sha512-2Ye1ilMtKXxl8qZUrQ5j0CdgenFp/HFQmta6rfRyfEsTG69L6Wk+tWuNoHYHMx9E8tF2Slvdg1FuwDvAXdy1LQ==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.8", + "@vue/shared": "3.5.42", + "entities": "^7.0.1", + "estree-walker": "^2.0.2", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-dom": { + "version": "3.5.42", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.42.tgz", + "integrity": "sha512-qbhQZEFmycr+ni/qyuccS4sucNN7VAbDfbkvNxWOX2VfgFm90MNs3/UhRNKoPMEIVn0F8gdlYjLPvqxHwHeQOA==", + "license": "MIT", + "dependencies": { + "@vue/compiler-core": "3.5.42", + "@vue/shared": "3.5.42" + } + }, + "node_modules/@vue/compiler-sfc": { + "version": "3.5.42", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.42.tgz", + "integrity": "sha512-fkCAFB4okcAANGMThboWnScp/gzWjU0ZSkVnjTIiplmMDq2uq0tIB3j+xVu4rhv5rvOgBySCysudmbMd6xRRqw==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.8", + "@vue/compiler-core": "3.5.42", + "@vue/compiler-dom": "3.5.42", + "@vue/compiler-ssr": "3.5.42", + "@vue/shared": "3.5.42", + "estree-walker": "^2.0.2", + "magic-string": "^0.30.21", + "postcss": "^8.5.19", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-ssr": { + "version": "3.5.42", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.42.tgz", + "integrity": "sha512-xmLk3wLkbizPAiLyomjgFFosf2ys9b5Ghb+oh/k2tnvipNz8OFrQOiTcWCzyK7MpBp9KkyGtfvgfLUivbmuGYA==", + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.42", + "@vue/shared": "3.5.42" + } + }, + "node_modules/@vue/devtools-api": { + "version": "8.2.1", + "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-8.2.1.tgz", + "integrity": "sha512-6u4vXBlIBAC1wMplIZgpyPn7uh/s4Bf6F5bMzvLv+EdJ0aHs/+4B7Ygv864EStQSjRbsRzTko/kUG1A1IejQ3A==", + "license": "MIT", + "peer": true, + "dependencies": { + "@vue/devtools-kit": "^8.2.1" + } + }, + "node_modules/@vue/devtools-kit": { + "version": "8.2.1", + "resolved": "https://registry.npmjs.org/@vue/devtools-kit/-/devtools-kit-8.2.1.tgz", + "integrity": "sha512-FIGIuq3AWReEpbAHY/cRGeHDfI0qOb8OCQ3YjbEAX04uaxIDbGc9rhkbVcG7rnfHPXE3RsU5KrWOu9V/okd8AQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "@vue/devtools-shared": "^8.2.1", + "birpc": "^2.6.1", + "hookable": "^5.5.3", + "perfect-debounce": "^2.0.0" + } + }, + "node_modules/@vue/devtools-shared": { + "version": "8.2.1", + "resolved": "https://registry.npmjs.org/@vue/devtools-shared/-/devtools-shared-8.2.1.tgz", + "integrity": "sha512-Fkac7lUdGReh6pVOi3AYPRGe82LQqRmAfThW7RRligOAP0ZA/Z1z9XLHDM9dv34pV2HRc79DK8uKPeG2fLnA/g==", + "license": "MIT", + "peer": true + }, + "node_modules/@vue/language-core": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/@vue/language-core/-/language-core-3.3.11.tgz", + "integrity": "sha512-QJmpliwAVpC/OxubIByPAhNzsQPRc8/gxlN2qnVzVfIMjMDz/9RnXRFoetjz5yEgXVXyp4LqhXq3V53PjmNzFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/language-core": "2.4.28", + "@vue/compiler-dom": "^3.5.0", + "@vue/shared": "^3.5.0", + "alien-signals": "^3.2.1", + "muggle-string": "^0.4.1", + "path-browserify": "^1.0.1", + "picomatch": "^4.0.4" + } + }, + "node_modules/@vue/reactivity": { + "version": "3.5.42", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.42.tgz", + "integrity": "sha512-TzNNfKpb7hDxbQltwAut8VDQA5YP+BuRlxntHUuRjyKwlMvmAPbs3unhCvieijifY6vFfVBwsS7wG/C7uq+bEQ==", + "license": "MIT", + "dependencies": { + "@vue/shared": "3.5.42" + } + }, + "node_modules/@vue/runtime-core": { + "version": "3.5.42", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.42.tgz", + "integrity": "sha512-9uACtuHs7vJGkm5Bp3xu4xRDLFTIYy5DgxpToVjqGIAhAEKwQfsaLvKINhM6nFVp6bZPRFGdDqd1g52MqKsotA==", + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.42", + "@vue/shared": "3.5.42" + } + }, + "node_modules/@vue/runtime-dom": { + "version": "3.5.42", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.42.tgz", + "integrity": "sha512-rsCmhiWLaRxGltLwhlCWyYkFn7WAbKRh0q17eZ1A6Dq6eqc2ACQ61IIryxz0LrsvCzHSilLA9JHovVwM8CNE2g==", + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.42", + "@vue/runtime-core": "3.5.42", + "@vue/shared": "3.5.42", + "csstype": "^3.2.3" + } + }, + "node_modules/@vue/server-renderer": { + "version": "3.5.42", + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.42.tgz", + "integrity": "sha512-2++5dUyYS4gvo7xQXSECUDhB7TS0aOl5SeVfC5qSq1Jgfhjvegw1zqhwTIR3imZ+QYPJQw9gfcFvXGAjGZ7ajQ==", + "license": "MIT", + "dependencies": { + "@vue/compiler-ssr": "3.5.42", + "@vue/runtime-dom": "3.5.42", + "@vue/shared": "3.5.42" + } + }, + "node_modules/@vue/shared": { + "version": "3.5.42", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.42.tgz", + "integrity": "sha512-2rPxex1jQf4jvl9MOHl6YaXCPcrNqz/FstMOEh3QWY+/OME9nQTvl9WYeCwhW7AFjaR0SnngZGlp/wkR6rkI6g==", + "license": "MIT" + }, + "node_modules/@vue/tsconfig": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/@vue/tsconfig/-/tsconfig-0.9.1.tgz", + "integrity": "sha512-buvjm+9NzLCJL29KY1j1991YYJ5e6275OiK+G4jtmfIb+z4POywbdm0wXusT9adVWqe0xqg70TbI7+mRx4uU9w==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "typescript": ">= 5.8", + "vue": "^3.4.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + }, + "vue": { + "optional": true + } + } + }, + "node_modules/@vueuse/core": { + "version": "14.4.0", + "resolved": "https://registry.npmjs.org/@vueuse/core/-/core-14.4.0.tgz", + "integrity": "sha512-X4WHz1HlCzCBoYXesUkifzzWBAcZgXG8Fi5iNPQg/epdzOB3gu8Fawj3hvuwYR1nGcXGnvxwYYcUC/71++svtQ==", + "license": "MIT", + "dependencies": { + "@types/web-bluetooth": "^0.0.21", + "@vueuse/metadata": "14.4.0", + "@vueuse/shared": "14.4.0" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "vue": "^3.5.0" + } + }, + "node_modules/@vueuse/metadata": { + "version": "14.4.0", + "resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-14.4.0.tgz", + "integrity": "sha512-swx/255R6JyHZFJhx845iz5CRWDZdCfvkZOpACWc5+c5WHcG24mv8gUT1WIdFQaHt6dq79rvILd9QnCWiyVm9g==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@vueuse/shared": { + "version": "14.4.0", + "resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-14.4.0.tgz", + "integrity": "sha512-JRgY90Sz8DDtPMsaDflvPMp9xYk69JZAmbuDvAquUVXKr2gEjqtzGNTTthLfckH0BzBqvnu31gb4a8TGLRe79g==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "vue": "^3.5.0" + } + }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "license": "MIT", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/alien-signals": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/alien-signals/-/alien-signals-3.2.1.tgz", + "integrity": "sha512-I8FjmltrfnDFoZedi5CG8DghVYNhzb/Ijluz7tCSJH0xpd0484Kowhbb1XDYOxfJpU1p5wnM2X54dA+IfGyD1g==", + "dev": true, + "license": "MIT" + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, + "node_modules/axios": { + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.20.0.tgz", + "integrity": "sha512-r8aOh8j9cGKpgQAqpzrUHnSIc6a59Y3Xf/cv8sy1DrHCkZHzQGEuoq1tARk6qSyDdtQGSDgpb9kFlruzPvrgwg==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.16.0", + "form-data": "^4.0.6", + "https-proxy-agent": "^5.0.1", + "proxy-from-env": "^2.1.0" + } + }, + "node_modules/birpc": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/birpc/-/birpc-2.9.0.tgz", + "integrity": "sha512-KrayHS5pBi69Xi9JmvoqrIgYGDkD6mcSe/i6YKi3w5kekCLzrX4+nawcXqrj2tIp50Kw/mT/s3p+GVK0A0sKxw==", + "license": "MIT", + "peer": true, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/entities": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-7.0.1.tgz", + "integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz", + "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "license": "MIT" + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/follow-redirects": { + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.0.tgz", + "integrity": "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/form-data": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.6.tgz", + "integrity": "sha512-vKatAh4SlVfgbv+YtmhiRjhEMJsYpsG1Y2rMQtR+SVSbytsSD1YGzDIcrAJmdFec88u/+VoGmxnl+80gL1tRCQ==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.4", + "mime-types": "^2.1.35" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", + "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hookable": { + "version": "5.5.3", + "resolved": "https://registry.npmjs.org/hookable/-/hookable-5.5.3.tgz", + "integrity": "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==", + "license": "MIT", + "peer": true + }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "license": "MIT", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/lightningcss": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.33.0.tgz", + "integrity": "sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.33.0", + "lightningcss-darwin-arm64": "1.33.0", + "lightningcss-darwin-x64": "1.33.0", + "lightningcss-freebsd-x64": "1.33.0", + "lightningcss-linux-arm-gnueabihf": "1.33.0", + "lightningcss-linux-arm64-gnu": "1.33.0", + "lightningcss-linux-arm64-musl": "1.33.0", + "lightningcss-linux-x64-gnu": "1.33.0", + "lightningcss-linux-x64-musl": "1.33.0", + "lightningcss-win32-arm64-msvc": "1.33.0", + "lightningcss-win32-x64-msvc": "1.33.0" + } + }, + "node_modules/lightningcss-android-arm64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.33.0.tgz", + "integrity": "sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.33.0.tgz", + "integrity": "sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.33.0.tgz", + "integrity": "sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.33.0.tgz", + "integrity": "sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.33.0.tgz", + "integrity": "sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.33.0.tgz", + "integrity": "sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.33.0.tgz", + "integrity": "sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.33.0.tgz", + "integrity": "sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.33.0.tgz", + "integrity": "sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.33.0.tgz", + "integrity": "sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.33.0.tgz", + "integrity": "sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/muggle-string": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/muggle-string/-/muggle-string-0.4.1.tgz", + "integrity": "sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.18", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.18.tgz", + "integrity": "sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/nostics": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/nostics/-/nostics-1.2.0.tgz", + "integrity": "sha512-FGqEfhQjrvo1lL8KFifdTQiNwwQHJxC1jtYE1Rc54qF/jxONUNL+kC9gS1krX8Q65PgrQ5fCqH/I4NhWBvdSqg==", + "license": "MIT" + }, + "node_modules/path-browserify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", + "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", + "dev": true, + "license": "MIT" + }, + "node_modules/perfect-debounce": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-2.1.0.tgz", + "integrity": "sha512-LjgdTytVFXeUgtHZr9WYViYSM/g8MkcTPYDlPa3cDqMirHjKiSZPYd6DoL7pK8AJQr+uWkQvCjHNdiMqsrJs+g==", + "license": "MIT", + "peer": true + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", + "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pinia": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/pinia/-/pinia-4.0.3.tgz", + "integrity": "sha512-XMQqpvjgG7LMqVhhFUzKLT4KEbsYbfZZ0CZU9PgdFm1O2VKBmIkykL8+SfNhOaT7sR0wT6BEgKT0YNDYWgmVRA==", + "license": "MIT", + "dependencies": { + "nostics": "^1.1.4" + }, + "funding": { + "url": "https://github.com/sponsors/posva" + }, + "peerDependencies": { + "@vue/devtools-api": "^8.1.5", + "typescript": ">=5.6.0", + "vue": "^3.5.11" + }, + "peerDependenciesMeta": { + "@vue/devtools-api": { + "optional": false + }, + "typescript": { + "optional": true + } + } + }, + "node_modules/postcss": { + "version": "8.5.26", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.26.tgz", + "integrity": "sha512-u82N74LFzG8ca+dD8puPnplTXoGH4fTPpVGuIbt36G3qvNlkvfD0lEAZSxaly3KX8TS/L1A1gsCEmvKmBcVbkQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.17", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/proxy-from-env": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-2.1.0.tgz", + "integrity": "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/rolldown": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.2.6.tgz", + "integrity": "sha512-vMM4q3aixf46GiF1Kok8jDPFsEpXgFWGjUHXNkNHNm+Y2adXAG2dbX91jkti3i0ZRsOlcmbuzAz1poObSHCmUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@oxc-project/types": "=0.147.0", + "@rolldown/pluginutils": "^1.0.0" + }, + "bin": { + "rolldown": "bin/cli.mjs" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "optionalDependencies": { + "@rolldown/binding-android-arm-eabi": "1.2.6", + "@rolldown/binding-android-arm64": "1.2.6", + "@rolldown/binding-darwin-arm64": "1.2.6", + "@rolldown/binding-darwin-x64": "1.2.6", + "@rolldown/binding-freebsd-x64": "1.2.6", + "@rolldown/binding-linux-arm-gnueabihf": "1.2.6", + "@rolldown/binding-linux-arm64-gnu": "1.2.6", + "@rolldown/binding-linux-arm64-musl": "1.2.6", + "@rolldown/binding-linux-ppc64-gnu": "1.2.6", + "@rolldown/binding-linux-s390x-gnu": "1.2.6", + "@rolldown/binding-linux-x64-gnu": "1.2.6", + "@rolldown/binding-linux-x64-musl": "1.2.6", + "@rolldown/binding-openharmony-arm64": "1.2.6", + "@rolldown/binding-win32-arm64-msvc": "1.2.6", + "@rolldown/binding-win32-x64-msvc": "1.2.6" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/typescript": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz", + "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==", + "devOptional": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "7.18.2", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz", + "integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==", + "dev": true, + "license": "MIT" + }, + "node_modules/vite": { + "version": "8.2.2", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.2.2.tgz", + "integrity": "sha512-cFKLV/PRgAUlIRm5WjMjJ86jrftzpqcgH+Us+DS8mI3CDNiH30Whrz8uHL3+MOLPAgqbMBAqWdAHAphOAM+z/Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "lightningcss": "^1.33.0", + "picomatch": "^4.0.5", + "postcss": "^8.5.26", + "rolldown": "~1.2.4", + "tinyglobby": "^0.2.17" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "@vitejs/devtools": "^0.4.0 || ^0.5.0", + "esbuild": "^0.27.0 || ^0.28.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "@vitejs/devtools": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vscode-uri": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.2.0.tgz", + "integrity": "sha512-m2gXo3bn0G1kT9InzMf07fTbqMbGtyckj3bH5ktLO+1Ssv+yiATZ4dhwaQv9UZWxJh6E9IFGnQyjgWVDWVBDrg==", + "dev": true, + "license": "MIT" + }, + "node_modules/vue": { + "version": "3.5.42", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.42.tgz", + "integrity": "sha512-4RyHQTbQvOPs3MfvUO1Sg0YRrKNnA0mAVtvpd12Tg1fKDN7OHBUl1IqSn8zGJjK9nI3NkNp8cgTpVrSZC5TTcA==", + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.42", + "@vue/compiler-sfc": "3.5.42", + "@vue/runtime-dom": "3.5.42", + "@vue/server-renderer": "3.5.42", + "@vue/shared": "3.5.42" + }, + "peerDependencies": { + "typescript": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/vue-router": { + "version": "4.6.4", + "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.6.4.tgz", + "integrity": "sha512-Hz9q5sa33Yhduglwz6g9skT8OBPii+4bFn88w6J+J4MfEo4KRRpmiNG/hHHkdbRFlLBOqxN8y8gf2Fb0MTUgVg==", + "license": "MIT", + "dependencies": { + "@vue/devtools-api": "^6.6.4" + }, + "funding": { + "url": "https://github.com/sponsors/posva" + }, + "peerDependencies": { + "vue": "^3.5.0" + } + }, + "node_modules/vue-router/node_modules/@vue/devtools-api": { + "version": "6.6.4", + "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.6.4.tgz", + "integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==", + "license": "MIT" + }, + "node_modules/vue-tsc": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/vue-tsc/-/vue-tsc-3.3.11.tgz", + "integrity": "sha512-gOb0B9rtU2+f1dszwPqSH5kAieIF9ReeLhD3kSRNHv5WZZUQz/JdVXW0RTdqhNTMlQkqKzrTTviqKr/4FYZraQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/typescript": "2.4.28", + "@vue/language-core": "3.3.11" + }, + "bin": { + "vue-tsc": "bin/vue-tsc.js" + }, + "peerDependencies": { + "typescript": ">=5.0.0" + } + } + } +} diff --git a/webapp/package.json b/webapp/package.json new file mode 100644 index 0000000..ba733a3 --- /dev/null +++ b/webapp/package.json @@ -0,0 +1,26 @@ +{ + "name": "webapp", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vue-tsc -b && vite build", + "preview": "vite preview" + }, + "dependencies": { + "@vueuse/core": "^14.4.0", + "axios": "^1.20.0", + "pinia": "^4.0.3", + "vue": "^3.5.41", + "vue-router": "^4.6.4" + }, + "devDependencies": { + "@types/node": "^24.13.3", + "@vitejs/plugin-vue": "^6.0.8", + "@vue/tsconfig": "^0.9.1", + "typescript": "~6.0.2", + "vite": "^8.2.2", + "vue-tsc": "^3.3.11" + } +} diff --git a/webapp/public/favicon.svg b/webapp/public/favicon.svg new file mode 100644 index 0000000..6893eb1 --- /dev/null +++ b/webapp/public/favicon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/webapp/public/icons.svg b/webapp/public/icons.svg new file mode 100644 index 0000000..e952219 --- /dev/null +++ b/webapp/public/icons.svg @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/webapp/src/App.vue b/webapp/src/App.vue new file mode 100644 index 0000000..1b2656e --- /dev/null +++ b/webapp/src/App.vue @@ -0,0 +1,86 @@ + + + + + diff --git a/webapp/src/api/index.ts b/webapp/src/api/index.ts new file mode 100644 index 0000000..8f1f02d --- /dev/null +++ b/webapp/src/api/index.ts @@ -0,0 +1,262 @@ +import axios from 'axios' +import type { + ChatInitResponse, + RunStatus, + SSEEvent, + ReviewItem, + Metrics, +} from '@/types' + +const http = axios.create({ timeout: 10_000 }) + +// ── Chat ───────────────────────────────────────────────────────────────────── + +/** POST /chat:异步提交,立即返回 request_id */ +export async function chat(query: string, domain_group?: string) { + const { data } = await http.post('/chat', { query, domain_group }) + return data +} + +/** GET /runs/{id}/status:查询任务状态 */ +export async function getRunStatus(requestId: string) { + const { data } = await http.get(`/runs/${requestId}/status`) + return data +} + +/** GET /runs/{id}/workspace:获取交流文本全文 */ +export async function getWorkspace(requestId: string) { + const { data } = await http.get(`/runs/${requestId}/workspace`) + return data +} + +/** SSE /runs/{id}/stream:订阅实时协作事件 */ +export function watchRun(requestId: string) { + const es = new EventSource(`/runs/${requestId}/stream`) + return { + /** 监听器会在组件卸载时自动断开,调用者只需提供 onXxx 回调 */ + subscribe(opts: { + onWorkspace: (ws: import('@/types').Workspace) => void + onStatus: (s: string) => void + onError: (detail: string) => void + onDone: () => void + }) { + es.addEventListener('message', (ev) => { + const d: SSEEvent = JSON.parse(ev.data) + if (d.type === 'workspace') opts.onWorkspace(d.workspace) + else if (d.type === 'status') { + opts.onStatus(d.value) + if (d.value === 'done' || d.value === 'failed') opts.onDone() + } + else if (d.type === 'error') opts.onError(d.detail) + }) + es.addEventListener('error', () => { + // EventSource 自己会重连,这里只记录 + }) + }, + close() { es.close() }, + } +} + +// ── 人工检验 ───────────────────────────────────────────────────────────────── + +export async function listReviews(status?: string) { + const params = status ? { status } : {} + const { data } = await http.get('/review/queue', { params }) + return data +} + +export async function submitReview(reviewId: number, verdict: string, correction?: string) { + const { data } = await http.post(`/review/${reviewId}`, null, { + params: { verdict, correction }, + }) + return data +} + +// ── 指标 ───────────────────────────────────────────────────────────────────── + +export async function getMetrics() { + const { data } = await http.get('/api/metrics') + return data +} + +// ── 模型设置 ───────────────────────────────────────────────────────────────── + +export interface ModelSettings { + worker: { + backend: string + model: string + base_url: string + port: number + temperature: number + max_fix_attempts: number + code_timeout_s: number + } + architect: { + model: string + base_url: string + api_key: string + } + pipeline: { + fast_path: boolean + rounds_cap: number + api_token_cap: number + breach_policy: string + } +} + +/** GET /config:读取当前模型设置 */ +export async function getConfig() { + const { data } = await http.get('/config') + return data +} + +/** PUT /config:更新模型设置(部分更新) */ +export async function updateConfig(patch: Partial) { + const { data } = await http.put('/config', patch) + return data +} + +/** POST /config/reset:恢复默认设置 */ +export async function resetConfig() { + const { data } = await http.post('/config/reset') + return data +} + +// ── 模型发现 & 连接验证 ─────────────────────────────────────────────────────── + +export interface ModelInfo { + id: string + name: string +} + +/** + * GET /config/models?backend=llama_server&base_url=...&api_key=... + * 返回 { models: ModelInfo[] } 或 { error: string } + */ +export async function listModels( + backend: string, + base_url: string, + api_key: string, +) { + const params: Record = { backend } + if (base_url) params.base_url = base_url + if (api_key) params.api_key = api_key + const { data } = await http.get<{ models?: ModelInfo[]; error?: string }>( + '/config/models', { params }, + ) + return data +} + +/** + * GET /config/ping?backend=...&base_url=...&api_key=... + * 返回 { ok: boolean, detail?: string } + */ +export async function pingBackend( + backend: string, + base_url: string, + api_key: string, +) { + const params: Record = { backend } + if (base_url) params.base_url = base_url + if (api_key) params.api_key = api_key + const { data } = await http.get<{ ok: boolean; detail?: string; status_code?: number }>( + '/config/ping', { params }, + ) + return data +} + +// ── llama-server 内置管理 ───────────────────────────────────────────────────── + +export interface LlamaStatus { + running: boolean + pid?: number | null + model?: string | null + port?: number | null + base_url?: string | null + started_at?: number | null + error?: string | null +} + +export interface LocalModel { + id: string + name: string + size_mb: number + path: string +} + +export interface DownloadProgress { + url: string + dest: string + downloaded_bytes: number + total_bytes?: number | null + progress_pct: number + speed: string + eta: string + done: boolean + error?: string | null +} + +/** GET /llama/status */ +export async function getLlamaStatus() { + const { data } = await http.get('/llama/status') + return data +} + +/** GET /llama/models */ +export async function listLocalModels() { + const { data } = await http.get<{ models: LocalModel[] }>('/llama/models') + return data.models +} + +/** POST /llama/start */ +export async function startLlama(model: string, port = 8901, ngl = 99, ctx = 4096) { + const { data } = await http.post('/llama/start', null, { + params: { model, port, ngl, ctx }, + }) + return data +} + +/** POST /llama/stop */ +export async function stopLlama() { + const { data } = await http.post<{ running: boolean }>('/llama/stop') + return data +} + +/** POST /llama/download */ +export async function downloadModel(url: string, dest?: string) { + const params: Record = { url } + if (dest) params.dest = dest + const { data } = await http.post('/llama/download', null, { params }) + return data +} + +/** SSE /llama/download/stream?url=... */ +export function watchDownload(url: string) { + const es = new EventSource(`/llama/download/stream?url=${encodeURIComponent(url)}`) + return { + subscribe(opts: { onProgress: (p: DownloadProgress) => void; onDone: (p: DownloadProgress) => void }) { + es.addEventListener('message', (ev) => { + const p: DownloadProgress = JSON.parse(ev.data) + opts.onProgress(p) + if (p.done) opts.onDone(p) + }) + es.addEventListener('error', () => { es.close() }) + }, + close() { es.close() }, + } +} + +// ── 辅助:轮询直到完成(用于不需要 SSE 的场景)────────────────────────────────── + +export async function pollUntilDone( + requestId: string, + { timeout = 60_000, interval = 500 }: { timeout?: number; interval?: number } = {}, +) { + const t0 = Date.now() + while (Date.now() - t0 < timeout) { + const s = await getRunStatus(requestId) + if (s.status === 'done' || s.status === 'failed') return s + await new Promise((r) => setTimeout(r, interval)) + } + throw new Error('poll timeout') +} diff --git a/webapp/src/assets/hero.png b/webapp/src/assets/hero.png new file mode 100644 index 0000000..02251f4 Binary files /dev/null and b/webapp/src/assets/hero.png differ diff --git a/webapp/src/assets/vite.svg b/webapp/src/assets/vite.svg new file mode 100644 index 0000000..5101b67 --- /dev/null +++ b/webapp/src/assets/vite.svg @@ -0,0 +1 @@ +Vite diff --git a/webapp/src/assets/vue.svg b/webapp/src/assets/vue.svg new file mode 100644 index 0000000..770e9d3 --- /dev/null +++ b/webapp/src/assets/vue.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/webapp/src/components/HelloWorld.vue b/webapp/src/components/HelloWorld.vue new file mode 100644 index 0000000..75c05b1 --- /dev/null +++ b/webapp/src/components/HelloWorld.vue @@ -0,0 +1,95 @@ + + + diff --git a/webapp/src/main.ts b/webapp/src/main.ts new file mode 100644 index 0000000..7b5cd15 --- /dev/null +++ b/webapp/src/main.ts @@ -0,0 +1,9 @@ +import { createApp } from 'vue' +import { createPinia } from 'pinia' +import App from './App.vue' +import router from './router' + +const app = createApp(App) +app.use(createPinia()) +app.use(router) +app.mount('#app') diff --git a/webapp/src/router/index.ts b/webapp/src/router/index.ts new file mode 100644 index 0000000..6bbb399 --- /dev/null +++ b/webapp/src/router/index.ts @@ -0,0 +1,41 @@ +import { createRouter, createWebHistory } from 'vue-router' +import ChatView from '@/views/ChatView.vue' + +// Vite 构建时 base=/static/,但 FastAPI 在 /metrics 等路径提供 SPA(不在 /static/ 下), +// 所以 history 固定用 '/',避免 Vue Router 把 /metrics 当作 /static/metrics 解析导致路由不匹配。 +const router = createRouter({ + history: createWebHistory('/'), + routes: [ + { + path: '/', + redirect: '/chat', + }, + { + path: '/chat', + name: 'chat', + component: ChatView, + }, + { + path: '/collaboration', + name: 'collaboration', + component: () => import('@/views/CollaborationView.vue'), + }, + { + path: '/review', + name: 'review', + component: () => import('@/views/ReviewView.vue'), + }, + { + path: '/metrics', + name: 'metrics', + component: () => import('@/views/MetricsView.vue'), + }, + { + path: '/settings', + name: 'settings', + component: () => import('@/views/SettingsView.vue'), + }, + ], +}) + +export default router diff --git a/webapp/src/stores/chat.ts b/webapp/src/stores/chat.ts new file mode 100644 index 0000000..04f34db --- /dev/null +++ b/webapp/src/stores/chat.ts @@ -0,0 +1,61 @@ +import { defineStore } from 'pinia' +import { ref } from 'vue' +import { chat, getRunStatus, pollUntilDone } from '@/api' +import type { ChatInitResponse, RunStatus, Workspace } from '@/types' + +// 单次会话记录 +export interface ChatSession { + requestId: string + query: string + init: ChatInitResponse + status: RunStatus | null + workspace: Workspace | null + error: string | null +} + +export const useChatStore = defineStore('chat', () => { + const sessions = ref([]) + const currentId = ref(null) + + const current = () => sessions.value.find((s) => s.requestId === currentId.value) ?? null + + async function sendQuery(query: string) { + const init = await chat(query) + const session: ChatSession = { + requestId: init.request_id, + query, + init, + status: null, + workspace: null, + error: null, + } + sessions.value.unshift(session) + currentId.value = init.request_id + return session + } + + async function pollStatus(requestId: string) { + const s = await getRunStatus(requestId) + const session = sessions.value.find((x) => x.requestId === requestId) + if (session) session.status = s + return s + } + + async function waitForDone(requestId: string) { + const s = await pollUntilDone(requestId) + const session = sessions.value.find((x) => x.requestId === requestId) + if (session) session.status = s + return s + } + + function updateWorkspace(requestId: string, ws: Workspace) { + const session = sessions.value.find((x) => x.requestId === requestId) + if (session) session.workspace = ws + } + + function setCurrent(id: string | null) { + currentId.value = id + } + + return { sessions, currentId, current, sendQuery, pollStatus, waitForDone, updateWorkspace, setCurrent } +}) diff --git a/webapp/src/style.css b/webapp/src/style.css new file mode 100644 index 0000000..527d4fb --- /dev/null +++ b/webapp/src/style.css @@ -0,0 +1,296 @@ +:root { + --text: #6b6375; + --text-h: #08060d; + --bg: #fff; + --border: #e5e4e7; + --code-bg: #f4f3ec; + --accent: #aa3bff; + --accent-bg: rgba(170, 59, 255, 0.1); + --accent-border: rgba(170, 59, 255, 0.5); + --social-bg: rgba(244, 243, 236, 0.5); + --shadow: + rgba(0, 0, 0, 0.1) 0 10px 15px -3px, rgba(0, 0, 0, 0.05) 0 4px 6px -2px; + + --sans: system-ui, 'Segoe UI', Roboto, sans-serif; + --heading: system-ui, 'Segoe UI', Roboto, sans-serif; + --mono: ui-monospace, Consolas, monospace; + + font: 18px/145% var(--sans); + letter-spacing: 0.18px; + color-scheme: light dark; + color: var(--text); + background: var(--bg); + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + + @media (max-width: 1024px) { + font-size: 16px; + } +} + +@media (prefers-color-scheme: dark) { + :root { + --text: #9ca3af; + --text-h: #f3f4f6; + --bg: #16171d; + --border: #2e303a; + --code-bg: #1f2028; + --accent: #c084fc; + --accent-bg: rgba(192, 132, 252, 0.15); + --accent-border: rgba(192, 132, 252, 0.5); + --social-bg: rgba(47, 48, 58, 0.5); + --shadow: + rgba(0, 0, 0, 0.4) 0 10px 15px -3px, rgba(0, 0, 0, 0.25) 0 4px 6px -2px; + } + + #social .button-icon { + filter: invert(1) brightness(2); + } +} + +body { + margin: 0; +} + +h1, +h2 { + font-family: var(--heading); + font-weight: 500; + color: var(--text-h); +} + +h1 { + font-size: 56px; + letter-spacing: -1.68px; + margin: 32px 0; + @media (max-width: 1024px) { + font-size: 36px; + margin: 20px 0; + } +} +h2 { + font-size: 24px; + line-height: 118%; + letter-spacing: -0.24px; + margin: 0 0 8px; + @media (max-width: 1024px) { + font-size: 20px; + } +} +p { + margin: 0; +} + +code, +.counter { + font-family: var(--mono); + display: inline-flex; + border-radius: 4px; + color: var(--text-h); +} + +code { + font-size: 15px; + line-height: 135%; + padding: 4px 8px; + background: var(--code-bg); +} + +.counter { + font-size: 16px; + padding: 5px 10px; + border-radius: 5px; + color: var(--accent); + background: var(--accent-bg); + border: 2px solid transparent; + transition: border-color 0.3s; + margin-bottom: 24px; + + &:hover { + border-color: var(--accent-border); + } + &:focus-visible { + outline: 2px solid var(--accent); + outline-offset: 2px; + } +} + +.hero { + position: relative; + + .base, + .framework, + .vite { + inset-inline: 0; + margin: 0 auto; + } + + .base { + width: 170px; + position: relative; + z-index: 0; + } + + .framework, + .vite { + position: absolute; + } + + .framework { + z-index: 1; + top: 34px; + height: 28px; + transform: perspective(2000px) rotateZ(300deg) rotateX(44deg) rotateY(39deg) + scale(1.4); + } + + .vite { + z-index: 0; + top: 107px; + height: 26px; + width: auto; + transform: perspective(2000px) rotateZ(300deg) rotateX(40deg) rotateY(39deg) + scale(0.8); + } +} + +#app { + width: 1126px; + max-width: 100%; + margin: 0 auto; + text-align: center; + border-inline: 1px solid var(--border); + min-height: 100svh; + display: flex; + flex-direction: column; + box-sizing: border-box; +} + +#center { + display: flex; + flex-direction: column; + gap: 25px; + place-content: center; + place-items: center; + flex-grow: 1; + + @media (max-width: 1024px) { + padding: 32px 20px 24px; + gap: 18px; + } +} + +#next-steps { + display: flex; + border-top: 1px solid var(--border); + text-align: left; + + & > div { + flex: 1 1 0; + padding: 32px; + @media (max-width: 1024px) { + padding: 24px 20px; + } + } + + .icon { + margin-bottom: 16px; + width: 22px; + height: 22px; + } + + @media (max-width: 1024px) { + flex-direction: column; + text-align: center; + } +} + +#docs { + border-right: 1px solid var(--border); + + @media (max-width: 1024px) { + border-right: none; + border-bottom: 1px solid var(--border); + } +} + +#next-steps ul { + list-style: none; + padding: 0; + display: flex; + gap: 8px; + margin: 32px 0 0; + + .logo { + height: 18px; + } + + a { + color: var(--text-h); + font-size: 16px; + border-radius: 6px; + background: var(--social-bg); + display: flex; + padding: 6px 12px; + align-items: center; + gap: 8px; + text-decoration: none; + transition: box-shadow 0.3s; + + &:hover { + box-shadow: var(--shadow); + } + .button-icon { + height: 18px; + width: 18px; + } + } + + @media (max-width: 1024px) { + margin-top: 20px; + flex-wrap: wrap; + justify-content: center; + + li { + flex: 1 1 calc(50% - 8px); + } + + a { + width: 100%; + justify-content: center; + box-sizing: border-box; + } + } +} + +#spacer { + height: 88px; + border-top: 1px solid var(--border); + @media (max-width: 1024px) { + height: 48px; + } +} + +.ticks { + position: relative; + width: 100%; + + &::before, + &::after { + content: ''; + position: absolute; + top: -4.5px; + border: 5px solid transparent; + } + + &::before { + left: 0; + border-left-color: var(--border); + } + &::after { + right: 0; + border-right-color: var(--border); + } +} diff --git a/webapp/src/types/index.ts b/webapp/src/types/index.ts new file mode 100644 index 0000000..21f3453 --- /dev/null +++ b/webapp/src/types/index.ts @@ -0,0 +1,117 @@ +// ── 协作任务状态 ────────────────────────────────────────────────────────────── + +/** POST /chat 立即返回 */ +export interface ChatInitResponse { + request_id: string + status: 'pending' +} + +/** GET /runs/{id}/status 返回 */ +export interface RunStatus { + request_id: string + status: 'pending' | 'running' | 'done' | 'failed' // JobStore 任务状态 + ws_state: string | null // workspace.json 内 state + started_at: number + finished_at: number + error: string | null + // PipelineResult 等效字段(扁平时 TaskInfo) + response: string | null + pipeline_status: string | null // done | fast_path | escalated | failed + fast_path: boolean + rounds_used: number + api_input_tokens: number + api_output_tokens: number + cost_est: number + model_used: string | null + latency_ms: number | null + route: string[] + workspace_path: string | null +} + +// ── SSE 事件 ───────────────────────────────────────────────────────────────── + +export type SSEEvent = + | { type: 'workspace'; version: number; state: string; request_id: string; workspace: Workspace } + | { type: 'status'; value: string; request_id: string } + | { type: 'error'; detail: string } + +// ── 交流文本(workspace.json)──────────────────────────────────────────────── + +export interface Workspace { + request_id: string + query: string + brief: Brief | null + plan: PlanStep[] + progress: ProgressStep[] + issues: Issue[] + decisions: Decision[] + archive: string[] + meta: WsMeta + status?: string + error?: string +} + +export interface Brief { + goal: string + tags: string[] + domain: string + constraints: string[] +} + +export interface PlanStep { + id: string + task: string + deps: string[] + status?: string +} + +export interface ProgressStep { + step: string + status: 'pending' | 'running' | 'done' + artifact?: string + note?: string +} + +export interface Issue { + id: string + step: string + description: string + suggestion?: string +} + +export interface Decision { + ref: string + reply: string + patch_plan?: PlanStep[] +} + +export interface WsMeta { + state: 'draft' | 'in_progress' | 'reviewing' | 'done' | 'failed' + round: number + rounds_cap: number + version?: number + api_input_tokens: number + api_output_tokens: number +} + +// ── 人工检验 ───────────────────────────────────────────────────────────────── + +export interface ReviewItem { + id: number + request_id: string + query: string + response: string + verdict: 'pending' | 'approved' | 'rejected' + tags: string[] + created_at: string + correction?: string +} + +// ── 指标 ───────────────────────────────────────────────────────────────────── + +export interface Metrics { + router: Record + cache: Record + v2?: Record + review?: { pending: number; total: number } +} diff --git a/webapp/src/views/ChatView.vue b/webapp/src/views/ChatView.vue new file mode 100644 index 0000000..e939cd8 --- /dev/null +++ b/webapp/src/views/ChatView.vue @@ -0,0 +1,338 @@ + + + + + diff --git a/webapp/src/views/CollaborationView.vue b/webapp/src/views/CollaborationView.vue new file mode 100644 index 0000000..728c411 --- /dev/null +++ b/webapp/src/views/CollaborationView.vue @@ -0,0 +1,450 @@ + + + + + diff --git a/webapp/src/views/MetricsView.vue b/webapp/src/views/MetricsView.vue new file mode 100644 index 0000000..e3879e9 --- /dev/null +++ b/webapp/src/views/MetricsView.vue @@ -0,0 +1,171 @@ + + + + + diff --git a/webapp/src/views/ReviewView.vue b/webapp/src/views/ReviewView.vue new file mode 100644 index 0000000..f847bdd --- /dev/null +++ b/webapp/src/views/ReviewView.vue @@ -0,0 +1,196 @@ +