# פייפליינים

> התחלת פייפליין של סיפור ← וידאו מ-TypeScript, בדיקת המצב שלו שלב אחר שלב, אישור שלבים, צ׳אט עם הבמאי וקריאת ציר הזמן של הסרט המוגמר.

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

**`client.pipelines`** מריץ פייפליינים של **סיפור ← וידאו**: הפקות בכמה שלבים שהופכות פרומפט של סיפור לסרט. פייפליין עובר שמונה שלבים, `script`, `characters`, `objects`, `locations`, `shot_list`, `scene_images`, `animate_audio_edit` ו-`post_merge`, ויכול לעצור בכל אחד מהם לאישור שלכם. זו הגרסה התכנותית של הכפתור **יצירת סרט** ב-Studio. המתודות קוראות לנקודות הקצה שבדף ה-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` | קריאת הפלט של שלב אחד |
| [`getTimeline(id)`](#gettimelineid) | `pipelines:read` | קריאת הסרט שהורכב |
| [`branch(id, input)`](#branchid-input) | `pipelines:execute` | הרצה מחדש של פייפליין גמור משלב מסוים |
| [`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: "אורך הסרט הרצוי בשניות, בתוך הטווח של הפורמט. לכל היותר 600." },
format: { type: '"trailer" | "short_film" | "music_video" | "reel" | "commercial"', required: true, description: "סוג הסרט." },
root_node_id: { type: 'string', required: true, description: "המזהה של רכיב השורש של הפייפליין בתהליך שלו." },
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: "הגדרות אופציונליות שגוברות על ההגדרות של הפייפליין. ביניהן המתגים music_enabled, narration_enabled ו-lipsync_enabled, כל אחד מהם true כברירת מחדל, והשדות image_model, video_model ו-script_llm, שבוחרים את המודלים." },
}}
/>

```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: "מזהה הפייפליין." },
}}
/>

```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`, `failure_reason` (שמוגדר כשהסטטוס הוא `failed`) ו-`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: "מזהה הפייפליין." },
}}
/>

```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: "מזהה הפייפליין." },
}}
/>

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

### approveStage(id, stage, edits?)‎
מאשרת שלב, כדי שהפייפליין ימשיך. כדי לשנות את הפלט של השלב לפני שהוא מאושר, העבירו את `edits`, שהוא JSON Patch.

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

<TypeTable
type={{
id: { type: 'string', required: true, description: "מזהה הפייפליין." },
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`. אפשר לדחות את התסריט פעמיים לכל היותר, ופחות מזה אם מבקר התסריט האוטומטי כבר תיקן אותו.

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

<TypeTable
type={{
id: { type: 'string', required: true, description: "מזהה הפייפליין." },
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. ראו בדף ה-API [שבירות מאץ׳ קאט בשלב תמונות הסצנה](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: "מזהה הפייפליין." },
gate: { type: 'SubGateName', required: true, description: "הבדיקה לאישור, כגון dialogue_recheck." },
}}
/>

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

### getStage(id, stage)‎
מחזירה את ה-`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: "מזהה הפייפליין." },
stage: { type: 'PipelineStageName', required: true, description: "השלב לקריאה." },
}}
/>

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

### getTimeline(id)‎
מחזירה את הסרט שהורכב: הסצנות לפי הסדר עם האורכים שלהן, כתובות האודיו וההתקדמות של האנימציה. רנדרו אותו בעצמכם, או העבירו אותו לעורך.

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

<TypeTable
type={{
id: { type: 'string', required: true, description: "מזהה הפייפליין." },
}}
/>

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

ל-`PipelineTimeline` יש `fps`, `width`, `height`, `scenes` (כל אחת בצורה `{ compositeUrl, durationSeconds }`), `musicUrl`, `narrationUrl` ו-`animateProgress` עם `totalShots`, `shotsDone` ו-`percent`.

### branch(id, input)‎
מריצה מחדש פייפליין גמור החל משלב אחד, כפייפליין חדש. השלבים שלפניו מועתקים במצב מאושר. הפייפליין המקורי נשאר בסטטוס `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`. הפייפליין חייב להיות במצב `guided`, והשלב חייב להיות בסטטוס `awaiting_approval`. בתשובה יכול להופיע `proposed_change`, שאפשר לקבל עם `applyChatProposal()`. הצ׳אט עובד בשלבים שתומכים בו, כגון `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: "מזהה הפייפליין." },
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: "מזהה הפייפליין." },
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: "מזהה הפייפליין." },
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`.

## שאלות נפוצות

### מהו פייפליין ב-Nodaro?

פייפליין מפיק סרט מפרומפט של סיפור, בשלבים: תסריט, דמויות, אובייקטים, מיקומים, רשימת שוטים, תמונות הסצנות, אנימציה עם אודיו ועריכה, ולבסוף המיזוג הסופי. הפייפליין הוא הגרסה ללא ממשק של הכפתור יצירת סרט ב-Studio.

### איך מריצים פייפליין בלי לאשר כל שלב?

צרו את הפייפליין כש-mode מוגדר ל-auto. המנוע מאשר כל שלב בעצמו ורץ עד הסוף. בדקו את הסטטוס מדי כמה שניות עם client.pipelines.get(id)‎, וקבלו את התוצאה עם getTimeline(id)‎. ההרצה עוצרת רק כשמאץ׳ קאט מתוכנן נשבר. הסטטוס נשאר אז running, והמתודה pendingApprovals(id)‎ מחזירה את השלב scene_images עד שכל שבירה מאושרת.

### איך משנים את הפלט של שלב לפני שמאשרים אותו?

העבירו JSON Patch כארגומנט השלישי של המתודה approveStage. ה-JSON Patch מוחל על הפלט של השלב לפני שהשלב מאושר.

### אילו הרשאות OAuth נדרשות לפייפליינים?

פייפליינים צריכים את ההרשאה pipelines:read כדי לקרוא פייפליינים ושלבים, את pipelines:execute כדי ליצור פייפליינים, לבטל אותם וליצור מהם הסתעפויות, ואת pipelines:approve כדי לאשר שלבים, לדחות את התסריט ולשוחח על שלבים.
