UNPKG

@sogni-ai/sogni-creative-agent-skill

Version:

Sogni Creative Agent Skill: agent skill and CLI for Sogni AI image, 3D, speech, video, and music generation.

166 lines (156 loc) • 9.95 kB
import { SpeechModel, getSpeechMode, validateSpeechRequest, DEFAULT_SPEECH_VOICE, SPEECH_CREATIVITY, SPEECH_REFERENCE_SECONDS, MUSIC_MODELS, MusicModel } from '@sogni-ai/sogni-intelligence-client/media'; export const PIXAL3D_MODEL_ID = 'pixal3d_int8_i23d'; // A front view (--image-to-3d) plus any subset of three orbit views. export const PIXAL3D_MULTIVIEW_MODEL_ID = 'pixal3d_multiview_int8_i23d'; export const BIREFNET_MODEL_ID = 'birefnet_image_background_removal_fp16'; export const MUSIC3_MODEL_ID = MusicModel.best; export const MUSIC3_DEFAULTS = MUSIC_MODELS.best; export { SPEECH_REFERENCE_SECONDS }; export const SPEECH_VALUE_FLAGS = { '--speech-mode': 'speechMode', '--speech-voice': 'speechVoice', '--voice-description': 'voiceDescription', '--voice-reference': 'voiceReference', '--voice-transcript': 'voiceTranscript' }; // Pixal3D multi-view orbit views, named by the SUBJECT's own sides, not the // viewer's: --left-view shows the subject turned so its own left side faces the // camera (it faces screen-left), --right-view its own right side (it faces // screen-right), --back-view the subject from behind. Templates that label the // subject's right side "left" build a model turned 180 degrees. export const PIXAL3D_VIEW_FLAGS = { '--left-view': 'leftViewImage', '--back-view': 'backViewImage', '--right-view': 'rightViewImage' }; export const MESH_FLAGS = { '--mesh-faces': ['meshTargetFaces', 5000, 700000], '--texture-size': ['textureSize', 1024, 4096], '--normal-map-size': ['normalMapSize', 512, 2048], '--ao-map-size': ['ambientOcclusionSize', 256, 1024], '--shape-resolution': ['shapeResolution', 1024, 1536] }; // Validate explicit controls before image/video inference can reinterpret them. export function prepareMediaUtilityOptions(options, explicit) { const fail = message => { throw new Error(message); }; const selectedSpeechMode = getSpeechMode(options.model); if (selectedSpeechMode) { if (options.speechMode && options.speechMode !== selectedSpeechMode) fail('--speech-mode conflicts with --model.'); options.speech = true; options.speechMode = selectedSpeechMode; } const orbitViews = Object.keys(options.pixal3dViews); if (options.model === PIXAL3D_MODEL_ID && !options.imageTo3d) fail('Pixal3D requires --image-to-3d <original-image>.'); if (options.model === PIXAL3D_MULTIVIEW_MODEL_ID && !options.imageTo3d) { fail('Pixal3D multi-view requires --image-to-3d <front-view-image> plus --left-view, --back-view and/or --right-view.'); } if (orbitViews.length && !options.imageTo3d) { fail('--left-view, --back-view and --right-view add views to --image-to-3d, which takes the front view.'); } if (options.model === BIREFNET_MODEL_ID && !options.removeBackground) fail('BiRefNet requires --remove-background <original-image>.'); if (Object.keys(options.meshSettings).length && !options.imageTo3d) fail('Mesh settings require --image-to-3d.'); if (options.matte && !options.removeBackground) fail('--matte requires --remove-background.'); if (!options.speech && (options.speechMode || options.speechVoice || options.voiceDescription || options.voiceReference || options.voiceTranscript || options.speechCreativity !== null)) { fail('Speech controls require --speech.'); } const active = options.speech || options.imageTo3d || options.removeBackground; if (!active) return; if ([options.speech, options.imageTo3d, options.removeBackground, options.music, options.video, options.segmentImage, options.upscaleImage, options.upscaleVideo, options.apiChat, options.apiWorkflowAction].filter(Boolean).length !== 1) { fail('Choose one generation mode: speech, image-to-3d, remove-background, music, video, segmentation, or upscale.'); } const unsupported = ['quality', 'width', 'height', 'strictSize', 'duration', 'fps', 'frames', 'steps', 'guidance', 'sampler', 'scheduler', 'context', 'refImage', 'refImageEnd', 'refAudio', 'refVideo', 'targetResolution', 'musicLyrics', 'musicBpm', 'musicKeyscale', 'musicTimesig', 'musicComposerMode', 'musicPromptStrength', 'musicCreativity', 'musicShift', 'returnLastFrame']; if (unsupported.some(key => explicit[key]) || options.contextImages.length || options.loras.length || options.multiAngle || options.photobooth || options.looping || options.personaName || options.referenceAudioIdentity || options.voicePersonaName) { fail('This mode does not accept image/video references, sizing, diffusion, or music controls. Use its dedicated input flags.'); } if (options.speech) { options.speechMode ||= 'voice'; const model = SpeechModel[options.speechMode]; if (!model) fail('Speech mode must be voice, clone, or design.'); if (explicit.model && options.model !== model) fail('--model conflicts with the selected speech mode.'); options.model = model; options.musicLanguage = (options.musicLanguage || 'auto').toLowerCase(); const error = validateSpeechRequest({ mode: options.speechMode, script: options.prompt, language: options.musicLanguage, voice: options.speechVoice || undefined, instruct: options.voiceDescription || undefined, referenceText: options.voiceTranscript || undefined, hasReferenceAudio: Boolean(options.voiceReference) }); if (error) fail(error); if (options.speechMode === 'voice') options.speechVoice ||= DEFAULT_SPEECH_VOICE; if (options.speechCreativity !== null && (!Number.isFinite(options.speechCreativity) || options.speechCreativity < SPEECH_CREATIVITY.min || options.speechCreativity > SPEECH_CREATIVITY.max)) { fail(`--speech-creativity must be between ${SPEECH_CREATIVITY.min} and ${SPEECH_CREATIVITY.max}.`); } options.outputFormat = (options.outputFormat || 'wav').toLowerCase(); } else { if (options.prompt) fail('Pixal3D and BiRefNet are promptless; supply only the original image.'); if (options.count !== 1) fail('Image utilities accept one image and produce one result; --count must be 1.'); if (explicit.musicLanguage || explicit.seed || options.lastSeed) fail('Image utilities do not accept language or seed controls.'); const model = options.imageTo3d ? (orbitViews.length ? PIXAL3D_MULTIVIEW_MODEL_ID : PIXAL3D_MODEL_ID) : BIREFNET_MODEL_ID; if (explicit.model && options.model !== model) { if (options.model === PIXAL3D_MULTIVIEW_MODEL_ID) { fail(`${PIXAL3D_MULTIVIEW_MODEL_ID} needs --left-view, --back-view and/or --right-view; a front image alone uses ${PIXAL3D_MODEL_ID}.`); } if (options.model === PIXAL3D_MODEL_ID && orbitViews.length) { fail(`${PIXAL3D_MODEL_ID} reconstructs from one image; orbit views use ${PIXAL3D_MULTIVIEW_MODEL_ID}.`); } fail('--model conflicts with the selected image utility.'); } options.model = model; const format = options.imageTo3d ? 'glb' : 'png'; if (options.outputFormat && options.outputFormat.toLowerCase() !== format) fail(`This mode produces ${format.toUpperCase()} only.`); options.outputFormat = format; if (options.imageTo3d && options.meshSettings.shapeResolution !== undefined && ![1024, 1536].includes(options.meshSettings.shapeResolution)) { fail('--shape-resolution must be 1024 or 1536.'); } } if (options.output) { const extension = options.output.match(/\.([^./\\]+)$/)?.[1]?.toLowerCase(); if (extension && extension !== options.outputFormat) fail(`Output filename must use .${options.outputFormat}, matching --output-format.`); } } // viewBytes maps leftViewImage/backViewImage/rightViewImage to image bytes. export function imageUtilityConfig(options, bytes, viewBytes = {}) { return { modelId: options.model, positivePrompt: '', startingImage: bytes, numberOfMedia: 1, tokenType: options.tokenType || 'spark', waitForCompletion: false, ...(options.imageTo3d ? { ...options.meshSettings, ...viewBytes } : { applyMask: !options.matte, outputFormat: 'png' }) }; } export function speechConfig(options, referenceAudio) { return { modelId: options.model, positivePrompt: options.prompt, numberOfMedia: options.count, tokenType: options.tokenType || 'spark', waitForCompletion: false, disableNSFWFilter: options.noFilter === true, language: options.musicLanguage, outputFormat: options.outputFormat, ...(options.speechVoice ? { speaker: options.speechVoice } : {}), ...(options.voiceDescription ? { instruct: options.voiceDescription } : {}), ...(referenceAudio ? { referenceAudio } : {}), ...(options.voiceTranscript ? { referenceText: options.voiceTranscript } : {}), ...(options.speechCreativity !== null ? { creativity: options.speechCreativity } : {}), ...(options.seed !== null ? { seed: options.seed } : {}) }; } export function utilityResultMetadata(options) { if (options.speech) return { speechMode: options.speechMode, speaker: options.speechVoice, language: options.musicLanguage, ...(options.voiceDescription ? { voiceDescription: options.voiceDescription } : {}), ...(options.voiceReference ? { voiceReference: options.voiceReference } : {}), ...(options.voiceTranscript ? { voiceTranscript: options.voiceTranscript } : {}), ...(options.speechCreativity !== null ? { creativity: options.speechCreativity } : {}) }; if (options.imageTo3d) return { sourceImage: options.imageTo3d, ...(Object.keys(options.pixal3dViews).length ? { orbitViews: { ...options.pixal3dViews } } : {}), meshSettings: { shapeResolution: 1024, ...options.meshSettings } }; if (options.removeBackground) return { sourceImage: options.removeBackground, applyMask: !options.matte }; return {}; } export function validateGlb(bytes) { if (bytes.length < 20 || bytes.toString('ascii', 0, 4) !== 'glTF' || bytes.readUInt32LE(4) !== 2 || bytes.readUInt32LE(8) !== bytes.length) { throw new Error('Pixal3D returned an invalid GLB file; the result was not saved.'); } }