212 lines
7.9 KiB
TypeScript
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);
|
|
});
|
|
});
|