# Pipelines

> Inicie um pipeline de história para vídeo em TypeScript, acompanhe cada etapa, aprove etapas, converse com o diretor e leia a linha do tempo final.

Source: https://nodaro.ai/pt-BR/docs/developers/sdk/pipelines

**`client.pipelines`** executa pipelines de história para vídeo: produções em várias etapas que transformam um prompt de história em um filme. Um pipeline passa por oito etapas, `script`, `characters`, `objects`, `locations`, `shot_list`, `scene_images`, `animate_audio_edit` e `post_merge`, e pode parar em cada uma delas para a sua aprovação. É a versão programática do **Criar filme** no estúdio. Os métodos chamam a [API REST de pipelines](https://nodaro.ai/docs/developers/api/pipelines).

## Modos
| Modo | O que acontece em cada etapa |
| --- | --- |
| `manual` (o padrão) | Toda etapa espera a sua aprovação. Aprove cada etapa; o roteiro também pode ser rejeitado. |
| `auto` | O mecanismo aprova todas as etapas por conta própria e vai até o fim. Ele só para em uma [quebra de match cut](https://nodaro.ai/docs/developers/api/pipelines#match-cut-breaks-in-the-scene-images-stage), até que todas as quebras sejam aceitas. Enquanto isso, o status continua `running`, e `pendingApprovals(id)` lista a etapa `scene_images`. |
| `guided` | Como o manual, e você também pode conversar com o diretor sobre uma etapa e aplicar as alterações que ele propõe. |

O status de um pipeline é `queued`, `running`, `awaiting_approval`, `completed`, `failed`, `cancelled` ou `forked`.

## Métodos
| Método | Escopo | O que faz |
| --- | --- | --- |
| [`create(input)`](#createinput) | `pipelines:execute` | Inicia um pipeline |
| [`get(id)`](#getid) | `pipelines:read` | Lê o status e os créditos de um pipeline |
| [`list()`](#list) | `pipelines:read` | Lista os seus pipelines |
| [`cancel(id)`](#cancelid) | `pipelines:execute` | Para um pipeline |
| [`pendingApprovals(id)`](#pendingapprovalsid) | `pipelines:read` | Lista as etapas que aguardam aprovação |
| [`approveStage(id, stage, edits?)`](#approvestageid-stage-edits) | `pipelines:approve` | Aprova uma etapa, com edições opcionais |
| [`rejectStage(id, stage, feedback)`](#rejectstageid-stage-feedback) | `pipelines:approve` | Rejeita o roteiro e o escreve de novo |
| [`approveSubGate(id, gate)`](#approvesubgateid-gate) | `pipelines:approve` | Aprova uma verificação dentro da etapa de animação |
| [`getStage(id, stage)`](#getstageid-stage) | `pipelines:read` | Lê a saída de uma etapa |
| [`getTimeline(id)`](#gettimelineid) | `pipelines:read` | Lê o filme montado |
| [`branch(id, input)`](#branchid-input) | `pipelines:execute` | Executa de novo um pipeline concluído a partir de uma etapa |
| [`chatStage(pipelineId, stage, message)`](#chatstagepipelineid-stage-message) | `pipelines:approve` | Pede ao diretor que altere uma etapa |
| [`applyChatProposal(pipelineId, stage, turnId)`](#applychatproposalpipelineid-stage-turnid) | `pipelines:approve` | Aceita uma alteração que o diretor propôs |
| [`getStageChat(pipelineId, stage)`](#getstagechatpipelineid-stage) | `pipelines:read` | Lê a conversa de uma etapa |

Os escopos valem para tokens OAuth. Tokens de API e sessões não são limitados por escopos.

## client.pipelines
### create(input)
Inicia um pipeline. No modo `auto`, ele vai até o fim sozinho, a menos que uma quebra de match cut o pare; consulte `get()` periodicamente para ver o status e `getTimeline()` para ver o resultado. No modo `manual` ou `guided`, conduza o pipeline com `pendingApprovals()`, `approveStage()` e `approveSubGate()`.

```ts
create(input: PipelineInput): Promise<{ id: string }>
```

<TypeTable
type={{
story_prompt: { type: 'string', required: true, description: "A história, de 1 a 4.000 caracteres." },
target_duration_seconds: { type: 'number', required: true, description: "A duração desejada do filme em segundos, dentro da faixa do formato. No máximo 600." },
format: { type: '"trailer" | "short_film" | "music_video" | "reel" | "commercial"', required: true, description: "O tipo de filme." },
root_node_id: { type: 'string', required: true, description: "O ID do nó raiz do pipeline no workflow dele." },
workflow_id: { type: 'string', description: "O workflow ao qual o pipeline pertence." },
pipeline_type: { type: '"story_to_video" | "song_to_music_video"', default: '"story_to_video"', description: "O tipo de pipeline." },
mode: { type: '"manual" | "auto" | "guided"', default: '"manual"', description: "Como as etapas são aprovadas." },
output_resolution: { type: 'string', default: '"720p"', description: "A resolução do filme final." },
language: { type: 'string', default: '"en"', description: "O idioma do roteiro e das vozes." },
max_cost_credits: { type: 'number', description: "Um limite de gastos para a execução inteira, em créditos." },
style_directives: { type: 'object', description: "Instruções de estilo para o filme inteiro." },
config: { type: 'object', description: "Substituições das configurações do pipeline, como os modelos usados." },
}}
/>

```ts
const { id } = await client.pipelines.create({
story_prompt: "A lighthouse keeper finds a message in a bottle that predicts tomorrow's storm.",
target_duration_seconds: 60,
format: "short_film",
root_node_id: "pipeline-root",
mode: "auto",
})
```

### get(id)
Lê o estado atual de um pipeline. Consulte-o periodicamente para acompanhar uma execução `auto` até o fim. Uma execução parada em uma quebra de match cut continua `running`, então verifique também `pendingApprovals(id)`.

```ts
get(id: string): Promise<PipelineRecord>
```

<TypeTable
type={{
id: { type: 'string', required: true, description: "O ID do pipeline." },
}}
/>

```ts
const pipeline = await client.pipelines.get(id)
console.log(pipeline.status, pipeline.current_stage, pipeline.current_progress_message)
```

Um `PipelineRecord` tem `id`, `status`, `current_stage`, `mode`, `spent_credits`, `reserved_credits`, `upfront_credit_estimate`, `failure_reason` (definido quando o status é `failed`), `current_progress_message` e, em uma derivação, `branched_from_pipeline_id` e `branched_from_stage`.

### list()
Lista os seus pipelines, dos mais recentes para os mais antigos.

```ts
list(): Promise<PipelineRecord[]>
```

```ts
const pipelines = await client.pipelines.list()
```

### cancel(id)
Para um pipeline em execução. Os créditos reservados e não gastos são reembolsados. Cancelar um pipeline que já terminou não muda nada.

```ts
cancel(id: string): Promise<{ ok: true }>
```

<TypeTable
type={{
id: { type: 'string', required: true, description: "O ID do pipeline." },
}}
/>

```ts
await client.pipelines.cancel(id)
```

### pendingApprovals(id)
Lista as etapas que aguardam aprovação, cada uma com a sua saída. A lista fica vazia durante uma execução `auto`, porque o mecanismo aprova as etapas sozinho, a menos que uma quebra de match cut o pare.

```ts
pendingApprovals(id: string): Promise<Array<{ stage_name: PipelineStageName; output: unknown }>>
```

<TypeTable
type={{
id: { type: 'string', required: true, description: "O ID do pipeline." },
}}
/>

```ts
const approvals = await client.pipelines.pendingApprovals(id)
for (const { stage_name, output } of approvals) console.log(stage_name, output)
```

### approveStage(id, stage, edits?)
Aprova uma etapa para que o pipeline siga em frente. Passe `edits`, um JSON Patch, para alterar a saída da etapa antes de ela ser aprovada.

```ts
approveStage(id: string, stage: PipelineStageName, edits?: unknown): Promise<{ ok: true }>
```

<TypeTable
type={{
id: { type: 'string', required: true, description: "O ID do pipeline." },
stage: { type: 'PipelineStageName', required: true, description: "A etapa a aprovar, como script." },
edits: { type: 'JSON Patch operations', description: "Alterações a aplicar primeiro à saída da etapa, como uma lista de operações JSON Patch." },
}}
/>

```ts
await client.pipelines.approveStage(id, "script")

await client.pipelines.approveStage(id, "script", [
{ op: "replace", path: "/title", value: "The Keeper's Warning" },
])
```

### rejectStage(id, stage, feedback)
Rejeita o roteiro com uma observação, e o mecanismo o escreve de novo levando a observação em conta. Só a etapa `script` pode ser rejeitada; outra etapa falha com `stage_not_implemented`. Você pode rejeitar o roteiro no máximo duas vezes, e menos vezes se o revisor automático de roteiros já o tiver revisado.

```ts
rejectStage(id: string, stage: PipelineStageName, feedback: string): Promise<{ ok: true }>
```

<TypeTable
type={{
id: { type: 'string', required: true, description: "O ID do pipeline." },
stage: { type: 'PipelineStageName', required: true, description: "A etapa a rejeitar: script." },
feedback: { type: 'string', required: true, description: "O que mudar." },
}}
/>

```ts
await client.pipelines.rejectStage(id, "script", "Make the story darker and more suspenseful")
```

### approveSubGate(id, gate)
Aprova uma verificação dentro da etapa `animate_audio_edit`, como `dialogue_recheck`, para que o pipeline continue com o próximo passo.

A etapa `scene_images` tem o próprio ponto de controle, `match_cut_break_pending`, que `approveSubGate` não libera. Em vez disso, aceite cada quebra pela API REST. Veja [Quebras de match cut na etapa de imagens das cenas](https://nodaro.ai/docs/developers/api/pipelines#match-cut-breaks-in-the-scene-images-stage).

```ts
approveSubGate(id: string, gate: SubGateName): Promise<{ ok: true; gate: SubGateName; resumed_at: string }>
```

<TypeTable
type={{
id: { type: 'string', required: true, description: "O ID do pipeline." },
gate: { type: 'SubGateName', required: true, description: "A verificação a aprovar, como dialogue_recheck." },
}}
/>

```ts
await client.pipelines.approveSubGate(id, "dialogue_recheck")
```

### getStage(id, stage)
Lê o `status`, a `output` e o `critic_feedback` de uma etapa. Use-o para examinar o roteiro ou o plano antes de aprová-lo.

```ts
getStage(id: string, stage: PipelineStageName): Promise<{ status: string; output: unknown; critic_feedback: unknown }>
```

<TypeTable
type={{
id: { type: 'string', required: true, description: "O ID do pipeline." },
stage: { type: 'PipelineStageName', required: true, description: "A etapa a ler." },
}}
/>

```ts
const { status, output } = await client.pipelines.getStage(id, "script")
```

### getTimeline(id)
Lê o filme montado: as cenas em ordem com as durações delas, as URLs de áudio e o progresso da animação. Renderize-o você mesmo ou entregue-o a um editor.

```ts
getTimeline(id: string): Promise<PipelineTimeline>
```

<TypeTable
type={{
id: { type: 'string', required: true, description: "O ID do pipeline." },
}}
/>

```ts
const timeline = await client.pipelines.getTimeline(id)
for (const scene of timeline.scenes) console.log(scene.compositeUrl, scene.durationSeconds)
```

Um `PipelineTimeline` tem `fps`, `width`, `height`, `scenes` (cada uma `{ compositeUrl, durationSeconds }`), `musicUrl`, `narrationUrl` e `animateProgress` com `totalShots`, `shotsDone` e `percent`.

### branch(id, input)
Executa de novo um pipeline concluído a partir de uma etapa, como um novo pipeline. As etapas anteriores a ela são copiadas como aprovadas. O pipeline original continua `completed`.

```ts
branch(id: string, input: { fromStage: PipelineStageName }): Promise<{ pipelineId: string; clonedStages: string[]; clonedEntities: number }>
```

<TypeTable
type={{
id: { type: 'string', required: true, description: "O pipeline concluído." },
fromStage: { type: 'PipelineStageName', required: true, description: "A primeira etapa a executar de novo." },
}}
/>

```ts
const { pipelineId } = await client.pipelines.branch(id, { fromStage: "scene_images" })
```

### chatStage(pipelineId, stage, message)
Envia uma mensagem ao diretor no modo `guided`. O modo do pipeline precisa ser `guided`, e a etapa precisa estar em `awaiting_approval`. A resposta pode trazer uma `proposed_change` que você pode aceitar com `applyChatProposal()`. A conversa funciona nas etapas que a aceitam, como `script`.

```ts
chatStage(pipelineId: string, stage: ChatEnabledStage, message: string): Promise<{
turnId: string
role: "assistant"
content: string
proposed_change: ProposedChange | null
}>
```

<TypeTable
type={{
pipelineId: { type: 'string', required: true, description: "O ID do pipeline." },
stage: { type: 'ChatEnabledStage', required: true, description: "A etapa a discutir." },
message: { type: 'string', required: true, description: "O seu pedido." },
}}
/>

```ts
const { content, proposed_change, turnId } = await client.pipelines.chatStage(
id,
"script",
"Can you make the protagonist's motivation clearer in scene 2?",
)
```

### applyChatProposal(pipelineId, stage, turnId)
Aceita a alteração que o diretor propôs em uma rodada anterior. A alteração é verificada e salva como uma nova versão da etapa, e a etapa é aprovada.

```ts
applyChatProposal(pipelineId: string, stage: ChatEnabledStage, turnId: string): Promise<
| { applied: true; attemptId: string; newOutput: unknown }
| { applied: false; error: { code: string; detail?: unknown } }
>
```

<TypeTable
type={{
pipelineId: { type: 'string', required: true, description: "O ID do pipeline." },
stage: { type: 'ChatEnabledStage', required: true, description: "A etapa." },
turnId: { type: 'string', required: true, description: "A rodada do assistente cuja proposta aplicar." },
}}
/>

```ts
const result = await client.pipelines.applyChatProposal(id, "script", turnId)
if (result.applied) console.log("Approved:", result.newOutput)
else console.log("Not applied:", result.error.code)
```

Quando a alteração não pode ser aplicada, mas é possível se recuperar, o resultado é `applied: false`, e o diretor já adicionou uma resposta com uma dica. Uma falha definitiva lança um erro 409.

### getStageChat(pipelineId, stage)
Lê o histórico da conversa de uma etapa. Ele fica vazio antes da primeira mensagem.

```ts
getStageChat(pipelineId: string, stage: ChatEnabledStage): Promise<{ turns: ChatTurn[] }>
```

<TypeTable
type={{
pipelineId: { type: 'string', required: true, description: "O ID do pipeline." },
stage: { type: 'ChatEnabledStage', required: true, description: "A etapa." },
}}
/>

```ts
const { turns } = await client.pipelines.getStageChat(id, "script")
```

Cada `ChatTurn` tem `id`, `turn_n`, `role`, `content`, `proposed_change`, `applied_to_attempt_id` e `created_at`.

## Frequently asked questions

### O que é um pipeline do Nodaro?

Um pipeline faz um filme a partir de um prompt de história, em etapas: roteiro, personagens, objetos, locais, lista de tomadas, imagens das cenas, animação com áudio e edição, e a junção final. É a versão sem interface do Criar filme do estúdio.

### Como executo um pipeline sem aprovar cada etapa?

Crie o pipeline com o modo auto. O mecanismo aprova cada etapa por conta própria e vai até o fim. Consulte client.pipelines.get(id) periodicamente para ver o status e leia o resultado com getTimeline(id). A execução só para quando um match cut planejado quebra. O status então continua running, e pendingApprovals(id) lista a etapa scene_images até que todas as quebras sejam aceitas.

### Como altero a saída de uma etapa antes de aprová-la?

Passe um JSON Patch como terceiro argumento de approveStage. Ele é aplicado à saída da etapa antes de a etapa ser aprovada.

### Quais escopos OAuth os pipelines exigem?

pipelines:read para ler pipelines e etapas, pipelines:execute para criá-los, cancelá-los e ramificá-los, e pipelines:approve para aprovar etapas, rejeitar o roteiro e conversar sobre as etapas.
