mirror of
https://github.com/tiennm99/goclaw.git
synced 2026-10-03 04:54:36 +00:00
feat(skills): add slash command activation
This commit is contained in:
1 parent
14b32bd219
commit
c99f7e1fa4
24 files changed
+978
-40
No files matched your search
@@ -45,6 +45,10 @@ export interface InitState {
|
||||
bgProvider: string;
|
||||
bgModel: string;
|
||||
skillUploadMaxSize: string;
|
||||
skillSlashEnabled: boolean;
|
||||
skillSlashSuggest: boolean;
|
||||
skillSlashPartial: boolean;
|
||||
skillSlashPrefix: string;
|
||||
}
|
||||
|
||||
export const DEFAULTS: InitState = {
|
||||
@@ -56,6 +60,10 @@ export const DEFAULTS: InitState = {
|
||||
kgProvider: "", kgModel: "", kgMinConfidence: "0.75",
|
||||
bgProvider: "", bgModel: "",
|
||||
skillUploadMaxSize: "20",
|
||||
skillSlashEnabled: true,
|
||||
skillSlashSuggest: true,
|
||||
skillSlashPartial: false,
|
||||
skillSlashPrefix: "/",
|
||||
};
|
||||
|
||||
export function parseBool(v: string | undefined, fallback: boolean): boolean {
|
||||
|
||||
@@ -62,6 +62,10 @@ export function SystemSettingsModal({ open, onOpenChange }: SystemSettingsModalP
|
||||
const [bgProvider, setBgProvider] = useState("");
|
||||
const [bgModel, setBgModel] = useState("");
|
||||
const [skillUploadMaxSize, setSkillUploadMaxSize] = useState("20");
|
||||
const [skillSlashEnabled, setSkillSlashEnabled] = useState(true);
|
||||
const [skillSlashSuggest, setSkillSlashSuggest] = useState(true);
|
||||
const [skillSlashPartial, setSkillSlashPartial] = useState(false);
|
||||
const [skillSlashPrefix, setSkillSlashPrefix] = useState("/");
|
||||
|
||||
const applyConfigs = useCallback((
|
||||
configs: Record<string, string>,
|
||||
@@ -79,6 +83,10 @@ export function SystemSettingsModal({ open, onOpenChange }: SystemSettingsModalP
|
||||
kgMinConfidence: String(kgSettings?.min_confidence ?? 0.75),
|
||||
bgProvider: configs["background.provider"] ?? "", bgModel: configs["background.model"] ?? "",
|
||||
skillUploadMaxSize: configs["skills.max_upload_size_mb"] ?? "20",
|
||||
skillSlashEnabled: parseBool(configs["skills.slash_commands.enabled"], true),
|
||||
skillSlashSuggest: parseBool(configs["skills.slash_commands.suggest_not_found"], true),
|
||||
skillSlashPartial: parseBool(configs["skills.slash_commands.partial_matching"], false),
|
||||
skillSlashPrefix: configs["skills.slash_commands.prefix"] ?? "/",
|
||||
};
|
||||
setInit(s);
|
||||
setEmbProvider(s.embProvider); setEmbModel(s.embModel); setEmbMaxChunkLen(s.embMaxChunkLen); setEmbChunkOverlap(s.embChunkOverlap);
|
||||
@@ -87,6 +95,10 @@ export function SystemSettingsModal({ open, onOpenChange }: SystemSettingsModalP
|
||||
setKgProvider(s.kgProvider); setKgModel(s.kgModel); setKgMinConfidence(s.kgMinConfidence);
|
||||
setBgProvider(s.bgProvider); setBgModel(s.bgModel);
|
||||
setSkillUploadMaxSize(s.skillUploadMaxSize);
|
||||
setSkillSlashEnabled(s.skillSlashEnabled);
|
||||
setSkillSlashSuggest(s.skillSlashSuggest);
|
||||
setSkillSlashPartial(s.skillSlashPartial);
|
||||
setSkillSlashPrefix(s.skillSlashPrefix);
|
||||
resetEmb();
|
||||
}, [resetEmb]);
|
||||
|
||||
@@ -131,6 +143,10 @@ export function SystemSettingsModal({ open, onOpenChange }: SystemSettingsModalP
|
||||
if (bgProvider !== init.bgProvider) updates["background.provider"] = bgProvider;
|
||||
if (bgModel !== init.bgModel) updates["background.model"] = bgModel;
|
||||
if (skillUploadMaxSize !== init.skillUploadMaxSize) updates["skills.max_upload_size_mb"] = skillUploadMaxSize;
|
||||
if (skillSlashEnabled !== init.skillSlashEnabled) updates["skills.slash_commands.enabled"] = String(skillSlashEnabled);
|
||||
if (skillSlashSuggest !== init.skillSlashSuggest) updates["skills.slash_commands.suggest_not_found"] = String(skillSlashSuggest);
|
||||
if (skillSlashPartial !== init.skillSlashPartial) updates["skills.slash_commands.partial_matching"] = String(skillSlashPartial);
|
||||
if (skillSlashPrefix !== init.skillSlashPrefix) updates["skills.slash_commands.prefix"] = skillSlashPrefix.trim() || "/";
|
||||
for (const [key, value] of Object.entries(updates)) await http.put(`/v1/system-configs/${key}`, { value });
|
||||
const kgChanged = kgProvider !== init.kgProvider || kgModel !== init.kgModel || kgMinConfidence !== init.kgMinConfidence;
|
||||
if (kgChanged) {
|
||||
@@ -211,6 +227,14 @@ export function SystemSettingsModal({ open, onOpenChange }: SystemSettingsModalP
|
||||
<SystemSettingsSkillsCard
|
||||
uploadMaxSize={skillUploadMaxSize}
|
||||
setUploadMaxSize={setSkillUploadMaxSize}
|
||||
slashEnabled={skillSlashEnabled}
|
||||
setSlashEnabled={setSkillSlashEnabled}
|
||||
slashSuggest={skillSlashSuggest}
|
||||
setSlashSuggest={setSkillSlashSuggest}
|
||||
slashPartial={skillSlashPartial}
|
||||
setSlashPartial={setSkillSlashPartial}
|
||||
slashPrefix={skillSlashPrefix}
|
||||
setSlashPrefix={setSkillSlashPrefix}
|
||||
/>
|
||||
|
||||
<SystemSettingsCompactionCard
|
||||
|
||||
@@ -3,15 +3,32 @@ import { useTranslation } from "react-i18next";
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { Switch } from "@/components/ui/switch";
|
||||
|
||||
interface SystemSettingsSkillsCardProps {
|
||||
uploadMaxSize: string;
|
||||
setUploadMaxSize: (value: string) => void;
|
||||
slashEnabled: boolean;
|
||||
setSlashEnabled: (value: boolean) => void;
|
||||
slashSuggest: boolean;
|
||||
setSlashSuggest: (value: boolean) => void;
|
||||
slashPartial: boolean;
|
||||
setSlashPartial: (value: boolean) => void;
|
||||
slashPrefix: string;
|
||||
setSlashPrefix: (value: string) => void;
|
||||
}
|
||||
|
||||
export function SystemSettingsSkillsCard({
|
||||
uploadMaxSize,
|
||||
setUploadMaxSize,
|
||||
slashEnabled,
|
||||
setSlashEnabled,
|
||||
slashSuggest,
|
||||
setSlashSuggest,
|
||||
slashPartial,
|
||||
setSlashPartial,
|
||||
slashPrefix,
|
||||
setSlashPrefix,
|
||||
}: SystemSettingsSkillsCardProps) {
|
||||
const { t } = useTranslation("system-settings");
|
||||
|
||||
@@ -24,7 +41,7 @@ export function SystemSettingsSkillsCard({
|
||||
</CardTitle>
|
||||
<CardDescription>{t("skills.description")}</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-2 pt-0">
|
||||
<CardContent className="space-y-4 pt-0">
|
||||
<div className="flex items-start justify-between gap-4">
|
||||
<div className="space-y-0.5">
|
||||
<Label htmlFor="skillUploadMaxSize" className="text-sm font-medium">
|
||||
@@ -42,7 +59,71 @@ export function SystemSettingsSkillsCard({
|
||||
className="w-24 shrink-0 text-base md:text-sm"
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-3 border-t pt-4">
|
||||
<SkillSwitchRow
|
||||
id="skillSlashEnabled"
|
||||
label={t("skills.slashEnabled")}
|
||||
hint={t("skills.slashEnabledHint")}
|
||||
checked={slashEnabled}
|
||||
onCheckedChange={setSlashEnabled}
|
||||
/>
|
||||
<SkillSwitchRow
|
||||
id="skillSlashSuggest"
|
||||
label={t("skills.slashSuggest")}
|
||||
hint={t("skills.slashSuggestHint")}
|
||||
checked={slashSuggest}
|
||||
onCheckedChange={setSlashSuggest}
|
||||
/>
|
||||
<SkillSwitchRow
|
||||
id="skillSlashPartial"
|
||||
label={t("skills.slashPartial")}
|
||||
hint={t("skills.slashPartialHint")}
|
||||
checked={slashPartial}
|
||||
onCheckedChange={setSlashPartial}
|
||||
/>
|
||||
<div className="flex items-start justify-between gap-4">
|
||||
<div className="space-y-0.5">
|
||||
<Label htmlFor="skillSlashPrefix" className="text-sm font-medium">
|
||||
{t("skills.slashPrefix")}
|
||||
</Label>
|
||||
<p className="text-xs text-muted-foreground">{t("skills.slashPrefixHint")}</p>
|
||||
</div>
|
||||
<Input
|
||||
id="skillSlashPrefix"
|
||||
maxLength={1}
|
||||
value={slashPrefix}
|
||||
onChange={(e) => setSlashPrefix(e.target.value.slice(0, 1))}
|
||||
className="w-16 shrink-0 text-center text-base md:text-sm"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
function SkillSwitchRow({
|
||||
id,
|
||||
label,
|
||||
hint,
|
||||
checked,
|
||||
onCheckedChange,
|
||||
}: {
|
||||
id: string;
|
||||
label: string;
|
||||
hint: string;
|
||||
checked: boolean;
|
||||
onCheckedChange: (value: boolean) => void;
|
||||
}) {
|
||||
return (
|
||||
<div className="flex items-start justify-between gap-4">
|
||||
<div className="space-y-0.5">
|
||||
<Label htmlFor={id} className="text-sm font-medium">
|
||||
{label}
|
||||
</Label>
|
||||
<p className="text-xs text-muted-foreground">{hint}</p>
|
||||
</div>
|
||||
<Switch id={id} checked={checked} onCheckedChange={onCheckedChange} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -66,9 +66,17 @@
|
||||
},
|
||||
"skills": {
|
||||
"title": "Skills",
|
||||
"description": "Tenant skill package upload limits.",
|
||||
"description": "Tenant skill package and slash command settings.",
|
||||
"maxUploadSize": "Max upload size",
|
||||
"maxUploadSizeHint": "Per-file ZIP limit in MB. Allowed range: 1-500."
|
||||
"maxUploadSizeHint": "Per-file ZIP limit in MB. Allowed range: 1-500.",
|
||||
"slashEnabled": "Enable slash commands",
|
||||
"slashEnabledHint": "Detect /skill-name and /use skill-name at the start of user prompts.",
|
||||
"slashSuggest": "Suggest similar skills",
|
||||
"slashSuggestHint": "Show close matches when a requested skill is not found.",
|
||||
"slashPartial": "Allow partial matching",
|
||||
"slashPartialHint": "Let unique prefixes like /frontend activate matching skills.",
|
||||
"slashPrefix": "Command prefix",
|
||||
"slashPrefixHint": "Single character used before skill commands."
|
||||
},
|
||||
"compaction": {
|
||||
"title": "Pending Message Compaction",
|
||||
|
||||
@@ -66,9 +66,17 @@
|
||||
},
|
||||
"skills": {
|
||||
"title": "Skills",
|
||||
"description": "Giới hạn tải lên gói skill theo tenant.",
|
||||
"description": "Cài đặt gói skill và slash command theo tenant.",
|
||||
"maxUploadSize": "Dung lượng tải lên tối đa",
|
||||
"maxUploadSizeHint": "Giới hạn ZIP theo từng tệp, tính bằng MB. Khoảng cho phép: 1-500."
|
||||
"maxUploadSizeHint": "Giới hạn ZIP theo từng tệp, tính bằng MB. Khoảng cho phép: 1-500.",
|
||||
"slashEnabled": "Bật slash command",
|
||||
"slashEnabledHint": "Nhận diện /skill-name và /use skill-name ở đầu prompt.",
|
||||
"slashSuggest": "Gợi ý skill gần đúng",
|
||||
"slashSuggestHint": "Hiển thị các skill gần giống khi không tìm thấy skill được gọi.",
|
||||
"slashPartial": "Cho phép khớp một phần",
|
||||
"slashPartialHint": "Cho phép prefix duy nhất như /frontend kích hoạt skill tương ứng.",
|
||||
"slashPrefix": "Prefix command",
|
||||
"slashPrefixHint": "Một ký tự dùng trước skill command."
|
||||
},
|
||||
"compaction": {
|
||||
"title": "Nén tin nhắn chờ",
|
||||
|
||||
@@ -66,9 +66,17 @@
|
||||
},
|
||||
"skills": {
|
||||
"title": "技能",
|
||||
"description": "租户技能包上传限制。",
|
||||
"description": "租户技能包和斜杠命令设置。",
|
||||
"maxUploadSize": "最大上传大小",
|
||||
"maxUploadSizeHint": "单个 ZIP 文件限制,单位 MB。允许范围:1-500。"
|
||||
"maxUploadSizeHint": "单个 ZIP 文件限制,单位 MB。允许范围:1-500。",
|
||||
"slashEnabled": "启用斜杠命令",
|
||||
"slashEnabledHint": "在用户提示开头检测 /skill-name 和 /use skill-name。",
|
||||
"slashSuggest": "建议相似技能",
|
||||
"slashSuggestHint": "找不到请求的技能时显示接近匹配项。",
|
||||
"slashPartial": "允许部分匹配",
|
||||
"slashPartialHint": "允许像 /frontend 这样的唯一前缀激活匹配技能。",
|
||||
"slashPrefix": "命令前缀",
|
||||
"slashPrefixHint": "技能命令前使用的单个字符。"
|
||||
},
|
||||
"compaction": {
|
||||
"title": "待处理消息压缩",
|
||||
|
||||
Reference in new issue
Block a user