Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,20 @@

All notable changes to OpenReels will be documented in this file.

## [0.18.0] - 2026-04-10

### Added
- **Creative direction file** (`--direction <file>`): attach a free-form markdown brief describing visual style, script notes, music mood, and scene ideas. The AI reads it like a human editor would, constraining the DirectorScore while preserving creative judgment. Works across CLI, API (`direction` field), and web UI (textarea in Advanced panel).
- **Score replay** (`--score <path>`): reuse a previous `score.json` to skip research, director, and critic stages. Re-renders with different provider config without regenerating the creative plan. Saves $0.50-2.00 and 10-30 seconds per replay.
- **Replay-aware cost estimation**: cost estimates during replay accurately omit skipped LLM calls (research, creative director, critic), so users see the true execution cost.
- **Direction provenance in log.json**: direction text, replay flag, and source score path are recorded in the run log for auditability.
- **Example creative brief**: `examples/direction-brief.md` demonstrates the direction file format with a deep-sea exploration theme.

### For contributors
- `PipelineOptions` has new optional fields: `direction?: string` and `replayScore?: DirectorScore`.
- `estimateCost()` accepts an optional `{ replay?: boolean }` options object as the last parameter.
- `CreateJobBody` (server) and `CreateJobRequest` (web) support `direction` and `score` fields.

## [0.17.0] - 2026-04-10

### Added
Expand Down
1 change: 1 addition & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ web/ # React + Tailwind SPA (Vite)
hooks/ # useApi, useSSE
components/ # Layout, StageCard, pipeline/, shadcn/ui
lib/ # scene-assets, utils
examples/ # direction-brief.md (sample creative brief for --direction)
prompts/ # system prompts for each agent + playbook
fixtures/ # sample DirectorScore JSONs
```
Expand Down
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,12 @@ pnpm start "your topic" --dry-run

# Specific archetype and provider combo
pnpm start "your topic" --archetype anime_illustration --provider openai

# Creative direction file (guide the AI with a brief)
pnpm start "deep sea exploration" --direction examples/direction-brief.md

# Replay from a previous score (skip research + director, re-render)
pnpm start "your topic" --score output/2026-04-10-111939-.../score.json
```

### API keys
Expand Down Expand Up @@ -180,6 +186,8 @@ pnpm start "your topic" --archetype anime_illustration --provider openai
| `--stock-max-attempts <n>` | Max stock API calls per scene | `4` |
| `--video-model <model>` | Video model override | provider default |
| `--kokoro-voice <voice>` | Kokoro voice preset | `af_heart` |
| `--direction <file>` | Creative brief file (markdown) to guide the AI | — |
| `--score <path>` | Replay from a saved `score.json`, skipping research + director | — |
| `-y, --yes` | Auto-confirm cost estimation (Docker/CI) | off |

## Cost transparency
Expand Down
2 changes: 1 addition & 1 deletion TODOS.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@
**Priority:** P3
Deferred from plan: tsensei-main-design-20260401-202558.md

- [ ] **Retry from failed stage** — Add POST /api/v1/jobs/:id/retry endpoint and pipeline resume capability. Requires: orchestrator accepts "start from stage" parameter, worker loads prior stage artifacts from disk, new API endpoint creates a re-run job. UI shows retry button in failed state.
- [ ] **Retry from failed stage** — Add POST /api/v1/jobs/:id/retry endpoint and pipeline resume capability. Requires: orchestrator accepts "start from stage" parameter, worker loads prior stage artifacts from disk, new API endpoint creates a re-run job. UI shows retry button in failed state. Note: score replay (`--score`, v0.18.0) partially covers the "rerun from known-good score" case. The general retry system should subsume `--score` for API/UI while `--score` remains a CLI convenience.
**Priority:** P2
Deferred from plan: Pipeline Page UI/UX Redesign (eng review: no backend support exists)

Expand Down
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.17.0
0.18.0
5 changes: 5 additions & 0 deletions docs/content/docs/api/rest.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,8 @@ Creates a new video generation job and adds it to the processing queue.
| `dryRun` | boolean | No | `false` | Skip rendering, produce only the DirectorScore |
| `noMusic` | boolean | No | `false` | Skip music generation/selection |
| `noVideo` | boolean | No | `false` | Skip AI video generation for scenes |
| `direction` | string | No | — | Creative direction text (free-form markdown, max 10KB). Guides the AI's DirectorScore generation. |
| `score` | object | No | — | A previous DirectorScore JSON for replay. Skips research, director, and critic stages. Must be a valid DirectorScore schema. |
| `providers` | object | No | see defaults | Provider selection per category |
| `keys` | object | No | `{}` | BYOK (Bring Your Own Key) API keys |

Expand All @@ -133,6 +135,9 @@ Creates a new video generation job and adds it to the processing queue.
| `400` | Unknown `archetype` |
| `400` | Unknown `pacing` tier |
| `400` | Unknown `platform` |
| `400` | `direction` exceeds 10KB |
| `400` | `direction` is not a string |
| `400` | `score` is not a valid DirectorScore schema |

---

Expand Down
22 changes: 22 additions & 0 deletions docs/content/docs/getting-started/cli.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ The topic is a required positional argument. Everything else is optional.
| `--stock-max-attempts <n>` | Max stock API calls per scene | `4` |
| `--verification-model <model>` | Model override for stock verification VLM | provider default |
| `--kokoro-voice <voice>` | Kokoro voice preset (e.g. `af_heart`, `bf_emma`, `am_fenrir`) | `af_heart` |
| `--direction <file>` | Creative brief file (markdown) to guide the AI's creative decisions | — |
| `--score <path>` | Replay from a saved `score.json`, skipping research and director stages | — |
| `-y, --yes` | Auto-confirm cost estimation prompt (for Docker/CI) | off |

## Provider meta-flags
Expand Down Expand Up @@ -120,6 +122,26 @@ Generate only AI images and stock footage, no AI video:
pnpm start "your topic" --no-video
```

### Creative direction file

Guide the AI with a free-form creative brief. Describe visual style, mood, script notes, scene ideas, and music preferences in a markdown file:

```bash
pnpm start "deep sea exploration" --direction examples/direction-brief.md
```

The AI reads the brief like a human editor would, honoring your constraints while still exercising creative judgment. See `examples/direction-brief.md` for a sample.

### Replay from a saved score

If a previous run produced a great DirectorScore but downstream stages failed (e.g., TTS generated wrong-duration audio), replay from the saved score without re-running research and director:

```bash
pnpm start "your topic" --score output/2026-04-10-.../score.json
```

This skips research, director, and critic stages, saving $0.50-2.00 and 10-30 seconds. The topic argument is still required for the output directory name but is otherwise unused during replay.

## Cost estimation

Before spending any money, the CLI shows a detailed cost breakdown and asks for confirmation:
Expand Down
6 changes: 6 additions & 0 deletions docs/content/docs/getting-started/quickstart.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,12 @@ pnpm start "5 stoic lessons" --provider local

# Dry run — outputs the DirectorScore JSON without generating any assets
pnpm start "your topic" --dry-run

# Creative direction — guide the AI with a brief
pnpm start "deep sea exploration" --direction examples/direction-brief.md

# Replay — reuse a previous score, re-render with different config
pnpm start "your topic" --score output/2026-04-10-.../score.json
```

See [CLI Usage](/docs/getting-started/cli) for all available flags.
Expand Down
2 changes: 2 additions & 0 deletions docs/content/docs/getting-started/web-ui.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ Click **Advanced** to expand the full provider panel:
- **Music Provider** — Bundled (free) or Lyria 3 Pro ($0.08/track)
- **Style Override** — Same as the archetype gallery, as a dropdown
- **Dry Run** — Toggle on to output the DirectorScore JSON without generating assets or spending money
- **Creative Direction** — Free-form textarea where you can describe your creative vision: visual style, mood, script notes, scene ideas, music preferences. The AI reads it like a brief from a producer. A character counter shows usage against the 10,000 character limit.
- **Replay from Score** — Upload a previous `score.json` file to skip research and director stages. Useful when a previous run produced a great creative plan but downstream stages failed.

### 4. Generate

Expand Down
16 changes: 16 additions & 0 deletions docs/content/docs/pipeline/creative-director.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,22 @@ The full content playbook (`prompts/playbook.md`) is injected into the Creative
- CTA patterns for the final scene
- Audience psychology triggers

## Creative direction file

The `--direction <file>` flag (or API `direction` field) lets you attach a free-form markdown brief that influences how the Director generates the score. The direction text is appended to the user message as a `## Creative Direction (from the producer)` section.

Direction is injected into both the initial generation and the revision prompts, so the critic/revise loop stays aligned with your creative intent. The AI treats it like a creative brief from a producer: it honors your constraints on visual style, mood, script notes, and scene ideas while still exercising judgment on anything you didn't specify.

An empty direction file is treated as no direction. Direction is ignored when replaying from `--score` (the score already incorporates creative intent from its original generation).

See `examples/direction-brief.md` for a sample brief.

## Score replay

When `--score <path>` is provided, the Director stage skips LLM generation entirely. Instead, it loads the provided `score.json`, populates the shared closure state (archetype config, cost breakdown), and passes the score to downstream stages.

The revision loop is skipped completely. Cost estimation runs with a `replay: true` flag that omits the research, director, and critic LLM cost terms, giving users an accurate estimate of only the execution costs (TTS, images, video, music).

## Retry logic

The Director has a built-in retry loop (up to 3 attempts). If the LLM output fails Zod validation (wrong field types, golden rule violation, too few scenes), the error message is appended to the next attempt's prompt so the LLM can self-correct. Token usage is accumulated across retries.
Expand Down
4 changes: 4 additions & 0 deletions docs/content/docs/pipeline/critic.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,10 @@ The Critic's system prompt is augmented with two sections extracted from the pla

This ensures the Critic evaluates against the same standards the Creative Director was instructed to follow.

## Skipping during replay

When replaying from a saved score (`--score`), the final Critic stage is skipped entirely. The Critic evaluates the DirectorScore text plan, not the rendered video, so re-evaluating an already-accepted score provides no value. This saves one LLM call (~$0.03-0.10). The stage emits an `onStageSkip` event with reason "Replaying from saved score."

## Graceful degradation

If the Critic evaluation fails (LLM error, parsing failure), the stage is skipped rather than crashing the pipeline. The rendered video is still usable -- it just lacks a quality score. The failure is logged in `log.json`.
Expand Down
32 changes: 31 additions & 1 deletion docs/content/docs/pipeline/overview.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -86,11 +86,41 @@ After the Director stage produces the DirectorScore, the pipeline computes an es

After all stages complete, actual LLM token usage is aggregated and the real cost is computed and reported.

## Score replay

If you already have a `score.json` from a previous run, you can replay it with `--score <path>` (CLI) or the `score` field (API). This skips three stages:

```
score.json (loaded)
|
v
Director ---- populates closure bindings, runs cost estimation (no LLM calls)
|
v
TTS --------- generates voiceover from the replayed score's script lines
|
v
Visuals ----- resolves assets and music
|
v
Assembly ---- renders the final video
```

Research and Critic are skipped entirely. The Director step still runs to populate the shared state that downstream stages depend on (archetype config, cost breakdown), but it uses the provided score directly instead of calling the LLM. Cost estimates during replay accurately omit the skipped LLM calls.

This is useful when a previous run produced a great creative plan but downstream stages failed (wrong audio duration, image generation error, etc.). Replay saves $0.50-2.00 and 10-30 seconds per run.

## Creative direction

The `--direction <file>` flag lets you attach a free-form markdown brief that influences the DirectorScore generation. The AI reads it like a creative brief from a producer, honoring your constraints on visual style, mood, script notes, and scene ideas while still exercising creative judgment on anything not specified.

Direction text is injected into both the initial generation and revision prompts, so the critic/revise loop stays aligned with your intent. See `examples/direction-brief.md` for a sample.

## Early exits

The pipeline supports two early exit paths:

- **Dry run** (`--dry-run`) -- stops after the Director stage (including the quality gate revision loop) and prints the final DirectorScore JSON. No assets are generated.
- **Dry run** (`--dry-run`) -- stops after the Director stage (including the quality gate revision loop) and prints the final DirectorScore JSON. No assets are generated. Works with both `--direction` and `--score`.
- **Cost rejection** -- if the user declines at the cost estimate prompt, the pipeline exits cleanly.

Both paths still write `log.json` to the output directory.
Expand Down
4 changes: 4 additions & 0 deletions docs/content/docs/pipeline/research.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,10 @@ The research system prompt (`prompts/researcher.md`) instructs the agent to:
4. Focus on **vivid, lesser-known details** for timeless topics (history, science)
5. Note when web search returns nothing useful and knowledge is used instead

## Skipping during replay

When replaying from a saved score (`--score`), the Research stage is skipped entirely. It returns the same minimal fallback as the web search failure case (below) and emits an `onStageSkip` event with reason "Replaying from saved score." No LLM call is made. The research output is unused since the Director stage also short-circuits during replay.

## Graceful degradation

If web search fails entirely (network error, provider unavailable), the Research stage does not crash the pipeline. Instead it returns a minimal fallback:
Expand Down
26 changes: 26 additions & 0 deletions examples/direction-brief.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Creative Brief: Deep Sea Exploration

## Visual Style
Dark, cinematic documentary feel. Think BBC Planet Earth meets Interstellar.
Use moody_cinematic or cinematic_documentary archetype. Deep blues, bioluminescent
accents. Prefer real stock footage of ocean depths over AI-generated underwater scenes.

## Mood & Tone
Awe mixed with unease. The ocean is beautiful AND terrifying.
Start with wonder, build to something more unsettling, end with perspective.

## Scene Ideas
- Open with a slow zoom into dark water. No narration for the first beat, just music.
- Use text_card for a shocking statistic ("We've explored less than 5% of the ocean").
- Show bioluminescent creatures mid-video for the "whoa" moment.
- End on Earth from space, pulling back to show how much is water.

## Script Notes
- Use "we" not "you" for narration. Feels more communal.
- No rhetorical questions in the hook. State a fact that stops the scroll.
- Keep sentences short. 8-12 words each. Let the visuals breathe.
- End with a thought-provoking statement, not a call-to-action.

## Music
mysterious_ambient or dark_cinematic. No upbeat pop. The music should feel
like pressure, like depth. Something that makes you hold your breath.
16 changes: 12 additions & 4 deletions src/agents/creative-director.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ export async function generateDirectorScore(
llm: LLMProvider,
topic: string,
researchContext: ResearchResult,
options?: { archetype?: string; pacing?: string; videoEnabled?: boolean },
options?: { archetype?: string; pacing?: string; videoEnabled?: boolean; direction?: string },
): Promise<DirectorScoreOutput> {
const systemPrompt = loadDirectorSystemPrompt();

Expand All @@ -80,6 +80,10 @@ export async function generateDirectorScore(
// Resolve pacing tier: explicit --pacing override > archetype default > lookup table
const pacingInstruction = buildPacingInstruction(options?.archetype, options?.pacing);

const directionSection = options?.direction?.trim()
? `\n## Creative Direction (from the producer)\n\n${options.direction}\n\nHonor these creative constraints while exercising your judgment on anything not specified.\n`
: "";

const userMessage = `Topic: ${topic}

Research context:
Expand All @@ -94,7 +98,7 @@ ${archetypeInstruction}

${pacingInstruction}
Use ${visualTypes}.${videoGuidance}
CRITICAL RULE: Never use the same visual_type more than 2 times in a row. With more scenes, plan your visual_type sequence BEFORE writing scenes to ensure variety.
${directionSection}CRITICAL RULE: Never use the same visual_type more than 2 times in a row. With more scenes, plan your visual_type sequence BEFORE writing scenes to ensure variety.
Every scene MUST have a script_line (the voiceover text).
The first scene should be a strong hook.
If over budget, cut a scene rather than cramming.`;
Expand Down Expand Up @@ -197,7 +201,7 @@ export async function reviseDirectorScore(
researchContext: ResearchResult,
originalScore: DirectorScore,
critique: CritiqueResult,
options?: { archetype?: string; pacing?: string; videoEnabled?: boolean },
options?: { archetype?: string; pacing?: string; videoEnabled?: boolean; direction?: string },
): Promise<DirectorScoreOutput> {
const systemPrompt = loadDirectorSystemPrompt();

Expand All @@ -212,6 +216,10 @@ export async function reviseDirectorScore(
? "all 5 visual types (ai_image, ai_video, stock_image, stock_video, text_card)"
: "all 4 visual types (ai_image, stock_image, stock_video, text_card)";

const directionSection = options?.direction?.trim()
? `\n## Creative Direction (from the producer)\n\n${options.direction}\n\nHonor these creative constraints while exercising your judgment on anything not specified.\n`
: "";

const userMessage = `Topic: ${topic}

Research context:
Expand All @@ -224,7 +232,7 @@ Mood: ${researchContext.mood}

${pacingInstruction}
Use ${visualTypes}.

${directionSection}
## Current Plan (score: ${critique.score}/10)

${JSON.stringify(originalScore, null, 2)}
Expand Down
9 changes: 9 additions & 0 deletions src/cli/args.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ export interface CLIOptions {
stockConfidence: number;
stockMaxAttempts: number;
verificationModel?: string;
direction?: string;
score?: string;
usage: boolean;
}

Expand Down Expand Up @@ -167,6 +169,11 @@ export function parseArgs(): CLIOptions {
"Video model override (e.g. veo-3.1-lite-preview, fal-ai/kling-video/v2.1/standard/image-to-video)",
)
.option("--video", "Enable AI video generation (use --no-video to disable)", true)
.option(
"--direction <file>",
"Creative brief file (markdown). Describe visual style, script notes, music mood, scene ideas. The AI reads it like a human editor would. See examples/direction-brief.md",
)
.option("--score <path>", "Replay from a saved score.json, skipping research and director stages")
.option("--usage", "Show cost usage report from past runs in the output directory", false)
.parse();

Expand Down Expand Up @@ -244,6 +251,8 @@ export function parseArgs(): CLIOptions {
stockConfidence: opts["stockConfidence"] as number,
stockMaxAttempts: opts["stockMaxAttempts"] as number,
verificationModel: opts["verificationModel"] as string | undefined,
direction: opts["direction"] as string | undefined,
score: opts["score"] as string | undefined,
usage: false,
};
}
Loading
Loading