Files
UHD_Media_Tool/apps/web/app/components/settings/pi-shared.tsx
T
rootandClaude Opus 5.5 31a54186c7 copy: second-round copy review across console, settings, agent and server messages
- Trim descriptions, empty states, confirm buttons and toasts to facts
- Unify terms per glossary: 待处理/待识别/整理记录/运行记录/凭证/子会话/Agent 上游
- Subscription-login termsNote states facts only (borrowed client identity, ToS/ban risk)
- Shorter, concrete goal templates for built-in Agent checks
- Human-readable nouns instead of table names in instance CRUD errors
- Audit labels for pi_upstream / schedule / automation_grant actions
- Glossary: add 上游、订阅登录、凭证、定时任务、巡检、运行记录、子会话、停止、持续授权

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-28 13:29:42 +08:00

109 lines
3.2 KiB
TypeScript

import type { PiAuthKind, PiProviderGroup, PiProviderInfo, PiThinkingLevel, PiUpstream } from "@umt/contracts";
import { StatusBadge, type StatusTone } from "~/components/console/status-badge.tsx";
import { Input } from "~/components/ui/input.tsx";
// Agent 上游的界面标签(术语见 docs/design/glossary.md:上游配置叫"Agent 上游")
export const GROUP_LABEL: Record<PiProviderGroup, string> = {
api_key: "API Key",
subscription: "订阅登录",
cloud: "云凭证",
custom: "兼容端点",
test: "测试",
};
export const GROUP_ORDER: PiProviderGroup[] = ["api_key", "subscription", "cloud", "custom", "test"];
export const AUTH_LABEL: Record<PiAuthKind, string> = {
api_key: "API Key",
oauth: "订阅登录",
none: "不需要凭证",
};
export const THINKING_LABEL: Record<PiThinkingLevel, string> = {
off: "关闭",
minimal: "最低",
low: "低",
medium: "中",
high: "高",
xhigh: "很高",
max: "最高",
};
export const THINKING_OPTIONS = (Object.keys(THINKING_LABEL) as PiThinkingLevel[]).map((v) => ({
value: v,
label: `${THINKING_LABEL[v]}(${v})`,
}));
export const API_OPTIONS = [
{ value: "openai-completions", label: "OpenAI Chat Completions" },
{ value: "openai-responses", label: "OpenAI Responses" },
{ value: "anthropic-messages", label: "Anthropic Messages" },
{ value: "google-generative-ai", label: "Google Generative AI" },
];
export function upstreamStatus(u: Pick<PiUpstream, "status" | "authKind" | "enabled">): {
label: string;
tone: StatusTone;
} {
if (!u.enabled) return { label: "已停用", tone: "neutral" };
switch (u.status) {
case "ok":
return { label: "可用", tone: "success" };
case "auth_required":
return { label: u.authKind === "oauth" ? "待登录" : "缺 API Key", tone: "warning" };
case "expired":
return { label: "登录已失效", tone: "destructive" };
case "error":
return { label: "出错", tone: "destructive" };
}
}
export function UpstreamStatusBadge({ u }: { u: Pick<PiUpstream, "status" | "authKind" | "enabled"> }) {
const s = upstreamStatus(u);
return <StatusBadge tone={s.tone}>{s.label}</StatusBadge>;
}
export const providerName = (providers: PiProviderInfo[] | undefined, id: string) =>
providers?.find((p) => p.id === id)?.name ?? id;
/** 模型 id:可从列表挑,也可手填(目录滞后于上游时) */
export function ModelInput({
id,
value,
onChange,
models,
disabled,
invalid,
placeholder,
}: {
id: string;
value: string;
onChange: (v: string) => void;
models: { id: string; name: string }[];
disabled?: boolean;
invalid?: boolean;
placeholder?: string;
}) {
const listId = `${id}-models`;
return (
<>
<Input
id={id}
list={listId}
value={value}
onChange={(e) => onChange(e.target.value)}
disabled={disabled}
aria-invalid={invalid}
autoComplete="off"
placeholder={placeholder ?? (models.length ? `从 ${models.length} 个模型中选,或手填 id` : "模型 id")}
/>
<datalist id={listId}>
{models.map((m) => (
<option key={m.id} value={m.id}>
{m.name}
</option>
))}
</datalist>
</>
);
}