refactor: trim custom-endpoint comments and share synthetic provider const
Review pass: shorten verbose doc comments across the custom-endpoint code, merge duplicate imports, share one synthetic provider entry between the keys section and setup dialog, collapse the duplicated connect-editor block, revert an unrelated settings-route drive-by.
This commit is contained in:
@@ -58,10 +58,8 @@ interface CustomStatus {
|
||||
* section can never drift from core/auth-keys.ts again. */
|
||||
const TOOL_IDS = AUTH_KEYS.map((k) => k.id) as readonly string[];
|
||||
|
||||
/** The custom endpoint as it appears in the Add-provider dropdown — a synthetic
|
||||
* entry (it is not one of pi-ai's 33), offered only while unconfigured; once
|
||||
* connected it graduates to its own row like any stored provider. */
|
||||
const CUSTOM_PROVIDER_INFO: ProviderKeyInfo = {
|
||||
/** The custom endpoint's synthetic Add-provider entry (offered only while unconfigured). */
|
||||
export const CUSTOM_PROVIDER_INFO: ProviderKeyInfo = {
|
||||
id: CUSTOM_PROVIDER.id,
|
||||
envVar: CUSTOM_PROVIDER.envVar,
|
||||
label: CUSTOM_PROVIDER.label,
|
||||
@@ -489,10 +487,10 @@ export function KeysSection({
|
||||
onToggle={() => setAddingCustom((v) => !v)}
|
||||
/>
|
||||
)}
|
||||
{custom?.baseUrl && addingCustom && (
|
||||
{addingCustom && (
|
||||
<CustomEndpointEditor
|
||||
baseUrl0={custom.baseUrl}
|
||||
keyPresent={custom.keyPresent}
|
||||
baseUrl0={custom?.baseUrl ?? ""}
|
||||
keyPresent={custom?.keyPresent ?? false}
|
||||
onSaved={async () => {
|
||||
setAddingCustom(false);
|
||||
await reload();
|
||||
@@ -516,18 +514,6 @@ export function KeysSection({
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{!custom?.baseUrl && addingCustom && (
|
||||
<CustomEndpointEditor
|
||||
baseUrl0=""
|
||||
keyPresent={false}
|
||||
onSaved={async () => {
|
||||
setAddingCustom(false);
|
||||
await reload();
|
||||
notifySettingsSaved("keys");
|
||||
}}
|
||||
onCancel={() => setAddingCustom(false)}
|
||||
/>
|
||||
)}
|
||||
{adding && (
|
||||
<div className="flex items-center gap-2 rounded-sm px-1 py-1.5" data-testid={`key-${adding.id}-editor`}>
|
||||
<span className="shrink-0 font-ui text-xs text-foreground">{adding.label}</span>
|
||||
@@ -637,9 +623,7 @@ function AddProviderEditor({
|
||||
);
|
||||
}
|
||||
|
||||
/** The custom endpoint's status row in the provider list — one more provider,
|
||||
* shown only once it's configured (exactly like a built-in appears only once
|
||||
* its key is stored). Clicking re-opens the connect editor. */
|
||||
/** The custom endpoint's status row (shown once configured); clicking re-opens the editor. */
|
||||
function CustomEndpointRow({
|
||||
status,
|
||||
expanded,
|
||||
@@ -669,12 +653,8 @@ function CustomEndpointRow({
|
||||
);
|
||||
}
|
||||
|
||||
/** The custom endpoint's connect editor — base URL + optional key, exactly the
|
||||
* built-ins' key-paste flow plus the one extra URL field. Connect POSTs to
|
||||
* /api/custom-provider (validate + discover + register); on success the parent
|
||||
* reloads so the row appears and the catalog widens with no restart. Image
|
||||
* capability is detected automatically from the endpoint — there is nothing to
|
||||
* tick here. Keyless local servers (Ollama / LM Studio) connect with no key. */
|
||||
/** The custom endpoint's connect editor: base URL + key → POST /api/custom-provider
|
||||
* (validate + discover + register). Image capability is detected automatically. */
|
||||
function CustomEndpointEditor({
|
||||
baseUrl0,
|
||||
keyPresent,
|
||||
@@ -691,9 +671,8 @@ function CustomEndpointEditor({
|
||||
const [keyTouched, setKeyTouched] = useState(false);
|
||||
const [busy, setBusy] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
// A key is required to enable chatting (the send gate needs a provider key);
|
||||
// local servers accept any value. When one is already stored, leaving the
|
||||
// field blank keeps it.
|
||||
// A key is required to enable chatting; local servers accept any value. A
|
||||
// stored key is kept when the field is left blank.
|
||||
const needsKey = !keyPresent;
|
||||
const canConnect = Boolean(baseUrl.trim()) && (!needsKey || Boolean(key.trim()));
|
||||
|
||||
|
||||
@@ -16,10 +16,9 @@
|
||||
*/
|
||||
|
||||
import { useEffect, useState } from "react";
|
||||
import { CUSTOM_PROVIDER, MODEL_SLOTS, type AuthKeyStatus } from "@/src/core";
|
||||
import { CUSTOM_PROVIDER, MODEL_SLOTS, type AuthKeyStatus, type ModelCatalogResponse } from "@/src/core";
|
||||
import { classifyKeys, type ProviderKeyInfo } from "@/components/settings/keys-section";
|
||||
import { useModelCatalog } from "@/components/model-catalog";
|
||||
import type { ModelCatalogResponse } from "@/src/core";
|
||||
|
||||
/** Pure: does a MODEL-PROVIDER key exist among the statuses? Reuses
|
||||
* classifyKeys (the keys section's own classification — no drift):
|
||||
|
||||
@@ -48,7 +48,7 @@ import { notifySettingsSaved, useSettingsSaved } from "@/components/settings-sig
|
||||
import { useModelCatalog } from "@/components/model-catalog";
|
||||
import { savedSlotsConfigured } from "@/components/setup-cta";
|
||||
import { Hint } from "@/components/ui/hint";
|
||||
import { type ProviderKeyInfo } from "@/components/settings/keys-section";
|
||||
import { type ProviderKeyInfo, CUSTOM_PROVIDER_INFO } from "@/components/settings/keys-section";
|
||||
import { ProviderMenuList } from "@/components/settings/model-menu";
|
||||
import { ChatGPTBackgroundAccess } from "@/components/settings/chatgpt-connection";
|
||||
import { NativeSubscriptionBackgroundAccess, SubscriptionConnections } from "@/components/settings/subscription-connections";
|
||||
@@ -65,11 +65,6 @@ const SLOT_META: Record<ModelSlotName, { label: string; roster: string }> = {
|
||||
};
|
||||
const SLOT_ORDER: readonly ModelSlotName[] = ["strong", "cheap", "vision"];
|
||||
|
||||
/** The custom endpoint's synthetic picker entry — one more provider in the
|
||||
* Stage 1 roster, connected by a base URL + key (POST
|
||||
* /api/custom-provider). Not part of the /api/keys plain-key roster. */
|
||||
const CUSTOM_PICKER: ProviderKeyInfo = { id: CUSTOM_PROVIDER.id, envVar: CUSTOM_PROVIDER.envVar, label: CUSTOM_PROVIDER.label };
|
||||
|
||||
/** The GET /api/models response fields the flow consumes. */
|
||||
interface ModelsBody {
|
||||
models: ModelCatalogEntry[];
|
||||
@@ -159,9 +154,8 @@ export function SetupKeysBody({
|
||||
onSave: () => void;
|
||||
/** The MenuShell anchor — wired by the wrapper (its triggerRef). */
|
||||
menuAnchor: React.RefObject<HTMLDivElement | null>;
|
||||
/** The chosen provider is the custom endpoint: a base URL field joins the
|
||||
* key field (a key is required — local servers accept any value). All three
|
||||
* optional so the plain-key callers (and tests) stay unchanged. */
|
||||
/** Chosen provider is the custom endpoint: a base URL field joins the key
|
||||
* field. All three optional so plain-key callers (and tests) stay unchanged. */
|
||||
isCustom?: boolean;
|
||||
baseUrl?: string;
|
||||
onBaseUrl?: (v: string) => void;
|
||||
@@ -408,8 +402,7 @@ function OpenSetupDialog({ onClose, onDone }: SetupDialogProps) {
|
||||
if (!keyValue.trim()) return;
|
||||
setKeySaving(true);
|
||||
setKeyError(null);
|
||||
// The custom endpoint connects through its own route (base URL + key +
|
||||
// discovery in one action); every other provider is a plain key PUT.
|
||||
// Custom endpoint connects through its own route; every other provider is a plain key PUT.
|
||||
const res = isCustom
|
||||
? await fetch("/api/custom-provider", {
|
||||
method: "POST",
|
||||
@@ -460,7 +453,7 @@ function OpenSetupDialog({ onClose, onDone }: SetupDialogProps) {
|
||||
}
|
||||
|
||||
const keysBody = <SetupKeysBody
|
||||
providers={keyProviders ? [...keyProviders, CUSTOM_PICKER] : undefined}
|
||||
providers={keyProviders ? [...keyProviders, CUSTOM_PROVIDER_INFO] : undefined}
|
||||
provider={keyProvider}
|
||||
value={keyValue}
|
||||
saving={keySaving}
|
||||
|
||||
Reference in New Issue
Block a user