@tonyclaw/llm-inspector 1.14.1 → 1.14.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,5 @@
1
1
  import { r as reactExports, j as jsxRuntimeExports, a as React } from "../_libs/react.mjs";
2
- import { C as CapturedLogSchema, a as parseRequest, s as stripClaudeCodeBillingHeader, R as RuntimeConfigSchema, P as ProviderConfigSchema, p as parseOpenAIResponse, I as InspectorResponseSchema, S as StreamingChunkSchema$1 } from "./router-B8X3GXM2.mjs";
2
+ import { C as CapturedLogSchema, a as parseRequest, s as stripClaudeCodeBillingHeader, R as RuntimeConfigSchema, c as createPendingProviderTestResults, P as ProviderTestResultsSchema, b as createFailedProviderTestResults, d as ProviderConfigSchema, p as parseOpenAIResponse, I as InspectorResponseSchema } from "./router-Cz7UcQ5N.mjs";
3
3
  import { u as useSWR, a as useSWRConfig } from "../_libs/swr.mjs";
4
4
  import { u as useVirtualizer } from "../_libs/tanstack__react-virtual.mjs";
5
5
  import { J as JSZip } from "../_libs/jszip.mjs";
@@ -12,7 +12,7 @@ import { R as Root2, T as Trigger, I as Icon, V as Value, P as Portal, C as Cont
12
12
  import "../_libs/modelcontextprotocol__server.mjs";
13
13
  import { D as Download, L as LayoutGrid, a as List, G as GitCompareArrows, X, S as Settings, C as ChevronDown, b as Check, R as RotateCcw, U as Upload, P as Plus, c as Copy, d as CircleAlert, e as ChevronUp, f as ChevronRight, g as Clock, M as MessageSquare, Z as Zap, h as LoaderCircle, W as Wrench, i as Globe, j as User, F as FileTerminal, k as Radio, l as Rows3, m as Columns2, n as Minus, o as Pencil, E as Equal, p as EyeOff, q as Eye, r as ExternalLink, s as RotateCw, T as Trash2, A as ArrowUp, t as ArrowDown, u as TriangleAlert, v as CircleCheckBig, w as CircleStop, x as CircleQuestionMark, y as Server, z as Gauge, B as Lock, H as Wifi, I as WifiOff, J as ChevronsUp, K as ChevronsDown, N as Brain, O as Terminal } from "../_libs/lucide-react.mjs";
14
14
  import { M as Markdown } from "../_libs/react-markdown.mjs";
15
- import { a as array, b as string, u as union, d as object, l as literal, n as number, c as boolean, r as record, _ as _enum } from "../_libs/zod.mjs";
15
+ import { a as array, b as string, u as union, d as object, l as literal, n as number, c as boolean } from "../_libs/zod.mjs";
16
16
  import { R as Root2$1, L as List$1, T as Trigger$2, C as Content$1 } from "../_libs/radix-ui__react-tabs.mjs";
17
17
  import { S as Slot } from "../_libs/radix-ui__react-slot.mjs";
18
18
  import { P as Provider, R as Root3, T as Trigger$3, a as Portal$2, C as Content2$1, A as Arrow2 } from "../_libs/radix-ui__react-tooltip.mjs";
@@ -276,7 +276,7 @@ async function exportLogsAsZip(logs) {
276
276
  document.body.removeChild(anchor);
277
277
  URL.revokeObjectURL(url);
278
278
  }
279
- const version = "1.14.1";
279
+ const version = "1.14.3";
280
280
  const packageJson = {
281
281
  version
282
282
  };
@@ -2984,6 +2984,19 @@ function TestStatus({ result }) {
2984
2984
  ] })
2985
2985
  ] });
2986
2986
  }
2987
+ function formatTimeAgo(isoString) {
2988
+ const now = Date.now();
2989
+ const then = new Date(isoString).getTime();
2990
+ const diffMs = now - then;
2991
+ const diffSec = Math.floor(diffMs / 1e3);
2992
+ if (diffSec < 60) return "just now";
2993
+ const diffMin = Math.floor(diffSec / 60);
2994
+ if (diffMin < 60) return `${diffMin}m ago`;
2995
+ const diffHr = Math.floor(diffMin / 60);
2996
+ if (diffHr < 24) return `${diffHr}h ago`;
2997
+ const diffDay = Math.floor(diffHr / 24);
2998
+ return `${diffDay}d ago`;
2999
+ }
2987
3000
  function ProviderCard({
2988
3001
  provider,
2989
3002
  testResults,
@@ -2996,6 +3009,27 @@ function ProviderCard({
2996
3009
  }) {
2997
3010
  const [showApiKey, setShowApiKey] = reactExports.useState(false);
2998
3011
  const [copied, setCopied] = reactExports.useState(false);
3012
+ const [showModelResults, setShowModelResults] = reactExports.useState(false);
3013
+ const hasLoggedRef = reactExports.useRef(null);
3014
+ const lastTestedAtRef = reactExports.useRef(void 0);
3015
+ if (testResults?.testedAt !== void 0 && testResults.testedAt !== lastTestedAtRef.current) {
3016
+ lastTestedAtRef.current = testResults.testedAt;
3017
+ hasLoggedRef.current = null;
3018
+ }
3019
+ const handleToggleModelResults = reactExports.useCallback(() => {
3020
+ setShowModelResults((v) => {
3021
+ const next = !v;
3022
+ if (next && hasLoggedRef.current === null && testResults?.models !== void 0) {
3023
+ hasLoggedRef.current = testResults.testedAt ?? "";
3024
+ void fetch(`/api/providers/${provider.id}/test/log`, {
3025
+ method: "POST",
3026
+ headers: { "Content-Type": "application/json" },
3027
+ body: JSON.stringify(testResults)
3028
+ });
3029
+ }
3030
+ return next;
3031
+ });
3032
+ }, [provider.id, testResults]);
2999
3033
  function handleCopy() {
3000
3034
  navigator.clipboard.writeText(provider.apiKey).catch(() => {
3001
3035
  });
@@ -3012,7 +3046,7 @@ function ProviderCard({
3012
3046
  children: [
3013
3047
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-start justify-between gap-2", children: [
3014
3048
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 min-w-0", children: [
3015
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-medium truncate", children: provider.model !== void 0 && provider.model !== "" ? `${provider.model} (${provider.name})` : provider.name }),
3049
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-medium truncate", children: provider.name }),
3016
3050
  provider.source === "company" && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-xs px-1.5 py-0.5 rounded bg-blue-100 text-blue-700 dark:bg-blue-900/30 dark:text-blue-400 shrink-0", children: "公司" }),
3017
3051
  provider.source === "personal" && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-xs px-1.5 py-0.5 rounded bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400 shrink-0", children: "个人" })
3018
3052
  ] }),
@@ -3031,6 +3065,7 @@ function ProviderCard({
3031
3065
  }
3032
3066
  )
3033
3067
  ] }),
3068
+ provider.models !== void 0 && provider.models.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex flex-wrap gap-1", children: provider.models.map((m) => /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-xs px-1.5 py-0.5 rounded bg-muted text-muted-foreground", children: m }, m)) }),
3034
3069
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [
3035
3070
  /* @__PURE__ */ jsxRuntimeExports.jsx("code", { className: "text-xs text-muted-foreground bg-muted px-2 py-1 rounded flex-1 truncate", children: showApiKey ? provider.apiKey : maskApiKey(provider.apiKey) }),
3036
3071
  /* @__PURE__ */ jsxRuntimeExports.jsx(
@@ -3070,6 +3105,41 @@ function ProviderCard({
3070
3105
  ] }),
3071
3106
  testResults && /* @__PURE__ */ jsxRuntimeExports.jsx(TestStatus, { result: testResults.openai.nonStreaming })
3072
3107
  ] }),
3108
+ testResults?.testedAt !== void 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-xs text-muted-foreground flex items-center gap-1", children: [
3109
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Clock, { className: "size-3" }),
3110
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { children: [
3111
+ "Tested ",
3112
+ formatTimeAgo(testResults.testedAt)
3113
+ ] })
3114
+ ] }),
3115
+ testResults?.models !== void 0 && Object.keys(testResults.models).length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "border-t pt-2", children: [
3116
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
3117
+ "button",
3118
+ {
3119
+ type: "button",
3120
+ onClick: handleToggleModelResults,
3121
+ className: "text-xs text-muted-foreground hover:text-foreground transition-colors flex items-center gap-1",
3122
+ children: [
3123
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-mono", children: showModelResults ? "▾" : "▸" }),
3124
+ "Model Test Results (",
3125
+ Object.keys(testResults.models).length,
3126
+ ")"
3127
+ ]
3128
+ }
3129
+ ),
3130
+ showModelResults && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mt-2 overflow-x-auto", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("table", { className: "w-full text-xs border-collapse", children: [
3131
+ /* @__PURE__ */ jsxRuntimeExports.jsx("thead", { children: /* @__PURE__ */ jsxRuntimeExports.jsxs("tr", { className: "border-b border-border", children: [
3132
+ /* @__PURE__ */ jsxRuntimeExports.jsx("th", { className: "text-left py-1 px-2 font-medium text-muted-foreground", children: "Model" }),
3133
+ provider.anthropicBaseUrl !== void 0 && provider.anthropicBaseUrl !== "" && /* @__PURE__ */ jsxRuntimeExports.jsx("th", { className: "text-left py-1 px-2 font-medium text-muted-foreground", children: "Anthropic" }),
3134
+ provider.openaiBaseUrl !== void 0 && provider.openaiBaseUrl !== "" && /* @__PURE__ */ jsxRuntimeExports.jsx("th", { className: "text-left py-1 px-2 font-medium text-muted-foreground", children: "OpenAI" })
3135
+ ] }) }),
3136
+ /* @__PURE__ */ jsxRuntimeExports.jsx("tbody", { children: Object.entries(testResults.models).map(([modelName, modelResult]) => /* @__PURE__ */ jsxRuntimeExports.jsxs("tr", { className: "border-b border-border/50 last:border-0", children: [
3137
+ /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "py-1 px-2 font-medium", children: modelName }),
3138
+ provider.anthropicBaseUrl !== void 0 && provider.anthropicBaseUrl !== "" && /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "py-1 px-2", children: /* @__PURE__ */ jsxRuntimeExports.jsx(TestStatus, { result: modelResult.anthropic.nonStreaming }) }),
3139
+ provider.openaiBaseUrl !== void 0 && provider.openaiBaseUrl !== "" && /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "py-1 px-2", children: /* @__PURE__ */ jsxRuntimeExports.jsx(TestStatus, { result: modelResult.openai.nonStreaming }) })
3140
+ ] }, modelName)) })
3141
+ ] }) })
3142
+ ] }),
3073
3143
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex gap-2 pt-1 border-t", children: [
3074
3144
  onTest !== void 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs(
3075
3145
  Button,
@@ -3143,11 +3213,14 @@ const MINIMAX_MODELS = [
3143
3213
  ];
3144
3214
  const ALIBABA_MODELS = ["glm-5", "glm-5.1", "qwen3.6-plus", "qwen3.7-max"];
3145
3215
  function ProviderForm({ provider, onSubmit, onCancel }) {
3146
- const [name, setName] = reactExports.useState(provider?.name ?? "Provider Name");
3216
+ const [name, setName] = reactExports.useState(provider?.name ?? "");
3147
3217
  const [apiKey, setApiKey] = reactExports.useState(provider?.apiKey ?? "");
3148
3218
  const [showApiKey, setShowApiKey] = reactExports.useState(false);
3149
3219
  const [copied, setCopied] = reactExports.useState(false);
3150
- const [model, setModel] = reactExports.useState(provider?.model ?? "");
3220
+ const initialModels = provider?.models;
3221
+ const [models, setModels] = reactExports.useState(
3222
+ initialModels !== void 0 && initialModels.length > 0 ? initialModels : [""]
3223
+ );
3151
3224
  const [activeTab, setActiveTab] = reactExports.useState("anthropic");
3152
3225
  const [anthropicBaseUrl, setAnthropicBaseUrl] = reactExports.useState(provider?.anthropicBaseUrl ?? "");
3153
3226
  const [openaiBaseUrl, setOpenaiBaseUrl] = reactExports.useState(provider?.openaiBaseUrl ?? "");
@@ -3155,6 +3228,21 @@ function ProviderForm({ provider, onSubmit, onCancel }) {
3155
3228
  const [source, setSource] = reactExports.useState(provider?.source);
3156
3229
  const [errors, setErrors] = reactExports.useState({});
3157
3230
  const [isSubmitting, setIsSubmitting] = reactExports.useState(false);
3231
+ const [openModelDropdown, setOpenModelDropdown] = reactExports.useState(null);
3232
+ const modelRowRefs = reactExports.useRef([]);
3233
+ reactExports.useEffect(() => {
3234
+ if (openModelDropdown === null) return;
3235
+ const index = openModelDropdown;
3236
+ function handleClick(e) {
3237
+ if (!(e.target instanceof Node)) return;
3238
+ const ref = modelRowRefs.current[index];
3239
+ if (ref !== null && ref !== void 0 && !ref.contains(e.target)) {
3240
+ setOpenModelDropdown(null);
3241
+ }
3242
+ }
3243
+ document.addEventListener("mousedown", handleClick);
3244
+ return () => document.removeEventListener("mousedown", handleClick);
3245
+ }, [openModelDropdown]);
3158
3246
  const [manualAnthropicUrlOverride, setManualAnthropicUrlOverride] = reactExports.useState(false);
3159
3247
  const [manualOpenaiUrlOverride, setManualOpenaiUrlOverride] = reactExports.useState(false);
3160
3248
  const isMiniMax = name.toLowerCase().includes("minimax");
@@ -3169,7 +3257,7 @@ function ProviderForm({ provider, onSubmit, onCancel }) {
3169
3257
  if (provider) {
3170
3258
  setName(provider.name);
3171
3259
  setApiKey(provider.apiKey);
3172
- setModel(provider.model ?? "");
3260
+ setModels((provider.models?.length ?? 0) > 0 ? provider.models : [""]);
3173
3261
  setAnthropicBaseUrl(provider.anthropicBaseUrl ?? "");
3174
3262
  setOpenaiBaseUrl(provider.openaiBaseUrl ?? "");
3175
3263
  setApiDocsUrl(provider.apiDocsUrl ?? "");
@@ -3190,11 +3278,11 @@ function ProviderForm({ provider, onSubmit, onCancel }) {
3190
3278
  if (preset.apiDocsUrl !== void 0 && !apiDocsUrl) {
3191
3279
  setApiDocsUrl(preset.apiDocsUrl);
3192
3280
  }
3193
- if (keyword === "minimax" && !model) {
3194
- setModel(MINIMAX_MODELS[0] ?? "");
3281
+ if (keyword === "minimax" && models.length === 1 && models[0] === "") {
3282
+ setModels([MINIMAX_MODELS[0] ?? ""]);
3195
3283
  }
3196
- if (keyword === "alibaba" && !model) {
3197
- setModel(ALIBABA_MODELS[0] ?? "");
3284
+ if (keyword === "alibaba" && models.length === 1 && models[0] === "") {
3285
+ setModels([ALIBABA_MODELS[0] ?? ""]);
3198
3286
  }
3199
3287
  break;
3200
3288
  }
@@ -3208,8 +3296,8 @@ function ProviderForm({ provider, onSubmit, onCancel }) {
3208
3296
  if (!apiKey.trim()) {
3209
3297
  newErrors.apiKey = "API key is required";
3210
3298
  }
3211
- if (!model.trim()) {
3212
- newErrors.model = "Model is required";
3299
+ if (models.length === 0 || models.every((m) => !m.trim())) {
3300
+ newErrors.models = "At least one model is required";
3213
3301
  }
3214
3302
  if (anthropicBaseUrl.trim() && !isValidUrl(anthropicBaseUrl.trim())) {
3215
3303
  newErrors.anthropicBaseUrl = "Invalid URL format";
@@ -3239,7 +3327,7 @@ function ProviderForm({ provider, onSubmit, onCancel }) {
3239
3327
  onSubmit({
3240
3328
  name: name.trim(),
3241
3329
  apiKey: apiKey.trim(),
3242
- model: model.trim() || void 0,
3330
+ models: models.map((m) => m.trim()).filter((m) => m !== ""),
3243
3331
  anthropicBaseUrl: anthropicBaseUrl.trim() || void 0,
3244
3332
  openaiBaseUrl: openaiBaseUrl.trim() || void 0,
3245
3333
  apiDocsUrl: apiDocsUrl.trim() || void 0,
@@ -3262,7 +3350,7 @@ function ProviderForm({ provider, onSubmit, onCancel }) {
3262
3350
  type: "text",
3263
3351
  value: name,
3264
3352
  onChange: (e) => setName(e.target.value),
3265
- placeholder: "Model Name",
3353
+ placeholder: "Provider Name",
3266
3354
  className: "w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:border-ring focus-visible:outline-ring focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50"
3267
3355
  }
3268
3356
  ),
@@ -3333,31 +3421,107 @@ function ProviderForm({ provider, onSubmit, onCancel }) {
3333
3421
  errors.apiKey !== void 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-destructive", children: errors.apiKey })
3334
3422
  ] }),
3335
3423
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-2", children: [
3336
- /* @__PURE__ */ jsxRuntimeExports.jsxs("label", { htmlFor: "provider-model", className: "text-sm font-medium", children: [
3337
- "Model ",
3424
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("label", { className: "text-sm font-medium", children: [
3425
+ "Models ",
3338
3426
  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-destructive", children: "*" })
3339
3427
  ] }),
3340
- isMiniMax || isAlibaba ? /* @__PURE__ */ jsxRuntimeExports.jsx(
3341
- "select",
3428
+ models.map((m, i) => /* @__PURE__ */ jsxRuntimeExports.jsxs(
3429
+ "div",
3342
3430
  {
3343
- id: "provider-model",
3344
- value: model,
3345
- onChange: (e) => setModel(e.target.value),
3346
- className: "w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background focus-visible:border-ring focus-visible:outline-ring focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50",
3347
- children: (isMiniMax ? MINIMAX_MODELS : ALIBABA_MODELS).map((m) => /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: m, children: m }, m))
3348
- }
3349
- ) : /* @__PURE__ */ jsxRuntimeExports.jsx(
3350
- "input",
3431
+ ref: (el) => {
3432
+ modelRowRefs.current[i] = el;
3433
+ },
3434
+ className: "flex items-center gap-2",
3435
+ children: [
3436
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "relative flex-1", children: [
3437
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
3438
+ "input",
3439
+ {
3440
+ type: "text",
3441
+ value: m,
3442
+ onChange: (e) => {
3443
+ setModels((prev) => {
3444
+ const next = [...prev];
3445
+ next[i] = e.target.value;
3446
+ return next;
3447
+ });
3448
+ },
3449
+ placeholder: isMiniMax || isAlibaba ? "Type or select a model..." : "Model name",
3450
+ className: "w-full rounded-md border border-input bg-background px-4 py-3 pr-8 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:border-ring focus-visible:outline-ring focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50"
3451
+ }
3452
+ ),
3453
+ (isMiniMax || isAlibaba) && /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
3454
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
3455
+ "button",
3456
+ {
3457
+ type: "button",
3458
+ onClick: () => setOpenModelDropdown(openModelDropdown === i ? null : i),
3459
+ className: "absolute right-1 top-1/2 -translate-y-1/2 text-muted-foreground hover:text-foreground transition-colors p-1 z-10",
3460
+ "aria-label": "Show model suggestions",
3461
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronDown, { className: "size-4" })
3462
+ }
3463
+ ),
3464
+ openModelDropdown === i && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute left-0 right-0 top-full mt-1 z-50 bg-popover border border-border rounded-md shadow-md max-h-48 overflow-y-auto", children: (isMiniMax ? MINIMAX_MODELS : ALIBABA_MODELS).map((opt) => /* @__PURE__ */ jsxRuntimeExports.jsx(
3465
+ "button",
3466
+ {
3467
+ type: "button",
3468
+ onClick: () => {
3469
+ setModels((prev) => {
3470
+ const next = [...prev];
3471
+ next[i] = opt;
3472
+ return next;
3473
+ });
3474
+ setOpenModelDropdown(null);
3475
+ },
3476
+ className: "w-full text-left px-3 py-2 text-sm hover:bg-muted transition-colors",
3477
+ children: opt
3478
+ },
3479
+ opt
3480
+ )) })
3481
+ ] })
3482
+ ] }),
3483
+ models.length > 1 && /* @__PURE__ */ jsxRuntimeExports.jsx(
3484
+ "button",
3485
+ {
3486
+ type: "button",
3487
+ onClick: () => setModels((prev) => prev.filter((_, idx) => idx !== i)),
3488
+ className: "text-muted-foreground hover:text-destructive transition-colors p-1 shrink-0",
3489
+ "aria-label": "Remove model",
3490
+ children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
3491
+ "svg",
3492
+ {
3493
+ xmlns: "http://www.w3.org/2000/svg",
3494
+ width: "16",
3495
+ height: "16",
3496
+ viewBox: "0 0 24 24",
3497
+ fill: "none",
3498
+ stroke: "currentColor",
3499
+ strokeWidth: "2",
3500
+ strokeLinecap: "round",
3501
+ strokeLinejoin: "round",
3502
+ children: [
3503
+ /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M3 6h18" }),
3504
+ /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6" }),
3505
+ /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2" })
3506
+ ]
3507
+ }
3508
+ )
3509
+ }
3510
+ )
3511
+ ]
3512
+ },
3513
+ i
3514
+ )),
3515
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
3516
+ "button",
3351
3517
  {
3352
- id: "provider-model",
3353
- type: "text",
3354
- value: model,
3355
- onChange: (e) => setModel(e.target.value),
3356
- placeholder: "",
3357
- className: "w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:border-ring focus-visible:outline-ring focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50"
3518
+ type: "button",
3519
+ onClick: () => setModels((prev) => [...prev, ""]),
3520
+ className: "text-xs text-primary hover:underline flex items-center gap-1",
3521
+ children: "+ Add Model"
3358
3522
  }
3359
3523
  ),
3360
- errors.model !== void 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-destructive", children: errors.model })
3524
+ errors.models !== void 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-destructive", children: errors.models })
3361
3525
  ] }),
3362
3526
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-2", children: [
3363
3527
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex gap-1 border-b border-border", children: [
@@ -3441,83 +3605,6 @@ function ProviderForm({ provider, onSubmit, onCancel }) {
3441
3605
  ] })
3442
3606
  ] });
3443
3607
  }
3444
- const ProviderTestErrorTypeSchema = _enum([
3445
- "timeout",
3446
- "network_unreachable",
3447
- "connection_refused",
3448
- "auth_failed",
3449
- "rate_limited",
3450
- "server_error",
3451
- "invalid_response",
3452
- "unknown"
3453
- ]);
3454
- const ProviderTestErrorSchema = object({
3455
- message: string(),
3456
- type: ProviderTestErrorTypeSchema,
3457
- details: string().optional(),
3458
- hint: string().optional()
3459
- });
3460
- const ProviderTestContentSchema = object({
3461
- type: _enum(["text", "thinking"]),
3462
- text: string().optional(),
3463
- thinking: string().optional()
3464
- });
3465
- const ProviderTestResultSchema = object({
3466
- success: boolean(),
3467
- error: ProviderTestErrorSchema.optional(),
3468
- model: string().optional(),
3469
- inputTokens: number().optional(),
3470
- outputTokens: number().optional(),
3471
- cacheCreationInputTokens: number().optional(),
3472
- cacheReadInputTokens: number().optional(),
3473
- latencyMs: number().optional(),
3474
- content: array(ProviderTestContentSchema).optional(),
3475
- rawResponse: string().optional(),
3476
- streaming: boolean().optional(),
3477
- streamingChunks: object({
3478
- chunks: array(StreamingChunkSchema$1),
3479
- truncated: boolean().default(false)
3480
- }).optional(),
3481
- requestHeaders: record(string(), string()).optional()
3482
- });
3483
- const ProviderTestStateSchema = union([
3484
- ProviderTestResultSchema,
3485
- object({ notConfigured: literal(true) }),
3486
- object({ testing: literal(true) })
3487
- ]);
3488
- const ProviderFormatTestResultsSchema = object({
3489
- nonStreaming: ProviderTestStateSchema,
3490
- streaming: ProviderTestStateSchema
3491
- });
3492
- const ProviderTestResultsSchema = object({
3493
- anthropic: ProviderFormatTestResultsSchema,
3494
- openai: ProviderFormatTestResultsSchema
3495
- });
3496
- function createPendingProviderTestResults() {
3497
- return {
3498
- anthropic: {
3499
- nonStreaming: { testing: true },
3500
- streaming: { testing: true }
3501
- },
3502
- openai: {
3503
- nonStreaming: { testing: true },
3504
- streaming: { testing: true }
3505
- }
3506
- };
3507
- }
3508
- function createFailedProviderTestResults(message, type) {
3509
- const createFormatResult = () => ({
3510
- nonStreaming: {
3511
- success: false,
3512
- error: { message, type }
3513
- },
3514
- streaming: { notConfigured: true }
3515
- });
3516
- return {
3517
- anthropic: createFormatResult(),
3518
- openai: createFormatResult()
3519
- };
3520
- }
3521
3608
  const ConfigPathsResponseSchema = object({
3522
3609
  providerConfig: string()
3523
3610
  });
@@ -3533,7 +3620,7 @@ function createProviderPayload(data) {
3533
3620
  return {
3534
3621
  name: data.name,
3535
3622
  apiKey: data.apiKey,
3536
- model: data.model,
3623
+ models: data.models,
3537
3624
  anthropicBaseUrl: (data.anthropicBaseUrl?.length ?? 0) > 0 ? data.anthropicBaseUrl : void 0,
3538
3625
  openaiBaseUrl: (data.openaiBaseUrl?.length ?? 0) > 0 ? data.openaiBaseUrl : void 0,
3539
3626
  apiDocsUrl: (data.apiDocsUrl?.length ?? 0) > 0 ? data.apiDocsUrl : void 0,
@@ -3566,7 +3653,16 @@ function ProvidersPanel({
3566
3653
  const listScrollRef = reactExports.useRef(null);
3567
3654
  const highlightTimeoutRef = reactExports.useRef(null);
3568
3655
  const providers = externalProviders ?? [];
3569
- const filteredProviders = sourceFilter === "all" ? providers : providers.filter((p) => p.source === sourceFilter);
3656
+ const filteredProviders = reactExports.useMemo(() => {
3657
+ const filtered = sourceFilter === "all" ? providers : providers.filter((p) => p.source === sourceFilter);
3658
+ if (sourceFilter === "all") {
3659
+ return [...filtered].sort((a, b) => {
3660
+ const order = { personal: 0, company: 1 };
3661
+ return (order[a.source ?? ""] ?? 2) - (order[b.source ?? ""] ?? 2);
3662
+ });
3663
+ }
3664
+ return filtered;
3665
+ }, [providers, sourceFilter]);
3570
3666
  const testResults = externalTestResults ?? internalTestResults;
3571
3667
  const testingProviders = externalTestingProviders ?? internalTestingProviders;
3572
3668
  const testingTimeLeft = externalTestingTimeLeft ?? internalTestingTimeLeft;
@@ -3731,7 +3827,7 @@ function ProvidersPanel({
3731
3827
  setEditingProvider(void 0);
3732
3828
  triggerHighlight(updated.id);
3733
3829
  refreshProviders();
3734
- const criticalFieldsChanged = (data.apiKey ?? "") !== (editingProvider.apiKey ?? "") || (data.model ?? "") !== (editingProvider.model ?? "") || (data.anthropicBaseUrl ?? "") !== (editingProvider.anthropicBaseUrl ?? "") || (data.openaiBaseUrl ?? "") !== (editingProvider.openaiBaseUrl ?? "");
3830
+ const criticalFieldsChanged = (data.apiKey ?? "") !== (editingProvider.apiKey ?? "") || JSON.stringify(data.models) !== JSON.stringify(editingProvider.models) || (data.anthropicBaseUrl ?? "") !== (editingProvider.anthropicBaseUrl ?? "") || (data.openaiBaseUrl ?? "") !== (editingProvider.openaiBaseUrl ?? "");
3735
3831
  if (criticalFieldsChanged) {
3736
3832
  await runTest(updated.id);
3737
3833
  }
@@ -3817,7 +3913,7 @@ function ProvidersPanel({
3817
3913
  }
3818
3914
  if (showForm || editingProvider) {
3819
3915
  return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-4", children: [
3820
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex items-center justify-between", children: /* @__PURE__ */ jsxRuntimeExports.jsx("h3", { className: "text-lg font-medium", children: editingProvider ? "Edit Provider" : "Add New Provider" }) }),
3916
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex items-center justify-between sticky top-0 bg-background z-10 pb-2", children: /* @__PURE__ */ jsxRuntimeExports.jsx("h3", { className: "text-lg font-medium", children: editingProvider ? "Edit Provider" : "Add New Provider" }) }),
3821
3917
  /* @__PURE__ */ jsxRuntimeExports.jsx(
3822
3918
  ProviderForm,
3823
3919
  {
@@ -4009,7 +4105,7 @@ function SettingsDialog() {
4009
4105
  /* @__PURE__ */ jsxRuntimeExports.jsx(TabsTrigger, { value: "providers", children: "Providers" }),
4010
4106
  /* @__PURE__ */ jsxRuntimeExports.jsx(TabsTrigger, { value: "proxy", children: "Proxy" })
4011
4107
  ] }),
4012
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mt-4 overflow-y-auto flex-1", children: [
4108
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mt-4 overflow-y-auto flex-1 pr-3", children: [
4013
4109
  /* @__PURE__ */ jsxRuntimeExports.jsx(TabsContent, { value: "providers", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
4014
4110
  ProvidersPanel,
4015
4111
  {
@@ -198,7 +198,7 @@ function getResponse() {
198
198
  return event.res;
199
199
  }
200
200
  async function getStartManifest(matchedRoutes) {
201
- const { tsrStartManifest } = await import("../_tanstack-start-manifest_v-vO4aM6jK.mjs");
201
+ const { tsrStartManifest } = await import("../_tanstack-start-manifest_v-DxqMZv-B.mjs");
202
202
  const startManifest = tsrStartManifest();
203
203
  const rootRoute = startManifest.routes[rootRouteId] = startManifest.routes[rootRouteId] || {};
204
204
  rootRoute.assets = rootRoute.assets || [];
@@ -767,7 +767,7 @@ let entriesPromise;
767
767
  let baseManifestPromise;
768
768
  let cachedFinalManifestPromise;
769
769
  async function loadEntries() {
770
- const routerEntry = await import("./router-B8X3GXM2.mjs").then((n) => n.r);
770
+ const routerEntry = await import("./router-Cz7UcQ5N.mjs").then((n) => n.r);
771
771
  const startEntry = await import("./start-HYkvq4Ni.mjs");
772
772
  return { startEntry, routerEntry };
773
773
  }