feat(ai): add opt-in auto intent routing for the assistant

This commit is contained in:
Abeautifulsnow authored and GitHub committed 2026-09-17 12:53:55 +08:00
1 parent 023f0074bc
commit 3f3c21aae9
26 files changed
+1454 -16

No files matched your search

+40
View File
@@ -1678,6 +1678,7 @@ describe("settingsStore activeModel lifecycle", () => {
active: undefined,
effortPreferences: [],
defaultMode: "ask",
defaultAutoRouting: false,
restoreLastConversation: false,
}),
);
@@ -1766,6 +1767,7 @@ describe("settingsStore activeModel lifecycle", () => {
},
],
defaultMode: "ask",
defaultAutoRouting: false,
restoreLastConversation: false,
});
});
@@ -1875,4 +1877,42 @@ describe("settingsStore defaultAiMode lifecycle", () => {
await vi.waitFor(() => expect(saveAiChatSelection).toHaveBeenCalled());
expect(saveAiChatSelection.mock.calls[0][0]).toMatchObject({ defaultMode: "agent" });
});
it("falls back to disabled auto routing when the saved chat selection has none", async () => {
vi.doMock("@/lib/backend/api", () => ({
loadAiConfigs: vi.fn().mockResolvedValue([]),
loadAiConfig: vi.fn().mockResolvedValue(null),
loadAiProviderConfigs: vi.fn().mockResolvedValue(null),
loadAiChatSelection: vi.fn().mockResolvedValue(null),
saveAiChatSelection: vi.fn().mockResolvedValue(undefined),
}));
const { useSettingsStore } = await import("@/stores/settingsStore");
const store = useSettingsStore();
await store.initAiConfigs();
expect(store.defaultAutoRouting).toBe(false);
});
it("restores and persists the default auto-routing preference", async () => {
const saveAiChatSelection = vi.fn().mockResolvedValue(undefined);
vi.doMock("@/lib/backend/api", () => ({
loadAiConfigs: vi.fn().mockResolvedValue([]),
loadAiConfig: vi.fn().mockResolvedValue(null),
loadAiProviderConfigs: vi.fn().mockResolvedValue(null),
loadAiChatSelection: vi.fn().mockResolvedValue({ version: 1, effortPreferences: [], defaultAutoRouting: true }),
saveAiChatSelection,
}));
const { useSettingsStore } = await import("@/stores/settingsStore");
const store = useSettingsStore();
await store.initAiConfigs();
expect(store.defaultAutoRouting).toBe(true);
store.setDefaultAutoRouting(false);
store.setDefaultAutoRouting(true);
await vi.waitFor(() => expect(saveAiChatSelection).toHaveBeenLastCalledWith(expect.objectContaining({ defaultAutoRouting: true })));
});
});
+13
View File
@@ -1729,6 +1729,9 @@ export const useSettingsStore = defineStore("settings", () => {
const activeModel = ref<{ configId: string; modelId: string } | null>(null);
const effortPreferences = ref<AiModelEffortPreference[]>([]);
const defaultAiMode = ref<AiAssistantMode>("ask");
// Opt-in (#9118): new conversations land on the `auto` picker entry only when
// the user turned the default on in Settings > AI.
const defaultAutoRouting = ref(false);
const restoreLastConversation = ref(false);
// Per-db_type prompt template defaults (explicit opt-in) and last-used
// fallback; both resolved when an AI panel mounts or its namespace changes.
@@ -1933,6 +1936,7 @@ export const useSettingsStore = defineStore("settings", () => {
const savedSelection = await api.loadAiChatSelection().catch(() => null);
effortPreferences.value = (savedSelection?.effortPreferences ?? []).filter((preference) => aiConfigs.value.some((config) => config.id === preference.configId));
defaultAiMode.value = savedSelection?.defaultMode ?? "ask";
defaultAutoRouting.value = savedSelection?.defaultAutoRouting ?? false;
restoreLastConversation.value = savedSelection?.restoreLastConversation ?? false;
aiDefaultTemplatesByDbType.value = normalizeTemplateIdsByDbType(savedSelection?.defaultTemplatesByDbType);
aiLastUsedTemplatesByDbType.value = normalizeTemplateIdsByDbType(savedSelection?.lastUsedTemplatesByDbType);
@@ -2074,6 +2078,12 @@ export const useSettingsStore = defineStore("settings", () => {
persistAiChatSelection();
}
function setDefaultAutoRouting(value: boolean) {
if (value === defaultAutoRouting.value) return;
defaultAutoRouting.value = value;
persistAiChatSelection();
}
function setRestoreLastConversation(value: boolean) {
if (value === restoreLastConversation.value) return;
restoreLastConversation.value = value;
@@ -2140,6 +2150,7 @@ export const useSettingsStore = defineStore("settings", () => {
selection: { ...preference.selection },
})),
defaultMode: defaultAiMode.value,
defaultAutoRouting: defaultAutoRouting.value,
restoreLastConversation: restoreLastConversation.value,
// Match the backend's skip_serializing_if(empty): omit the per-db_type
// records entirely while nothing is configured so the payload stays
@@ -2533,6 +2544,8 @@ export const useSettingsStore = defineStore("settings", () => {
activeEffort,
defaultAiMode,
setDefaultAiMode,
defaultAutoRouting,
setDefaultAutoRouting,
restoreLastConversation,
setRestoreLastConversation,
aiDefaultTemplatesByDbType,