feat(FN-4942): complete Step 1 — auto-attach summarizer in createTask
Fusion-Task-Id: FN-4942 Fusion-Task-Lineage: 20865705-e5ec-4261-932c-4d47c2d8dc12
This commit is contained in:
committed by
gsxdsm
parent
f5cd0dd472
commit
fc98f46196
@@ -1,5 +1,17 @@
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
const { summarizeTitleMock } = vi.hoisted(() => ({
|
||||
summarizeTitleMock: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("../ai-summarize.js", async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import("../ai-summarize.js")>();
|
||||
return {
|
||||
...actual,
|
||||
summarizeTitle: summarizeTitleMock,
|
||||
};
|
||||
});
|
||||
|
||||
import { setTaskCreatedHook } from "../task-creation-hooks.js";
|
||||
import { createTaskStoreTestHarness } from "./store-test-helpers.js";
|
||||
|
||||
@@ -8,6 +20,7 @@ describe("task creation hook", () => {
|
||||
|
||||
beforeEach(async () => {
|
||||
setTaskCreatedHook(undefined);
|
||||
summarizeTitleMock.mockReset();
|
||||
await harness.beforeEach();
|
||||
});
|
||||
|
||||
@@ -202,5 +215,40 @@ describe("task creation hook", () => {
|
||||
expect(hook).toHaveBeenCalledTimes(1);
|
||||
expect(onSummarize).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("auto-attaches summarizer from settings when options are omitted", async () => {
|
||||
const store = harness.store();
|
||||
const hook = vi.fn();
|
||||
summarizeTitleMock.mockResolvedValue("Auto Generated Title");
|
||||
setTaskCreatedHook(hook);
|
||||
|
||||
await store.updateSettings({
|
||||
autoSummarizeTitles: true,
|
||||
titleSummarizerProvider: "openai",
|
||||
titleSummarizerModelId: "gpt-5-mini",
|
||||
});
|
||||
|
||||
await store.createTask({ description: "a".repeat(201) });
|
||||
|
||||
expect(hook).not.toHaveBeenCalled();
|
||||
await vi.waitFor(() => {
|
||||
expect(hook).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
expect(summarizeTitleMock).toHaveBeenCalledTimes(1);
|
||||
expect(hook).toHaveBeenCalledWith(expect.objectContaining({ title: "Auto Generated Title" }), store);
|
||||
});
|
||||
|
||||
it("fires hook synchronously when auto-summarize is enabled but no model resolves", async () => {
|
||||
const store = harness.store();
|
||||
const hook = vi.fn();
|
||||
setTaskCreatedHook(hook);
|
||||
|
||||
await store.updateSettings({ autoSummarizeTitles: true });
|
||||
|
||||
await store.createTask({ description: "b".repeat(201) });
|
||||
|
||||
expect(hook).toHaveBeenCalledTimes(1);
|
||||
expect(summarizeTitleMock).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -33,8 +33,9 @@ import { ensureMemoryFileWithBackend } from "./project-memory.js";
|
||||
import { runCommandAsync } from "./run-command.js";
|
||||
import { createLogger } from "./logger.js";
|
||||
import { validateNodeOverrideChange } from "./node-override-guard.js";
|
||||
import { sanitizeTitle } from "./ai-summarize.js";
|
||||
import { sanitizeTitle, summarizeTitle } from "./ai-summarize.js";
|
||||
import { extractTaskIdTokens, normalizeTitleForTaskId } from "./task-title-id-drift.js";
|
||||
import { resolveTitleSummarizerSettingsModel } from "./model-resolution.js";
|
||||
import { getErrorMessage } from "./error-message.js";
|
||||
import { getTaskCreatedHook } from "./task-creation-hooks.js";
|
||||
import { assertProjectRootDir } from "./project-root-guard.js";
|
||||
@@ -2940,13 +2941,41 @@ export class TaskStore extends EventEmitter<TaskStoreEvents> {
|
||||
);
|
||||
}
|
||||
|
||||
let resolvedSettings = options?.settings;
|
||||
if (!resolvedSettings) {
|
||||
try {
|
||||
resolvedSettings = await this.getSettings();
|
||||
} catch {
|
||||
resolvedSettings = {};
|
||||
}
|
||||
}
|
||||
|
||||
let onSummarize = options?.onSummarize;
|
||||
if (!onSummarize && resolvedSettings?.autoSummarizeTitles === true) {
|
||||
const summarizerModel = resolveTitleSummarizerSettingsModel(resolvedSettings);
|
||||
if (summarizerModel.provider && summarizerModel.modelId) {
|
||||
onSummarize = async (description: string) => {
|
||||
try {
|
||||
return await summarizeTitle(
|
||||
description,
|
||||
this.getRootDir(),
|
||||
summarizerModel.provider,
|
||||
summarizerModel.modelId,
|
||||
);
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Determine if we should try to summarize the title
|
||||
const title = input.title?.trim() || undefined;
|
||||
const shouldSummarize =
|
||||
!title &&
|
||||
input.description.length > 200 &&
|
||||
(input.summarize === true || options?.settings?.autoSummarizeTitles === true);
|
||||
const hasPendingSummarization = shouldSummarize && typeof options?.onSummarize === "function";
|
||||
(input.summarize === true || resolvedSettings?.autoSummarizeTitles === true);
|
||||
const hasPendingSummarization = shouldSummarize && typeof onSummarize === "function";
|
||||
const shouldInvokeTaskCreatedHook = options?.invokeTaskCreatedHook !== false;
|
||||
|
||||
// Determine enabledWorkflowSteps: explicit input takes precedence, otherwise auto-apply default-on steps
|
||||
@@ -2994,7 +3023,7 @@ export class TaskStore extends EventEmitter<TaskStoreEvents> {
|
||||
const id = task.id;
|
||||
Promise.resolve().then(async () => {
|
||||
try {
|
||||
const generatedTitle = await options.onSummarize!(input.description);
|
||||
const generatedTitle = await onSummarize!(input.description);
|
||||
const sanitizedTitle = sanitizeTitle(generatedTitle);
|
||||
if (sanitizedTitle) {
|
||||
const currentTask = this.readTaskFromDb(id);
|
||||
@@ -3006,7 +3035,7 @@ export class TaskStore extends EventEmitter<TaskStoreEvents> {
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
const autoEnabled = options?.settings?.autoSummarizeTitles === true;
|
||||
const autoEnabled = resolvedSettings?.autoSummarizeTitles === true;
|
||||
const errorMessage = err instanceof Error ? err.message : String(err);
|
||||
storeLog.warn(
|
||||
`Title summarization failed for task ${id}: ${errorMessage} (desc length: ${input.description.length}, auto-summarize: ${autoEnabled})`,
|
||||
@@ -3036,7 +3065,7 @@ export class TaskStore extends EventEmitter<TaskStoreEvents> {
|
||||
});
|
||||
}
|
||||
}).catch((err) => {
|
||||
const autoEnabled = options?.settings?.autoSummarizeTitles === true;
|
||||
const autoEnabled = resolvedSettings?.autoSummarizeTitles === true;
|
||||
storeLog.error("Unexpected title summarization promise-chain failure", {
|
||||
taskId: id,
|
||||
descriptionLength: input.description.length,
|
||||
|
||||
Reference in New Issue
Block a user