Files
openmaic/OpenMAIC/tests/prompts/media-conditional.test.ts
2026-08-16 14:58:47 +08:00

212 lines
7.9 KiB
TypeScript

import { describe, expect, test } from 'vitest';
import { buildPrompt, PROMPT_IDS, processConditionalBlocks } from '@openmaic/generation';
import { buildPrompt as buildAppPrompt, PROMPT_IDS as APP_PROMPT_IDS } from '@/lib/prompts';
function buildOutlinePrompt(
flags: {
hasSourceImages?: boolean;
imageEnabled?: boolean;
videoEnabled?: boolean;
},
promptId:
| 'requirements-to-outlines'
| 'interactive-outlines' = PROMPT_IDS.REQUIREMENTS_TO_OUTLINES,
) {
const imageEnabled = flags.imageEnabled ?? false;
const videoEnabled = flags.videoEnabled ?? false;
const builder =
promptId === 'interactive-outlines'
? (variables: Record<string, unknown>) =>
buildAppPrompt(APP_PROMPT_IDS.INTERACTIVE_OUTLINES, variables)
: (variables: Record<string, unknown>) =>
buildPrompt(PROMPT_IDS.REQUIREMENTS_TO_OUTLINES, variables);
return builder({
requirement: 'Teach water cycle basics',
pdfContent: 'None',
availableImages: flags.hasSourceImages ? '- img_1: water cycle diagram' : 'No images available',
userProfile: '',
researchContext: 'None',
teacherContext: '',
hasSourceImages: flags.hasSourceImages ?? false,
imageEnabled,
videoEnabled,
mediaEnabled: imageEnabled || videoEnabled,
});
}
function buildSlidePrompt(flags: {
imageElementEnabled?: boolean;
generatedImageEnabled?: boolean;
generatedVideoEnabled?: boolean;
}) {
const generatedImageEnabled = flags.generatedImageEnabled ?? false;
const generatedVideoEnabled = flags.generatedVideoEnabled ?? false;
return buildPrompt(PROMPT_IDS.SLIDE_CONTENT, {
title: 'Water Cycle',
description: 'Explain evaporation and condensation',
keyPoints: '1. Evaporation\\n2. Condensation',
assignedImages: flags.imageElementEnabled ? '- img_1: source image' : 'No images available',
canvas_width: 1000,
canvas_height: 562.5,
teacherContext: '',
languageDirective: 'Teach in English.',
imageElementEnabled: flags.imageElementEnabled ?? false,
generatedImageEnabled,
generatedVideoEnabled,
mediaElementEnabled: generatedImageEnabled || generatedVideoEnabled,
});
}
function combined(prompt: { system: string; user: string } | null) {
expect(prompt).not.toBeNull();
return `${prompt!.system}\n${prompt!.user}`;
}
describe('conditional blocks', () => {
test('processConditionalBlocks includes content only when flag is truthy', () => {
expect(processConditionalBlocks('A {{#if enabled}}INCLUDED{{/if}} B', { enabled: true })).toBe(
'A INCLUDED B',
);
expect(processConditionalBlocks('A {{#if enabled}}INCLUDED{{/if}} B', { enabled: false })).toBe(
'A B',
);
});
});
describe('requirements-to-outlines media prompt conditions', () => {
test('omits media generation instructions when image and video generation are disabled', () => {
const text = combined(buildOutlinePrompt({ hasSourceImages: false }));
expect(text).not.toContain('mediaGenerations');
expect(text).not.toContain('suggestedImageIds');
expect(text).not.toContain('gen_img_');
expect(text).not.toContain('gen_vid_');
expect(text).not.toContain('{{');
});
test('includes image generation instructions without video instructions when only images are enabled', () => {
const text = combined(buildOutlinePrompt({ hasSourceImages: true, imageEnabled: true }));
expect(text).toContain('suggestedImageIds');
expect(text).toContain('mediaGenerations');
expect(text).toContain('gen_img_1');
expect(text).not.toContain('gen_vid_');
expect(text).not.toContain('{{');
});
test('includes video generation instructions without image generation placeholders when only video is enabled', () => {
const text = combined(buildOutlinePrompt({ videoEnabled: true }));
expect(text).toContain('mediaGenerations');
expect(text).toContain('gen_vid_1');
expect(text).not.toContain('gen_img_');
expect(text).not.toContain('suggestedImageIds');
expect(text).not.toContain('{{');
});
test('includes both image and video generation instructions when both are enabled', () => {
const text = combined(
buildOutlinePrompt({ hasSourceImages: true, imageEnabled: true, videoEnabled: true }),
);
expect(text).toContain('suggestedImageIds');
expect(text).toContain('mediaGenerations');
expect(text).toContain('gen_img_1');
expect(text).toContain('gen_vid_1');
expect(text).toContain('Content Safety Guidelines');
expect(text).not.toContain('{{');
});
});
describe('interactive-outlines media prompt conditions', () => {
// Interactive Mode (Ultra Mode) uses a separate outline template. It must gate
// the same media snippets as the standard template, or weak local models never
// emit `mediaGenerations` and images silently never generate (issue #626).
const INTERACTIVE = APP_PROMPT_IDS.INTERACTIVE_OUTLINES;
test('omits media generation instructions when image and video generation are disabled', () => {
const text = combined(buildOutlinePrompt({ hasSourceImages: false }, INTERACTIVE));
expect(text).not.toContain('mediaGenerations');
expect(text).not.toContain('gen_img_');
expect(text).not.toContain('gen_vid_');
expect(text).not.toContain('{{');
});
test('includes image generation instructions without video instructions when only images are enabled', () => {
const text = combined(buildOutlinePrompt({ imageEnabled: true }, INTERACTIVE));
expect(text).toContain('mediaGenerations');
expect(text).toContain('gen_img_1');
expect(text).not.toContain('gen_vid_');
expect(text).not.toContain('{{');
});
test('includes video generation instructions without image generation placeholders when only video is enabled', () => {
const text = combined(buildOutlinePrompt({ videoEnabled: true }, INTERACTIVE));
expect(text).toContain('mediaGenerations');
expect(text).toContain('gen_vid_1');
expect(text).not.toContain('gen_img_');
expect(text).not.toContain('{{');
});
test('includes both image and video instructions plus safety guidelines when both are enabled', () => {
const text = combined(
buildOutlinePrompt({ imageEnabled: true, videoEnabled: true }, INTERACTIVE),
);
expect(text).toContain('mediaGenerations');
expect(text).toContain('gen_img_1');
expect(text).toContain('gen_vid_1');
expect(text).toContain('Content Safety Guidelines');
expect(text).not.toContain('{{');
});
});
describe('slide-content media prompt conditions', () => {
test('omits image and video element rules when no media resources are available', () => {
const text = combined(buildSlidePrompt({}));
expect(text).not.toContain('ImageElement');
expect(text).not.toContain('VideoElement');
expect(text).not.toContain('gen_img_');
expect(text).not.toContain('gen_vid_');
expect(text).not.toContain('{{');
});
test('allows source images without exposing generated image placeholders', () => {
const text = combined(buildSlidePrompt({ imageElementEnabled: true }));
expect(text).toContain('ImageElement');
expect(text).toContain('img_1');
expect(text).not.toContain('gen_img_');
expect(text).not.toContain('VideoElement');
expect(text).not.toContain('{{');
});
test('allows generated videos without exposing image element rules', () => {
const text = combined(buildSlidePrompt({ generatedVideoEnabled: true }));
expect(text).toContain('VideoElement');
expect(text).toContain('mediaRef');
expect(text).not.toContain('"src": "gen_vid_1"');
expect(text).not.toContain('ImageElement');
expect(text).not.toContain('gen_img_');
expect(text).not.toContain('{{');
});
test('is shorter when all media rules are omitted', () => {
const noMedia = combined(buildSlidePrompt({}));
const allMedia = combined(
buildSlidePrompt({
imageElementEnabled: true,
generatedImageEnabled: true,
generatedVideoEnabled: true,
}),
);
expect(noMedia.length).toBeLessThan(allMedia.length - 1000);
});
});