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
9 changes: 1 addition & 8 deletions astrid/packs/_core/skill/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,7 @@ Before rendering an iteration video, run `python3 -m astrid.packs.builtin.iterat
| `builtin.render` | Render a hype timeline to hype.mp4 through the Remotion compositor. |
| `builtin.scene_describe` | Caption each detected scene with a vision model for downstream selection. |
| `builtin.scenes` | Detect source-video scene boundaries with ffmpeg-driven analysis. |
| `builtin.script_pipeline` | Generate short scripts through rough attempts, synthesis, style pass, and optional judging. |
| `builtin.search_loras` | Search Hugging Face Hub for LoRAs associated with a base model. |
| `builtin.shots` | Slice scenes into shot windows for downstream pool building. |
| `builtin.spatial_audio_page` | Build a static page that mixes Foley tracks anchored to spatial rectangles via Web Audio. |
Expand All @@ -307,12 +308,6 @@ Before rendering an iteration video, run `python3 -m astrid.packs.builtin.iterat
| `external.vibecomfy.validate` | Validate a VibeComfy / ComfyUI workflow JSON without executing it. |
| `iteration.assemble` | Adapt prepared iteration data into canonical iteration artifacts and render-ready hype inputs. |
| `iteration.prepare` | Collect thread provenance, quality scores, and candidate runs into iteration prepare artifacts. |
| `seinfeld.aitoolkit_stage` | Generate ai-toolkit job config from manifest + vocabulary; upload to pod; start AI Toolkit UI on :8675. |
| `seinfeld.aitoolkit_train` | Kick off ai-toolkit training on a pod and mirror remote logs locally. |
| `seinfeld.lora_eval_grid` | Run baseline LTX + per-checkpoint inference samples, download MP4s, write static index.html viewer. |
| `seinfeld.lora_register` | Pure-local: copy chosen .safetensors into registered/ and write registered_lora.json. |
| `seinfeld.repo_setup` | Idempotent git submodule add + checkout of ostris/ai-toolkit for config-schema reference. |
| `seinfeld.script_pipeline` | Generate Seinfeld-style short scene scripts through ideation, synthesis, and voice passes. |
| `upload.youtube` | Upload a finished video to YouTube via the shared banodoco-social Zapier integration. |

### Orchestrators
Expand All @@ -329,8 +324,6 @@ Before rendering an iteration video, run `python3 -m astrid.packs.builtin.iterat
| `builtin.thumbnail_maker` | Plan source evidence and thumbnail generation candidates for a video/query pair. |
| `builtin.training_run` | Run a generic LoRA training job from a prepared dataset manifest. |
| `builtin.vary_grid` | Iterative grid editor: take an existing grid image and emit a new grid of variations via fal. |
| `seinfeld.dataset_build` | Bucket-fill loop that builds the Seinfeld LoRA training set from YouTube. |
| `seinfeld.lora_train` | Train an LTX 2.3 LoRA on the Seinfeld dataset via ai-toolkit on RunPod. |

### Elements

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "https://astrid.builtin-training/schemas/ai-toolkit-adapter-manifest.schema.json",
"title": "AI-Toolkit Adapter Manifest",
"description": "The flat 'clips' shape expected by seinfeld.lora_train preflight and ai-toolkit staging. This is what the 'ai-toolkit-ltx' manifest adapter exports from the canonical manifest.",
"description": "The flat 'clips' shape expected by builtin.training_run preflight and ai-toolkit staging. This is what the 'ai-toolkit-ltx' manifest adapter exports from the canonical manifest.",
"type": "object",
"required": ["clips"],
"additionalProperties": false,
Expand Down
24 changes: 24 additions & 0 deletions astrid/packs/builtin/script_pipeline/STAGE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# builtin.script_pipeline

Preset-driven creative-writing executor. It preserves the original three-pass
shape while moving topic-specific prompt rules into config:

1. Generate rough attempts in parallel.
2. Synthesize one structured draft from the rough attempts.
3. Apply a voice/style pass.
4. Optionally judge multiple final candidates and select the winner.

Use fake mode for no-network smoke tests:

```bash
python3 -m astrid executors run builtin.script_pipeline -- \
--preset seinfeld \
--produces-dir runs/script-pipeline/produces \
--fake \
--candidates 2 \
--rough-attempts 3 \
--select-best
```

Live DeepSeek runs read provider/model data from the preset and require the
configured API-key environment variable, defaulting to `DEEPSEEK_API_KEY`.
3 changes: 3 additions & 0 deletions astrid/packs/builtin/script_pipeline/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
"""Generic creative-writing script pipeline executor."""

__all__ = ["run"]
35 changes: 35 additions & 0 deletions astrid/packs/builtin/script_pipeline/executor.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
{
"id": "builtin.script_pipeline",
"name": "Script Pipeline",
"kind": "built_in",
"version": "0.1.0",
"short_description": "Generate short scripts through rough attempts, synthesis, style pass, and optional judging.",
"description": "Preset-driven creative-writing pipeline. Runs parallel rough attempts, synthesizes a draft, applies a voice/style pass, optionally judges multiple candidates, and writes candidate markdown, selected markdown, and manifest.json. DeepSeek is the initial live provider; --fake mode is available for no-network smoke tests.",
"keywords": ["script", "writing", "deepseek", "creative", "preset"],
"inputs": [
{"name": "preset", "type": "string", "required": false, "description": "Built-in preset name or path to YAML/JSON preset."},
{"name": "prompt", "type": "string", "required": false, "description": "Scene brief override."},
{"name": "candidates", "type": "integer", "required": false, "description": "Complete pipeline candidates to generate."},
{"name": "rough_attempts", "type": "integer", "required": false, "description": "Parallel rough attempts per candidate."},
{"name": "fake", "type": "boolean", "required": false, "description": "Use deterministic no-network provider outputs."}
],
"outputs": [
{"name": "selected_scene", "type": "file", "path_template": "{out}/produces/selected_scene.md"},
{"name": "manifest", "type": "file", "path_template": "{out}/produces/manifest.json"}
],
"command": {
"argv": [
"{python_exec}",
"-m",
"astrid.packs.builtin.script_pipeline.run",
"--produces-dir",
"{out}/produces"
]
},
"cache": {"mode": "none"},
"isolation": {"mode": "subprocess", "network": true},
"metadata": {
"runtime_module": "astrid.packs.builtin.script_pipeline.run",
"runtime_file": "run.py"
}
}
81 changes: 81 additions & 0 deletions astrid/packs/builtin/script_pipeline/presets/always_sunny.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
schema_version: 1
id: always_sunny
title: Always Sunny Script Pipeline
provider:
name: deepseek
model: deepseek-v4-pro
endpoint: https://api.deepseek.com/v1/chat/completions
api_key_env: DEEPSEEK_API_KEY
timeout_seconds: 320
defaults:
candidates: 1
rough_attempts: 5
select_best: false
rough_temperature: 1.8
synth_temperature: 1.1
voice_temperature: 1.0
judge_temperature: 0.2
max_tokens: 8192
judge_max_tokens: 1024
prompt: >-
Write a very short It's Always Sunny in Philadelphia-style bar scene, about
30 seconds of screen time. The gang tries to turn an ordinary minor
inconvenience into a selfish business scheme. Keep it to 10-14 short lines,
mostly overlapping argument. No laugh tags and no studio-audience rhythm.
prompts:
rough_system: >-
You are a dark farce comedy writer. You write fast, selfish, argumentative
bar scenes where every character believes they are the only rational person
in the room.
synth_system: >-
You are a structure editor for chaotic ensemble comedy. Thread multiple
rough attempts into one escalating bar argument with a bad plan, a worse
justification, and a quick reversal.
synth_template: |
Below are {rough_attempts} attempts at a short ensemble bar scene.

Original brief:
{prompt}

Build one coherent short scene from the strongest material.

Rules for this pass:
- Center the scene on a selfish plan that should obviously fail.
- Keep the setting grounded in a grimy neighborhood bar or a back office.
- Make the characters interrupt, accuse, reframe, and escalate.
- The comedy should come from bad incentives, denial, and group delusion.
- Do not add laugh tags, applause tags, or sitcom audience beats.
- End on a hard turn or an immediately worse idea.
- Output only the script.

ATTEMPTS:

{attempts_blob}
voice_system: |
You are a voice pass editor for a chaotic bar ensemble comedy. Keep the structure and line count, but sharpen each line toward selfish motive, defensive denial, or a bad-faith argument.

VOICE RULES:

DENNIS is controlled, grandiose, and chillingly image-obsessed. He should sound like he is managing a brand crisis around himself.

DEE is combative, insecure, and desperate to be credited. She attacks status and fairness.

MAC is overconfident, physical, and invents rules that flatter him.

CHARLIE is literal, grimy, and strangely practical in the wrong direction.

FRANK is blunt, venal, and excited when the plan becomes worse.

Do not add laugh tags, applause tags, or audience cues. Output only the corrected script.
voice_template: |
Here is the draft scene. Rewrite only lines that violate the voice rules. Preserve the structure, order, and approximate line count.

DRAFT:

{draft_scene}
judge_system: >-
You are judging Always Sunny-style short bar scenes. Pick the strongest
candidate for selfish motive, ensemble argument, escalating bad logic,
grimy specificity, and absence of laugh tags or studio-audience rhythm.
Return strict JSON only:
{"winner": <1-based index>, "reason": "<one concise paragraph>"}.
84 changes: 84 additions & 0 deletions astrid/packs/builtin/script_pipeline/presets/seinfeld.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
schema_version: 1
id: seinfeld
title: Seinfeld Script Pipeline
provider:
name: deepseek
model: deepseek-v4-pro
endpoint: https://api.deepseek.com/v1/chat/completions
api_key_env: DEEPSEEK_API_KEY
timeout_seconds: 320
defaults:
candidates: 1
rough_attempts: 5
select_best: false
rough_temperature: 2.0
synth_temperature: 1.0
voice_temperature: 1.0
judge_temperature: 0.2
max_tokens: 8192
judge_max_tokens: 1024
prompt: >-
Write a VERY SHORT Seinfeld scene - about 30 seconds of screen time. Keep it
to 8-12 short lines of dialogue total. Kramer bursts in EXTREMELY excited
about open source AI. George is skeptical / dismissive. Jerry is confused -
keeps interrupting with 'Who?' / 'What?' because none of the names or terms
register. End on a button (one clean closing line). Format as a script:
NAME: line. No stage directions beyond the opening.
prompts:
rough_system: >-
You are a comedy writer with deep knowledge of Seinfeld's voice and rhythm.
You write tight, fast scenes - no fat.
synth_system: >-
You are a comedy writer with a deep ear for Seinfeld's rhythm. Your one job
in this pass is STRUCTURE - turning multiple rough attempts into a single
coherent scene. Don't worry about polishing character voices yet; that's a
later pass. Just thread the best material.
synth_template: |
Below are {rough_attempts} attempts at a short scene.

Original brief:
{prompt}

Pick the strongest weird ideas, specific name-jokes, and character-true beats from across the attempts, and weave them into ONE coherent ~12-line scene. Loose threading - connected enough to flow, not so neat it reads as a sketch.

Rules for this pass:
- Preserve the brief's requested wackiness level: funny, wacky, somewhat grounded in reality, and also over the top.
- Choose one concrete, playable modern-life problem or object as the scene engine.
- Keep the absurdity practical: Kramer's scheme can be ridiculous, but it should involve specific objects, errands, apartments, neighbors, dating, food, money, etiquette, or daily inconvenience.
- Open with a one-line stage direction: location + what each character is doing.
- Kramer bursts in early.
- Include the "puffy shirt" callback if any attempt has it.
- Add a small physical beat inline somewhere mid-scene.
- End on an action or exit line, not a clever metaphor.
- No laugh tags this pass. Just dialogue.
- Output only the script. No commentary, no headers.

ATTEMPTS:

{attempts_blob}
voice_system: |
You are a script doctor for Seinfeld. You receive a structurally-correct draft scene and your ONLY job is to fix lines that violate character voice. You do NOT restructure, reorder, add, or remove lines. You replace individual lines that sound wrong with versions that sound right. After the voice pass, you insert laugh tags.

CHARACTER VOICES - concrete rules:

GEORGE doesn't construct cute analogies. He panics, catastrophizes, and complains about specific people. His comedy comes from his neuroses leaking out, not from clever metaphors.

JERRY's voice is flat and declarative, not literary. He repeats himself slightly.

KRAMER is concrete-absurd, not ideological or abstract. He physicalizes everything.

LAUGH TAGS - after the voice fixes, insert at most 5 tags total, only on the biggest beats. Forms: [LAUGHTER], [BIG LAUGHTER], [LAUGHTER AND APPLAUSE]. Plus [APPLAUSE] on Kramer's entrance and [END SCENE] at the end. Do not tag every line.

Output only the corrected script. No commentary.
voice_template: |
Here is the draft scene. Find lines that violate the character-voice rules and rewrite only those lines in place. Leave any line that already sounds right untouched. Do not change the structure, the order, or the count of lines. Then insert the laugh tags per the rules above.

DRAFT:

{draft_scene}
judge_system: >-
You are judging generated Seinfeld-style short scene scripts. Pick the
single strongest candidate for concrete Kramer physical absurdity, Jerry's
flat confusion, George's neurotic specificity, coherent escalation, and
sparse laugh tags. Return strict JSON only:
{"winner": <1-based index>, "reason": "<one concise paragraph>"}.
Loading