mirror of
https://github.com/tiennm99/goclaw.git
synced 2026-10-14 00:16:38 +00:00
feat(web): add KG extraction settings to system settings modal and tighten code block spacing
- Knowledge Graph section: provider/model picker + min confidence threshold - Saves via builtin tools API (knowledge_graph_search settings) - Reduce code block vertical margin and padding in markdown renderer
This commit is contained in:
1 parent
028ea8a7d8
commit
db03bf92e0
5 files changed
+126
-7
No files matched your search
@@ -2,7 +2,7 @@ import { useState, useEffect, useCallback } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import {
|
||||
Settings2, Loader2, Save, AlertTriangle, Info, ExternalLink,
|
||||
Brain, Eye, MessageSquareText, Archive, Clock, Hash, CheckCircle2, XCircle,
|
||||
Brain, Eye, MessageSquareText, Archive, Clock, Hash, CheckCircle2, XCircle, Network,
|
||||
} from "lucide-react";
|
||||
import { Link } from "react-router";
|
||||
import {
|
||||
@@ -74,6 +74,9 @@ interface InitState {
|
||||
compThreshold: string;
|
||||
compKeepRecent: string;
|
||||
compMaxTokens: string;
|
||||
kgProvider: string;
|
||||
kgModel: string;
|
||||
kgMinConfidence: string;
|
||||
}
|
||||
|
||||
const DEFAULTS: InitState = {
|
||||
@@ -82,6 +85,7 @@ const DEFAULTS: InitState = {
|
||||
toolStatus: true, blockReply: false, intentClassify: true,
|
||||
compProvider: "", compModel: "",
|
||||
compThreshold: "", compKeepRecent: "", compMaxTokens: "",
|
||||
kgProvider: "", kgModel: "", kgMinConfidence: "0.75",
|
||||
};
|
||||
|
||||
function parseBool(v: string | undefined, fallback: boolean): boolean {
|
||||
@@ -117,7 +121,15 @@ export function SystemSettingsModal({ open, onOpenChange }: SystemSettingsModalP
|
||||
const [compKeepRecent, setCompKeepRecent] = useState("");
|
||||
const [compMaxTokens, setCompMaxTokens] = useState("");
|
||||
|
||||
const applyConfigs = useCallback((configs: Record<string, string>) => {
|
||||
// Knowledge Graph
|
||||
const [kgProvider, setKgProvider] = useState("");
|
||||
const [kgModel, setKgModel] = useState("");
|
||||
const [kgMinConfidence, setKgMinConfidence] = useState("0.75");
|
||||
|
||||
const applyConfigs = useCallback((
|
||||
configs: Record<string, string>,
|
||||
kgSettings?: { extraction_provider?: string; extraction_model?: string; min_confidence?: number },
|
||||
) => {
|
||||
const s: InitState = {
|
||||
embProvider: configs["embedding.provider"] ?? "",
|
||||
embModel: configs["embedding.model"] ?? "",
|
||||
@@ -131,6 +143,9 @@ export function SystemSettingsModal({ open, onOpenChange }: SystemSettingsModalP
|
||||
compThreshold: configs["compaction.threshold"] ?? "",
|
||||
compKeepRecent: configs["compaction.keep_recent"] ?? "",
|
||||
compMaxTokens: configs["compaction.max_tokens"] ?? "",
|
||||
kgProvider: kgSettings?.extraction_provider ?? "",
|
||||
kgModel: kgSettings?.extraction_model ?? "",
|
||||
kgMinConfidence: String(kgSettings?.min_confidence ?? 0.75),
|
||||
};
|
||||
setInit(s);
|
||||
setEmbProvider(s.embProvider);
|
||||
@@ -145,15 +160,22 @@ export function SystemSettingsModal({ open, onOpenChange }: SystemSettingsModalP
|
||||
setCompThreshold(s.compThreshold);
|
||||
setCompKeepRecent(s.compKeepRecent);
|
||||
setCompMaxTokens(s.compMaxTokens);
|
||||
setKgProvider(s.kgProvider);
|
||||
setKgModel(s.kgModel);
|
||||
setKgMinConfidence(s.kgMinConfidence);
|
||||
resetEmb();
|
||||
}, [resetEmb]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!open) return;
|
||||
setLoading(true);
|
||||
http
|
||||
.get<Record<string, string>>("/v1/system-configs")
|
||||
.then(applyConfigs)
|
||||
Promise.all([
|
||||
http.get<Record<string, string>>("/v1/system-configs"),
|
||||
http.get<{ settings?: Record<string, unknown> }>("/v1/tools/builtin/knowledge_graph_search")
|
||||
.then((r) => r.settings as { extraction_provider?: string; extraction_model?: string; min_confidence?: number } | undefined)
|
||||
.catch(() => undefined),
|
||||
])
|
||||
.then(([configs, kgSettings]) => applyConfigs(configs, kgSettings))
|
||||
.catch((err) => toast.error(err instanceof Error ? err.message : t("loadFailed")))
|
||||
.finally(() => setLoading(false));
|
||||
}, [open, http, applyConfigs, t]);
|
||||
@@ -195,6 +217,19 @@ export function SystemSettingsModal({ open, onOpenChange }: SystemSettingsModalP
|
||||
for (const [key, value] of Object.entries(updates)) {
|
||||
await http.put(`/v1/system-configs/${key}`, { value });
|
||||
}
|
||||
|
||||
// KG extraction — save via builtin tools API
|
||||
const kgChanged = kgProvider !== init.kgProvider || kgModel !== init.kgModel || kgMinConfidence !== init.kgMinConfidence;
|
||||
if (kgChanged) {
|
||||
await http.put("/v1/tools/builtin/knowledge_graph_search", {
|
||||
settings: {
|
||||
extraction_provider: kgProvider,
|
||||
extraction_model: kgModel,
|
||||
min_confidence: Number(kgMinConfidence) || 0.75,
|
||||
extract_on_memory_write: !!(kgProvider && kgModel),
|
||||
},
|
||||
});
|
||||
}
|
||||
toast.success(t("saved"));
|
||||
onOpenChange(false);
|
||||
} catch (err) {
|
||||
@@ -344,6 +379,51 @@ export function SystemSettingsModal({ open, onOpenChange }: SystemSettingsModalP
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* ── Knowledge Graph Extraction ── */}
|
||||
<Card className="border-violet-200 dark:border-violet-800">
|
||||
<CardHeader>
|
||||
<CardTitle className="flex items-center gap-2 text-base">
|
||||
<Network className="h-4 w-4 text-violet-500" />
|
||||
{t("kg.title")}
|
||||
</CardTitle>
|
||||
<CardDescription>{t("kg.description")}</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-4 pt-0">
|
||||
<ProviderModelSelect
|
||||
provider={kgProvider}
|
||||
onProviderChange={(v) => { setKgProvider(v); setKgModel(""); }}
|
||||
model={kgModel}
|
||||
onModelChange={setKgModel}
|
||||
allowEmpty
|
||||
providerLabel={t("kg.provider")}
|
||||
modelLabel={t("kg.model")}
|
||||
providerTip={t("kg.providerTip")}
|
||||
modelTip={t("kg.modelTip")}
|
||||
providerPlaceholder={t("kg.providerPlaceholder")}
|
||||
modelPlaceholder={t("kg.modelPlaceholder")}
|
||||
/>
|
||||
|
||||
<div className="space-y-1.5">
|
||||
<Label htmlFor="kgMinConf" className="text-xs">{t("kg.minConfidence")}</Label>
|
||||
<Input
|
||||
id="kgMinConf"
|
||||
type="number"
|
||||
min={0} max={1} step={0.05}
|
||||
placeholder="0.75"
|
||||
value={kgMinConfidence}
|
||||
onChange={(e) => setKgMinConfidence(e.target.value)}
|
||||
className="max-w-[120px] text-base md:text-sm"
|
||||
/>
|
||||
<p className="text-xs text-muted-foreground">{t("kg.minConfidenceHint")}</p>
|
||||
</div>
|
||||
|
||||
<div className="flex items-start gap-2 rounded-md border border-violet-200 bg-violet-50 px-3 py-2 text-xs text-violet-700 dark:border-violet-800 dark:bg-violet-950/30 dark:text-violet-300">
|
||||
<Info className="mt-0.5 h-3.5 w-3.5 shrink-0" />
|
||||
<span>{t("kg.info")}</span>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* ── UX Behavior — uses FeatureSwitchGroup (same as config page) ── */}
|
||||
<FeatureSwitchGroup
|
||||
title={t("ux.title")}
|
||||
|
||||
@@ -33,7 +33,7 @@ function CodeBlock({
|
||||
const lang = className?.replace("language-", "") ?? "";
|
||||
|
||||
return (
|
||||
<div className="not-prose group relative my-6 overflow-hidden rounded-lg border border-border/60">
|
||||
<div className="not-prose group relative my-3 overflow-hidden rounded-lg border border-border/60">
|
||||
<div className="flex items-center justify-between border-b border-border/40 bg-muted/70 px-3 py-1.5 text-[11px] font-medium tracking-wide text-muted-foreground uppercase">
|
||||
<span>{lang || "code"}</span>
|
||||
<button
|
||||
@@ -45,7 +45,7 @@ function CodeBlock({
|
||||
{copied ? <Check className="h-3.5 w-3.5" /> : <Copy className="h-3.5 w-3.5" />}
|
||||
</button>
|
||||
</div>
|
||||
<pre className="overflow-x-auto bg-muted/30 p-4 text-[13px] leading-relaxed text-foreground whitespace-pre">
|
||||
<pre className="overflow-x-auto bg-muted/30 p-3 text-[13px] leading-normal text-foreground whitespace-pre">
|
||||
<code className={className} style={{ fontFamily: "'JetBrains Mono', 'Fira Code', ui-monospace, monospace", wordWrap: "normal", overflowWrap: "normal" }}>{children}</code>
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
@@ -40,6 +40,19 @@
|
||||
"intentClassifyHint": "Classify user intent before routing to the agent.",
|
||||
"intentClassifyInfo": "Messages are classified to optimize agent routing and behavior."
|
||||
},
|
||||
"kg": {
|
||||
"title": "Knowledge Graph",
|
||||
"description": "Extract entities and relationships from agent memory into a searchable knowledge graph.",
|
||||
"provider": "Extraction Provider",
|
||||
"model": "Extraction Model",
|
||||
"providerTip": "LLM provider used for extracting entities from memory.",
|
||||
"modelTip": "Model for entity/relation extraction (e.g. claude-sonnet-4-20250514).",
|
||||
"providerPlaceholder": "Select provider",
|
||||
"modelPlaceholder": "Select model",
|
||||
"minConfidence": "Min Confidence",
|
||||
"minConfidenceHint": "Minimum confidence threshold (0–1) for extracted entities.",
|
||||
"info": "Automatically extracts entities and relationships when agents write to memory. Leave provider empty to disable."
|
||||
},
|
||||
"compaction": {
|
||||
"title": "Pending Message Compaction",
|
||||
"description": "Summarize long pending message history to stay within context limits.",
|
||||
|
||||
@@ -40,6 +40,19 @@
|
||||
"intentClassifyHint": "Phân loại ý định người dùng trước khi chuyển tới agent.",
|
||||
"intentClassifyInfo": "Tin nhắn được phân loại để tối ưu định tuyến agent."
|
||||
},
|
||||
"kg": {
|
||||
"title": "Đồ thị tri thức",
|
||||
"description": "Trích xuất thực thể và quan hệ từ bộ nhớ agent vào đồ thị tri thức có thể tìm kiếm.",
|
||||
"provider": "Provider trích xuất",
|
||||
"model": "Model trích xuất",
|
||||
"providerTip": "Provider LLM dùng để trích xuất thực thể từ bộ nhớ.",
|
||||
"modelTip": "Model trích xuất thực thể/quan hệ (vd: claude-sonnet-4-20250514).",
|
||||
"providerPlaceholder": "Chọn provider",
|
||||
"modelPlaceholder": "Chọn model",
|
||||
"minConfidence": "Độ tin cậy tối thiểu",
|
||||
"minConfidenceHint": "Ngưỡng độ tin cậy (0–1) cho thực thể được trích xuất.",
|
||||
"info": "Tự động trích xuất thực thể và quan hệ khi agent ghi vào bộ nhớ. Để trống provider để tắt."
|
||||
},
|
||||
"compaction": {
|
||||
"title": "Nén tin nhắn chờ",
|
||||
"description": "Tóm tắt lịch sử tin nhắn chờ dài để nằm trong giới hạn context.",
|
||||
|
||||
@@ -40,6 +40,19 @@
|
||||
"intentClassifyHint": "在路由到Agent之前分类用户意图。",
|
||||
"intentClassifyInfo": "消息被分类以优化Agent路由和行为。"
|
||||
},
|
||||
"kg": {
|
||||
"title": "知识图谱",
|
||||
"description": "从Agent记忆中提取实体和关系到可搜索的知识图谱。",
|
||||
"provider": "提取Provider",
|
||||
"model": "提取模型",
|
||||
"providerTip": "用于从记忆中提取实体的LLM Provider。",
|
||||
"modelTip": "实体/关系提取模型(如claude-sonnet-4-20250514)。",
|
||||
"providerPlaceholder": "选择Provider",
|
||||
"modelPlaceholder": "选择模型",
|
||||
"minConfidence": "最低置信度",
|
||||
"minConfidenceHint": "提取实体的最低置信度阈值(0–1)。",
|
||||
"info": "当Agent写入记忆时自动提取实体和关系。留空Provider以禁用。"
|
||||
},
|
||||
"compaction": {
|
||||
"title": "待处理消息压缩",
|
||||
"description": "压缩长待处理消息历史以保持在上下文限制内。",
|
||||
|
||||
Reference in new issue
Block a user