mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-05 09:31:27 +00:00
fix(video): skip delivering tasks in active-task prompt guard (#96018)
Merged via squash.
Prepared head SHA: cbf32de95e
Co-authored-by: palomyates516-alt <231502129+palomyates516-alt@users.noreply.github.com>
Co-authored-by: vincentkoc <25068+vincentkoc@users.noreply.github.com>
Reviewed-by: @vincentkoc
This commit is contained in:
committed by
GitHub
parent
e4763b0631
commit
ae9474b5fd
126
src/agents/media-generation-task-status-shared.test.ts
Normal file
126
src/agents/media-generation-task-status-shared.test.ts
Normal file
@@ -0,0 +1,126 @@
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import type { TaskRecord } from "../tasks/task-registry.types.js";
|
||||
import {
|
||||
buildActiveMediaGenerationTaskPromptContextForSession,
|
||||
findActiveMediaGenerationTaskForSession,
|
||||
findDuplicateGuardMediaGenerationTaskForSession,
|
||||
listActiveMediaGenerationTasksForSession,
|
||||
MEDIA_GENERATION_DELIVERING_COMPLETION_PROGRESS,
|
||||
resetRecentMediaGenerationDuplicateGuardsForTests,
|
||||
} from "./media-generation-task-status-shared.js";
|
||||
|
||||
const taskRuntimeInternalMocks = vi.hoisted(() => ({
|
||||
listFreshTasksForOwnerKey: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("../tasks/runtime-internal.js", () => taskRuntimeInternalMocks);
|
||||
|
||||
function makeTask(overrides: Partial<TaskRecord> = {}): TaskRecord {
|
||||
const now = Date.now();
|
||||
return {
|
||||
taskId: "task-1",
|
||||
runtime: "cli",
|
||||
taskKind: "video-generate",
|
||||
sourceId: "video-generate:byteplus",
|
||||
requesterSessionKey: "session/A",
|
||||
ownerKey: "session/A",
|
||||
scopeKind: "session",
|
||||
runId: "run-1",
|
||||
task: "generate clip 01",
|
||||
status: "running",
|
||||
deliveryStatus: "not_applicable",
|
||||
notifyPolicy: "silent",
|
||||
createdAt: now,
|
||||
startedAt: now,
|
||||
lastEventAt: now,
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
resetRecentMediaGenerationDuplicateGuardsForTests();
|
||||
taskRuntimeInternalMocks.listFreshTasksForOwnerKey.mockReset();
|
||||
});
|
||||
|
||||
describe("media generation delivery-phase prompt guard", () => {
|
||||
it("does not warn about a task waiting only for completion delivery", () => {
|
||||
taskRuntimeInternalMocks.listFreshTasksForOwnerKey.mockReturnValue([
|
||||
makeTask({ progressSummary: MEDIA_GENERATION_DELIVERING_COMPLETION_PROGRESS }),
|
||||
]);
|
||||
|
||||
expect(
|
||||
buildActiveMediaGenerationTaskPromptContextForSession({
|
||||
sessionKey: "session/A",
|
||||
taskKind: "video-generate",
|
||||
sourcePrefix: "video-generate",
|
||||
nounLabel: "video",
|
||||
toolName: "video_generate",
|
||||
completionLabel: "video",
|
||||
}),
|
||||
).toBeUndefined();
|
||||
});
|
||||
|
||||
it("still warns while media generation is running", () => {
|
||||
taskRuntimeInternalMocks.listFreshTasksForOwnerKey.mockReturnValue([
|
||||
makeTask({ progressSummary: "Generating video" }),
|
||||
]);
|
||||
|
||||
expect(
|
||||
buildActiveMediaGenerationTaskPromptContextForSession({
|
||||
sessionKey: "session/A",
|
||||
taskKind: "video-generate",
|
||||
sourcePrefix: "video-generate",
|
||||
nounLabel: "video",
|
||||
toolName: "video_generate",
|
||||
completionLabel: "video",
|
||||
}),
|
||||
).toContain("Do not call `video_generate` again for the same request");
|
||||
});
|
||||
|
||||
it("keeps delivery-phase tasks available to duplicate/status lookups", () => {
|
||||
const task = makeTask({ progressSummary: MEDIA_GENERATION_DELIVERING_COMPLETION_PROGRESS });
|
||||
taskRuntimeInternalMocks.listFreshTasksForOwnerKey.mockReturnValue([task]);
|
||||
|
||||
expect(
|
||||
listActiveMediaGenerationTasksForSession({
|
||||
sessionKey: "session/A",
|
||||
taskKind: "video-generate",
|
||||
sourcePrefix: "video-generate",
|
||||
}),
|
||||
).toEqual([task]);
|
||||
expect(
|
||||
findActiveMediaGenerationTaskForSession({
|
||||
sessionKey: "session/A",
|
||||
taskKind: "video-generate",
|
||||
sourcePrefix: "video-generate",
|
||||
}),
|
||||
).toEqual(task);
|
||||
});
|
||||
|
||||
it("blocks the same prompt while allowing a distinct prompt", () => {
|
||||
const task = makeTask({
|
||||
task: "generate clip 01",
|
||||
progressSummary: MEDIA_GENERATION_DELIVERING_COMPLETION_PROGRESS,
|
||||
});
|
||||
taskRuntimeInternalMocks.listFreshTasksForOwnerKey.mockReturnValue([task]);
|
||||
|
||||
expect(
|
||||
findDuplicateGuardMediaGenerationTaskForSession({
|
||||
sessionKey: "session/A",
|
||||
taskKind: "video-generate",
|
||||
sourcePrefix: "video-generate",
|
||||
taskLabel: "generate clip 01",
|
||||
maxAgeMs: 120_000,
|
||||
}),
|
||||
).toEqual(task);
|
||||
expect(
|
||||
findDuplicateGuardMediaGenerationTaskForSession({
|
||||
sessionKey: "session/A",
|
||||
taskKind: "video-generate",
|
||||
sourcePrefix: "video-generate",
|
||||
taskLabel: "generate clip 02",
|
||||
maxAgeMs: 120_000,
|
||||
}),
|
||||
).toBeUndefined();
|
||||
});
|
||||
});
|
||||
@@ -14,6 +14,10 @@ import type { TaskRecord } from "../tasks/task-registry.types.js";
|
||||
import { buildSessionAsyncTaskStatusDetails } from "./session-async-task-status.js";
|
||||
import { stableStringify } from "./stable-stringify.js";
|
||||
|
||||
/** Marks media as ready while requester delivery is still being confirmed. */
|
||||
export const MEDIA_GENERATION_DELIVERING_COMPLETION_PROGRESS =
|
||||
"Generated media; delivering completion";
|
||||
|
||||
type RecentMediaGenerationTaskStart = {
|
||||
task: TaskRecord;
|
||||
requestKey?: string;
|
||||
@@ -299,6 +303,7 @@ export function findActiveMediaGenerationTaskForSession(params: {
|
||||
taskKind: string;
|
||||
sourcePrefix: string;
|
||||
taskLabel?: string;
|
||||
excludeDeliveringCompletion?: boolean;
|
||||
}): TaskRecord | undefined {
|
||||
return listActiveMediaGenerationTasksForSession(params)[0];
|
||||
}
|
||||
@@ -309,6 +314,7 @@ export function listActiveMediaGenerationTasksForSession(params: {
|
||||
taskKind: string;
|
||||
sourcePrefix: string;
|
||||
taskLabel?: string;
|
||||
excludeDeliveringCompletion?: boolean;
|
||||
}): TaskRecord[] {
|
||||
const sessionKey = normalizeOptionalString(params.sessionKey);
|
||||
if (!sessionKey) {
|
||||
@@ -331,6 +337,12 @@ export function listActiveMediaGenerationTasksForSession(params: {
|
||||
if (taskLabel && !mediaGenerationTaskLabelMatches(task, taskLabel)) {
|
||||
return false;
|
||||
}
|
||||
if (
|
||||
params.excludeDeliveringCompletion &&
|
||||
task.progressSummary === MEDIA_GENERATION_DELIVERING_COMPLETION_PROGRESS
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
return [
|
||||
@@ -456,6 +468,7 @@ export function buildActiveMediaGenerationTaskPromptContextForSession(params: {
|
||||
sessionKey: params.sessionKey,
|
||||
taskKind: params.taskKind,
|
||||
sourcePrefix: params.sourcePrefix,
|
||||
excludeDeliveringCompletion: true,
|
||||
});
|
||||
if (!task) {
|
||||
return undefined;
|
||||
|
||||
@@ -34,6 +34,7 @@ import {
|
||||
type AgentGeneratedAttachment,
|
||||
} from "../generated-attachments.js";
|
||||
import { formatAgentInternalEventsForPrompt, type AgentInternalEvent } from "../internal-events.js";
|
||||
import { MEDIA_GENERATION_DELIVERING_COMPLETION_PROGRESS } from "../media-generation-task-status-shared.js";
|
||||
import {
|
||||
deliverSubagentAnnouncement,
|
||||
loadRequesterSessionEntry,
|
||||
@@ -447,7 +448,7 @@ export function scheduleMediaGenerationTaskCompletion<
|
||||
try {
|
||||
params.lifecycle.recordTaskProgress({
|
||||
handle: params.handle,
|
||||
progressSummary: "Generated media; delivering completion",
|
||||
progressSummary: MEDIA_GENERATION_DELIVERING_COMPLETION_PROGRESS,
|
||||
});
|
||||
} catch (error) {
|
||||
params.onWakeFailure(`${params.toolName} completion progress update failed`, {
|
||||
|
||||
@@ -646,6 +646,7 @@ export function createMusicGenerateTool(options?: {
|
||||
|
||||
const activeDuplicateGuardResult = createMusicGenerateDuplicateGuardResult(
|
||||
options?.agentSessionKey,
|
||||
{ prompt },
|
||||
);
|
||||
if (activeDuplicateGuardResult) {
|
||||
return activeDuplicateGuardResult;
|
||||
@@ -703,7 +704,7 @@ export function createMusicGenerateTool(options?: {
|
||||
});
|
||||
const duplicateGuardResult = createMusicGenerateDuplicateGuardResult(
|
||||
options?.agentSessionKey,
|
||||
{ requestKey },
|
||||
{ prompt, requestKey },
|
||||
);
|
||||
if (duplicateGuardResult) {
|
||||
return duplicateGuardResult;
|
||||
|
||||
@@ -1005,6 +1005,7 @@ export function createVideoGenerateTool(options?: {
|
||||
|
||||
const activeDuplicateGuardResult = createVideoGenerateDuplicateGuardResult(
|
||||
options?.agentSessionKey,
|
||||
{ prompt },
|
||||
);
|
||||
if (activeDuplicateGuardResult) {
|
||||
return activeDuplicateGuardResult;
|
||||
@@ -1113,7 +1114,7 @@ export function createVideoGenerateTool(options?: {
|
||||
});
|
||||
const duplicateGuardResult = createVideoGenerateDuplicateGuardResult(
|
||||
options?.agentSessionKey,
|
||||
{ requestKey },
|
||||
{ prompt, requestKey },
|
||||
);
|
||||
if (duplicateGuardResult) {
|
||||
return duplicateGuardResult;
|
||||
|
||||
Reference in New Issue
Block a user