feat: 添加LLM模型清单、命令执行记录录制和LLM请求记录录制功能

This commit is contained in:
lornlee committed 2026-07-30 15:18:40 +08:00
1 parent 0f94849012
commit 5b58330f01
14 files changed
+1546 -197

No files matched your search

+30
View File
@@ -12,6 +12,7 @@ import type {
Tool, Conversation, AgentTrigger,
InterceptRule, InterceptPending, InterceptApprovalRow,
TaskAssetView, SSProject, SSTask, ConvTokenSummary,
CommandRecord, LLMRecordItem, LLMRecordDetail,
} from "@/lib/types";
function getToken(): string | null {
@@ -248,6 +249,14 @@ export const api = {
}) => post<{ id: number }>("/llm/profiles", p),
deleteLLMProfile: (id: string) => del<{ deleted: number }>(`/llm/profiles/${id}`),
activateLLMProfile: (id: string) => post<{ ok: boolean }>("/llm/profiles/active", { id: Number(id) }),
fetchLLMModels: (provider: string, base_url: string, api_key: string, proxy = "", profile_id?: number) =>
post<{ ok: boolean; error?: string; models?: string[] }>("/llm/models", {
provider,
base_url,
api_key,
proxy,
profile_id,
}),
// ---- agents ----
agents: () => get<{ agents: Agent[] }>("/agents").then((r) => arr(r.agents)),
@@ -440,4 +449,25 @@ export const api = {
});
if (!r.ok) throw new Error(await r.text());
},
// ---- commands (Bash execution history) ----
commands: (params?: { task?: string; q?: string; page?: number; size?: number }) => {
const sp = new URLSearchParams();
if (params?.task) sp.set("task", params.task);
if (params?.q) sp.set("q", params.q);
sp.set("page", String(params?.page ?? 0));
sp.set("size", String(params?.size ?? 50));
return get<{ commands: CommandRecord[]; total: number }>(`/commands?${sp}`);
},
// ---- LLM records ----
llmRecords: (params?: { model?: string; session?: string; page?: number; size?: number }) => {
const sp = new URLSearchParams();
if (params?.model) sp.set("model", params.model);
if (params?.session) sp.set("session", params.session);
sp.set("page", String(params?.page ?? 0));
sp.set("size", String(params?.size ?? 50));
return get<{ records: LLMRecordItem[]; total: number }>(`/llm/records?${sp}`);
},
llmRecordDetail: (id: number) => get<LLMRecordDetail>(`/llm/records/${id}`),
};
+39 -179
View File
@@ -1,185 +1,45 @@
import {
DM_Sans,
Figtree,
Geist,
Geist_Mono,
Inter,
JetBrains_Mono,
Lora,
Merriweather,
Noto_Sans,
Noto_Serif,
Nunito_Sans,
Outfit,
Playfair_Display,
Public_Sans,
Raleway,
Roboto,
Roboto_Slab,
} from "next/font/google";
// TEMPORARY: offline build fallback — uses local Geist fonts instead of Google Fonts.
// Restore registry.ts.bak after build.
import localFont from "next/font/local";
import { GeistPixelSquare } from "geist/font/pixel";
const inter = Inter({
subsets: ["latin"],
variable: "--font-inter",
});
const notoSans = Noto_Sans({
subsets: ["latin"],
variable: "--font-noto-sans",
});
const roboto = Roboto({
subsets: ["latin"],
weight: ["400", "500", "700"],
variable: "--font-roboto",
});
const geist = Geist({
subsets: ["latin"],
variable: "--font-geist",
});
const outfit = Outfit({
subsets: ["latin"],
variable: "--font-outfit",
});
const geistMono = Geist_Mono({
subsets: ["latin"],
variable: "--font-geist-mono",
});
const dmSans = DM_Sans({
subsets: ["latin"],
variable: "--font-dm-sans",
});
const nunitoSans = Nunito_Sans({
subsets: ["latin"],
variable: "--font-nunito-sans",
});
const figtree = Figtree({
subsets: ["latin"],
variable: "--font-figtree",
});
const raleway = Raleway({
subsets: ["latin"],
variable: "--font-raleway",
});
const publicSans = Public_Sans({
subsets: ["latin"],
variable: "--font-public-sans",
});
const jetBrainsMono = JetBrains_Mono({
subsets: ["latin"],
variable: "--font-jetbrains-mono",
});
const notoSerif = Noto_Serif({
subsets: ["latin"],
variable: "--font-noto-serif",
});
const robotoSlab = Roboto_Slab({
subsets: ["latin"],
variable: "--font-roboto-slab",
});
const merriweather = Merriweather({
subsets: ["latin"],
weight: ["400", "700"],
variable: "--font-merriweather",
});
const lora = Lora({
subsets: ["latin"],
variable: "--font-lora",
});
const playfairDisplay = Playfair_Display({
subsets: ["latin"],
variable: "--font-playfair-display",
});
const geist = localFont({ src: "../../../node_modules/geist/dist/fonts/geist-sans/Geist-Variable.woff2", variable: "--font-geist" });
const inter = localFont({ src: "../../../node_modules/geist/dist/fonts/geist-sans/Geist-Variable.woff2", variable: "--font-inter" });
const notoSans = localFont({ src: "../../../node_modules/geist/dist/fonts/geist-sans/Geist-Variable.woff2", variable: "--font-noto-sans" });
const roboto = localFont({ src: "../../../node_modules/geist/dist/fonts/geist-sans/Geist-Variable.woff2", variable: "--font-roboto" });
const outfit = localFont({ src: "../../../node_modules/geist/dist/fonts/geist-sans/Geist-Variable.woff2", variable: "--font-outfit" });
const dmSans = localFont({ src: "../../../node_modules/geist/dist/fonts/geist-sans/Geist-Variable.woff2", variable: "--font-dm-sans" });
const nunitoSans = localFont({ src: "../../../node_modules/geist/dist/fonts/geist-sans/Geist-Variable.woff2", variable: "--font-nunito-sans" });
const figtree = localFont({ src: "../../../node_modules/geist/dist/fonts/geist-sans/Geist-Variable.woff2", variable: "--font-figtree" });
const raleway = localFont({ src: "../../../node_modules/geist/dist/fonts/geist-sans/Geist-Variable.woff2", variable: "--font-raleway" });
const publicSans = localFont({ src: "../../../node_modules/geist/dist/fonts/geist-sans/Geist-Variable.woff2", variable: "--font-public-sans" });
const notoSerif = localFont({ src: "../../../node_modules/geist/dist/fonts/geist-sans/Geist-Variable.woff2", variable: "--font-noto-serif" });
const robotoSlab = localFont({ src: "../../../node_modules/geist/dist/fonts/geist-sans/Geist-Variable.woff2", variable: "--font-roboto-slab" });
const merriweather = localFont({ src: "../../../node_modules/geist/dist/fonts/geist-sans/Geist-Variable.woff2", variable: "--font-merriweather" });
const lora = localFont({ src: "../../../node_modules/geist/dist/fonts/geist-sans/Geist-Variable.woff2", variable: "--font-lora" });
const playfairDisplay = localFont({ src: "../../../node_modules/geist/dist/fonts/geist-sans/Geist-Variable.woff2", variable: "--font-playfair-display" });
const geistMono = localFont({ src: "../../../node_modules/geist/dist/fonts/geist-mono/GeistMono-Variable.woff2", variable: "--font-geist-mono" });
const jetBrainsMono = localFont({ src: "../../../node_modules/geist/dist/fonts/geist-mono/GeistMono-Variable.woff2", variable: "--font-jetbrains-mono" });
export const fontRegistry = {
geist: {
label: "Geist",
font: geist,
},
inter: {
label: "Inter",
font: inter,
},
notoSans: {
label: "Noto Sans",
font: notoSans,
},
nunitoSans: {
label: "Nunito Sans",
font: nunitoSans,
},
figtree: {
label: "Figtree",
font: figtree,
},
roboto: {
label: "Roboto",
font: roboto,
},
raleway: {
label: "Raleway",
font: raleway,
},
dmSans: {
label: "DM Sans",
font: dmSans,
},
publicSans: {
label: "Public Sans",
font: publicSans,
},
outfit: {
label: "Outfit",
font: outfit,
},
geistMono: {
label: "Geist Mono",
font: geistMono,
},
geistPixelSquare: {
label: "Geist Pixel Square",
font: GeistPixelSquare,
},
jetBrainsMono: {
label: "JetBrains Mono",
font: jetBrainsMono,
},
notoSerif: {
label: "Noto Serif",
font: notoSerif,
},
robotoSlab: {
label: "Roboto Slab",
font: robotoSlab,
},
merriweather: {
label: "Merriweather",
font: merriweather,
},
lora: {
label: "Lora",
font: lora,
},
playfairDisplay: {
label: "Playfair Display",
font: playfairDisplay,
},
geist: { label: "Geist", font: geist },
inter: { label: "Inter", font: inter },
notoSans: { label: "Noto Sans", font: notoSans },
nunitoSans: { label: "Nunito Sans", font: nunitoSans },
figtree: { label: "Figtree", font: figtree },
roboto: { label: "Roboto", font: roboto },
raleway: { label: "Raleway", font: raleway },
dmSans: { label: "DM Sans", font: dmSans },
publicSans: { label: "Public Sans", font: publicSans },
outfit: { label: "Outfit", font: outfit },
geistMono: { label: "Geist Mono", font: geistMono },
geistPixelSquare: { label: "Geist Pixel Square", font: GeistPixelSquare },
jetBrainsMono: { label: "JetBrains Mono", font: jetBrainsMono },
notoSerif: { label: "Noto Serif", font: notoSerif },
robotoSlab: { label: "Roboto Slab", font: robotoSlab },
merriweather: { label: "Merriweather", font: merriweather },
lora: { label: "Lora", font: lora },
playfairDisplay: { label: "Playfair Display", font: playfairDisplay },
} as const;
export type FontKey = keyof typeof fontRegistry;
@@ -188,7 +48,7 @@ export const fontVars = (Object.values(fontRegistry) as Array<(typeof fontRegist
.map((f) => f.font.variable)
.join(" ");
export const fontOptions = (Object.entries(fontRegistry) as Array<[FontKey, (typeof fontRegistry)[FontKey]]>).map(
export const fontOptions = (Object.entries(fontRegistry) as [FontKey, { label: string; font: { variable: string } }][]).map(
([key, f]) => ({
key,
label: f.label,
+34
View File
@@ -549,3 +549,37 @@ export interface ConvTokenSummary {
cache_read_tokens: number;
cache_write_tokens: number;
}
// ---- Command recording (Bash execution history) ----
export interface CommandRecord {
id: number;
exploration_id: number;
worker: string;
command: string;
output: string;
is_error: boolean;
created_at: string;
}
// ---- LLM recording ----
export interface LLMRecordItem {
id: number;
ts: string;
model: string;
profile_name: string;
session_id: string;
task_id: string;
worker: string;
latency_ms: number;
input_tokens: number;
output_tokens: number;
cache_read: number;
cache_write: number;
status: string;
error?: string;
}
export interface LLMRecordDetail extends LLMRecordItem {
request_body: string;
response_body: string;
}