אובייקטים ויצורים
יצירת אובייקטים ויצורים, תמונות ראשיות ווריאנטים, הנפשה שלהם ויצור שמדבר, מקוד TypeScript עם ה-SDK של Nodaro.
client.objects מאפשר לעשות בסקריפט כל מה שעושים באולפן האובייקטים/אביזרים לאביזרים, למוצרים ולכלי רכב, והמשאב client.creatures עושה את אותו דבר לחיות וליצורים. שניהם יוצרים ועורכים פריטים, יוצרים מועמדים לתמונה הראשית, מאשרים אחד מהם, מוסיפים וריאנטים וקליפי תנועה וכותבים תיאור ששומר על עקביות הפריט בפרומפטים מאוחרים יותר. המתודות קוראות לנקודות הקצה שבדפי ה-API אובייקטים ויצורים. את אותה עבודה בעורך מתארים המדריכים אובייקטים ואביזרים וחיות ויצורים.
מתודות
לשני המשאבים יש אותן מתודות.
| מתודה | מה היא עושה |
|---|---|
objects.list(params?), creatures.list(params?) | רשימת הפריטים שלכם |
objects.listArchived(params?) | רשימת הפריטים שלכם שבארכיון |
objects.get(id) | קריאת פריט אחד, עם המשימות שלו שעדיין רצות |
objects.create(input), creatures.create(input) | יצירת פריט |
objects.update(id, input), creatures.update(id, input) | שינוי פריט |
objects.delete(id) ו-restore(id) | העברת פריט לארכיון, או שחזור שלו |
objects.permanentDelete(id) | מחיקה לצמיתות של פריט שבארכיון ושל הקבצים שלו |
objects.generate(input), creatures.generate(input) | יצירת מועמדים לתמונה הראשית |
objects.generateAsset(input), creatures.generateAsset(input) | יצירת וריאנט |
objects.generateMotion(input), creatures.generateMotion(input) | הנפשת התמונה הראשית לקליפ |
objects.approveMainImage(id, candidateJobId, expectedUpdatedAt?) | קביעת מועמד כתמונה הראשית |
objects.recaption(id) | כתיבה מחדש של התיאור |
client.objects
לאובייקט יש תמונה ראשית ב-sourceImageUrl, ארבע קבוצות וריאנטים (angles, materials, variations ו-motionClips, כל אחת רשימה של { name, url }) וגם boards, referencePhotos, canonicalDescription ו-styleLock. השדה category הוא אחד מהערכים furniture, vehicle, weapon, food, clothing, electronics, nature, tool, animal או other.
לטיפוס Object יש אותו שם כמו לאובייקט הגלובלי של JavaScript. כשצריך את שניהם, ייבאו אותו בשם אחר: import type { Object as NodaroObject } from "@nodaro/sdk".
objects.list(params?)
מחזירה את רשימת האובייקטים שלכם. כברירת מחדל, היא מחזירה רק אובייקטים פעילים. החלוקה לעמודים היא אופציונלית: בלי limit מקבלים את הרשימה המלאה, ועם limit, עד 500, מקבלים עמוד אחד ו-nextCursor.
list(params?: { archived?: boolean; projectId?: string; q?: string; limit?: number; cursor?: string }): Promise<{
objects: Object[]
nextCursor?: string | null
}>מאפיין
סוג
const { objects } = await client.objects.list()
const { objects: cars } = await client.objects.list({ q: "red car" })
const page = await client.objects.list({ limit: 100 })objects.listArchived(params?)
מחזירה את רשימת האובייקטים שלכם שבארכיון, כקיצור דרך ל-list({ archived: true }). המתודה creatures.listArchived() עובדת באותו אופן.
listArchived(params?: { projectId?: string; limit?: number; cursor?: string }): Promise<{ objects: Object[]; nextCursor?: string | null }>מאפיין
סוג
const { objects: archived } = await client.objects.listArchived()objects.get(id)
מחזירה אובייקט אחד, עם pendingJobs: הווריאנטים שעדיין נוצרים. אובייקט שבארכיון לא מוחזר: המתודה זורקת NotFoundError. המתודה creatures.get() עובדת באותו אופן.
get(id: string): Promise<ObjectDetail>מאפיין
סוג
const object = await client.objects.get(objectId)
console.log(object.sourceImageUrl, object.materials)objects.create(input)
יוצרת אובייקט. השדות name ו-nodeId נדרשים. סקריפט בלי רכיב על הקנבס יכול להעביר "mcp-managed" בתור nodeId.
create(input: CreateObjectInput): Promise<{ id: string }>מאפיין
סוג
const { id: objectId } = await client.objects.create({
nodeId: "mcp-managed",
name: "Antique Lantern",
description: "Weathered brass lantern with hand-engraved filigree",
category: "tool",
style: "realistic",
})objects.update(id, input)
משנה אובייקט. רק השדות שאתם שולחים נכתבים. קבוצות הווריאנטים הן לא חלק מהקריאה הזו, כי משימות יצירה מוסיפות להן בזמן שאתם עובדים.
update(id: string, input: UpdateObjectInput): Promise<{ id: string; updatedAt: string }>מאפיין
סוג
await client.objects.update(objectId, {
canonicalDescription: "A weathered brass lantern with engraved filigree and a glass chimney",
expectedUpdatedAt: object.updatedAt,
})השגיאה 409 concurrent_modification מגיעה כ-NodaroError רגיל. טענו את האובייקט שוב, מזגו את השינויים ונסו שוב.
objects.delete(id) ו-restore(id)
המתודה delete() מעבירה אובייקט לארכיון. חזרה על הקריאה, כשהאובייקט כבר בארכיון, לא משנה כלום. המתודה restore() משחזרת אותו; כשהשם שלו זהה עכשיו לשם של אובייקט פעיל, בלי הבדל בין אותיות גדולות לקטנות, השרת מוסיף (restored) (שוחזר) ומחזיר את השם שבו הוא השתמש.
delete(id: string): Promise<{ success: true; archived: true }>
restore(id: string): Promise<{ id: string; name: string }>מאפיין
סוג
await client.objects.delete(objectId)
const { name } = await client.objects.restore(objectId)objects.permanentDelete(id)
מוחקת לצמיתות אובייקט שבארכיון ואת כל הקבצים השמורים שהוא מצביע עליהם. המתודה עובדת רק על אובייקטים שבארכיון: על אובייקט פעיל היא נכשלת עם 400 not_archived. העבירו אותו קודם לארכיון עם delete(). המתודה creatures.permanentDelete() עובדת באותו אופן.
permanentDelete(id: string): Promise<{ success: true; permanent: true }>מאפיין
סוג
await client.objects.delete(objectId)
await client.objects.permanentDelete(objectId)כלי ה-MCP של Nodaro לא מציעים את הפעולה הזו, ולכן עוזר AI לא יכול למחוק לצמיתות את האובייקטים שלכם.
objects.generate(input)
יוצרת מועמדים לתמונה הראשית (POST /v1/generate-object). כש-count גדול מ-1, הקרדיטים לכל המשימות משוריינים לפני שאחת מהן מתחילה, ולכן כשל באמצע מבטל את כל האצווה.
generate(input: GenerateObjectInput): Promise<{ jobIds: string[]; jobId?: string }>מאפיין
סוג
const { jobIds } = await client.objects.generate({ name: "Antique Lantern", count: 4 })
for (const jobId of jobIds) {
// poll each candidate with client.jobs.getStatus(jobId)
}השדה jobIds תמיד מופיע, עם מזהה אחד לכל מועמד. השדה jobId הוא שם חלופי ישן יותר למועמד יחיד; השתמשו ב-jobIds. עם attachToObjectId ומועמד אחד, התוצאה הופכת לתמונה הראשית כשהמשימה מסתיימת. אחרת, בחרו מועמד עם approveMainImage().
objects.generateAsset(input)
יוצרת וריאנט אחד (POST /v1/generate-object-asset). עם attachToObjectId, attachToColumn ו-attachName, התוצאה מתווספת לקבוצה הזו כשהמשימה מסתיימת.
generateAsset(input: GenerateObjectAssetInput): Promise<{ jobId: string }>מאפיין
סוג
const { jobId } = await client.objects.generateAsset({
name: "Antique Lantern",
assetType: "materials",
variant: "gold",
attachToObjectId: objectId,
attachToColumn: "materials",
attachName: "gold",
})ב-angles, ב-materials, ב-variations וב-motion, הקבוצה נקבעת לפי סוג הווריאנט. וריאנט custom צריך attachToColumn.
objects.generateMotion(input)
מנפישה את התמונה של האובייקט לקליפ (POST /v1/generate-object-motion), בעזרת רכיב יצירת וידאו במצב תמונה לווידאו. הקליפ תמיד נשמר ב-motionClips. ברירות המחדל מתאימות לשוטים של מוצר: המודל הוא kling-turbo, ויחס הגובה־רוחב הוא 1:1.
generateMotion(input: GenerateObjectMotionInput): Promise<{ jobId: string }>מאפיין
סוג
const { jobId } = await client.objects.generateMotion({
name: "Antique Lantern",
motionPrompt: "Slow 360-degree rotation, soft golden rim light",
sourceImageUrl: object.sourceImageUrl!,
attachToObjectId: objectId,
attachName: "rotate-360",
})objects.approveMainImage(id, candidateJobId, expectedUpdatedAt?)
קובעת מועמד שהושלם מ-generate() כתמונה הראשית של האובייקט. אחר כך מודל ראייה כותב את התיאור של האובייקט, והמתודה מחזירה את שניהם.
approveMainImage(id: string, candidateJobId: string, expectedUpdatedAt?: string): Promise<{
sourceImageUrl: string
canonicalDescription: string | null
}>מאפיין
סוג
const { sourceImageUrl, canonicalDescription } = await client.objects.approveMainImage(objectId, jobIds[0])השדה canonicalDescription הוא null כשאי אפשר היה לכתוב את התיאור. התמונה הראשית בכל זאת נקבעת; קראו למתודה recaption() כדי לנסות שוב.
objects.recaption(id)
כותבת מחדש את התיאור של האובייקט מהתמונה הראשית הנוכחית שלו. אפשר לחזור על הקריאה בלי חשש, והיא לא מקבלת אסימון מקביליות.
recaption(id: string): Promise<{ canonicalDescription: string }>מאפיין
סוג
const { canonicalDescription } = await client.objects.recaption(objectId)המתודה נכשלת עם 400 main_image_required כשלאובייקט אין תמונה ראשית, ועם 502 כשמודל הראייה נכשל.
client.creatures
יצור הוא חיה או יצור פנטזיה. הוא עובד כמו אובייקט, עם ארבעה הבדלים:
speciesהוא סוג בטקסט חופשי, למשלdragon(דרקון) אוwolf(זאב), והוא הנושא של הפרומפט לתמונה הראשית. גםcategoryהוא טקסט חופשי.posesמחליף אתmaterials, ולכן הקבוצות הןangles,poses,variationsו-motionClips. סוגי הווריאנטים הםangles,poses,variationsו-custom.boardsכולל עד 24 לוחות יצור עם שם: גיליונות ייחוס צפופים שנוצרים עם הערכה לוח יצור של רכיב יצירת תמונה. את הרשימה הזו אתם מנהלים: המתודותcreate()ו-update()מחליפות אותה כולה.voiceהופך את היצור ליצור שמדבר. המבנה שלו זהה לקול של דמות:{ voiceId, voiceName, traits, voiceType?, previewUrl?, ttsProvider? }. העבירוvoice: nullכדי להסיר אותו.
המתודות creatures.listArchived(), get(), delete(), restore(), permanentDelete(), approveMainImage() ו-recaption() מקבלות את אותם ארגומנטים ומתנהגות כמו המתודות של האובייקטים שלמעלה.
creatures.list(params?)
list(params?: { archived?: boolean; projectId?: string; q?: string; limit?: number; cursor?: string }): Promise<{
creatures: Creature[]
nextCursor?: string | null
}>מאפיין
סוג
const { creatures } = await client.creatures.list({ q: "dragon" })המתודה creatures.list() מקבלת את אותם פרמטרים כמו objects.list(), כולל החלוקה האופציונלית לעמודים עם limit ו-cursor. כללי החיפוש של q מפורטים בדף דמויות.
creatures.create(input)
create(input: CreateCreatureInput): Promise<{ id: string }>מאפיין
סוג
const { id: creatureId } = await client.creatures.create({
nodeId: "mcp-managed",
name: "Biscuit",
species: "ginger cat",
style: "realistic",
})creatures.update(id, input)
מקבלת את השדות של create() חוץ מ-nodeId, ובנוסף boards, selectedAssetByVariant ו-expectedUpdatedAt. רק השדות שאתם שולחים נכתבים.
update(id: string, input: UpdateCreatureInput): Promise<{ id: string; updatedAt: string }>מאפיין
סוג
await client.creatures.update(creatureId, {
voice: { voiceId: chosenVoiceId, voiceName: "Aria", traits: "smug, unhurried" },
})creatures.generate(input)
יוצרת מועמדים לתמונה הראשית. המתודה מקבלת את השדות של objects.generate(), ובנוסף species, ומחזירה { jobIds }.
generate(input: GenerateCreatureInput): Promise<{ jobIds: string[]; jobId?: string }>מאפיין
סוג
const { jobIds } = await client.creatures.generate({ name: "Biscuit", species: "ginger cat", count: 4 })creatures.generateAsset(input)
יוצרת וריאנט אחד. המתודה עובדת כמו objects.generateAsset(), עם attachToCreatureId.
generateAsset(input: GenerateCreatureAssetInput): Promise<{ jobId: string }>מאפיין
סוג
await client.creatures.generateAsset({
name: "Biscuit",
assetType: "poses",
variant: "sitting",
attachToCreatureId: creatureId,
attachToColumn: "poses",
attachName: "sitting",
})creatures.generateMotion(input)
מנפישה את התמונה של היצור לקליפ. המתודה עובדת כמו objects.generateMotion(), עם אותן ברירות מחדל, kling-turbo ו-1:1, ומוסיפה את הקליפ ל-motionClips.
generateMotion(input: GenerateCreatureMotionInput): Promise<{ jobId: string }>מאפיין
סוג
await client.creatures.generateMotion({
name: "Biscuit",
motionPrompt: "The cat stretches, then yawns",
sourceImageUrl: creature.sourceImageUrl!,
attachToCreatureId: creatureId,
})יצור שמדבר
לדיבור לא צריך מתודה מיוחדת ליצורים. רנדרו את השורה בקול של היצור, ואז סנכרנו אותה לתמונה של היצור בעזרת סנכרון שפתיים:
const creature = await client.creatures.get(creatureId)
// 1. Speak the line in the creature's voice
const speech = await client.nodes.runAndWait("text-to-speech", {
text: "I knocked the vase off the shelf. I regret nothing.",
voice: creature.voice!.voiceId,
provider: creature.voice!.ttsProvider,
voiceType: creature.voice!.voiceType,
})
// 2. Lip-sync the audio onto the creature's main image
const clip = await client.nodes.runAndWait("lip-sync", {
imageUrl: creature.sourceImageUrl!,
audioUrl: speech.audioUrl,
provider: "kling-avatar",
})
console.log(clip.videoUrl)רכיב סנכרון שפתיים גם מדבב סרטון קיים: העבירו videoUrl ומודל שמקבל וידאו. המודל volcengine-lipsync הוא האפשרות הזולה ביותר לדיבוב, והיחידה שמטפלת בכמה דוברים:
const dub = await client.nodes.runAndWait("lip-sync", {
videoUrl: "https://example.com/scene.mp4",
audioUrl: "https://example.com/new-vocal.mp3",
provider: "volcengine-lipsync",
mode: "basic", // for complex scenes
openScenedet: true, // several speakers: scene and speaker detection
audioDurationSec: 42, // sets the per-second price; without it, you pay for 5 minutes
})כדי להכניס יצור לשוט כייחוס, בנו את הייחוס עם toConnectedReference({ kind: "creature", id, name, url, description }) מ-@nodaro/shared. רכיב יצירת תמונה מוסיף אז שורה ששומרת על האנטומיה, על הסימונים ועל הצבעים של היצור. ראו את הסעיף ייחוסים בדף הרצת רכיבים.
שאלות נפוצות
מה ההבדל בין אובייקט ליצור ב-Nodaro?
אובייקט הוא אביזר, מוצר או רכב, עם וריאנטים של זווית, חומר ווריאציה. יצור הוא חיה או יצור פנטזיה, עם וריאנטים של זווית, תנוחה ווריאציה, מין, לוחות ייחוס וקול אופציונלי.
איך יוצרים מועמדים לתמונה הראשית של אובייקט?
קראו למתודה client.objects.generate עם שם ועם count. המתודה תמיד מחזירה jobIds, מזהה אחד לכל מועמד. כשהמשימות מסתיימות, אשרו מועמד אחד עם המתודה client.objects.approveMainImage.
האם אפשר למחוק אובייקט לצמיתות עם ה-SDK?
כן, בשני שלבים. העבירו את האובייקט לארכיון עם המתודה delete(), ואז קראו למתודה permanentDelete(), שמסירה את האובייקט ואת כל הקבצים שהוא מצביע עליהם. בקשה למחוק לצמיתות אובייקט פעיל נדחית עם 400 not_archived.
איך גורמים לחיה או ליצור לדבר?
רנדרו דיבור עם רכיב טקסט לדיבור ועם הקול של היצור, ואז הריצו את רכיב סנכרון שפתיים עם התמונה הראשית של היצור ועם האודיו הזה.
דפים קשורים
אובייקטים ואביזרים
חיות ויצורים
אובייקטים
יצורים
דמויות
עודכן לאחרונה