Merge remote-tracking branch 'origin/master' into ji
This commit is contained in:
commit
4761677676
74 files changed
+9770
-2887
No files matched your search
@@ -21,6 +21,13 @@ import {
|
||||
CardTitle,
|
||||
} from "@/components/ui/card";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from "@/components/ui/select";
|
||||
import { Switch } from "@/components/ui/switch";
|
||||
import {
|
||||
useCuratorConfig,
|
||||
@@ -31,6 +38,7 @@ import {
|
||||
} from "@/core/curator/hooks";
|
||||
import type { CuratorConfig } from "@/core/curator/types";
|
||||
import { useI18n } from "@/core/i18n/hooks";
|
||||
import { useModels } from "@/core/models/hooks";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
export function SkillEvolutionPanel() {
|
||||
@@ -68,6 +76,7 @@ function StatusCard() {
|
||||
const { t } = useI18n();
|
||||
const tt = t.settings.skillEvolution.status;
|
||||
const { status, isLoading } = useCuratorStatus();
|
||||
const { config } = useCuratorConfig();
|
||||
const runCurator = useRunCurator();
|
||||
|
||||
const lastRunDisplay = useMemo(() => {
|
||||
@@ -79,10 +88,25 @@ function StatusCard() {
|
||||
}
|
||||
}, [status?.last_run_at, tt.lastRunNever]);
|
||||
|
||||
// 总开关或 curator 子开关任一关闭时,手动触发跑不起来 —— 直接禁用按钮。
|
||||
const runDisabled = !config || !config.enabled || !config.curator.enabled;
|
||||
|
||||
const handleRun = async () => {
|
||||
try {
|
||||
await runCurator.mutateAsync();
|
||||
toast.success(tt.runStarted);
|
||||
const res = await runCurator.mutateAsync();
|
||||
if (res.triggered) {
|
||||
toast.success(tt.runStarted);
|
||||
} else {
|
||||
// 后端预检拦下了(开关未开 / 已有任务在跑)—— 给出对应原因,而不是误报"已开始"。
|
||||
const reasonMsg: Record<string, string> = {
|
||||
evolution_disabled: tt.runBlockedEvolutionDisabled,
|
||||
curator_disabled: tt.runBlockedCuratorDisabled,
|
||||
already_running: tt.runBlockedAlreadyRunning,
|
||||
};
|
||||
toast.warning(
|
||||
reasonMsg[res.reason ?? ""] ?? tt.runBlockedEvolutionDisabled,
|
||||
);
|
||||
}
|
||||
} catch (err) {
|
||||
toast.error(err instanceof Error ? err.message : String(err));
|
||||
}
|
||||
@@ -107,7 +131,8 @@ function StatusCard() {
|
||||
<Button
|
||||
size="sm"
|
||||
onClick={handleRun}
|
||||
disabled={runCurator.isPending}
|
||||
disabled={runCurator.isPending || runDisabled}
|
||||
title={runDisabled ? tt.runDisabledHint : undefined}
|
||||
>
|
||||
{runCurator.isPending ? (
|
||||
<Loader2Icon className="size-4 animate-spin" />
|
||||
@@ -154,6 +179,73 @@ function StatusCard() {
|
||||
</span>
|
||||
</span>
|
||||
</StatRow>
|
||||
<StatRow label={tt.lastActions} className="sm:col-span-2">
|
||||
<div className="flex flex-col gap-2 text-xs">
|
||||
<div>
|
||||
<div className="font-medium">{tt.llmSuggested}</div>
|
||||
{status.last_consolidation_suggestions.consolidations.length ===
|
||||
0 &&
|
||||
status.last_consolidation_suggestions.prunings.length === 0 ? (
|
||||
<span className="text-muted-foreground">
|
||||
{tt.noActions}
|
||||
</span>
|
||||
) : (
|
||||
<ul className="text-muted-foreground mt-0.5 list-disc pl-4">
|
||||
{status.last_consolidation_suggestions.consolidations.map(
|
||||
(c, i) => (
|
||||
<li key={`c${i}`}>
|
||||
{(c.from ?? "?") + " → " + (c.into ?? "?")}
|
||||
{c.reason ? `(${c.reason})` : ""}
|
||||
</li>
|
||||
),
|
||||
)}
|
||||
{status.last_consolidation_suggestions.prunings.map(
|
||||
(p, i) => (
|
||||
<li key={`p${i}`}>
|
||||
{p.name ?? "?"}
|
||||
{p.reason ? `(${p.reason})` : ""}
|
||||
</li>
|
||||
),
|
||||
)}
|
||||
</ul>
|
||||
)}
|
||||
</div>
|
||||
<div>
|
||||
<div className="font-medium">{tt.actuallyDid}</div>
|
||||
{status.last_actual_actions.length === 0 ? (
|
||||
<span className="text-muted-foreground">
|
||||
{tt.noActions}
|
||||
</span>
|
||||
) : (
|
||||
<ul className="mt-0.5 flex flex-col gap-1">
|
||||
{status.last_actual_actions.map((a, i) => (
|
||||
<li
|
||||
key={i}
|
||||
className="flex flex-wrap items-center gap-1.5"
|
||||
>
|
||||
<span className="font-mono">{a.tool ?? "?"}</span>
|
||||
{typeof a.args?.name === "string" && (
|
||||
<span className="text-muted-foreground">
|
||||
{String(a.args.name)}
|
||||
</span>
|
||||
)}
|
||||
<Badge
|
||||
variant={
|
||||
a.status === "error"
|
||||
? "destructive"
|
||||
: "secondary"
|
||||
}
|
||||
className="px-1 text-[10px]"
|
||||
>
|
||||
{a.status}
|
||||
</Badge>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</StatRow>
|
||||
</div>
|
||||
) : null}
|
||||
</CardContent>
|
||||
@@ -178,11 +270,15 @@ function StatRow({
|
||||
);
|
||||
}
|
||||
|
||||
/** Select 不允许空字符串 value,用此哨兵代表"用主模型"(对应 model_name=null)。 */
|
||||
const DEFAULT_MODEL_VALUE = "__default__";
|
||||
|
||||
function ConfigCard() {
|
||||
const { t } = useI18n();
|
||||
const tt = t.settings.skillEvolution.config;
|
||||
const { config, isLoading } = useCuratorConfig();
|
||||
const updateConfig = useUpdateCuratorConfig();
|
||||
const { models } = useModels();
|
||||
|
||||
const [draft, setDraft] = useState<CuratorConfig | null>(null);
|
||||
useEffect(() => {
|
||||
@@ -311,22 +407,35 @@ function ConfigCard() {
|
||||
/>
|
||||
</FieldRow>
|
||||
<FieldRow
|
||||
label={tt.moderationModelLabel}
|
||||
helper={tt.moderationModelHelper}
|
||||
label={tt.curatorModelLabel}
|
||||
helper={tt.curatorModelHelper}
|
||||
>
|
||||
<Input
|
||||
type="text"
|
||||
className="max-w-[20rem]"
|
||||
value={draft.moderation_model_name ?? ""}
|
||||
placeholder=""
|
||||
onChange={(e) =>
|
||||
<Select
|
||||
value={draft.curator.model_name ?? DEFAULT_MODEL_VALUE}
|
||||
onValueChange={(v) =>
|
||||
setDraft({
|
||||
...draft,
|
||||
moderation_model_name:
|
||||
e.target.value.trim() === "" ? null : e.target.value,
|
||||
curator: {
|
||||
...draft.curator,
|
||||
model_name: v === DEFAULT_MODEL_VALUE ? null : v,
|
||||
},
|
||||
})
|
||||
}
|
||||
/>
|
||||
>
|
||||
<SelectTrigger className="max-w-[20rem]">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value={DEFAULT_MODEL_VALUE}>
|
||||
{tt.curatorModelDefault}
|
||||
</SelectItem>
|
||||
{models.map((m) => (
|
||||
<SelectItem key={m.name} value={m.name}>
|
||||
{m.display_name || m.name}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</FieldRow>
|
||||
|
||||
<div className="flex justify-end gap-2 pt-2">
|
||||
|
||||
Reference in new issue
Block a user