diff --git a/sundynix-desktop/frontend/src/App.tsx b/sundynix-desktop/frontend/src/App.tsx index 0582008..d2192da 100644 --- a/sundynix-desktop/frontend/src/App.tsx +++ b/sundynix-desktop/frontend/src/App.tsx @@ -5,6 +5,7 @@ import { LeftNav, type ViewKey } from "./shell/LeftNav"; import { BottomDrawer } from "./shell/BottomDrawer"; import { StudioView } from "./studio/StudioView"; import { MemoryView } from "./views/MemoryView"; +import { KbView } from "./views/KbView"; import { Placeholder } from "./views/Placeholder"; import { submitTask, streamTokens, type Identity } from "./lib/api"; import type { TaskDsl } from "./lib/dsl"; @@ -70,6 +71,8 @@ export default function App() {
{view === "studio" ? ( + ) : view === "kb" ? ( + ) : view === "memory" ? ( ) : ( diff --git a/sundynix-desktop/frontend/src/lib/api.ts b/sundynix-desktop/frontend/src/lib/api.ts index 5ed32c5..e01850b 100644 --- a/sundynix-desktop/frontend/src/lib/api.ts +++ b/sundynix-desktop/frontend/src/lib/api.ts @@ -47,6 +47,35 @@ export function streamTokens( return () => es.close(); } +// ingestKb: POST /api/v1/kb/ingest,把文本入库(→ mcp-go kb_ingest:切块/embedding/Milvus)。 +export async function ingestKb(kb: string, text: string): Promise { + const res = await fetch(`${GATEWAY}/api/v1/kb/ingest`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ kb, text }), + }); + const data = (await res.json()) as { message?: string; error?: string }; + if (!res.ok) throw new Error(data.error ?? `ingest failed: ${res.status}`); + return data.message ?? "ok"; +} + +export interface KbHit { + text: string; + score: number; +} + +// searchKb: POST /api/v1/kb/search,检索台查询(→ mcp-go kb_search,带分数)。 +export async function searchKb(kb: string, q: string, topK = 5): Promise { + const res = await fetch(`${GATEWAY}/api/v1/kb/search`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ kb, q, topK }), + }); + const data = (await res.json()) as { hits?: KbHit[]; error?: string }; + if (!res.ok) throw new Error(data.error ?? `search failed: ${res.status}`); + return data.hits ?? []; +} + // setMemory: PUT /api/v1/memory,登记一条用户偏好(→ mcp-go memory_upsert)。 export async function setMemory( id: Identity, diff --git a/sundynix-desktop/frontend/src/shell/LeftNav.tsx b/sundynix-desktop/frontend/src/shell/LeftNav.tsx index 7dedf45..00fe3f2 100644 --- a/sundynix-desktop/frontend/src/shell/LeftNav.tsx +++ b/sundynix-desktop/frontend/src/shell/LeftNav.tsx @@ -19,7 +19,7 @@ interface Item { const ITEMS: Item[] = [ { key: "home", label: "工作台", icon: "■" }, { key: "studio", label: "编排", icon: "◆", group: "BUILD", ready: true }, - { key: "kb", label: "知识库", icon: "▣", group: "BUILD" }, + { key: "kb", label: "知识库", icon: "▣", group: "BUILD", ready: true }, { key: "report", label: "报告", icon: "▤", group: "BUILD" }, { key: "runs", label: "运行", icon: "▸", group: "RUN" }, { key: "memory", label: "记忆", icon: "◇", group: "MANAGE", ready: true }, diff --git a/sundynix-desktop/frontend/src/views/KbView.tsx b/sundynix-desktop/frontend/src/views/KbView.tsx new file mode 100644 index 0000000..cf7fd9d --- /dev/null +++ b/sundynix-desktop/frontend/src/views/KbView.tsx @@ -0,0 +1,144 @@ +import { useState } from "react"; +import { ingestKb, searchKb, type KbHit } from "../lib/api"; + +interface IngestLog { + t: string; + msg: string; + ok: boolean; +} + +// 知识库管理:入库监控(切块/embedding/Milvus)+ 检索调试台(带分数与来源)。 +export function KbView() { + const [kb, setKb] = useState("docs"); + const [text, setText] = useState(""); + const [logs, setLogs] = useState([]); + const [ingesting, setIngesting] = useState(false); + + const [q, setQ] = useState(""); + const [topK, setTopK] = useState(5); + const [hits, setHits] = useState(null); + const [searching, setSearching] = useState(false); + const [err, setErr] = useState(""); + + const stamp = () => new Date().toLocaleTimeString(); + + const onIngest = async () => { + if (!text.trim()) return; + setIngesting(true); + try { + const msg = await ingestKb(kb, text); + setLogs((l) => [{ t: stamp(), msg, ok: true }, ...l]); + setText(""); + } catch (e) { + setLogs((l) => [{ t: stamp(), msg: (e as Error).message, ok: false }, ...l]); + } finally { + setIngesting(false); + } + }; + + const onSearch = async () => { + if (!q.trim()) return; + setSearching(true); + setErr(""); + try { + setHits(await searchKb(kb, q, topK)); + } catch (e) { + setErr((e as Error).message); + setHits(null); + } finally { + setSearching(false); + } + }; + + return ( +
+
+ 知识库 + setKb(e.target.value)} + placeholder="知识库名" + title="知识库(Milvus kb 字段分区)" + /> + 入库 → 切块 / embedding / Milvus;检索 → 向量召回 +
+ +
+ {/* 左:入库 + 监控日志 */} +
+

入库(按行切块)

+