# הרצת רכיבים

> הרצת כל רכיב של Nodaro מ-TypeScript בלי תהליך: גילוי סוגי רכיבים, התחלת הרצות, המתנה לתוצאות ושליחה של ייחוסים ושל הכוונת מצלמה.

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

**`client.nodes`** מחזיר את רשימת סוגי הרכיבים ששרת Nodaro תומך בהם, ומריץ כל אחד מהם ישירות, בלי לבנות תהליך. הרצה שולחת את הפרמטרים שלכם לנתיב של הרכיב עצמו, `POST /v1/<type>` ברוב סוגי הרכיבים, ומחזירה משימה שאפשר להמתין לה. זה אותו נתיב שה-[CLI](https://nodaro.ai/docs/developers/cli) משתמש בו ב-`nodaro nodes run` ושכלי ה-MCP של Nodaro משתמשים בו. לגרסת ה-REST, ראו את דף ה-API [הרצת רכיב יחיד](https://nodaro.ai/docs/developers/api/nodes).

## מתודות
| מתודה | מה היא עושה |
| --- | --- |
| [`nodes.list()`](#list) | רשימת כל סוגי הרכיבים, עם המודלים והמחיר בקרדיטים של כל אחד |
| [`nodes.get(type)`](#gettype) | קריאת סוג רכיב אחד |
| [`nodes.run(type, params?, options?)`](#runtype-params-options) | התחלת רכיב והחזרה מיידית של מזהה המשימה שלו |
| [`nodes.quote(type, params?)`](#quotetype-params) | אומדן מחיר להרצה לפני שמתחילים אותה |
| [`nodes.runAndWait(type, params?, opts?)`](#runandwaittype-params-opts) | התחלת רכיב, בדיקת הסטטוס של המשימה שלו והחזרת הפלט |
| [`nodes.runMany(type, paramsList, opts?)`](#runmanytype-paramslist-opts) | התחלה של כמה הרצות של רכיב אחד בבת אחת, והמתנה לכולן |
| [`nodes.runBatchAndWait(type, params, opts?)`](#runbatchandwaittype-params-opts) | יצירת כמה תמונות בקריאה אחת לרכיב יצירת תמונה, והמתנה לכל אחת מהן |

## client.nodes
### list()‎
מחזירה את כל סוגי הרכיבים שהשרת תומך בהם. השרת עשוי לשמור את התשובה במטמון למשך 5 דקות. הקריאה לא עולה כלום ולא דורשת הרשאות.

```ts
list(): Promise<{ data: NodeDescriptor[] }>
```

```ts
const { data: nodes } = await client.nodes.list()

const imageGenerators = nodes.filter((n) => n.category === "ai-image")
const takesReferences = nodes.filter((n) => n.capabilities?.includes("supports-reference-image"))
```

לכל `NodeDescriptor` יש השדות האלה:

| שדה | סוג | תיאור |
| --- | --- | --- |
| `type` | `string` | הסוג ב-API, למשל `generate-image`. העבירו אותו ל-`run()`. |
| `label` | `string` | השם של הרכיב בעורך. |
| `category` | `string` | הקטגוריה, למשל `ai-image`, `ai-video`, `ai-audio`, `ai-text`, `processing` או `parameter`. |
| `description` | `string` | תיאור בשורה אחת. |
| `outputType` | `string` | `text`, `image`, `video`, `audio`, `data` או `none`. |
| `creditCost` | `number \| string` | המחיר בקרדיטים שנגבה על הרצה, אותו מספר שמופיע בכפתור **הרצה** של הרכיב: מספר כשהמחיר קבוע, או טווח כגון `"3-682"` כשהמחיר נקבע לפי המודל. ברכיבים קיצוץ וידאו, לולאת וידאו ושילוב סרטונים, הערך הוא במקום זאת המחיר של מדרגה אחת של 5 שניות, והרצה עולה כמה מדרגות כאלה. ב-Nodaro Cloud בלבד. |
| `estimate` | `boolean` | הערך הוא `true` כשהשדה `creditCost` הוא אומדן: הקריאה הטיפוסית של רכיב LLM עם חיוב לפי השימוש, במודל ברירת המחדל שלו. הציגו אותו כ-`≈N`. רכיב LLM עם חיוב לפי השימוש, שלמודל ברירת המחדל שלו עדיין אין מחיר, מופיע ברשימה עם `creditCost: "unpriced"`. |
| `creditRate` | `NodeCreditRate` | לרכיב שהמחיר שלו נקבע לפי יחידה של מדד משלו, כגון רכיב [מסגרות דוברים](https://nodaro.ai/docs/nodes/video/speaker-frames) (עם `creditCost` שערכו `"per-1000-frames"`): `{ identifier, unit, creditsPerUnit, minUnits, rounding, note }`. הרצה של N יחידות עולה `Math.ceil(Math.max(minUnits, N) * creditsPerUnit)`, ומעגלים פעם אחת בלבד. |
| `providers` | `string[]` | מזהי המודלים שהרכיב יכול להריץ, בשביל הפרמטר `provider`. |
| `capabilities` | `string[]` | דגלים, כגון `supports-reference-image` או `supports-end-frame`. |
| `inputSchema` | `{ fields }` | שדות הקלט שאפשר לקבוע, כל אחד עם `key`, `type`, `required` ו-`options`. |
| `maxDurationSec` | `number` | משך הזמן הארוך ביותר שהרכיב מקבל, ברכיבים שיש להם מגבלה כזו. |
| `providerResolutions` | `Record<string, string[]>` | הרזולוציות שכל מודל מקבל, כשהן שונות בין המודלים. |

בהתקנות של מהדורת Community ומהדורת Business באירוח עצמי אין מערכת קרדיטים, ולכן המתארים שלהן לא כוללים את `creditCost`.

### get(type)‎
מחזירה את המתאר של סוג רכיב אחד.

```ts
get(type: string): Promise<{ data: NodeDescriptor }>
```

<TypeTable
type={{
type: { type: 'string', required: true, description: "סוג הרכיב ב-API, למשל generate-image או generate-video." },
}}
/>

```ts
const { data: node } = await client.nodes.get("generate-video")
console.log(node.providers)  // every video model id
console.log(node.creditCost) // Nodaro Cloud only
```

כדי להציג מחירים ליד המודלים, העבירו את `node.providers` ל-[`client.credits.modelCosts()`](https://nodaro.ai/docs/developers/sdk/models-and-credits).

### run(type, params?, options?)‎
מתחילה רכיב אחד ומחזירה תשובה מיד. גוף הבקשה נשלח לנתיב של הרכיב, `POST /v1/<type>` ברוב סוגי הרכיבים. שמות השדות זהים לשדות הקלט של הרכיב.

```ts
run(type: string, params?: Record<string, unknown>, options?: { idempotencyKey?: string }): Promise<RunNodeResult>
```

<TypeTable
type={{
type: { type: 'string', required: true, description: "סוג הרכיב ב-API, למשל generate-image, image-to-video או text-to-speech." },
params: { type: 'Record<string, unknown>', default: '{}', description: "גוף הבקשה. לכמה סוגי רכיבים יש פרמטרים עם טיפוסים, שמפורטים בהמשך." },
idempotencyKey: { type: 'string', description: "נשלח בכותרת Idempotency-Key. השתמשו שוב באותו ערך כשאתם מנסים שוב בקשה שהגיעה לפסק זמן, כדי שההרצה לא תתחיל ולא תחויב פעמיים. גם כל overload עם טיפוסים מקבל אותו, כולל ה-overloads של הווידאו." },
}}
/>

```ts
const result = await client.nodes.run("generate-image", {
prompt: "A snow leopard in the mountains",
provider: "nano-banana-2",
})

if ("jobId" in result) {
const { data: job } = await client.jobs.getStatus(result.jobId)
console.log(job.status)
}
```

**סוגי רכיבים עם נתיב משלהם.** כמה סוגי רכיבים רצים בנתיב אחר, והמתודה `run()` שולחת אליו בשבילכם:

| סוגי רכיבים | נתיב |
| --- | --- |
| `generate-3d-scene`, `edit-3d-scene` | `/v1/3d-scene/generate`, `/v1/3d-scene/edit` |
| `llm-chat`, `after-effects`, `motion-graphics`, `lottie-overlay`, `3d-title` | `/v1/<type>/generate` |
| `image-to-text` | `/v1/image-to-text/describe` |
| `video-composer` | `/v1/scene-graph/generate` |
| `telegram-channel-feed` | `/v1/telegram-channel/fetch` |
| כל רכיב `suno-*`, כגון `suno-generate` ו-`suno-cover` | `/v1/suno/<name>`. הרכיב `suno-voice` משתמש ב-`/v1/suno/voice/generate`. |
| `instagram-post`, `tiktok-post`, `youtube-upload`, `linkedin-post`, `x-post`, `facebook-post`, `telegram-post`, `publish-social` | `/v1/social/publish` |
| `ugc-creator`, `ugc-script`, `ugc-clips`, `ugc-clip`, `ugc-cards` | `/v1/ugc/creator`, `/v1/ugc/script` וכן הלאה. ב-Nodaro Cloud בלבד. |
| `render-video` עם `planType` | `/v1/render-video/plan` |

נתיב הפרסום דורש `platform`. רכיב פרסום של רשת אחת, כגון `x-post`, ממלא אותו לפי הסוג שלו כשלא שולחים אותו. הרכיב `publish-social` מקבל את ה-`platform` ששולחים.

**סוגי רכיבים שלא רצים לבד.** קריאה לסוג רכיב בלי נתיב משלו נכשלת עם `NodaroError` שה-`code` שלו הוא `node_not_runnable`, בלי שום פנייה ל-API. ההודעה מציינת מה להריץ במקומו:

- הרכיב `modify-image` רץ דרך `image-to-image`, או דרך `edit-image` במודל `nano-banana-edit`.
- הרכיבים `upscale-image` ו-`remove-background` רצים דרך `edit-image`.
- הרכיב `image-to-video` רץ דרך `generate-video`.
- הרכיבים `composite`, `manual-edit` ו-`paint-mask` רצים רק בתוך תהליך. הרכיבים `scene` ו-`generative-pipeline` רצים רק בתוך פייפליין.

קריאה לסוגי רכיבים שרצים רק בתוך תהליך, כגון קלטים, טריגרים, כלי עזר ובוררים (`upload-image`, `combine-text`), נכשלת באותו אופן ברגע שה-API עונה שאין לו נתיב בשבילם. שגיאת `404` על משאב חסר, כגון מזהה דמות שלא קיים, נשארת `NotFoundError`.

בקשה עם מודל שהפריסה הזו לא מציעה, או עם מודל שמנהל כיבה, נדחית לפני שמשוריינים קרדיטים. הקריאה נכשלת עם [`ModelUnavailableError`](https://nodaro.ai/docs/developers/sdk/errors#modelunavailableerror), וה-`code` הוא `model_not_available` או `model_disabled`. בחרו מודל אחר: ניסיון חוזר לא ישנה כלום.

**מה חוזר.** רוב סוגי הרכיבים הם אסינכרוניים: התוצאה כוללת `jobId`, ו-worker מבצע את היצירה. בדקו את הסטטוס עם [`client.jobs.getStatus(jobId)`](https://nodaro.ai/docs/developers/sdk/jobs-and-executions) מדי כמה שניות עד שהמשימה מסתיימת, או השתמשו ב-`runAndWait()`. כמה סוגי רכיבים סינכרוניים, כגון `meta-ads-scrape`, מחזירים את התוצאה המלאה שלהם מיד, בלי `jobId`. בדקו אם יש `jobId`, ופעלו לפי זה.

**תיקוני פרמטרים.** בסוגי רכיבי התמונה (`generate-image`, `image-to-image` ו-`edit-image`), השרת עשוי לתקן ערך שהמודל שנבחר לא מקבל. ההרצה ממשיכה עם הערך המתוקן, והקרדיטים שמשוריינים תואמים אותו. התוצאה כוללת אז את `adjustments`, פריט אחד לכל שדה שתוקן:

```ts
const result = await client.nodes.run("generate-image", {
prompt: "A snow leopard",
provider: "gpt-image-2",
aspectRatio: "3:2",
})
if ("adjustments" in result && result.adjustments?.length) {
for (const a of result.adjustments) {
console.warn(`${a.field}: ${a.from} -> ${a.to ?? "(dropped)"} (${a.reason})`)
}
}
```

בכל תיקון יש `field` (`aspectRatio`, `resolution`, `quality` או `duration`), `from`, `to` ו-`reason`. כששום דבר לא השתנה, השדה `adjustments` לא מופיע.

המתודה זורקת `InsufficientCreditsError` כשאין בחשבון מספיק קרדיטים, `StorageExceededError` כשהאחסון מלא ו-`JobBlockedError` כשמדיניות תוכן של הפריסה דוחה את הבקשה. ראו את הדף [שגיאות](https://nodaro.ai/docs/developers/sdk/errors).

### quote(type, params?)‎
נותנת אומדן מחיר להרצה לפני שמתחילים אותה, עם `POST /v1/quote`. העבירו בדיוק את גוף הבקשה שהייתם מעבירים למתודה `run`. הפלטפורמה מתמחרת אותו עם הקוד של נתיב ההרצה עצמו: אותה בדיקת תקינות, אותה מדידה של קליפי ייחוס, אותו מזהה קרדיטים ואותו סכום. שום דבר לא מתחיל, ולא נגבים קרדיטים. ב-Nodaro Cloud בלבד.

```ts
quote(type: string, params?: Record<string, unknown>): Promise<NodeQuote>
```

למתודה יש טיפוסים ל-`text-to-video`, ל-`generate-video`, ל-`video-to-video` ול-`video-upscale` (ההשלמה ב-1080p של טיוטה ב-Seedance 2.5 מתומחרת לפי הטיוטה): הסוגים שהפלטפורמה יכולה לתת להם אומדן מחיר כרגע. קריאה עם כל סוג אחר נדחית עם `400 quote_unsupported`.

```ts
const q = await client.nodes.quote("video-to-video", {
videoUrl, prompt: "make it black and white", provider: "seedance-2-5", resolution: "480p",
})
q.reserve   // credits held when the run starts
q.expected  // what the run should actually cost, when known
```

הטיפוס `NodeQuote` הוא `{ identifier, reserve, expected?, adjustments, warnings }`. ראו בדף ה-API [אומדני מחיר להרצה](https://nodaro.ai/docs/developers/api/nodes#run-quotes) לכל השדות והקודים.

### runAndWait(type, params?, opts?)‎
מריצה רכיב אסינכרוני אחד עד הסוף. המתודה קוראת ל-`run()`, לוקחת את ה-`jobId` ובודקת את הסטטוס עם [`client.jobs.getStatus()`](https://nodaro.ai/docs/developers/sdk/jobs-and-executions) מדי כמה שניות, עד שהמשימה מסתיימת. כשהסטטוס הוא `completed`, היא מחזירה את הפלט של המשימה.

```ts
runAndWait(type: string, params?: Record<string, unknown>, opts?: RunAndWaitOptions): Promise<NodeJobOutput>
```

<TypeTable
type={{
type: { type: 'string', required: true, description: "סוג הרכיב ב-API." },
params: { type: 'Record<string, unknown>', default: '{}', description: "גוף הבקשה, כמו ב-run()‎." },
signal: { type: 'AbortSignal', description: "עוצר את ההמתנה, והקריאה נכשלת עם JobAbortedError. המשימה עצמה ממשיכה לרוץ." },
onProgress: { type: '(status: JobStatusResult) => void', description: "נקרא עם הסטטוס של המשימה בכל בדיקה. השדה status.progress עולה מ-0 עד 100 כשהמודל מדווח עליו." },
pollMs: { type: 'number', default: '2000', description: "כל כמה זמן לבדוק את הסטטוס, באלפיות שנייה." },
maxMs: { type: 'number', default: '900000', description: "כמה זמן להמתין בסך הכול, באלפיות שנייה, לפני JobTimeoutError." },
idempotencyKey: { type: 'string', description: "נשלח עם בקשת ההרצה. השתמשו בו שוב כשאתם מנסים שוב את אותה בקשה." },
}}
/>

```ts
const output = await client.nodes.runAndWait(
"generate-video",
{ prompt: "Rain falls on a neon street at night", provider: "seedance-2-fast", duration: 4 },
{ onProgress: (s) => console.log(`${s.progress ?? 0}%`) },
)
console.log(output.videoUrl, output.thumbnailUrl)
```

במשימת תמונה, הטיפוס `NodeJobOutput` כולל גם את גודל התמונה בפיקסלים, כך שלקוח יכול לסדר רשת לפני שהתמונות נטענות:

| שדה | סוג | תיאור |
| --- | --- | --- |
| `imageUrl` | `string` | התמונה, או הראשונה מביניהן כשהמשימה יצרה כמה תמונות. |
| `imageUrls` | `readonly string[]` | כל התמונות, כשהמשימה יצרה יותר מתמונה אחת. |
| `width`, `height` | `number` | הגודל של `imageUrl` בפיקסלים, כפי שהקובץ שמור, אחרי שהכיוון לפי EXIF הוחל. סימן המים של המסלול החינמי כלול. |
| `images` | `readonly JobOutputImage[]` | אובייקט `{ url, width?, height? }` אחד לכל פריט ב-`imageUrls`, באותו סדר. בפריט בלי גודל, אי אפשר היה למדוד את התמונה. |

ארבעת שדות הגודל הם אופציונליים. במשימות ישנות יותר הם חסרים, והשרת אף פעם לא מנחש גודל שהוא לא הצליח לקרוא.

```ts
const output = await client.nodes.runAndWait("generate-image", { prompt: "a snow leopard in the mountains" })
console.log(output.imageUrl, output.width, output.height)
for (const image of output.images ?? []) console.log(image.url, image.width ?? "?", image.height ?? "?")
```

הפלט הוא `NodeJobOutput`: השדה `imageUrl` ברכיבי תמונה, `videoUrl` ו-`thumbnailUrl` ברכיבי וידאו, `audioUrl` ברכיבי אודיו ועוד כל שדה אחר שהרכיב כותב. רכיב [הפרדת אודיו](https://nodaro.ai/docs/nodes/audio/audio-separation), למשל, מוסיף כתובת לכל סטם, כגון `vocalUrl` ו-`instrumentalUrl`.

אלה השגיאות עם טיפוסים שהמתודה זורקת:

| שגיאה | מתי |
| --- | --- |
| `NodaroError` עם הקוד `batch_requires_run_batch_and_wait` | בקשת `generate-image` עם `count` גדול מ-1 או עם `models` יוצרת כמה משימות. המתודה `runAndWait()` דוחה אותה לפני שנשלח משהו. השתמשו ב-[`runBatchAndWait()`](#runbatchandwaittype-params-opts). |
| `InsufficientCreditsError`, `StorageExceededError`, `JobBlockedError` | בקשת ההרצה נדחתה, לפני כל בדיקת סטטוס. |
| `ModelUnavailableError` | הפריסה הזו לא מציעה את המודל, או שמנהל כיבה אותו. ההרצה נדחית לפני שמשוריינים קרדיטים. בחרו מודל אחר. |
| `JobFailedError` | המשימה הסתיימה בסטטוס `failed` או `cancelled`. השגיאה כוללת את `jobId` ואת הודעת השגיאה של המשימה. |
| `JobTimeoutError` | הזמן שב-`maxMs` עבר. המשימה לא מבוטלת. |
| `JobAbortedError` | ה-`signal` שלכם ביטל את ההמתנה. המשימה לא מבוטלת. |
| `JobHeldError` | המשימה ממתינה לבדיקה של אדם, בפריסות שיש להן מדיניות תוכן. |

**התאוששות איטית.** לפעמים מודל מוסר את התוצאה שלו אחרי שה-worker כבר ויתר עליה. המשימה נשארת אז בסטטוס `processing` עם `recovering: true` בזמן שהפלטפורמה מתקנת אותה, ובמודלים איטיים זה יכול לקחת עשרות דקות. אם השגיאה `JobTimeoutError` מסיימת את ההמתנה שלכם, שלפו את המשימה מאוחר יותר עם `client.jobs.get(jobId)`, או הגדילו את `maxMs`.

### runMany(type, paramsList, opts?)‎
מתחילה כמה הרצות של סוג רכיב אחד באותו זמן וממתינה לכולן, למשל כדי ליצור רשת של מועמדים. כל פריט רץ דרך `runAndWait()`.

```ts
runMany(type: string, paramsList: Record<string, unknown>[], opts?: RunAndWaitOptions): Promise<RunManyResult[]>
```

<TypeTable
type={{
type: { type: 'string', required: true, description: "סוג הרכיב ב-API, לכל ההרצות." },
paramsList: { type: 'Record<string, unknown>[]', required: true, description: "גוף בקשה אחד לכל הרצה." },
opts: { type: 'RunAndWaitOptions', description: "ה-signal, ה-onProgress, ה-pollMs וה-maxMs שמשותפים לכל ההרצות. ה-signal המשותף עוצר את כל האצווה." },
}}
/>

```ts
const results = await client.nodes.runMany("generate-image", [
{ prompt: "A snow leopard at sunrise" },
{ prompt: "A snow leopard at golden hour" },
{ prompt: "A snow leopard at blue hour" },
])
for (const { jobId, output } of results) console.log(jobId, output.imageUrl)
```

המתודה מחזירה תשובה כשכל ההרצות הסתיימו: אובייקט `{ jobId, output }` אחד לכל פריט, בסדר של הקלט. היא נכשלת ברגע שהרצה כלשהי נכשלת, עם אותן שגיאות כמו `runAndWait()`. פריט שהוא בעצמו אצווה, עם `count` גדול מ-1 או עם `models`, נדחה לפני שנשלח משהו. כדי לבחור אחר כך את התוצאה הטובה ביותר, העבירו את הכתובות ל-[`client.reduce.run()`](https://nodaro.ai/docs/developers/sdk/llm-and-reduce).

### runBatchAndWait(type, params, opts?)‎
יוצרת כמה תמונות בקריאה **אחת** לרכיב יצירת תמונה, וממתינה לכל אחת מהן. השתמשו ב-`count` כדי לקבל מ-1 עד 4 תמונות, כל אחת במשימה משלה. השתמשו ב-`models` כדי לשלוח את אותה בקשה אל 2–4 מודלי תמונה, במקום `provider` אחד. כשמשתמשים בשניהם, הפרמטר `count` חל על כל מודל, עד 8 תמונות בסך הכול.

```ts
runBatchAndWait(
type: "generate-image",
params: GenerateImageParams,
opts?: RunAndWaitOptions,
): Promise<GenerateImageBatchOutput>
```

המתודה שולחת את האצווה פעם אחת, ואז ממתינה לכל משימה כמו ש-`runAndWait()` ממתינה. האצווה נבדקת מול היתרה שלכם, והיא מחויבת כולה או לא מחויבת בכלל. עם `opts.idempotencyKey`, המפתח של תמונה מספר *i* הוא `<key>:<i>`, ולכן ניסיון חוזר מחזיר את אותן משימות ולא נגבים קרדיטים פעם נוספת. הפרמטר `presetId` משתלב עם שתי האפשרויות: מספר תמונות לפי `count` מהבקשה של הערכה, או הערכה שמוחלת פעם אחת לכל מודל.

```ts
const { batch, results } = await client.nodes.runBatchAndWait(
"generate-image",
{ prompt: "a lighthouse at dusk", models: ["gpt-image-2", "flux-2-max"], count: 2, seed: 41 },
{ idempotencyKey: crypto.randomUUID() },
)
console.log(batch.batchId, batch.totalCredits)
for (const r of results) {
if (r.status === "completed") console.log(r.model, r.output.imageUrl)
else console.warn(r.model, r.jobId, r.error.message)
}
```

המתודה מחזירה `{ batch, results }`:

- **`batch`** הוא התשובה לשליחה, מסוג `GenerateImageBatchResult`. יש בו `jobId` (המשימה הראשונה), `jobIds` (מודל אחר מודל, ובתוך כל מודל תמונה אחר תמונה), `batchId`, `groups` (`{ model, jobIds, credits?, adjustments? }` לכל מודל) ו-`totalCredits`. גם המתודה `nodes.run("generate-image", …)` מחזירה את אותו אובייקט כשהפרמטרים קובעים `count` גדול מ-1 או `models`.
- **`results`** כולל `BatchJobOutcome` אחד לכל משימה, בסדר של `batch.jobIds`. כל אחד מהם הוא `{ jobId, model, status: "completed", output }` או `{ jobId, model, status: "error", error }`. השדה `error` הוא השגיאה `JobFailedError`, `JobHeldError` או `JobTimeoutError` שסיימה את ההמתנה. תמונה אחת שנכשלה אף פעם לא מבטלת את התמונות האחרות.

המתודה נכשלת רק לפני שקיימת משימה כלשהי, עם השגיאות של השליחה עצמה, כגון `InsufficientCreditsError` עם הסכום הכולל של האצווה ב-`required`. היא נכשלת גם עם `JobAbortedError` כשה-`signal` שלכם מבטל את ההמתנה. בקשה לתמונה אחת נדחית לפני שנשלח משהו, עם הקוד `not_a_batch`: המתינו לה עם `runAndWait()`. עם `seed`, תמונה *i* מקבלת `seed + i`. כדי למצוא מאוחר יותר את המשימות של אצווה, השתמשו ב-`client.jobs.list({ batchId })`.

## פרמטרים עם טיפוסים
לסוגי הרכיבים האלה יש פרמטרים עם טיפוסים, ולכן עורך הקוד שלכם משלים את השדות שלהם ובודק אותם. כל סוג רכיב אחר מקבל אובייקט רגיל, והשדות שלו הם שדות הקלט של הרכיב, שמפורטים ב-`inputSchema` ובדף שלו ב[מדריך הרכיבים](https://nodaro.ai/docs/nodes). גם לרכיבי סצנות התלת־ממד יש פרמטרים עם טיפוסים: ראו את הדף [סצנות תלת־ממד](https://nodaro.ai/docs/developers/sdk/scenes-3d).

| סוג הרכיב | טיפוס הפרמטרים | דף הרכיב |
| --- | --- | --- |
| `generate-image` | `GenerateImageParams` | [יצירת תמונה](https://nodaro.ai/docs/nodes/image/generate-image) |
| `image-to-image` | `ImageToImageParams` | [עריכת תמונה](https://nodaro.ai/docs/nodes/image/modify-image) |
| `edit-image` | `EditImageParams` | [הגדלת תמונה](https://nodaro.ai/docs/nodes/image/upscale-image) |
| `split-image` | `SplitImageParams` | |
| `generate-mask` | `GenerateMaskParams` | [יצירת מסכה](https://nodaro.ai/docs/nodes/image/generate-mask) |
| `generate-video` | `GenerateVideoParams` | [יצירת וידאו](https://nodaro.ai/docs/nodes/video/generate-video) |
| `text-to-video` | `TextToVideoParams` | [יצירת וידאו](https://nodaro.ai/docs/nodes/video/generate-video) |
| `video-to-video` | `VideoToVideoParams` | [וידאו לווידאו](https://nodaro.ai/docs/nodes/video/video-to-video) |
| `assemble-narrated-video` | `AssembleNarratedVideoParams` | [הרכבת וידאו עם קריינות](https://nodaro.ai/docs/nodes/video/assemble-narrated-video) |
| `video-depth` | `VideoDepthParams` | [עומק וידאו](https://nodaro.ai/docs/nodes/video/video-depth) |
| `video-upscale` | `VideoUpscaleParams` | [הגדלת וידאו](https://nodaro.ai/docs/nodes/video/upscale-video) |

כל אובייקט פרמטרים עם טיפוסים מקבל גם שדות אחרים של הנתיב. השרת בודק את התקינות של גוף הבקשה כולו.

### GenerateImageParams
<TypeTable
type={{
prompt: { type: 'string', description: "מה התמונה מציגה." },
provider: { type: 'string', description: "מזהה המודל, למשל nano-banana-2 או gpt-image-2. השמיטו אותו כדי להשתמש במודל ברירת המחדל של הרכיב." },
negativePrompt: { type: 'string', description: "ממה המודל צריך להימנע." },
referenceImageUrls: { type: 'string[]', description: "כתובות של תמונות ייחוס, שמתווספות אחרי connectedReferences." },
connectedReferences: { type: 'ConnectedReference[]', description: "ייחוסים עם תוויות, כמו שהעורך מחבר אותם. ראו את הסעיף ״ייחוסים״ בהמשך." },
describedReferences: { type: 'DescribedReference[]', description: "עד 10 נושאים שאפשר לתת להם שם ולתאר אותם, אבל אין לכם תמונה שלהם." },
referenceOrder: { type: 'string[]', description: "מזהי הייחוסים, בסדר שבו המודל צריך לראות אותם." },
direction: { type: 'DirectionFields', description: "הכוונה קולנועית כמזהי בוררים, או פלטה או מודבורד. ראו את הסעיף ״הכוונה קולנועית״ בהמשך." },
subject: { type: 'Subject', description: "מי מופיע בשוט, כמזהי בוררים: האדם, הסטיילינג שלו והאביזרים בפריים. ראו את הסעיף ״הנושא״ בהמשך." },
count: { type: 'number', description: "מ-1 עד 4 תמונות, כל אחת במשימה משלה. מעל 1, השתמשו ב-runBatchAndWait()‎." },
models: { type: 'string[]', description: "בין 2 ל-4 מודלי תמונה שבהם תרוץ אותה בקשה, במקום provider. השתמשו ב-runBatchAndWait()‎." },
presetId: { type: 'string', description: "ערכה להחלה על הרכיב, כמו שהעורך מחיל אותה. ראו את הסעיף ״ערכות ברכיב יצירת תמונה״ בהמשך." },
presetVariables: { type: 'Record<string, string>', description: "ערכים למשתנים של הערכה שאין להם ברירת מחדל." },
promptPrefix: { type: 'string', description: "טקסט שמופיע לפני הפרומפט." },
promptSuffix: { type: 'string', description: "טקסט שמופיע אחרי הפרומפט." },
userPrompt: { type: 'string', description: "הפרומפט כפי שהמשתמש שלכם הקליד אותו. הוא נשמר במשימה ואף פעם לא נשלח למודל." },
baseImageUrl: { type: 'string', description: "להשלמה: התמונה שמשנים. שלחו אותה עם maskUrl." },
maskUrl: { type: 'string', description: "להשלמה: מסכה שבה הלבן הוא האזור לשינוי. כל מודל תמונה יכול לבצע השלמה. האזור הממוסך של התוצאה מוצב בחזרה מעל תמונת הבסיס." },
sessionId: { type: 'string', description: "סשן היצירה שבו תיווצר המשימה. ראו את המשאב client.sessions." },
origin: { type: 'string', description: "ה-slug של האפליקציה שלכם, שנשמר במשימה. אותיות לטיניות קטנות, ספרות ומקפים, עד 40 תווים." },
}}
/>

גם השדות `strength` ו-`guidanceScale` מוגדרים בטיפוס `GenerateImageParams`.

#### ערכות ברכיב יצירת תמונה
הפרמטר `presetId` מחיל ערכה בדיוק כמו שהעורך מחיל אותה: הפרומפט, הטקסט לפני ואחרי, ערכי ברירת המחדל של המשתנים, הסגנון, הפרומפט השלילי וההגדרות. כך המשימה זהה לזו שהכפתור **הרצה** בעורך היה יוצר. השתמשו במזהה מובנה מ-[`presets.listFactory("generate-image")`](https://nodaro.ai/docs/developers/sdk/pickers-and-prompts#presetslistfactorynodetype), או באחת הערכות שלכם. שדות שאתם מעבירים גוברים על הערכה.

משתנה בלי ברירת מחדל צריך ערך ב-`presetVariables`. בלי ערך, הקריאה נדחית עם `400 preset_variables_required`, והשגיאה מציינת את המשתנה.

```ts
await client.nodes.runAndWait("generate-image", {
presetId: "generate-image/logo-wordmark",
presetVariables: { BRAND: "Acme" },
})
```

### GenerateVideoParams
הנתיב מתמונה לווידאו: פריים פתיחה, פריים סיום אופציונלי וייחוסים.

<TypeTable
type={{
prompt: { type: 'string', description: "מה קורה בקליפ. אופציונלי כשיש פריים פתיחה." },
provider: { type: 'string', description: "מזהה המודל, למשל seedance-2-fast או kling-3.0." },
imageUrl: { type: 'string', description: "פריים הפתיחה." },
endFrameUrl: { type: 'string', description: "פריים הסיום, במודלים שתומכים בו." },
frameFit: { type: '"original" | "ratio" | "resolution"', default: '"resolution"', description: "איך פריים פתיחה או פריים סיום מותאם. הערך resolution משנה את הגודל שלו לגודל שהמודל מרנדר, הערך ratio מתקן רק את היחס, והערך original שולח אותו כמו שהוא." },
frameDelivery: { type: '"auto" | "frame" | "reference"', default: '"auto"', description: "אם הפריים נשלח כפריים פתיחה אמיתי, או כתמונת ייחוס שמצוינת כפריים הפתיחה. הערך auto בוחר לפי המודל." },
referenceImageUrls: { type: 'string[]', description: "תמונות ייחוס." },
referenceVideoUrls: { type: 'string[]', description: "סרטוני ייחוס, במודלים שמקבלים אותם." },
referenceAudioUrls: { type: 'string[]', description: "אודיו ייחוס, במודלים שמקבלים אותו." },
referenceVideoCaptions: { type: 'string[]', description: "כיתוב אחד לכל סרטון ייחוס, באותו סדר." },
referenceAudioCaptions: { type: 'string[]', description: "כיתוב אחד לכל קטע של אודיו ייחוס, באותו סדר." },
connectedReferences: { type: 'ConnectedReference[]', description: "ייחוסים עם תוויות." },
describedReferences: { type: 'DescribedReference[]', description: "נושאים עם שם, בלי תמונה." },
referenceOrder: { type: 'string[]', description: "סדר הייחוסים." },
draft: { type: 'boolean', description: "ב-seedance-2-5, טיוטה ב-480p במחיר של הרצה ב-480p. ראו את הסעיף ״טיוטות ב-Seedance 2.5״ בהמשך." },
direction: { type: 'object', description: "הכוונה קולנועית והכוונת תנועה כמזהי בוררים." },
}}
/>

### TextToVideoParams
הנתיב לווידאו מפרומפט בלבד, `POST /v1/text-to-video`. הפרומפט נדרש, ופריימי פתיחה וסיום שייכים ל-`generate-video`. בקשה למודל בלי מצב של טקסט לווידאו נדחית עם `400 image_required`.

<TypeTable
type={{
prompt: { type: 'string', required: true, description: "מה קורה בקליפ." },
provider: { type: 'string', description: "מזהה המודל." },
duration: { type: 'number', description: "האורך בשניות. הערכים המותרים נקבעים לפי המודל." },
sound: { type: 'boolean', description: "אודיו שהמודל יוצר בעצמו, במודלים שמרנדרים אודיו." },
negativePrompt: { type: 'string', description: "ממה להימנע." },
aspectRatio: { type: 'string', description: "יחס הגובה־רוחב, למשל 16:9." },
resolution: { type: 'string', description: "הרזולוציה, למשל 720p." },
seed: { type: 'number', description: "זרע קבוע, במודלים שתומכים בו." },
draft: { type: 'boolean', description: "ב-seedance-2-5, טיוטה ב-480p במחיר של הרצה ב-480p. כל רזולוציה הופכת ל-480p. ראו את הסעיף ״טיוטות ב-Seedance 2.5״ בהמשך." },
referenceImageUrls: { type: 'string[]', description: "תמונות ייחוס." },
referenceVideoUrls: { type: 'string[]', description: "סרטוני ייחוס." },
referenceAudioUrls: { type: 'string[]', description: "אודיו ייחוס." },
direction: { type: 'object', description: "הכוונה קולנועית והכוונת תנועה כמזהי בוררים." },
subject: { type: 'Subject', description: "מי מופיע בשוט, כמזהי בוררים, לפני הניסוח של ההכוונה. ראו את הסעיף ״הנושא״ בהמשך." },
}}
/>

### דחיות בגלל זכויות יוצרים
הטיפוס `TextToVideoParams` מקבל `contentPolicyRewrite?: boolean`. כשהשדה חסר או `true`, אחרי דחייה בגלל זכויות יוצרים יש ניסיון חוזר אחד עם פרומפט בניסוח אחר. עם `false`, דחייה בגלל זכויות יוצרים מכשילה את המשימה.

### טיוטות ב-Seedance 2.5
הטיפוסים `GenerateVideoParams` ו-`TextToVideoParams` מקבלים `draft: true`. ב-`seedance-2-5`, ההרצה היא טיוטה ב-480p במחיר של הרצה ב-480p. השלימו את הטייק שאהבתם עם `run("video-upscale", { provider: "seedance-2-5-hd", sourceJobId })`: אותו טייק, מרונדר מחדש ב-1080p, במחיר שנקבע לפי האורך של הטיוטה. מקור שהוא לא טיוטה שלכם שהסתיימה נדחה עם קוד 4xx לפני שנגבים קרדיטים. ראו את הסעיף [מצב טיוטה ב-Seedance 2.5](https://nodaro.ai/docs/nodes/video/generate-video#seedance-25-draft-mode) בדף יצירת וידאו.

```ts
const draft = await client.nodes.run("generate-video", { prompt, provider: "seedance-2-5", draft: true, duration: 8 })
// Wait for the draft and review it. When you like the take, finalize it at 1080p.
if ("jobId" in draft) {
const final = await client.nodes.runAndWait("video-upscale", { provider: "seedance-2-5-hd", sourceJobId: draft.jobId })
console.log(final.videoUrl)
}
```

**הגדלת וידאו עם Topaz.** הטיפוס `VideoUpscaleParams` מקבל `targetResolution` (`"720p"`, `"1080p"` או `"4k"`, לפי הצלע הקצרה של הפריים; ברירת המחדל היא 1080p) ו-`targetFps` (מ-15 עד 60; ברירת המחדל היא הקצב של המקור עצמו): `run("video-upscale", { videoUrl, targetResolution: "4k", targetFps: 60 })`. ב-Topaz החיוב הוא לפי שימוש. ראו את הסעיף [קרדיטים](https://nodaro.ai/docs/nodes/video/upscale-video#credits) בדף הגדלת וידאו. הפרמטר `upscaleFactor` מיושן. השרת עדיין מקבל אותו ומחיל אותו על הצלע הקצרה של המקור.

### VideoToVideoParams
נתיב עריכת הווידאו, `POST /v1/video-to-video`. עריכה ב-`seedance-2-5` נבנית בשרת, ולכן שולחים את אותו גוף בקשה כמו לכל מודל עריכה אחר. ראו את הדף [וידאו לווידאו](https://nodaro.ai/docs/nodes/video/video-to-video).

<TypeTable
type={{
videoUrl: { type: 'string', required: true, description: "סרטון המקור." },
prompt: { type: 'string', description: "איך לשנות את הסרטון." },
provider: { type: 'string', description: "wan, wan-flash, wan-videoedit, luma-modify, runway-aleph, happyhorse-edit או seedance-2-5, מודל העריכה של Seedance." },
duration: { type: '"5" | "10"', description: "האורך בשניות, במודלים שמקבלים אותו." },
resolution: { type: 'string', description: "הרזולוציה, במודלים שמקבלים אותה." },
aspectRatio: { type: 'string', description: "יחס הגובה־רוחב, במודלים שמקבלים אותו." },
referenceImageUrl: { type: 'string', description: "תמונת ייחוס, במודלים שמקבלים תמונה כזו. ב-seedance-2-5 היא ‎{image:1}‎." },
referenceImageUrls: { type: 'string[]', description: "ב-seedance-2-5 בלבד: עוד תמונות ייחוס, שממוספרות אחרי referenceImageUrl." },
referenceVideoUrls: { type: 'string[]', description: "ב-seedance-2-5 בלבד: קליפים נוספים. השדה videoUrl הוא ‎{video:1}‎, ואלה הם ‎{video:2}‎ והלאה. אל תחזרו על videoUrl." },
referenceAudioUrls: { type: 'string[]', description: "ב-seedance-2-5 בלבד: קטעים של אודיו ייחוס, ‎{audio:1}‎ והלאה." },
negativePrompt: { type: 'string', description: "ממה להימנע." },
seed: { type: 'number', description: "זרע קבוע, במודלים שתומכים בו." },
draft: { type: 'boolean', description: "ב-seedance-2-5, טיוטה ב-480p." },
}}
/>

הטיפוס כולל גם את `audio`, `multiShots`, `videoEditDuration`, `audioSetting` ו-`promptExtend` למודלים שמקבלים אותם, ובנוסף את `sessionId` ו-`origin`. כל מודל אחר דוחה את שלוש רשימות הייחוסים. המספור של הייחוסים והמגבלות מופיעים בדף [וידאו לווידאו](https://nodaro.ai/docs/nodes/video/video-to-video#api).

### נתיבי תמונה עם טיפוסים
ל-`image-to-image`, ל-`edit-image`, ל-`split-image` ול-`generate-mask` יש ב-`run` overloads עם טיפוסים. לשלושת הסוגים שמתחילים משימה יש אותם גם ב-`runAndWait`. כל טיפוס פרמטרים מבוסס על רשימות הערכים של הפלטפורמה עצמה, כגון `ModifyImageProvider`, `ImageEditProvider`, `ImageAspectRatio` ו-`ImageResolution`, שה-SDK מייצא מחדש. כל שדה אחר של הנתיב עדיין עובר.

הבדיקה בזמן ההידור חלה כשגוף הבקשה מוגדר מטיפוס הפרמטרים: `const params: EditImageParams = …`, או `{ … } satisfies EditImageParams` בתוך הקריאה. גוף בקשה שנכתב ישירות בתוך הקריאה, וה-overload עם הטיפוסים דוחה אותו, עדיין עובר הידור. הוא נופל ל-overload הכללי `run(type: string, …)`, שאין לו טיפוסים, והנתיב מחזיר `400`. השתמשו ב-`satisfies` כדי לקבל את הבדיקה.

- **`image-to-image`, `edit-image` ו-`generate-mask`.** המתודה `run` מחזירה `ImageJobStarted`, כלומר `{ jobId: string; adjustments? }`, ולכן השדה `result.jobId` לא צריך המרת טיפוס.
- **`ImageToImageParams`** דורש `imageUrl` ו-`prompt`. השדה `maskUrl` שלו (הלבן הוא האזור לשינוי) נשלח למודל כמסכה בפני עצמה, ולכן רק המודלים `ideogram-edit` ו-`flux-fill` משתמשים בו.
- **`EditImageParams`** הוא אחת משתי צורות. הוא מקבל `imageUrl` בכל מודל, חוץ מפעולות ה-task של Grok. בהן הוא מקבל `taskId` עם `provider: "grok-2-edit" | "grok-2-segment"`: מזהה ה-task של יצירה קודמת ב-Grok Imagine 2, ששמור בנתוני הפלט של המשימה ההיא. גוף בקשה עם טיפוסים ששולח את שניהם, או `taskId` עם מודל אחר, לא עובר הידור. מודל ברירת המחדל הוא `recraft-upscale`. השדה `prompt` נדרש ב-`nano-banana-edit` וב-`grok-2-edit`. השדה `maskIndexes` מיועד ל-`grok-2-edit` בלבד.
- **`split-image`** לא עולה כלום ומחזיר תשובה מיד. הוא לא יוצר משימה, ולכן המתודה `runAndWait` לא רלוונטית לו. המתודה `run` מחזירה `SplitImageResult`, כלומר `{ images: { name, url }[] }`: קובץ PNG אחד לכל אריח, בסדר של `names`. ב-`names` חייבים להיות בדיוק `gridCols × gridRows` פריטים, וכל אחד מ-`gridCols` ו-`gridRows` הוא בין 1 ל-6.
- **`generate-mask`** הוא הדרך של ה-SDK להתחיל משימה של רכיב [יצירת מסכה](https://nodaro.ai/docs/nodes/image/generate-mask). הקריאה `runAndWait("generate-mask", …)` מחזירה `GenerateMaskJobOutput`: השדה `maskUrl`, קובץ ה-PNG של המסכה, שבו הלבן הוא הנושא, והשדה `imageUrl`, תמונת המקור, שמועברת בלי שינוי.

```ts

const { maskUrl } = await client.nodes.runAndWait("generate-mask", { imageUrl, prompt: "the red car" } satisfies GenerateMaskParams)
await client.nodes.runAndWait("generate-image", { prompt: "a blue car", baseImageUrl: imageUrl, maskUrl } satisfies GenerateImageParams)
const { jobId } = await client.nodes.run("edit-image", { imageUrl, provider: "topaz-image-upscale", upscaleFactor: "4" } satisfies EditImageParams)
```

### VideoDepthParams
הרכיב הופך קליפ לסרטון עומק, שבו מה שקרוב בהיר ומה שרחוק כהה (`POST /v1/video-depth`). במשימה שהסתיימה יש `videoUrl` ו-`thumbnailUrl`. המחירים ומגבלות האורך מופיעים בדף [עומק וידאו](https://nodaro.ai/docs/nodes/video/video-depth).

<TypeTable
type={{
videoUrl: { type: 'string', required: true, description: "סרטון המקור, בכתובת http או https ציבורית." },
provider: { type: '"video-depth-anything" | "depth-anything-frames"', default: '"video-depth-anything"', description: "הספק video-depth-anything עקבי מפריים לפריים ומקבל את כל ההגדרות שלמטה. הספק depth-anything-frames רץ על כל פריים בנפרד ועשוי להבהב." },
modelSize: { type: '"small" | "base" | "large"', default: '"large"', description: "מודל גדול יותר נותן יותר פרטים ועובד לאט יותר." },
colormap: { type: '"grayscale" | "turbo" | "inferno" | "magma" | "viridis"', default: '"grayscale"', description: "הספק depth-anything-frames מרנדר רק grayscale ו-inferno. כל ערך אחר הופך ל-grayscale." },
resolution: { type: '"auto" | "360p" | "480p" | "720p" | "1080p"', default: '"auto"', description: "בספק video-depth-anything בלבד." },
sideBySide: { type: 'boolean', default: 'false', description: "בספק video-depth-anything בלבד. המקור והעומק זה לצד זה." },
}}
/>

השרת מתעלם מהגדרה שהספק שנבחר לא מקבל, ולכן החלפת ספק אף פעם לא מכשילה בקשה. ערך מחוץ לרשימות שלמעלה נדחה עם `400 validation_error`. הקליפ נמדד לפני שמשוריינים קרדיטים. קליפ ארוך מדי נדחה עם `400 video_too_long`: יותר מ-2,400 פריימים או מ-300 שניות ב-`video-depth-anything` ויותר מ-80 שניות ב-`depth-anything-frames`. קליפ שאי אפשר לקרוא נדחה עם `400 video_unreadable`.

```ts
const { videoUrl } = await client.nodes.runAndWait("video-depth", {
videoUrl: "https://cdn.example.com/clip.mp4",
colormap: "turbo",
resolution: "720p",
})
```

### AssembleNarratedVideoParams
הרכיב מאחד מקטעי וידאו עם קריינות לסרטון אחד. המחיר של ההרצה נקבע לפי מספר המקטעים, והיא עולה 44–143 קרדיטים: 3 יחידות ועוד יחידה אחת לכל 6 מקטעים, או חלק מהם. איך כל מקטע מותאם לקריינות שלו מוסבר בדף [הרכבת וידאו עם קריינות](https://nodaro.ai/docs/nodes/video/assemble-narrated-video).

<TypeTable
type={{
blocks: { type: '{ videoUrl: string; audioUrl?: string }[]', required: true, description: "מ-1 עד 60 מקטעים, בסדר ההשמעה." },
voiceVolume: { type: 'number', default: '100', description: "עוצמת הקריינות, מ-0 עד 200." },
clipAudioVolume: { type: 'number', default: '40', description: "עוצמת האודיו המקורי של הקליפים, מ-0 עד 200." },
maxSlowdown: { type: 'number', default: '1.5', description: "בכמה מותר להאט קליפ כדי שיתאים לקריינות שלו, מ-1 עד 2." },
trimStartFrames: { type: 'number', default: '0', description: "כמה פריימים לחתוך מתחילת כל קליפ, מ-0 עד 120." },
trimEndFrames: { type: 'number', default: '0', description: "כמה פריימים לחתוך מסוף כל קליפ, מ-0 עד 120." },
}}
/>

## ייחוסים
הרכיבים `generate-image`, `generate-video` ו-`text-to-video` מקבלים ייחוסים כמו שהעורך מחבר אותם. השרת הופך אותם בפרומפט להוראות ממוספרות, כגון `@image_1`, כך שלא צריך לכתוב בעצמכם "Image 1 is..." (תמונה 1 היא…).

- **`connectedReferences`** הוא רשימה של רשומות `ConnectedReference`, המבנה של ייחוס מחובר בעורך, שה-SDK מייצא. השרת מסיר כפילויות ומשאיר רק את מספר הייחוסים שהמודל מקבל. השדה `referenceOrder` קובע את הסדר שלהם לפי מזהה.
- **נעילת זהות.** רשומה יכולה לכלול `identityLock: { enabled: true, text? }`. השרת מוסיף אז שורה קצרה שמבקשת מהמודל לשמור על הזהות של הייחוס הזה. השדה `text` מחליף את הניסוח המובנה, והמשתנה `{ref}` בתוכו מייצג את השם של הייחוס. נעילת הזהות כבויה כברירת מחדל.
- **`describedReferences`** מקבל עד 10 רשומות `{ name, description }` לנושאים שאפשר לתת להם שם אבל אין לכם תמונה שלהם, כגון תפקיד בתסריט. כל רשומה הופכת בפרומפט לשורה `Name — description.`. השאירו את השם בטקסט של הפרומפט, כדי שהמודל יידע מי זה.
- **`descriptionOverride`** ברשומת ייחוס מחליף את התיאור השמור שלה, בהרצה הזו בלבד.
- **כיתובים לייחוסי וידאו ואודיו.** השדות `referenceVideoCaptions` ו-`referenceAudioCaptions` תואמים לסדר של `referenceVideoUrls` ו-`referenceAudioUrls`. כל כיתוב מופיע בפרומפט כ-`@video_1: caption.` או כ-`@audio_1: caption.`.
- **אזכור של תמונת ייחוס בפרומפט.** ב-`generate-image`, תנו שם לייחוס והזכירו אותו כ-`@<name>:<index>` או כ-`@<name>:<index>:<role>`, למשל `@town:1:background`. האזכור מציב את הייחוס הזה, או את התפקיד שלו, בנקודה הזו של הפרומפט. הסימונים `~lock` ו-`~nolock` עובדים כמו באזכורים של דמויות.

ראו את המדריך [תפקידי ייחוס](https://nodaro.ai/docs/guides/reference-roles) לתפקידים שאפשר להשתמש בהם ואת המדריך [דמויות עקביות](https://nodaro.ai/docs/guides/consistent-characters) לשמירה על זהות.

## הכוונה קולנועית
הרכיבים `generate-image`, `generate-video` ו-`text-to-video` מקבלים אובייקט `direction` של **מזהי** בוררים במקום מילים. Nodaro כותב לפרומפט ניסוח בדוק לכל מזהה, כך שהקוד שלכם שולח מזהים, והניסוח נשאר מעודכן.

```ts
await client.nodes.runAndWait("generate-image", {
prompt: "A detective waits under a street lamp",
direction: { shotSize: "wide-shot", timeOfDay: "golden-hour", mood: "suspicious" },
})
```

- **המפתחות** הם ממדים של בוררים, כגון `shotSize`, `lightingStyle`, `style`, `mood`, `photographer` או `era`. נתיבי הווידאו מוסיפים מפתחות תנועה, כגון `cameraMotion`, `actionFx`, `transition`, `loopSubject` ומפתחות ה-`temporal`.
- **הערכים** הם מזהה אחד או מערך של מזהים. ממד שמקבל כמה ערכים שומר לכל היותר את מספר הערכים שהמגבלה שלו מאפשרת ומשמיט את השאר. הבקשה נדחית רק מעל 8 ערכים למפתח או 100 תווים למזהה.
- **מפתחות ומזהים לא מוכרים מדולגים**, ולא נדחים. אובייקט `direction` ריק לא משנה את הפרומפט שלכם.
- **מפה אחת משרתת גם תמונות וגם וידאו.** מפתח שמיועד רק לתמונות ונשלח להרצת וידאו מתקבל ולא מוסיף כלום.
- הרכיב `extend-video` לא מקבל `direction`, כי הפרומפט שלו ממשיך קליפ קיים.
- **הפלטות והמודבורדים שלכם** עובדים ב-`generate-image`. קבעו את `colorLook` ל-`paletteDirectionId(id)`, או את `style` ל-`moodboardDirectionId(id)`. ראו את הסעיף [פלטות ומודבורדים](https://nodaro.ai/docs/developers/sdk/pickers-and-prompts#clientpalettes) בדף בוררים, ערכות ופרומפטים.

את המזהים התקפים אפשר לקבל מ-[`client.pickerCatalogs`](https://nodaro.ai/docs/developers/sdk/pickers-and-prompts). כל הממדים מפורטים בדף [קטלוגי הבוררים](https://nodaro.ai/docs/developers/picker-catalogs).

## הנושא
ב-`GenerateImageParams` וב-`TextToVideoParams` יש שדה אופציונלי `subject`, עם הטיפוס המיוצא `Subject`. השדה מתאר **מי מופיע בשוט**: האדם, הסטיילינג שלו והאביזרים בפריים, כמפה שטוחה משדה של בורר למזהה בקטלוג. אף פעם לא כותבים בשבילו טקסט של פרומפט. Nodaro כותב את הניסוח ומציב אותו מיד אחרי ה-`prompt` שלכם, לפני כל `direction`.

```ts

const subject: Subject = { type: "woman", age: "age-30s", hairBase: "base-short-straight", heldProp: "smartphone" }
await client.nodes.run("generate-image", { prompt: "on the seawall at dusk", subject })
```

- **שמות השדות והמזהים** מגיעים מקטלוגי הבוררים. הקריאות `client.pickerCatalogs.get("person")` ו-`get("styling")` מחזירות את ה-`field` של כל ממד, עם ה-`options[].id` שלו. הקריאות `get("held-prop")`, `get("material")` ו-`get("animal")` כותבות את המזהים שלהן ל-`valueField` של הקטלוג: `heldProp`, `material` ו-`animal`.
- **הערכים** הם מזהה אחד או רשימה של מזהים. מזהים מעבר למגבלה של הממד נשמטים, ולא נדחים. השדה `customAge` הוא המספר היחיד, בשנים, והשרת משתמש בו רק עם `age: "age-custom"`.
- **מגבלות.** בלי ערכה, הנתיב בודק את המפה. בקשה עם יותר מ-128 מפתחות, עם מפתח ארוך מ-64 תווים, עם יותר מ-8 מזהים במפתח אחד או עם מזהה ארוך מ-100 תווים נדחית עם `400 validation_error`. מפתחות ומזהים לא מוכרים בתוך המגבלות האלה מדולגים, והשדה `input_data.subject` של המשימה שומר בדיוק את המזהים שהיו בשימוש.
- **עם `presetId`**, השדה `subject` שלכם מחליף את זה של הערכה ומתווסף לפרומפט של הערכה, כמו `direction`. ערכים ארוכים מדי נשמטים במקום להידחות, והשדה `input_data` לא מתעד את הנושא.

כל המפתחות, והסדר שבו הניסוח מתווסף, מפורטים בדף ה-API [רכיבים](https://nodaro.ai/docs/developers/api/nodes).

## רכיבי מודל שפה
המתודה `run(type, params)` שולחת כל סוג של רכיב מודל שפה לנתיב שלו: הנתיב `/v1/<type>` ל-`generate-script`, ל-`image-critic`, ל-`qa-check` ול-`describe-to-picker`, ונתיב ארוך יותר לאחרים. לא צריך בשבילם את [`client.request()`](https://nodaro.ai/docs/developers/sdk/client#requestmethod-path-options):

| סוג הרכיב | נקודת קצה |
| --- | --- |
| `llm-chat` | `/v1/llm-chat/generate` |
| `after-effects` | `/v1/after-effects/generate` |
| `motion-graphics` | `/v1/motion-graphics/generate` |
| `lottie-overlay` | `/v1/lottie-overlay/generate` |
| `3d-title` | `/v1/3d-title/generate` |
| `image-to-text` | `/v1/image-to-text/describe` |
| `video-composer` | `/v1/scene-graph/generate` |

```ts
await client.nodes.run("generate-script", {
prompt: "A 3-scene product launch script for a smart water bottle",
reasoningEffort: "high",
})
```

- **`reasoningEffort`** הוא `"none"`, `"low"`, `"medium"`, `"high"`, `"xhigh"` או `"max"`, לפי המודל. השמיטו אותו, או שלחו רמה שהמודל לא תומך בה, כדי לקבל את ברירת המחדל של המודל. לרמת המאמץ אין מחיר משלה. כל קריאת LLM מחויבת לפי הטוקנים שהיא השתמשה בהם, בתעריף של המודל בשירות שהריץ אותה, ולכן רמה גבוהה יותר עולה יותר רק בגלל הטוקנים שהמודל כותב. במודל Claude, כל רמת מאמץ מריצה את הקריאה ב-API של היצרן עצמו, בתעריף של אותו שירות. המודלים מפורטים בדף [יצירת טקסט - כל LLM](https://nodaro.ai/docs/nodes/automate/prompt), והחיוב מוסבר בסעיף [איך מחויבות קריאות LLM](https://nodaro.ai/docs/concepts/credits#llm-calls) בדף קרדיטים. חריג אחד: הרכיב `describe-to-picker` וגרסת הטקסט שלו, `text-to-picker`, רצים ברמה `"high"` כשמשמיטים את השדה והמודל ב-`llmModel` הוא `claude-opus-5.5`. ההרצה הזו עוברת דרך ה-API של Anthropic עצמה, בתעריף של אותו שירות. שלחו רמה אחרת כדי לשנות אותה. כל מודל אחר שומר על ברירת המחדל שלו, כולל מודל ברירת המחדל של שני הסוגים האלה, Gemini 3.8 Flash.
- **`advancedMode: true`** מריץ מודל Gemini או Claude ישירות ב-API של היצרן שלו. רק שם `temperature`, `maxTokens` וכל טווח רמות המאמץ נכנסים לתוקף במלואם. הקריאה מחויבת בתעריף לטוקן של אותו שירות. בקשה עם מודל בלי האפשרות הזו נדחית עם `400 advanced_mode_unsupported`. כל עוד הספק של המודל עצמו לא יכול לקבל בקשות, הקריאה נכשלת עם `advanced_mode_unavailable` במקום לרוץ בשירות ברירת המחדל: עם הקוד `400` מראש, או `502` באמצע הקריאה, והקרדיטים מוחזרים אלא אם הספק חייב על הניסיון. כבו את המצב המתקדם כדי להריץ את המודל. רכיב תיאור לבורר הוא החריג: שם, הרצה של Claude שהספק דוחה עוברת ל-Gemini 3.8 Flash, כמו שקורה כשהמצב המתקדם כבוי.
- **אומדן מחיר לקריאה.** הקריאה `client.credits.modelCosts([llmPriceKey(feature, model)])` מחזירה אומדן של קריאה טיפוסית, שמופיע ב-`estimates`. הציגו אותו כ-`≈N`. הפונקציה `llmPriceKey` מגיעה מ-`@nodaro/shared/llm-models`. ראו את הדף [מודלים וקרדיטים](https://nodaro.ai/docs/developers/sdk/models-and-credits#creditsmodelcostsids).
- **אין מעטפת להזרמה.** ה-SDK לא קורא את התשובה בהזרמה של רכיב יצירת טקסט - כל LLM. בשבילה, השתמשו ב-`fetch` עם זרם קריא.

## כללים לפי מודל
כמה מודלי וידאו מקבלים ערכים שמודלים אחרים לא מקבלים. בדף של כל מודל מופיעים כל האפשרויות והמחירים.

- **[Seedance 2](https://nodaro.ai/docs/models/video/seedance-2)** מקבל `resolution: "4k"` ו-`aspectRatio: "adaptive"` או `"21:9"`. [Seedance 2 Fast](https://nodaro.ai/docs/models/video/seedance-2-fast) ו-[Seedance 2 Mini](https://nodaro.ai/docs/models/video/seedance-2-mini) מרנדרים ב-480p או ב-720p בלבד.
- **[Seedance 2.5](https://nodaro.ai/docs/models/video/seedance-2-5)** מרנדר ב-480p, ב-720p או ב-1080p, יוצר עד 30 שניות בקריאה אחת ומקבל 30 תמונות ייחוס, 10 סרטוני ייחוס ו-10 קטעי אודיו ייחוס. עם פריים פתיחה, הוא משתמש ביחס של הפריים הזה ודוחה `aspectRatio` מפורש.
- **[MiniMax H3](https://nodaro.ai/docs/models/video/minimax-h3)** (`minimax-h3`) מקבל 9 תמונות ייחוס, 3 סרטוני ייחוס ו-3 קטעי אודיו ייחוס, ב-`resolution: "2K"` (ברירת המחדל) או `"768P"`. כל ערך אחר מרונדר ומתומחר כמו 2K.
- **[Wan 3.0](https://nodaro.ai/docs/models/video/wan-3-0)** (`wan-3` ו-`wan-3-prime` המהיר יותר) מקבל 10 תמונות ייחוס, 5 סרטוני ייחוס ו-5 קטעי אודיו ייחוס. אי אפשר לשלב את רשימות הייחוסים עם `imageUrl` או עם `endFrameUrl`. הערך של `duration` הוא מספר שלם מ-2 עד 30, והערך של `resolution` הוא `480p`, `720p` או `1080p`.
- **[Gemini Omni Flash](https://nodaro.ai/docs/models/video/gemini-omni-flash)** מקבל אותה בקשה כמו [Gemini Omni](https://nodaro.ai/docs/models/video/gemini-omni): אורכים של 4, 6, 8 או 10 שניות, מ-720p עד 4K ורק 16:9 או 9:16.
- **[Kling 4.0](https://nodaro.ai/docs/nodes/video/generate-video#kling-40-references-characters-and-keyframes)** (`kling-4`) רץ מטקסט בלבד, או מחומרי ייחוס. הוא מקבל `referenceImageUrls` (עד 10), `referenceVideoUrls` (עד 5, כל אחד באורך של 2–30 שניות), `characterReferences` (עד 7, כנושאים עם שם) ו-`referenceAudioUrls` (עד 3). בקשה עם יותר מ-15 חומרים בסך הכול נדחית עם `400` לפני שמשוריינים קרדיטים. השדות `imageUrl` ו-`endFrameUrl`, כשהם נשלחים, מצטרפים לתמונות הייחוס. ב-`kling-4`, כל ייחוס דמות נבנה מ-`imageUrl`, מ-`bodyImageUrl` ומ-`extraImageUrls` (עד 3), מותר לשלוח פריים פתיחה, ואילו השדה `voice` נדחה. הערך `keepOriginalSound: true` שומר על הסאונד המקורי של סרטון ייחוס. קטע האודיו הראשון מתנגן כברירת מחדל כפסקול. הוא נשלח כעוד סרטון ייחוס, ולכן ההרצה מתומחרת כמו הרצה עם סרטון ייחוס, ושאר הקטעים נשארים קולות עם שם. הערך `audioSoundtrack: false` משאיר כל קטע כדגימת קול. בהרצה עם סרטון ייחוס משלמים על הפלט ועוד על הארוך מבין הפלט והקליפים, בתעריף הייחוס. **Kling 4.0 Keyframes** (`kling-4-keyframes`) מקבל `imageUrl`, עד 8 פריימי ביניים ב-`referenceImageUrls`, לפי הסדר, ו-`endFrameUrl`. המספר הכולל של הפריימים מוגבל לפי `duration`: מ-4 ב-3 שניות ועד 10 מ-9 שניות ומעלה, ובקשה עם יותר פריימים נדחית עם `400`. שני המודלים מקבלים `duration` מ-3 עד 30 (ברירת המחדל היא 5) ו-`resolution` של `720p` או `1080p`; כל ערך אחר מרונדר ב-720p. הערך של `aspectRatio` הוא `auto`, `16:9`, `9:16`, `1:1` או `21:9`. האודיו תמיד מופעל, ואין זרע.
- **Gemini Omni ו-Gemini Omni Flash** (`gemini-omni-video` ו-`gemini-omni-flash`) שומרים על הפנים של אדם אמיתי עם `characterReferences`. העבירו עד 3 רשומות בצורה `{ imageUrl, description, bodyImageUrl?, name?, voice? }`. השדה `voice` הוא `{ preset, description?, exampleLine? }`, והשדה `preset` הוא אחד המזהים של `GEMINI_OMNI_VOICE_PRESETS` מ-`@nodaro/shared`. השדה הזה שומר על קול אחד לדמות בכל הקליפים. בכל מודל אחר הבקשה נדחית עם `400`, ואי אפשר לשלב את `characterReferences` עם פריים פתיחה (`imageUrl`). כל דמות תופסת יחידה אחת מתוך 7 יחידות הקלט של המודל, או 2 עם `bodyImageUrl`, ותמונות ייחוס וסרטון מקור חולקים את אותן יחידות. ייחוסי דמויות לא מוסיפים קרדיטים. השתמשו בהם במקום ב-`referenceImageUrls` כשהפנים חייבות להישאר זהות.

**טקסט לדיבור.** כשמשמיטים את `provider`, רכיב [טקסט לדיבור](https://nodaro.ai/docs/nodes/audio/text-to-speech) משתמש ב-[ElevenLabs v4](https://nodaro.ai/docs/models/audio/elevenlabs-v4) לטקסט של עד 10,000 תווים. טקסט ארוך יותר עובר ל-[ElevenLabs Turbo v2.5](https://nodaro.ai/docs/models/audio/elevenlabs-turbo-v2-5), שהמגבלה שלו היא 40,000 תווים, כך שהוא לא נחתך. כשאתם מציינים `provider`, הרכיב תמיד משתמש בו. שלחו `provider: "elevenlabs-v3"` כדי להישאר עם ElevenLabs v3, שהמגבלה שלו היא 5,000 תווים.

**רציפות בין קליפים.** רכיב טקסט לדיבור מקבל גם `previousText` ו-`nextText`: השורות שנאמרות ממש לפני הקליפ ואחריו, עד 1,000 תווים כל אחת. הן לא מוקראות. ElevenLabs v4, v4 Turbo, Turbo v2.5 ו-Multilingual v2 משתמשים בהן כדי לשמור על אינטונציה אחת בקליפים שנוצרו בנפרד. ElevenLabs v3 מתעלם מהן.

## רכיבי גריפה ורכיבי קלט אחרים
רכיבי קלט רצים באותו אופן. רכיב גריפה, כגון [גריפת אתר](https://nodaro.ai/docs/nodes/automate/web-scrape), עונה מיד: התוצאה כוללת `jobId` בשביל ההיסטוריה שלכם וגם את הנתונים עצמם, כך שאפשר להשתמש בהם בלי לבדוק את הסטטוס מדי כמה שניות. שדות הבקשה הם שדות הקלט של הרכיב, שמפורטים ב-`inputSchema`.

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

### איך יוצרים תמונה עם ה-SDK של Nodaro?

קראו למתודה client.nodes.runAndWait עם סוג הרכיב generate-image ועם פרומפט. המתודה runAndWait מחזירה את הפלט של המשימה, וה-imageUrl שבפלט הוא התמונה שנוצרה.

### מה ההבדל בין המתודה run למתודה runAndWait?

המתודה run מתחילה את הרכיב ומחזירה jobId מיד. המתודה runAndWait מתחילה אותו, בודקת את הסטטוס של המשימה כל 2 שניות ומחזירה את הפלט כשהמשימה מסתיימת.

### כמה זמן המתודה runAndWait ממתינה?

המתודה runAndWait ממתינה עד 15 דקות כברירת מחדל. אפשר לשנות את הזמן עם האפשרות maxMs. פסק זמן לא מבטל את המשימה, ובדרך כלל היא עדיין מסתיימת בשרת.

### אילו סוגי רכיבים אפשר להריץ?

קראו למתודה client.nodes.list()‎ כדי לקבל כל סוג שהשרת תומך בו, עם הקטגוריה, סוג הפלט, המודלים והמחיר בקרדיטים שלו. לכל רכיב במדריך הרכיבים יש סוג ב-API.

### איך יוצרים כמה תמונות בקריאה אחת?

קראו למתודה client.nodes.runBatchAndWait עם generate-image ועם count (מ-1 עד 4 תמונות) או models (בין 2 ל-4 מודלי תמונה). האצווה מחויבת כולה או לא מחויבת בכלל, ותמונה אחת שנכשלה אף פעם לא מבטלת את התמונות האחרות.

### איך נמנעים מתשלום כפול כשמנסים שוב בקשה?

העבירו idempotencyKey באפשרויות של המתודה run או של המתודה runAndWait, והשתמשו באותו מפתח כשאתם מנסים שוב. הפלטפורמה מחזירה את ההרצה הראשונה במקום להתחיל הרצה שנייה.
