Skip to content

lasagnastack.stages.reel_pipeline.post_caption

lasagnastack.stages.reel_pipeline.post_caption

PostCaptionStage

PostCaptionStage(client: LLMClient | None = None)

Bases: Stage

Stage 7: generate an Instagram post caption for the finished reel.

Initialise with an optional LLM client.

Parameters:

Name Type Description Default
client LLMClient | None

LLM client to use. Defaults to GeminiClient.

None
Source code in src/lasagnastack/stages/reel_pipeline/post_caption.py
def __init__(self, client: LLMClient | None = None) -> None:
    """Initialise with an optional LLM client.

    Args:
        client: LLM client to use. Defaults to GeminiClient.
    """
    self._client = client

completion_message

completion_message(state: PipelineState) -> str

Return the post-stage confirmation message.

Parameters:

Name Type Description Default
state PipelineState

Current pipeline state.

required

Returns:

Type Description
str

Human-readable completion string.

Source code in src/lasagnastack/stages/reel_pipeline/post_caption.py
def completion_message(self, state: PipelineState) -> str:
    """Return the post-stage confirmation message.

    Args:
        state: Current pipeline state.

    Returns:
        Human-readable completion string.
    """
    path = io.post_caption_path(state.output_dir)
    return f"Stage 7 complete — post caption written to {path}."

run

Run the post caption stage.

Parameters:

Name Type Description Default
state PipelineState

Current pipeline state. state.cut_list and state.inventories must be set.

required

Returns:

Type Description
PipelineState

Updated pipeline state with post_caption set.

Source code in src/lasagnastack/stages/reel_pipeline/post_caption.py
def run(self, state: PipelineState) -> PipelineState:
    """Run the post caption stage.

    Args:
        state: Current pipeline state. ``state.cut_list`` and
            ``state.inventories`` must be set.

    Returns:
        Updated pipeline state with ``post_caption`` set.
    """
    assert isinstance(state, ReelPipelineState)
    assert state.cut_list is not None
    assert state.inventories is not None
    post_caption = run(
        state.cut_list,
        state.inventories,
        state.brief_path,
        state.output_dir,
        self._client,
        state.skill_path,
    )
    return dataclasses.replace(state, post_caption=post_caption)

run

run(
    cut_list: CutList,
    inventories: list[ClipInventory],
    brief_path: Path,
    output_dir: Path,
    client: LLMClient | None = None,
    skill_path: Path | None = None,
) -> PostCaption

Generate an Instagram post caption for the finished reel.

Sends the brief, reel metadata, and segment descriptions to the LLM and returns a PostCaption. Writes post_caption.txt to output_dir.

Parameters:

Name Type Description Default
cut_list CutList

Approved cut list from Stage 4 (provides reel metadata and per-cut captions used to ground the generated text).

required
inventories list[ClipInventory]

Clip inventories from Stage 2 (segment descriptions used to make the caption specific to what was actually filmed).

required
brief_path Path

Path to the freeform .txt creator brief.

required
output_dir Path

Pipeline root; post_caption.txt written here.

required
client LLMClient | None

LLM client to use. Defaults to GeminiClient.

None
skill_path Path | None

Optional path to a Markdown skill file injected into the prompt before the creator brief.

None

Returns:

Type Description
PostCaption

A PostCaption ready to copy-paste at publish time.

Source code in src/lasagnastack/stages/reel_pipeline/post_caption.py
def run(
    cut_list: CutList,
    inventories: list[ClipInventory],
    brief_path: Path,
    output_dir: Path,
    client: LLMClient | None = None,
    skill_path: Path | None = None,
) -> PostCaption:
    """Generate an Instagram post caption for the finished reel.

    Sends the brief, reel metadata, and segment descriptions to the LLM and
    returns a PostCaption. Writes post_caption.txt to output_dir.

    Args:
        cut_list: Approved cut list from Stage 4 (provides reel metadata and
            per-cut captions used to ground the generated text).
        inventories: Clip inventories from Stage 2 (segment descriptions used
            to make the caption specific to what was actually filmed).
        brief_path: Path to the freeform .txt creator brief.
        output_dir: Pipeline root; post_caption.txt written here.
        client: LLM client to use. Defaults to GeminiClient.
        skill_path: Optional path to a Markdown skill file injected into the
            prompt before the creator brief.

    Returns:
        A PostCaption ready to copy-paste at publish time.
    """
    if client is None:
        client = GeminiClient()

    prompt = _build_prompt(cut_list, inventories, brief_path, skill_path)
    log.info(
        "post_caption_start",
        reel_title=cut_list.reel_meta.title,
        clips=len(inventories),
    )

    post_caption: PostCaption = client.generate(prompt, PostCaption, temperature=0.7)

    io.write_post_caption(output_dir, post_caption.caption)
    log.info("post_caption_done", path=str(io.post_caption_path(output_dir)))
    return post_caption