Add assistant-ui data agent frontend

This commit is contained in:
武阳
2026-05-06 16:18:32 +08:00
parent d6a2359dc1
commit 7d4ae3e7ba
119 changed files with 14330 additions and 14420 deletions
+150
View File
@@ -0,0 +1,150 @@
"use client";
import { Settings2Icon } from "lucide-react";
import type { ReactNode } from "react";
import { useCallback, useEffect, useState } from "react";
import { Button } from "@/components/ui/button";
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog";
import { Input } from "@/components/ui/input";
type ClawState = {
base_url: string;
api_key?: string;
cwd?: string;
allow_shell?: boolean;
allow_write?: boolean;
};
const DEFAULT_STATE: ClawState = {
base_url: "http://model.mify.ai.srv/v1",
api_key: "sk-Jxoh5lf1jWg6HQZYYyfyagXudGxUXNAgkZklxMnnXHn7pFmU",
};
export function ClawLlmSettings() {
const [open, setOpen] = useState(false);
const [state, setState] = useState<ClawState>(DEFAULT_STATE);
const [status, setStatus] = useState<string>("");
const [isSaving, setIsSaving] = useState(false);
const loadState = useCallback(async () => {
setStatus("");
const response = await fetch("/api/claw/state");
const payload = await response.json();
if (!response.ok) {
throw new Error(payload.error ?? "读取后端配置失败");
}
setState((current) => ({
...DEFAULT_STATE,
...payload,
api_key: payload.api_key ?? current.api_key ?? DEFAULT_STATE.api_key,
}));
}, []);
useEffect(() => {
if (!open) return;
loadState().catch((err: unknown) => {
setStatus(err instanceof Error ? err.message : "读取后端配置失败");
});
}, [loadState, open]);
async function saveState() {
setIsSaving(true);
setStatus("");
try {
const response = await fetch("/api/claw/state", {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({
base_url: state.base_url,
api_key: state.api_key,
}),
});
const payload = await response.json();
if (!response.ok) {
throw new Error(payload.error ?? payload.detail ?? "保存失败");
}
setState({
...DEFAULT_STATE,
...payload,
api_key: state.api_key,
});
setStatus("已更新后端 API 配置");
} catch (err) {
setStatus(err instanceof Error ? err.message : "保存失败");
} finally {
setIsSaving(false);
}
}
return (
<Dialog open={open} onOpenChange={setOpen}>
<DialogTrigger asChild>
<Button variant="outline" size="sm">
<Settings2Icon />
LLM API
</Button>
</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle> LLM API</DialogTitle>
<DialogDescription>
Claw Agent 使 Base URL API Key
</DialogDescription>
</DialogHeader>
<div className="grid gap-4 py-2">
<Field label="Base URL">
<Input
value={state.base_url}
onChange={(event) =>
setState((current) => ({
...current,
base_url: event.target.value,
}))
}
/>
</Field>
<Field label="API Key">
<Input
type="password"
value={state.api_key ?? ""}
onChange={(event) =>
setState((current) => ({
...current,
api_key: event.target.value,
}))
}
/>
</Field>
</div>
{status ? (
<p className="text-muted-foreground text-sm">{status}</p>
) : null}
<DialogFooter>
<Button variant="outline" onClick={() => setState(DEFAULT_STATE)}>
</Button>
<Button onClick={saveState} disabled={isSaving}>
{isSaving ? "保存中..." : "保存"}
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
);
}
function Field({ label, children }: { label: string; children: ReactNode }) {
return (
<div className="grid gap-2 text-sm">
<span className="font-medium">{label}</span>
{children}
</div>
);
}