# パイプライン

> TypeScript から、ストーリー → 動画のパイプラインを開始し、ステージごとに進行を確認します。ステージを承認し、監督とチャットし、完成したタイムラインを読み取ります。

Source: https://nodaro.ai/ja/docs/developers/sdk/pipelines

**`client.pipelines`** は、ストーリー → 動画のパイプラインを実行します。ストーリーのプロンプトを映画にする、複数ステージのプロダクションです。パイプラインは、`script`、`characters`、`objects`、`locations`、`shot_list`、`scene_images`、`animate_audio_edit`、`post_merge` の 8 つのステージを進み、各ステージで、あなたの承認を待って停止できます。スタジオの**映画を作成**を、コードから使えるようにしたものです。これらのメソッドは、[パイプラインの REST API](https://nodaro.ai/docs/developers/api/pipelines) を呼び出します。

## モード
| モード | 各ステージで起こること |
| --- | --- |
| `manual`（デフォルト） | すべてのステージが、あなたの承認を待ちます。各ステージを承認します。脚本は却下することもできます。 |
| `auto` | エンジンがすべてのステージを自分で承認し、最後まで実行します。[マッチカットの不連続](https://nodaro.ai/docs/developers/api/pipelines#match-cut-breaks-in-the-scene-images-stage)があるときだけ停止し、すべての不連続が承認されるまで待機します。その間、ステータスは `running` のままで、`pendingApprovals(id)` の一覧に `scene_images` ステージが含まれます。 |
| `guided` | manual と同じですが、ステージについて監督とチャットし、提案された変更を適用することもできます。 |

パイプラインのステータスは、`queued`、`running`、`awaiting_approval`、`completed`、`failed`、`cancelled`、`forked` のいずれかです。

## メソッド
| メソッド | スコープ | 内容 |
| --- | --- | --- |
| [`create(input)`](#createinput) | `pipelines:execute` | パイプラインを開始します |
| [`get(id)`](#getid) | `pipelines:read` | パイプラインのステータスとクレジットを読み取ります |
| [`list()`](#list) | `pipelines:read` | 自分のパイプラインを一覧表示します |
| [`cancel(id)`](#cancelid) | `pipelines:execute` | パイプラインを停止します |
| [`pendingApprovals(id)`](#pendingapprovalsid) | `pipelines:read` | 承認待ちのステージを一覧表示します |
| [`approveStage(id, stage, edits?)`](#approvestageid-stage-edits) | `pipelines:approve` | 任意で編集を加えて、ステージを承認します |
| [`rejectStage(id, stage, feedback)`](#rejectstageid-stage-feedback) | `pipelines:approve` | 脚本を却下し、書き直させます |
| [`approveSubGate(id, gate)`](#approvesubgateid-gate) | `pipelines:approve` | 動画化ステージの中のチェックポイントを承認します |
| [`getStage(id, stage)`](#getstageid-stage) | `pipelines:read` | 1 つのステージの出力を読み取ります |
| [`getTimeline(id)`](#gettimelineid) | `pipelines:read` | 組み立てられた映画を読み取ります |
| [`branch(id, input)`](#branchid-input) | `pipelines:execute` | 完了したパイプラインを、1 つのステージから再実行します |
| [`chatStage(pipelineId, stage, message)`](#chatstagepipelineid-stage-message) | `pipelines:approve` | 監督に、ステージの変更を依頼します |
| [`applyChatProposal(pipelineId, stage, turnId)`](#applychatproposalpipelineid-stage-turnid) | `pipelines:approve` | 監督が提案した変更を受け入れます |
| [`getStageChat(pipelineId, stage)`](#getstagechatpipelineid-stage) | `pipelines:read` | ステージのチャットを読み取ります |

スコープが適用されるのは OAuth トークンです。API トークンとセッションは、スコープによる制限を受けません。

## client.pipelines
### create(input)
パイプラインを開始します。`auto` モードでは、マッチカットの不連続で停止しない限り、最後まで自動で実行されます。ステータスは `get()` を、結果は `getTimeline()` をポーリングしてください。`manual` モードまたは `guided` モードでは、`pendingApprovals()`、`approveStage()`、`approveSubGate()` で進めます。

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

<TypeTable
type={{
story_prompt: { type: 'string', required: true, description: "ストーリーで、1〜4,000 文字です。" },
target_duration_seconds: { type: 'number', required: true, description: "映画の目標の長さ（秒）で、format の範囲内である必要があります。最大 600 です。" },
format: { type: '"trailer" | "short_film" | "music_video" | "reel" | "commercial"', required: true, description: "映画の種類です。" },
root_node_id: { type: 'string', required: true, description: "そのワークフロー内にある、パイプラインのルートノードの id です。" },
workflow_id: { type: 'string', description: "パイプラインが属するワークフローです。" },
pipeline_type: { type: '"story_to_video" | "song_to_music_video"', default: '"story_to_video"', description: "パイプラインの種類です。" },
mode: { type: '"manual" | "auto" | "guided"', default: '"manual"', description: "ステージをどのように承認するかです。" },
output_resolution: { type: 'string', default: '"720p"', description: "完成した映画の解像度です。" },
language: { type: 'string', default: '"en"', description: "脚本と声の言語です。" },
max_cost_credits: { type: 'number', description: "実行全体の、クレジットでの支出上限です。" },
style_directives: { type: 'object', description: "映画全体へのスタイルの指示です。" },
config: { type: 'object', description: "使用するモデルなど、パイプラインの設定の上書きです。" },
}}
/>

```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)
パイプラインの現在の状態を読み取ります。`auto` の実行を最後まで追うには、ポーリングします。マッチカットの不連続で停止した実行は `running` のままなので、`pendingApprovals(id)` も確認してください。

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

<TypeTable
type={{
id: { type: 'string', required: true, description: "パイプラインの ID です。" },
}}
/>

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

`PipelineRecord` には、`id`、`status`、`current_stage`、`mode`、`spent_credits`、`reserved_credits`、`upfront_credit_estimate`、（`status` が `failed` のときに設定される）`failure_reason`、`current_progress_message` があり、分岐の場合は `branched_from_pipeline_id` と `branched_from_stage` もあります。

### list()
自分のパイプラインを、新しい順に一覧表示します。

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

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

### cancel(id)
実行中のパイプラインを停止します。使われなかった確保済みのクレジットは返還されます。すでに終了したパイプラインをキャンセルしても、何も変わりません。

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

<TypeTable
type={{
id: { type: 'string', required: true, description: "パイプラインの ID です。" },
}}
/>

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

### pendingApprovals(id)
承認待ちのステージを、それぞれの出力とともに一覧表示します。`auto` の実行では、エンジンが自分でステージを承認するため、マッチカットの不連続で止まらない限り、この一覧は空です。

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

<TypeTable
type={{
id: { type: 'string', required: true, description: "パイプラインの ID です。" },
}}
/>

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

### approveStage(id, stage, edits?)
ステージを承認し、パイプラインを先に進めます。承認する前にステージの出力を変更するには、JSON Patch である `edits` を渡します。

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

<TypeTable
type={{
id: { type: 'string', required: true, description: "パイプラインの ID です。" },
stage: { type: 'PipelineStageName', required: true, description: "承認するステージで、たとえば script です。" },
edits: { type: 'JSON Patch operations', description: "先にステージの出力に適用する変更で、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)
メモを付けて、脚本を却下します。エンジンは、そのメモを踏まえて、脚本を書き直します。却下できるのは `script` ステージだけで、ほかのステージは `stage_not_implemented` で失敗します。脚本を却下できるのは最大 2 回までで、脚本のクリティックがすでに脚本を修正している場合は、それより少なくなります。

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

<TypeTable
type={{
id: { type: 'string', required: true, description: "パイプラインの ID です。" },
stage: { type: 'PipelineStageName', required: true, description: "却下するステージで、script です。" },
feedback: { type: 'string', required: true, description: "変更してほしい内容です。" },
}}
/>

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

### approveSubGate(id, gate)
`animate_audio_edit` ステージの中にある、`dialogue_recheck` のようなチェックを承認し、パイプラインを次のステップに進めます。

`scene_images` ステージには独自のチェックポイント `match_cut_break_pending` があり、`approveSubGate` ではこれを解除できません。代わりに、REST API で不連続を 1 つずつ承認してください。[シーン画像ステージでのマッチカットの不連続](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: "パイプラインの ID です。" },
gate: { type: 'SubGateName', required: true, description: "承認するチェックで、たとえば dialogue_recheck です。" },
}}
/>

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

### getStage(id, stage)
1 つのステージの `status`、`output`、`critic_feedback` を読み取ります。承認する前に、脚本やプランを確認するために使います。

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

<TypeTable
type={{
id: { type: 'string', required: true, description: "パイプラインの ID です。" },
stage: { type: 'PipelineStageName', required: true, description: "読み取るステージです。" },
}}
/>

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

### getTimeline(id)
組み立てられた映画を読み取ります。順番に並んだシーンとその長さ、オーディオの URL、動画化の進行状況です。自分でレンダリングするか、編集アプリに渡してください。

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

<TypeTable
type={{
id: { type: 'string', required: true, description: "パイプラインの ID です。" },
}}
/>

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

`PipelineTimeline` には、`fps`、`width`、`height`、（それぞれ `{ compositeUrl, durationSeconds }` の）`scenes`、`musicUrl`、`narrationUrl`、そして `totalShots`、`shotsDone`、`percent` を持つ `animateProgress` があります。

### branch(id, input)
完了したパイプラインを、1 つのステージから新しいパイプラインとして再実行します。それより前のステージは、承認済みとしてコピーされます。元のパイプラインは `completed` のままです。

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

<TypeTable
type={{
id: { type: 'string', required: true, description: "完了したパイプラインです。" },
fromStage: { type: 'PipelineStageName', required: true, description: "再実行する最初のステージです。" },
}}
/>

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

### chatStage(pipelineId, stage, message)
`guided` モードで、監督にメッセージを送ります。パイプラインの mode が `guided` で、ステージが `awaiting_approval` である必要があります。返信には、`applyChatProposal()` で受け入れられる `proposed_change` が含まれることがあります。チャットが使えるのは、`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: "パイプラインの ID です。" },
stage: { type: 'ChatEnabledStage', required: true, description: "相談するステージです。" },
message: { type: 'string', required: true, description: "自分のリクエストです。" },
}}
/>

```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)
監督が以前のターンで提案した変更を受け入れます。変更は検証され、ステージの新しいバージョンとして保存され、ステージが承認されます。

```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: "パイプラインの ID です。" },
stage: { type: 'ChatEnabledStage', required: true, description: "ステージです。" },
turnId: { type: 'string', required: true, description: "適用する提案をした、アシスタントのターンです。" },
}}
/>

```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)
```

変更を適用できないが、まだ立て直せる場合、結果は `applied: false` になり、監督はすでに、ヒントを添えた返信を追加しています。回復できない失敗では、409 エラーがスローされます。

### getStageChat(pipelineId, stage)
ステージのチャット履歴を読み取ります。最初のメッセージより前は空です。

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

<TypeTable
type={{
pipelineId: { type: 'string', required: true, description: "パイプラインの ID です。" },
stage: { type: 'ChatEnabledStage', required: true, description: "ステージです。" },
}}
/>

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

各 `ChatTurn` には、`id`、`turn_n`、`role`、`content`、`proposed_change`、`applied_to_attempt_id`、`created_at` があります。

## Frequently asked questions

### Nodaro のパイプラインとは何ですか？

パイプラインは、ストーリーのプロンプトから、脚本、キャラクター、オブジェクト、ロケーション、ショットリスト、シーン画像、音声と編集を伴う動画化、最終的な結合という各ステージを経て、映画を作ります。スタジオの「映画を作成」のヘッドレス版です。

### 各ステージを承認せずに、パイプラインを実行するには、どうすればよいですか？

mode を auto にして作成します。エンジンが各ステージを自分で承認し、最後まで実行します。ステータスは client.pipelines.get(id) をポーリングし、結果は getTimeline(id) で読み取ります。実行が止まるのは、計画したマッチカットが成立しない場合だけです。その場合、ステータスは running のままで、すべての不連続が承認されるまで、pendingApprovals(id) の一覧に scene_images ステージが含まれます。

### 承認する前に、ステージの出力を変更するには、どうすればよいですか？

approveStage の第 3 引数として、JSON Patch を渡します。ステージが承認される前に、そのステージの出力に適用されます。

### パイプラインには、どの OAuth スコープが必要ですか？

パイプラインとステージの読み取りには pipelines:read、作成・キャンセル・分岐には pipelines:execute、ステージの承認・脚本の却下・ステージについてのチャットには pipelines:approve です。
