# Sogni SDK - Complete LLM Reference

> @sogni-ai/sogni-client - JavaScript/Node.js SDK for AI image, video, and audio generation
> Version: 4.2.0-alpha.24 | License: ISC | Platform: Node.js & Browser
> Repository: https://github.com/Sogni-AI/sogni-client
> API Docs: https://sdk-docs.sogni.ai

================================================================================
TABLE OF CONTENTS
================================================================================

1. Overview & Installation
2. Quick Start Examples
3. Client Initialization
4. Image Generation
5. Video Generation - WAN 2.2 Models
6. Video Generation - LTX-2.3 Models
7. Video Generation - Seedance 2.0 Models
8. Audio Generation - ACE-Step 1.5 Models
9. LLM Text Generation
10. LLM Tool Calling & Sogni Platform Tools
11. Vision Chat (Multimodal Image Understanding)
12. Complete Parameter Reference
13. Events & Progress Tracking
14. Models, Presets & Options
15. Cost Estimation
16. Error Handling
17. Advanced Patterns
18. Type Definitions

================================================================================

1. # OVERVIEW & INSTALLATION

The Sogni SDK provides access to the Sogni Supernet - a decentralized network
for AI inference. It supports:

- Image Generation: Stable Diffusion, Flux, Z-Image, Z-Image Turbo, Qwen Image Edit
- Video Generation: WAN 2.2 (5 workflows), LTX-2.3 models, Seedance 2.0 external API models
- Audio Generation: ACE-Step 1.5 music generation (Turbo and SFT models)
- LLM Text Generation: Chat completions with streaming, multi-turn, and thinking mode
- LLM Tool Calling: OpenAI-compatible function calling with custom and platform tools
- Sogni Platform Tools: Generate images, image edits, videos, audio-driven videos, video transforms, and music via natural language chat
- Vision Chat: Multimodal image understanding via VLM (scene description, OCR, object detection, visual analysis)
- Real-time Progress: WebSocket-based event system
- Multiple Networks: Fast (GPU) and Relaxed (Mac)

INSTALLATION:

```bash
npm install @sogni-ai/sogni-client
```

REQUIREMENTS:

- Node.js >=22 or modern browser
- Sogni account with token balance (free signup at app.sogni.ai)
- Each email-verified account is allowed 400 free Spark render credits per month. On the API,
  free credits can be used with Z-Image Turbo; paid credits can access all models and features.

PACKAGE SHAPE:

- CommonJS: `dist/index.js`
- ESM: `dist-esm/index.js`
- TypeScript declarations: `dist/index.d.ts`
- Public root class: `SogniClient`
- Public namespaces: `account`, `projects`, `chat`, `workflows`, `replay`, `stats`, `apiClient`
- Commonly used methods: `sogni.chat.completions.create()`, `sogni.projects.estimateCost()`, `sogni.projects.estimateVideoCost()`, `sogni.projects.estimateAudioCost()`, `sogni.workflows.start()`

================================================================================ 2. QUICK START EXAMPLES
================================================================================

MINIMAL IMAGE GENERATION:

```javascript
import { SogniClient } from '@sogni-ai/sogni-client';

// Option 1: API key auth (recommended) — no login() needed
const sogni = await SogniClient.createInstance({
  appId: 'my-unique-app-id',
  apiKey: 'your-api-key'
});

// Option 2: Username/password auth
// const sogni = await SogniClient.createInstance({ appId: 'my-unique-app-id' });
// await sogni.account.login('username', 'password');

await sogni.projects.waitForModels();

const project = await sogni.projects.create({
  type: 'image',
  modelId: 'flux1-schnell-fp8',
  positivePrompt: 'A cat wearing a hat',
  numberOfMedia: 1,
  steps: 4,
  guidance: 1
});

const urls = await project.waitForCompletion();
console.log('Image URL:', urls[0]); // Valid for 24 hours
```

MINIMAL VIDEO GENERATION:

```javascript
const project = await sogni.projects.create({
  type: 'video',
  network: 'fast',
  modelId: 'wan_v2.2-14b-fp8_t2v_lightx2v',
  positivePrompt: 'Ocean waves crashing on a beach at sunset',
  numberOfMedia: 1,
  duration: 5,
  fps: 16
});

const urls = await project.waitForCompletion();
console.log('Video URL:', urls[0]);
```

================================================================================ 3. CLIENT INITIALIZATION
================================================================================

CREATING THE CLIENT:

```javascript
import { SogniClient } from '@sogni-ai/sogni-client';

const sogni = await SogniClient.createInstance({
  appId: 'your-unique-app-id', // Required - UUID recommended
  network: 'fast' // 'fast' or 'relaxed'
});
```

AUTHENTICATION - API KEY (Recommended):
Get your API key: Log in to dashboard.sogni.ai and click your Username
dropdown in the top-right corner to provision your key.

```javascript
// API key auth — auto-authenticates via WebSocket, no login() needed
const sogni = await SogniClient.createInstance({
  appId: 'your-unique-app-id',
  network: 'fast',
  apiKey: 'your-api-key'
});
// Client is authenticated immediately after creation
```

AUTHENTICATION - USERNAME/PASSWORD:

```javascript
// Username/password login
const sogni = await SogniClient.createInstance({
  appId: 'your-unique-app-id',
  network: 'fast'
});
await sogni.account.login('username', 'password');
```

API KEY VS USERNAME/PASSWORD:

- API key: Pass `apiKey` to `createInstance()`. Auto-authenticates via WebSocket
  header. No `login()` call needed. Most REST API calls (balance, profile, etc.)
  available. Sensitive operations (withdrawals, staking, 2FA) not available.
- Username/password: Call `login()` after `createInstance()`. Full REST API access.

ACCOUNT INFO:

```javascript
// Get current user info
const user = await sogni.account.me();
console.log(user.username, user.email);

// Get balance
await sogni.account.refreshBalance();
console.log(sogni.account.currentAccount.balance);
```

SUBSCRIPTION ENTITLEMENTS:

```javascript
// Public plan catalog. Each entry is one plan/tier + billing term.
const plans = await sogni.account.getSubscriptionPlans();
// [{ planId, tier, term, interval, priceUsd, displayName }]

// Current wallet entitlement snapshot. Also updates currentAccount.subscription.
const subscription = await sogni.account.refreshSubscription();
if (sogni.account.currentAccount.isUnlimited) {
  console.log('Unlimited tier:', subscription.tier);
}
```

`SubscriptionEntitlementSnapshot.status` values are `none`, `trialing`,
`active`, `grace_period`, `past_due`, `paused`, `cancel_at_period_end`,
`canceled`, `expired`, and `refunded`. Period dates are ISO timestamp strings.
`currentAccount.isUnlimited` is true when the latest snapshot has `active:true`
and `tier` is `unlimited` or `unlimited_pro`; the server keeps `active:true`
through trials and cancel-at-period-end windows until access actually ends.
Grace-period snapshots are not entitled and return `active:false`.

Unlimited fair-use accounting and enforcement are dynamic and server-side in
the socket service. The SDK does not expose per-period usage counters or plan
limit tables for clients to store or display as fixed user-facing limits.

Stripe checkout uses catalog plan IDs (`unlimited` or `unlimited_pro`) plus a
term (`monthly` or `annual`). Checkout and portal sessions require user
authentication; API-key auth is rejected for those browser redirect operations.

```javascript
const { url } = await sogni.account.createSubscriptionCheckout('unlimited_pro', 'annual', {
  redirectType: 'web',
  appSource: 'my-integration'
});
const portal = await sogni.account.createSubscriptionPortalSession();
```

`checkAuth()` is only for cookie-auth browser flows on permitted Sogni domains. API-key auth auto-authenticates during `createInstance()`. Username/password auth uses `sogni.account.login()`. Token auth can be resumed with `sogni.setTokens()`.

NETWORK TYPES:

- 'fast': High-end GPU workers. Faster, higher cost. REQUIRED for video.
- 'relaxed': Mac device workers. Slower, lower cost. Image only.

CONNECTION EVENTS:

```javascript
sogni.apiClient.on('connected', ({ network }) => {
  console.log('Connected to:', network);
});

sogni.apiClient.on('disconnected', ({ code, reason }) => {
  console.log('Disconnected:', code, reason);
});
```

================================================================================ 4. IMAGE GENERATION
================================================================================

BASIC IMAGE GENERATION:

```javascript
const project = await sogni.projects.create({
  type: 'image',
  modelId: 'flux1-schnell-fp8',
  positivePrompt: 'A serene mountain landscape at dawn',
  negativePrompt: 'blurry, low quality, watermark',
  stylePrompt: 'photorealistic',
  numberOfMedia: 1,
  steps: 4,
  guidance: 1,
  outputFormat: 'jpg'
});

const urls = await project.waitForCompletion();
```

WITH SIZE PRESET:

```javascript
const project = await sogni.projects.create({
  type: 'image',
  modelId: 'flux1-schnell-fp8',
  positivePrompt: 'Portrait of a woman',
  numberOfMedia: 1,
  sizePreset: 'portrait_7_9',
  steps: 4,
  guidance: 1
});
```

WITH CUSTOM DIMENSIONS:

```javascript
const project = await sogni.projects.create({
  type: 'image',
  modelId: 'flux1-schnell-fp8',
  positivePrompt: 'Wide landscape',
  numberOfMedia: 1,
  sizePreset: 'custom',
  width: 1920,
  height: 1080,
  steps: 4,
  guidance: 1
});
```

IMAGE-TO-IMAGE (Starting Image):

```javascript
import fs from 'fs';

const project = await sogni.projects.create({
  type: 'image',
  modelId: 'coreml-cyberrealistic_v70_768',
  positivePrompt: 'Transform into oil painting style',
  startingImage: fs.readFileSync('./input.png'),
  startingImageStrength: 0.7, // 0-1, higher = more original preserved
  numberOfMedia: 1,
  steps: 20,
  guidance: 7.5
});
```

WITH CONTROLNET:

```javascript
const project = await sogni.projects.create({
  type: 'image',
  modelId: 'coreml-cyberrealistic_v70_768',
  positivePrompt: 'Professional portrait photo',
  numberOfMedia: 1,
  steps: 20,
  guidance: 7.5,
  controlNet: {
    name: 'openpose',
    image: fs.readFileSync('./pose-reference.png'),
    strength: 0.8,
    mode: 'balanced',
    guidanceStart: 0,
    guidanceEnd: 1
  }
});
```

CONTROLNET TYPES:

- canny: Edge detection
- depth: Depth map
- openpose: Body pose
- lineart: Line art extraction
- lineartanime: Anime-style lines
- scribble: Rough sketches
- softedge: Soft edge detection
- tile: Texture/detail preservation
- inpaint: Inpainting mask
- instrp2p: Instruction-based editing
- mlsd: Straight line detection
- normalbae: Normal map
- segmentation: Semantic segmentation
- shuffle: Style shuffle
- instantid: Face identity

WITH CONTEXT IMAGES (Qwen Image Edit):

```javascript
const project = await sogni.projects.create({
  type: 'image',
  modelId: 'qwen_image_edit_2511_fp8_lightning',
  positivePrompt: 'Combine these into one scene',
  contextImages: [
    fs.readFileSync('./image1.png'),
    fs.readFileSync('./image2.png'),
    fs.readFileSync('./image3.png')
  ],
  numberOfMedia: 1,
  steps: 4,
  guidance: 1
});
```

GPT Image 2 uses `modelId: 'gpt-image-2'`, supports up to 16 `contextImages`,
accepts `gptImageQuality: 'low' | 'medium' | 'high'`, supports
`outputFormat: 'png' | 'jpg' | 'webp'`, and is Spark-only.

RECOMMENDED MODELS:
| Model ID | Type | Steps | Guidance | Notes |
|----------|------|-------|----------|-------|
| flux1-schnell-fp8 | Fast | 4 | 1 | Best for quick iteration |
| z_image_turbo_bf16 | Ultra-fast | 8 | 1 | Fastest generation |
| z_image_bf16 | Quality | 20 | 3 | High quality Z-Image generation |
| qwen_image_edit_2511_fp8_lightning | Edit | 4 | 1 | Multi-image context |
| coreml-cyberrealistic_v70_768 | SD 1.5 | 20-40 | 7.5 | Photorealistic |

================================================================================ 5. VIDEO GENERATION - WAN 2.2 MODELS
================================================================================

## CRITICAL: WAN 2.2 FPS BEHAVIOR

WAN 2.2 models ALWAYS generate video at 16fps internally.
The 'fps' parameter (16 or 32) only controls POST-RENDER frame interpolation:

- fps: 16 -> No interpolation, output is 16fps
- fps: 32 -> Frames are doubled via interpolation, output is 32fps

Frame calculation: duration \* 16 + 1 (IGNORES fps setting)
Example: 5 seconds always = 81 frames generated, regardless of fps

MODEL VARIANTS:

- Speed models (with \_lightx2v suffix): 4-step, faster, good quality
- Quality models (without suffix): More steps, slower, best quality

WORKFLOW REFERENCE TABLE:
| Workflow | Model ID | Assets Required |
|----------|----------|-----------------|
| Text-to-Video | wan_v2.2-14b-fp8_t2v_lightx2v | None |
| Image-to-Video | wan_v2.2-14b-fp8_i2v_lightx2v | referenceImage |
| Sound-to-Video | wan_v2.2-14b-fp8_s2v_lightx2v | referenceImage + referenceAudio |
| Animate-Move | wan_v2.2-14b-fp8_animate-move_lightx2v | referenceImage + referenceVideo |
| Animate-Replace | wan_v2.2-14b-fp8_animate-replace_lightx2v | referenceImage + referenceVideo |

TEXT-TO-VIDEO:

```javascript
const project = await sogni.projects.create({
  type: 'video',
  network: 'fast',
  modelId: 'wan_v2.2-14b-fp8_t2v_lightx2v',
  positivePrompt: 'A hummingbird hovering near a flower, slow motion',
  negativePrompt: 'blurry, distorted',
  numberOfMedia: 1,
  duration: 5,
  fps: 16,
  shift: 5.0 // Motion intensity: 1.0-8.0
});

const urls = await project.waitForCompletion();
```

IMAGE-TO-VIDEO:

```javascript
const project = await sogni.projects.create({
  type: 'video',
  network: 'fast',
  modelId: 'wan_v2.2-14b-fp8_i2v_lightx2v',
  positivePrompt: 'The subject slowly turns their head and smiles',
  referenceImage: fs.readFileSync('./portrait.jpg'),
  numberOfMedia: 1,
  duration: 5,
  fps: 16
});
```

IMAGE-TO-VIDEO WITH END FRAME (Interpolation):

```javascript
const project = await sogni.projects.create({
  type: 'video',
  network: 'fast',
  modelId: 'wan_v2.2-14b-fp8_i2v_lightx2v',
  positivePrompt: 'Smooth transition between poses',
  referenceImage: fs.readFileSync('./start-pose.jpg'),
  referenceImageEnd: fs.readFileSync('./end-pose.jpg'),
  numberOfMedia: 1,
  duration: 5,
  fps: 16
});
```

SOUND-TO-VIDEO (Lip Sync):

```javascript
const project = await sogni.projects.create({
  type: 'video',
  network: 'fast',
  modelId: 'wan_v2.2-14b-fp8_s2v_lightx2v',
  positivePrompt: '', // Optional for s2v
  referenceImage: fs.readFileSync('./face.jpg'),
  referenceAudio: fs.readFileSync('./speech.m4a'),
  audioStart: 0, // Where to start in audio file (seconds)
  audioDuration: 5, // How much audio to use (seconds)
  numberOfMedia: 1,
  duration: 5,
  fps: 16
});
```

ANIMATE-MOVE (Motion Transfer):

```javascript
const project = await sogni.projects.create({
  type: 'video',
  network: 'fast',
  modelId: 'wan_v2.2-14b-fp8_animate-move_lightx2v',
  positivePrompt: '',
  referenceImage: fs.readFileSync('./character.jpg'), // Subject to animate
  referenceVideo: fs.readFileSync('./motion-source.mp4'), // Motion source
  videoStart: 0, // Where to start in video (seconds)
  numberOfMedia: 1,
  duration: 5,
  fps: 16
});
```

ANIMATE-REPLACE (Subject Replacement):

```javascript
const project = await sogni.projects.create({
  type: 'video',
  network: 'fast',
  modelId: 'wan_v2.2-14b-fp8_animate-replace_lightx2v',
  positivePrompt: '',
  referenceImage: fs.readFileSync('./new-character.jpg'), // New subject
  referenceVideo: fs.readFileSync('./original-video.mp4'), // Video with subject to replace
  videoStart: 0,
  numberOfMedia: 1,
  duration: 5,
  fps: 16
});
```

WAN 2.2 VIDEO PARAMETERS:
| Parameter | Type | Range | Default | Notes |
|-----------|------|-------|---------|-------|
| duration | number | 1-10 | - | Seconds of video |
| fps | number | 16, 32 | 16 | Only controls interpolation |
| shift | number | 1.0-8.0 | 5.0/8.0 | Motion intensity |
| steps | number | 4-50 | varies | More = quality, slower |
| teacacheThreshold | number | 0.0-1.0 | 0 | Speedup optimization |
| audioStart | number | 0+ | 0 | s2v: audio start position |
| audioDuration | number | 1-30 | 30 | s2v: audio length to use |
| videoStart | number | 0+ | 0 | animate: video start position |

================================================================================ 6. VIDEO GENERATION - LTX-2.3 MODELS
================================================================================

## CRITICAL: LTX FPS BEHAVIOR (Different from WAN!)

LTX-2.3 models generate at the ACTUAL specified FPS (1-60 range).
There is NO post-render interpolation.

Frame calculation: duration * fps + 1
Frame count must follow pattern: 1 + n*8 (valid: 1, 9, 17, 25, 33, 41, ...)
The SDK automatically snaps to valid frame counts.

Example: 5 seconds at 24fps = 121 frames (1 + 15\*8 = 121)

LTX-2.3 22B MODELS (Recommended):

- Speed models (with \_distilled suffix): 8-step, faster, good quality
- Quality models (with \_dev suffix): 20-step, slower, best quality

| Workflow             | Model ID (Fast)              | Model ID (Quality)     |
| -------------------- | ---------------------------- | ---------------------- |
| Text-to-Video        | ltx23-22b-fp8_t2v_distilled  | ltx23-22b-fp8_t2v_dev  |
| Image-to-Video       | ltx23-22b-fp8_i2v_distilled  | ltx23-22b-fp8_i2v_dev  |
| Audio-to-Video       | ltx23-22b-fp8_a2v_distilled  | ltx23-22b-fp8_a2v_dev  |
| Image+Audio-to-Video | ltx23-22b-fp8_ia2v_distilled | ltx23-22b-fp8_ia2v_dev |

VIDEO-TO-VIDEO CONTROLNET (LTX-2.3):
| Workflow | Model ID (Fast) | Model ID (Quality) |
|----------|-----------------|---------------------|
| Video-to-Video (ControlNet) | ltx23-22b-fp8_v2v_distilled | ltx23-22b-fp8_v2v_dev |

LTX-2.3 TEXT-TO-VIDEO:

```javascript
const project = await sogni.projects.create({
  type: 'video',
  network: 'fast',
  modelId: 'ltx23-22b-fp8_t2v_dev',
  positivePrompt: 'A butterfly landing on a flower',
  numberOfMedia: 1,
  duration: 8,
  fps: 24
});
```

LTX-2.3 IMAGE-TO-VIDEO WITH KEYFRAMES:

```javascript
const project = await sogni.projects.create({
  type: 'video',
  network: 'fast',
  modelId: 'ltx23-22b-fp8_i2v_dev',
  positivePrompt: 'Smooth camera movement',
  referenceImage: fs.readFileSync('./start.jpg'),
  referenceImageEnd: fs.readFileSync('./end.jpg'),
  firstFrameStrength: 0.6, // 0-1, how closely to match start
  lastFrameStrength: 0.6, // 0-1, how closely to match end
  numberOfMedia: 1,
  duration: 8,
  fps: 24
});
```

LTX-2.3 VIDEO-TO-VIDEO WITH CONTROLNET:

```javascript
const project = await sogni.projects.create({
  type: 'video',
  network: 'fast',
  modelId: 'ltx23-22b-fp8_v2v_distilled', // Must use v2v model for ControlNet
  positivePrompt: 'Transform into anime style',
  referenceVideo: fs.readFileSync('./source-video.mp4'),
  controlNet: {
    name: 'canny', // 'canny', 'pose', 'depth', or 'detailer'
    strength: 0.6 // Recommended: canny/depth 0.6, pose/detailer 0.85
  },
  numberOfMedia: 1,
  duration: 5,
  fps: 24
});
```

LTX VIDEO PARAMETERS:
| Parameter | Type | Range | Default | Notes |
|-----------|------|-------|---------|-------|
| duration | number | 4-20 | - | Seconds of video |
| fps | number | 1-60 | 24 | Actual generation FPS |
| firstFrameStrength | number | 0.0-1.0 | 0.6 | Keyframe matching |
| lastFrameStrength | number | 0.0-1.0 | 0.6 | Keyframe matching |

================================================================================ 7. VIDEO GENERATION - SEEDANCE 2.0 MODELS
================================================================================

Seedance 2.0 models use the external API path. They generate at fixed 24fps and
currently support 4-15 second direct SDK outputs. Seedance models are premium/external API models and are Spark-only.
Seedance 2.0 Fast accepts the same optional image, video, and audio references and caps output at 720p.
Seedance can combine reference media in one request: up to 9 image assets, 3 video
assets, 3 audio assets, and 12 total assets. Text+audio-only is unsupported; use at
least one image or video reference with audio references. Prompt-visible reference
tags use `@Image1`, `@Video1`, and `@Audio1`, counted independently by modality
in attachment order. Assign every useful reference a role and prefer positive
preservation constraints. Exact readable text/logos, lip-sync, voice cloning, and
real-human-reference behavior need review.

SEEDANCE MODEL IDS:
| Model ID | Context Assets | Notes |
|----------|----------------|-------|
| seedance-2-0 | Optional image, video, and audio refs | 24fps, 4-15s, 1080p |
| seedance-2-0-fast | Optional image, video, and audio refs | 24fps, 4-15s, 720p cap |

SEEDANCE TEXT-TO-VIDEO:

```javascript
const project = await sogni.projects.create({
  type: 'video',
  network: 'fast',
  modelId: 'seedance-2-0',
  positivePrompt: 'A cinematic neon skyline time lapse',
  numberOfMedia: 1,
  duration: 5,
  fps: 24,
  width: 1920,
  height: 1088,
  tokenType: 'spark'
});
```

SEEDANCE MULTIMODAL CONTEXT:

Use URL arrays for loose Seedance context references that should not lock first/last
frames. URLs must be HTTPS and reachable by the vendor. Use role-tagged prompts
such as `Use @Image1 for product identity, @Video1 for camera movement, and
@Audio1 for music rhythm. Keep the product silhouette and logo placement
consistent.`

```javascript
const project = await sogni.projects.create({
  type: 'video',
  network: 'fast',
  modelId: 'seedance-2-0',
  positivePrompt: 'Use @Image1 as the product identity, @Image2 for detail inserts, @Video1 for camera movement, and @Audio1 for music rhythm. Create one cohesive launch spot with smooth continuity and crisp product preservation.',
  duration: 8,
  fps: 24,
  width: 1920,
  height: 1088,
  referenceImageUrls: [
    'https://cdn.example.com/product-front.png',
    'https://cdn.example.com/product-detail.png'
  ],
  referenceVideoUrls: ['https://cdn.example.com/motion-reference.mp4'],
  referenceAudioUrls: ['https://cdn.example.com/music-reference.m4a']
});
```

SEEDANCE IMAGE-TO-VIDEO:

```javascript
const project = await sogni.projects.create({
  type: 'video',
  network: 'fast',
  modelId: 'seedance-2-0',
  positivePrompt: 'slow push-in, soft parallax, cinematic lighting',
  referenceImage: fs.readFileSync('./subject.jpg'),
  duration: 5,
  fps: 24
});
```

SEEDANCE VIDEO-TO-VIDEO:

```javascript
const project = await sogni.projects.create({
  type: 'video',
  network: 'fast',
  modelId: 'seedance-2-0',
  positivePrompt: 'Restyle this clip as a painted anime scene',
  referenceVideo: fs.readFileSync('./source.mp4'),
  duration: 5,
  fps: 24
});
```

Chat/tool aliases: `seedance2` and `seedance2-fast`. V2V uses `seedance2`
as the model selector and `seedance-v2v` as the control mode.

For focused endpoint coverage with server-side Seedance prompt expansion, see
`examples/workflow_partner_seedance_video.mjs`. It covers Seedance T2V, I2V,
IA2V, V2V, and multimodal context through the hosted Sogni tools and hosted workflow endpoint.

================================================================================ 8. AUDIO GENERATION - ACE-STEP 1.5 MODELS
================================================================================

ACE-Step 1.5 models generate music from text descriptions with optional lyrics.
Two model variants are available:

MODEL VARIANTS:
| Model ID | Name | Description |
|----------|------|-------------|
| ace_step_1.5_turbo | Fast & Catchy | Quick generation, best quality sound |
| ace_step_1.5_sft | More Control | More accurate lyrics, less stable |

TEXT-TO-MUSIC (Instrumental):

```javascript
const project = await sogni.projects.create({
  type: 'audio',
  modelId: 'ace_step_1.5_turbo',
  positivePrompt: 'Upbeat electronic dance music with synth leads and driving bass',
  numberOfMedia: 1,
  duration: 30, // seconds (10-600)
  bpm: 128, // beats per minute (30-300)
  keyscale: 'C major',
  timesignature: '4', // 4/4 time
  steps: 8,
  outputFormat: 'mp3'
});

const urls = await project.waitForCompletion();
console.log(urls[0]); // Audio URL (valid 24 hours)
```

TEXT-TO-MUSIC (With Lyrics):

```javascript
const project = await sogni.projects.create({
  type: 'audio',
  modelId: 'ace_step_1.5_sft',
  positivePrompt: 'Soft acoustic folk ballad with fingerpicked guitar',
  lyrics:
    'Verse 1:\nWalking down a quiet road\nWith the wind upon my face\n\nChorus:\nThis is where I find my peace\nIn this gentle, open space',
  language: 'en',
  numberOfMedia: 2, // Generate 2 versions
  duration: 60,
  bpm: 90,
  keyscale: 'A minor',
  timesignature: '3', // 3/4 waltz time
  steps: 12,
  composerMode: true,
  creativity: 0.85,
  promptStrength: 2.0
});
```

MULTIPLE VERSIONS:
Use `numberOfMedia` (1-4) to generate multiple variations of the same prompt.
Each version uses a different random seed, producing unique interpretations.

ACE-STEP 1.5 AUDIO PARAMETERS:
| Parameter | Type | Range | Default | Notes |
|-----------|------|-------|---------|-------|
| duration | number | 10-600 | 30 | Seconds of audio |
| bpm | number | 30-300 | 120 | Beats per minute |
| keyscale | string | see below | C major | Musical key and scale |
| timesignature | string | 2, 3, 4, 6 | 4 | Time signature (2/4, 3/4, 4/4, 6/8) |
| lyrics | string | - | - | Song lyrics (omit for instrumental) |
| language | string | en, ja, zh, etc. | en | Lyrics language code (51 languages) |
| composerMode | boolean | - | true | AI composer for higher quality |
| promptStrength | number | 0-10 | 2.0 | Prompt adherence (higher = stricter) |
| creativity | number | 0-2 | 0.85 | Composition temperature |
| shift | number | 1-5 | 3 | Denoising distribution (turbo only) |
| steps | number | 4-16 | 8 | Inference steps |
| outputFormat | string | mp3, wav, flac | mp3 | Audio output format |

SUPPORTED KEYS:
C, C#, Db, D, D#, Eb, E, F, F#, Gb, G, G#, Ab, A, A#, Bb, B
(each with major and minor variants, e.g., "C major", "A minor")

TIME SIGNATURES:

- 2 = 2/4 time (marches, polka)
- 3 = 3/4 time (waltzes, ballads)
- 4 = 4/4 time (most pop, rock, hip-hop)
- 6 = 6/8 time (compound time, folk dances)

SUPPORTED LANGUAGES:
en (English), ja (Japanese), zh (Chinese), es (Spanish), de (German),
fr (French), pt (Portuguese), ru (Russian), it (Italian), nl (Dutch),
pl (Polish), tr (Turkish), vi (Vietnamese), cs (Czech), fa (Persian),
id (Indonesian), ko (Korean), uk (Ukrainian), hu (Hungarian), ar (Arabic),
sv (Swedish), ro (Romanian), el (Greek), and more. Use 'unknown' for auto-detect.

================================================================================ 9. LLM TEXT GENERATION
================================================================================

The Sogni SDK supports LLM text generation through the Supernet with
socket-native chat completions, hosted REST chat completions, durable hosted
chat runs, streaming, multi-turn conversations, thinking/reasoning controls,
structured outputs, and tool calling.

DEFAULT LLM MODEL: qwen3.6-35b-a3b-gguf-iq4xs

PRIMARY CHAT SURFACES:

- `sogni.chat.completions.create(params)` - socket-native chat. Returns
  `ChatCompletionResult` for `stream: false` and `ChatStream` for `stream: true`.
- `sogni.chat.hosted.create(params)` - hosted REST chat completion at
  `/v1/chat/completions`; can execute hosted Sogni tools server-side.
- `sogni.chat.runs.{create,get,cancel,streamEvents}` - durable hosted runs at
  `/v1/chat/runs`; the server owns the LLM/tool loop and clients can reconnect
  through SSE event replay.

CHAT COMPLETION (Non-Streaming):

```javascript
const response = await sogni.chat.completions.create({
  model: 'qwen3.6-35b-a3b-gguf-iq4xs',
  messages: [
    { role: 'system', content: 'You are a helpful assistant.' },
    { role: 'user', content: 'Explain quantum computing briefly.' }
  ],
  max_tokens: 4096,
  temperature: 0.7,
  top_p: 0.9
});

console.log(response.content);
console.log(response.finishReason);
console.log(response.usage);
```

STREAMING CHAT COMPLETION:

```javascript
const stream = await sogni.chat.completions.create({
  model: 'qwen3.6-35b-a3b-gguf-iq4xs',
  messages: [{ role: 'user', content: 'Tell me a story about a cat' }],
  max_tokens: 4096,
  stream: true
});

for await (const chunk of stream) {
  if (chunk.content) process.stdout.write(chunk.content);
  if (chunk.tool_calls) {
    // Tool-call deltas can stream incrementally.
  }
}
```

MULTI-TURN CONVERSATION:

```javascript
const messages = [{ role: 'system', content: 'You are a helpful assistant.' }];

// Turn 1
messages.push({ role: 'user', content: 'What is the Sogni Supernet?' });
const response1 = await sogni.chat.completions.create({
  model: 'qwen3.6-35b-a3b-gguf-iq4xs',
  messages
});
messages.push({ role: 'assistant', content: response1.content });

// Turn 2 (with context from turn 1)
messages.push({ role: 'user', content: 'How does it compare to centralized services?' });
const response2 = await sogni.chat.completions.create({
  model: 'qwen3.6-35b-a3b-gguf-iq4xs',
  messages
});
console.log(response2.content);
```

THINKING MODE:
Control model reasoning/thinking via `think`:

- `think: true` sends `chat_template_kwargs: { enable_thinking: true }`.
- `think: false` sends `chat_template_kwargs: { enable_thinking: false }`.
- Omit `think` to use server defaults.

Streaming chunks and completion results expose generated text through `content`
and tool invocations through `tool_calls`. For structured outputs from
thinking-capable models, prefer tool calling or `response_format` instead of
parsing prose.

STRUCTURED OUTPUT:

```javascript
const result = await sogni.chat.completions.create({
  model: 'qwen3.6-35b-a3b-gguf-iq4xs',
  messages: [{ role: 'user', content: 'Return a two-item todo list as JSON.' }],
  response_format: {
    type: 'json_schema',
    json_schema: {
      name: 'todo_list',
      strict: true,
      schema: {
        type: 'object',
        properties: {
          items: {
            type: 'array',
            items: {
              type: 'object',
              properties: {
                title: { type: 'string' },
                priority: { type: 'string', enum: ['low', 'medium', 'high'] }
              },
              required: ['title', 'priority'],
              additionalProperties: false
            }
          }
        },
        required: ['items'],
        additionalProperties: false
      }
    }
  }
});
console.log(result.content);
```

HOSTED REST CHAT:

```javascript
const hosted = await sogni.chat.hosted.create({
  model: 'qwen3.6-35b-a3b-gguf-iq4xs',
  messages: [{ role: 'user', content: 'Write one concise product caption.' }],
  think: false,
  tokenType: 'spark'
});

console.log(hosted.choices[0].message.content);
```

DURABLE CHAT RUNS:

```javascript
const run = await sogni.chat.runs.create({
  model: 'qwen3.6-35b-a3b-gguf-iq4xs',
  messages: [{ role: 'user', content: 'Plan a 3-shot product video.' }],
  tokenType: 'spark',
  sessionId: 'session-123',
  idempotencyKey: 'message-123'
});

for await (const event of sogni.chat.runs.streamEvents(run.runId)) {
  console.log(event.type, event.payload);
}
```

Durable chat runs use uploaded HTTP(S) URLs for media references so run state,
event replay, and recovery can resolve the same media after the initial request.
Socket-native and hosted non-durable vision chat can accept inline image data
URIs after SDK validation.

LLM CHAT PARAMETERS:
| Parameter | Type | Default | Notes |
|-----------|------|---------|-------|
| model | string | qwen3.6-35b-a3b-gguf-iq4xs | LLM model ID |
| messages | array | - | Chat messages [{role, content}] |
| max_tokens | number | 4096 | Maximum output tokens |
| temperature | number | 0.7 | Sampling temperature (0-2) |
| top_p | number | 0.9 | Nucleus sampling (0-1) |
| top_k | number | model default | Top-k sampling |
| min_p | number | model default | Minimum probability sampling |
| repetition_penalty | number | model default | Repetition penalty |
| frequency_penalty | number | 0 | Repetition penalty (-2 to 2) |
| presence_penalty | number | 0 | Topic freshness penalty (-2 to 2) |
| stream | boolean | false | Enable token-by-token streaming |
| tools | array | - | Tool definitions for function calling |
| tool_choice | string/object | auto | Tool selection: 'auto', 'none', 'required', or specific function |
| sogni_tools | boolean/string | - | Inject hosted Sogni creative tools |
| sogni_tool_execution | boolean | false | Ask server to execute eligible hosted tools |
| autoExecuteTools | boolean | false | Client-side non-streaming Sogni tool loop |
| think | boolean | server default | Per-request thinking control |
| taskProfile | string | - | 'general', 'coding', or 'reasoning' preset hint |
| response_format | object | - | OpenAI-compatible text/json_object/json_schema constraint |

MESSAGE ROLES:

- 'system': System instructions (first message)
- 'user': User input
- 'assistant': Model responses (and tool call requests)
- 'tool': Tool execution results (with tool_call_id)

CHAT COMPLETION RESULT SHAPE:

```typescript
interface ChatCompletionResult {
  jobID: string;
  content: string;
  role: string;
  finishReason: string;
  usage: { prompt_tokens: number; completion_tokens: number; total_tokens?: number };
  timeTaken: number;
  workerName?: string;
  cost?: LLMJobCost;
  tool_calls?: ToolCall[];
  toolHistory?: ToolHistoryEntry[];
}
```

================================================================================ 10. LLM TOOL CALLING & SOGNI PLATFORM TOOLS
================================================================================

TOOL CALLING (Function Calling):
Define custom tools the LLM can invoke during conversations. The LLM decides
when to use a tool, returns structured arguments, and you execute the function
locally before feeding results back for a natural language answer.

DEFINING TOOLS (OpenAI-compatible format):

```javascript
const tools = [
  {
    type: 'function',
    function: {
      name: 'get_weather',
      description: 'Get current weather for a location',
      parameters: {
        type: 'object',
        properties: {
          location: { type: 'string', description: 'City name or location' }
        },
        required: ['location']
      }
    }
  },
  {
    type: 'function',
    function: {
      name: 'calculate',
      description: 'Evaluate a math expression',
      parameters: {
        type: 'object',
        properties: {
          expression: { type: 'string', description: 'Math expression to evaluate' }
        },
        required: ['expression']
      }
    }
  }
];
```

TOOL CALLING FLOW:

```javascript
// 1. Send message with tools
const messages = [{ role: 'user', content: "What's the weather in Austin, TX?" }];
const response = await sogni.chat.completions.create({
  model: 'qwen3.6-35b-a3b-gguf-iq4xs',
  messages,
  tools,
  tool_choice: 'auto'
});

// 2. Check if LLM wants to call a tool
if (response.tool_calls?.length) {
  messages.push({
    role: 'assistant',
    content: response.content || null,
    tool_calls: response.tool_calls
  });

  for (const toolCall of response.tool_calls) {
    const args = JSON.parse(toolCall.function.arguments);

    // 3. Execute the tool locally
    const result = await executeMyTool(toolCall.function.name, args);

    // 4. Feed result back
    messages.push({
      role: 'tool',
      tool_call_id: toolCall.id,
      name: toolCall.function.name,
      content: JSON.stringify(result)
    });
  }

  // 5. Get final natural language response
  const finalResponse = await sogni.chat.completions.create({
    model: 'qwen3.6-35b-a3b-gguf-iq4xs',
    messages,
    tools
  });
  console.log(finalResponse.content);
}
```

MULTI-ROUND TOOL CALLING:
The LLM may call multiple tools in sequence (e.g., get weather for two cities).
Implement a loop that continues until the LLM returns a content response
without tool calls, typically up to 5 rounds.

SOGNI PLATFORM TOOLS — MEDIA GENERATION VIA CHAT:
Combine LLM intelligence with Sogni's media generation capabilities. The LLM
detects when a user wants to create media, enhances the prompt, and calls
Sogni's generation APIs automatically:

- Image Generation: "Create an image of a cyberpunk city at night"
- Image Editing / Reference-Guided Images: "Edit this portrait to look like a renaissance painting"
- Video Generation: "Generate a video of ocean waves crashing at sunset"
- Sound-to-Video: "Turn this song into a music video"
- Video-to-Video: "Restyle this video as anime"
- Music Generation: "Compose a jazz song about the rain"

CANONICAL HOSTED CREATIVE-TOOL SURFACE (`SogniTools.all`, 24 tools):
The full hosted surface executes server-side via `sogni.chat.hosted.create()` / `sogni.chat.runs.create()`. The default `creative-tools` value of `sogni_tools` injects this surface server-side; `sogni_tools: "creative-agent"` adds workflow control and asset-manifest tools on top. The client-side `sogni.chat.tools.execute()` path directly dispatches only the core generation tools (`generate_image`, `edit_image`, `generate_video`, `sound_to_video`, `video_to_video`, `generate_music`); composition and post-production tools should be routed through hosted chat or durable runs.

- `generate_image`, `edit_image`, `generate_video`, `generate_music`, `sound_to_video`, `video_to_video` — core media-generation tools with creative-agent naming.
- `restore_photo`, `apply_style`, `refine_result`, `change_angle` — image-edit adapters backed by the shared image-edit workflow.
- `animate_photo` — image-to-video, with multi-source fan-out via `sourceImageIndices`. Fan-out (>1 source image) is composed into one MP4 by default; opt out with `stitched: false` for a flat clip list.
- `stitch_video` — concatenate selected video clips (with optional audio overlay via `audioIndex`) into one MP4.
- `orbit_video` — generate orbit clips around a subject and stitch them into one MP4.
- `dance_montage` — generate dance clips and stitch when multi-clip.
- `extend_video` — append new content to an existing video while preserving the original portion.
- `replace_video_segment` — replace a bounded window inside an existing video. Replacement sources can use `replacementStartSeconds` / `replacementEndSeconds` to splice a trimmed source slice.
- `overlay_video` — burn static text or image overlays onto an existing video.
- `add_subtitles` — burn subtitle cues onto an existing video.
- `enhance_prompt` — model-ready image/video/music/edit prompt expansion.
- `compose_script` — scripts, storyboards, trailers, social shorts, campaign beats, and video prompts.
- `compose_lyrics`, `compose_instrumental` — vocal lyrics or instrumental music structures.
- `compose_workflow`, `compose_workflow_template` — durable workflow and workflow-template planners.

PER-REQUEST MEDIA CONTEXT:
Generated images, videos, and audio are addressable by stable indices across tool rounds. Every hosted Sogni tool result returns `startIndex` and `indices` in its envelope so a later tool can target the result with `sourceImageIndex`, `videoIndices`, `audioIndex`, etc., without the model having to copy URLs back into prompt text.

COMPOSITION TOOLS:
`stitch_video`, `orbit_video`, `dance_montage`, and `animate_photo` fan-out return a single composed MP4 URL. Per-clip source URLs are preserved on the result envelope for inspection.

MEDIA-CONDITIONED TOOL INPUTS:

- `source_image_url`, `reference_image_urls`
- `reference_image_url`, `reference_image_end_url`
- `reference_audio_url`, `reference_audio_identity_url`
- `reference_video_url`
  These direct media arguments use inline base64-encoded `data:` URIs. Tool image inputs accept PNG/JPEG, tool audio inputs accept MP3/M4A/WAV, and tool video inputs accept MP4 or MOV/QuickTime. For uploaded or Sogni-hosted HTTP(S) assets, pass media through request-level `mediaReferences` / `mediaContext` or through `sogni.workflows` dependencies.

DEFAULT GENERATION MODELS (used by the canonical generation tools):
| Tool | Model ID | Description |
|------|----------|-------------|
| `generate_image` | z_image_turbo_bf16 | Z-Image Turbo, ultra-fast 8-step generation |
| `edit_image` | workflow/model-dependent | Reference-guided image editing with edit-capable Qwen/Flux models |
| `generate_video` | ltx23-22b-fp8_t2v_distilled / ltx23-22b-fp8_i2v_distilled / Seedance selectors | Text-to-video or image-to-video |
| `sound_to_video` | ltx23-22b-fp8_a2v_distilled / ltx23-22b-fp8_ia2v_distilled | Audio-driven video generation |
| `video_to_video` | ltx23-22b-fp8_v2v_distilled / seedance-2-0 / WAN animate models | Video transformation / motion transfer |
| `generate_music` | ace_step_1.5_turbo | ACE-Step 1.5 Turbo, fast music generation |

PLATFORM TOOL WORKFLOW:

1. User sends natural language request (e.g., "Make me an image of a dragon")
2. LLM detects media generation intent
3. LLM enhances the prompt for optimal output quality
4. LLM calls the appropriate Sogni generation tool with structured parameters
5. Your code executes the Sogni project creation (image/video/audio)
6. Result URL is returned and opened automatically

See `workflow_text_chat_sogni_tools.mjs` for a composition-pipeline example
covering the core image/video/music flows. See `workflow_creative_agent_tools.mjs`
for server-side hosted Sogni tool injection with `sogni_tools`.
Pass `SogniTools.all` (or individual tool defs) to the LLM via `tools` and
execute calls through `sogni.chat.hosted.create()` / `sogni.chat.runs.create()`.

DURABLE CREATIVE WORKFLOWS:
Use `sogni.workflows` for `/v1/creative-agent/workflows` coverage:
`start`, `list`, `get`, `events`, `streamEvents`, `resume`, `reseed`, `cancel`.
`start({ input })` runs an inline plan; `start({ workflowId, inputs })` runs a
saved template by id. Saved templates are managed through
`sogni.workflows.templates` — `list`, `get`, `create`, `update`, `delete`,
`fork`.
`resume(workflowId)` releases a workflow paused in `waiting_for_user`;
`reseed(workflowId, { seedOverrides })` clones a completed run with new seeds.
These endpoints use the SDK's active auth. See `workflow_creative_agent_workflows.mjs`.

EXAMPLE CLI USAGE:

```bash
# Generate an image through natural language
node workflow_text_chat_sogni_tools.mjs "Create an image of a cyberpunk city"

# Generate a video through natural language
node workflow_text_chat_sogni_tools.mjs "Generate a video of ocean waves at sunset"

# Generate music through natural language
node workflow_text_chat_sogni_tools.mjs "Compose a jazz song about the rain"

# Generate multiple items
node workflow_text_chat_sogni_tools.mjs -n 4 "Create images of fantasy landscapes"

# Server-side hosted creative tools
node workflow_creative_agent_tools.mjs "Create an orbit video plan for a crystal perfume bottle"

# Durable creative-agent workflow
node workflow_creative_agent_workflows.mjs "A chrome monorail over neon gardens" --watch

# Tool calling with external tools (weather, time, math, conversions)
node workflow_text_chat_tool_calling.mjs "What's the weather in Austin, TX?"
```

================================================================================ 11. VISION CHAT (MULTIMODAL IMAGE UNDERSTANDING)
================================================================================

The Sogni SDK supports multimodal vision chat via VLM (Vision-Language Model)
workers on the Sogni network. Send images alongside text messages for scene
description, OCR/text extraction, object detection, structured visual analysis,
and multi-image comparison.

VLM MODEL:
| Model ID | Name | Description |
|----------|------|-------------|
| qwen3.6-35b-a3b-gguf-iq4xs | Qwen3.6 35B VLM | Vision-language model with 262,144 native context length |

MULTIMODAL MESSAGE FORMAT:
Images are sent as base64-encoded JPEG or PNG data URIs using the OpenAI-compatible
`image_url` content type within a multipart user message:

```javascript
// Build a multimodal user message with image + text
const messages = [
  { role: 'system', content: 'You are a visual analysis assistant.' },
  {
    role: 'user',
    content: [
      { type: 'image_url', image_url: { url: 'data:image/jpeg;base64,...' } },
      { type: 'text', text: 'Describe this image in detail.' }
    ]
  }
];

const stream = await sogni.chat.completions.create({
  model: 'qwen3.6-35b-a3b-gguf-iq4xs',
  messages,
  max_tokens: 4096,
  stream: true,
  think: false,
  taskProfile: 'general'
});

for await (const chunk of stream) {
  if (chunk.content) process.stdout.write(chunk.content);
}
```

QWEN3.6 PRESET SELECTION:

- `think: true, taskProfile: 'general'` -> thinking mode for general tasks
- `think: true, taskProfile: 'coding'` -> thinking mode for precise coding
- `think: false, taskProfile: 'general'` -> instruct / non-thinking general
- `think: false, taskProfile: 'reasoning'` -> instruct / non-thinking reasoning

MANUAL OVERRIDES:
You can override the preset with explicit sampling params:
`temperature`, `top_p`, `top_k`, `min_p`, `presence_penalty`,
`repetition_penalty`.

MULTI-IMAGE COMPARISON:
Send two images in the same message for side-by-side analysis:

```javascript
const userMessage = {
  role: 'user',
  content: [
    { type: 'image_url', image_url: { url: imageDataUri1 } },
    { type: 'image_url', image_url: { url: imageDataUri2 } },
    { type: 'text', text: 'Compare these two images in detail.' }
  ]
};
```

LOADING LOCAL IMAGES:

```javascript
import * as fs from 'node:fs';
import * as path from 'node:path';

const buffer = fs.readFileSync('./photo.jpg');
const base64 = buffer.toString('base64');
const dataUri = `data:image/jpeg;base64,${base64}`;
```

VISION INPUT LIMITS: JPEG or PNG only, max 20 images per request, max 10MB per image, longest side capped at 1024px. This 1024px dimension cap applies only to vision `image_url` inputs, not to media-generation tool image inputs.

VISION CAPABILITIES:

- Scene description: Main subject, background, colors, lighting, mood, spatial
  relationships, composition, and visible text
- OCR / Text extraction: All visible text with approximate location and layout
  preservation, multi-language identification
- Object detection: Object identification with location (top-left, center, etc.),
  relative size, visual attributes, and spatial relationships
- Structured analysis: Subject, composition, lighting, color palette, technical
  aspects, mood, artistic style, and contextual elements
- Multi-image comparison: Side-by-side analysis of subject matter, color palette,
  composition, mood, quality, and notable differences

EXAMPLE CLI USAGE:

```bash
# Interactive vision chat
node workflow_text_chat_vision.mjs

# Pre-load an image
node workflow_text_chat_vision.mjs --image photo.jpg

# In-chat commands: /describe, /ocr, /objects, /analyze, /compare <path>
```

See `workflow_text_chat_vision.mjs` for a complete interactive vision chat
implementation with all commands and multi-turn conversation support.

================================================================================ 12. COMPLETE PARAMETER REFERENCE
================================================================================

BASE PARAMETERS (Both Image and Video):

```typescript
{
  modelId: string;           // Required - model identifier
  numberOfMedia: number;     // Required - how many to generate
  positivePrompt: string;    // Required - what to create
  negativePrompt?: string;   // What to avoid
  stylePrompt?: string;      // Style description
  steps?: number;            // Inference steps
  guidance?: number;         // Guidance scale
  network?: 'fast' | 'relaxed';
  seed?: number;             // Reproducibility seed
  tokenType?: 'sogni' | 'spark';
  disableNSFWFilter?: boolean;
  loras?: string[];          // LoRA IDs
  loraStrengths?: number[];  // LoRA weights (0-2)
}
```

IMAGE-SPECIFIC PARAMETERS:

```typescript
{
  type: 'image';
  sizePreset?: string;       // e.g., 'square_hd', 'portrait_7_9'
  width?: number;            // If sizePreset is 'custom'
  height?: number;           // If sizePreset is 'custom'
  sampler?: string;          // Model-specific
  scheduler?: string;        // Model-specific
  numberOfPreviews?: number; // Preview images during generation
  startingImage?: File | Buffer | Blob;
  startingImageStrength?: number;  // 0-1
  contextImages?: (File | Buffer | Blob)[];
  controlNet?: ControlNetParams;
  outputFormat?: 'png' | 'jpg' | 'webp';
}
```

VIDEO-SPECIFIC PARAMETERS:

```typescript
{
  type: 'video';
  duration?: number;         // Seconds
  fps?: number;              // Model-dependent behavior!
  shift?: number;            // Motion intensity (1.0-8.0)
  teacacheThreshold?: number;// Speedup (0.0-1.0)
  width?: number;
  height?: number;
  sampler?: string;
  scheduler?: string;
  referenceImage?: File | Buffer | Blob;
  referenceImageEnd?: File | Buffer | Blob;
  referenceAudio?: File | Buffer | Blob;
  referenceVideo?: File | Buffer | Blob;
  audioStart?: number;       // Seconds into audio
  audioDuration?: number;    // Seconds of audio
  videoStart?: number;       // Seconds into video
  firstFrameStrength?: number;  // LTX-2.3 only (0-1)
  lastFrameStrength?: number;   // LTX-2.3 only (0-1)
  trimEndFrame?: boolean;
  controlNet?: VideoControlNetParams;  // LTX-2.3 only
  outputFormat?: 'mp4';
}
```

AUDIO-SPECIFIC PARAMETERS:

```typescript
{
  type: 'audio';
  duration?: number;           // 10-600 seconds
  bpm?: number;                // 30-300 beats per minute
  keyscale?: string;           // e.g., 'C major', 'A minor'
  timesignature?: string;      // '2', '3', '4', or '6'
  lyrics?: string;             // Song lyrics (omit for instrumental)
  language?: string;           // Language code (default: 'en')
  composerMode?: boolean;      // AI composer mode (default: true)
  promptStrength?: number;     // 0-10, prompt adherence
  creativity?: number;         // 0-2, composition temperature
  shift?: number;              // 1-5, denoising distribution
  sampler?: string;            // Model-specific
  scheduler?: string;          // Model-specific
  outputFormat?: 'mp3' | 'wav' | 'flac';
}
```

================================================================================ 13. EVENTS & PROGRESS TRACKING
================================================================================

PROMISE-BASED COMPLETION:

```javascript
const project = await sogni.projects.create({ ... });
const urls = await project.waitForCompletion();
// urls is string[] of result URLs
```

PROJECT EVENTS:

```javascript
project.on('progress', (percent) => {
  console.log(`Overall: ${percent}%`);
});

project.on('jobStarted', (job) => {
  console.log(`Job ${job.id} started on ${job.workerName}`);
});

project.on('jobCompleted', (job) => {
  console.log(`Job ${job.id} done:`, job.resultUrl);
});

project.on('jobFailed', (job) => {
  console.log(`Job ${job.id} failed:`, job.error);
});

project.on('completed', (urls) => {
  console.log('All done:', urls);
});

project.on('failed', (error) => {
  console.error('Project failed:', error.message);
});
```

JOB EVENTS:

```javascript
const job = project.jobs[0];

job.on('progress', ({ step, stepCount, progress }) => {
  console.log(`Step ${step}/${stepCount} (${progress}%)`);
});

job.on('preview', (previewUrl) => {
  console.log('Preview available:', previewUrl);
});

job.on('completed', () => {
  console.log('Result:', job.resultUrl);
});

job.on('failed', (error) => {
  console.error('Job failed:', error);
});
```

External API-backed jobs may not report diffusion steps. The SDK keeps project/job
progress finite by using provider progress or ETA-derived progress, and direct
provider result URLs are preserved on `job.resultUrl` / `job.getResultUrl()`.

PROJECT PROPERTIES:

```javascript
project.id; // string
project.status; // 'pending' | 'queued' | 'processing' | 'completed' | 'failed' | 'canceled'
project.progress; // number (0-100)
project.queuePosition; // number | null
project.eta; // number | null (seconds)
project.jobs; // Job[]
project.resultUrls; // string[] (after completion)
```

JOB PROPERTIES:

```javascript
job.id; // string
job.status; // 'pending' | 'initiating' | 'processing' | 'completed' | 'failed' | 'canceled'
job.progress; // number (0-100)
job.step; // number (current step)
job.stepCount; // number (total steps)
job.seed; // number
job.previewUrl; // string | null
job.resultUrl; // string | null
job.hasResultMedia; // boolean
job.isNSFW; // boolean
job.workerName; // string
job.eta; // string | null
job.etaSeconds; // number | null
job.type; // 'image' | 'video'
```

FETCHING RESULTS:

```javascript
// Get signed URL (valid ~1 hour)
const signedUrl = await job.getResultUrl();

// Download as blob
const blob = await job.getResultData();
```

================================================================================ 14. MODELS, PRESETS & OPTIONS
================================================================================

WAIT FOR MODELS:

```javascript
const models = await sogni.projects.waitForModels();
// Returns AvailableModel[]
```

GET AVAILABLE MODELS:

```javascript
const models = sogni.projects.availableModels;
// or
const models = await sogni.projects.getAvailableModels('fast');
// Returns: { id, name, workerCount, media: 'image' | 'video' }[]
```

GET ALL SUPPORTED MODELS:

```javascript
const allModels = await sogni.projects.getSupportedModels();
// Returns full model list with tiers
```

GET SIZE PRESETS:

```javascript
const presets = await sogni.projects.getSizePresets('fast', 'flux1-schnell-fp8');
/*
Returns: [
  { id: 'square', label: 'Square', width: 512, height: 512, ratio: '1:1' },
  { id: 'square_hd', label: 'Square HD', width: 1024, height: 1024, ratio: '1:1' },
  { id: 'portrait_7_9', label: 'Portrait: Standard', width: 896, height: 1152, ratio: '7:9' },
  { id: 'landscape_7_4', label: 'Landscape: Widescreen', width: 1344, height: 768, ratio: '7:4' },
  ...
]
*/
```

GET SAMPLER/SCHEDULER OPTIONS:

```javascript
const options = await sogni.projects.getModelOptions('flux1-schnell-fp8');
/*
Returns: {
  sampler: {
    allowed: ['euler', 'euler_a', 'dpm_pp_2m', ...],
    default: 'euler'
  },
  scheduler: {
    allowed: ['simple', 'karras', 'linear', ...],
    default: 'simple'
  }
}
*/
```

================================================================================ 15. COST ESTIMATION
================================================================================

ESTIMATE IMAGE COST:

```javascript
const cost = await sogni.projects.estimateCost({
  network: 'fast',
  tokenType: 'sogni',
  model: 'flux1-schnell-fp8',
  imageCount: 4,
  stepCount: 4,
  previewCount: 0,
  sizePreset: 'square_hd'
});

console.log(cost.sogni); // Cost in Sogni tokens
console.log(cost.spark); // Cost in Spark tokens
console.log(cost.usd); // Cost in USD
```

ESTIMATE VIDEO COST:

```javascript
const cost = await sogni.projects.estimateVideoCost({
  tokenType: 'sogni',
  model: 'wan_v2.2-14b-fp8_t2v_lightx2v',
  width: 512,
  height: 512,
  duration: 5,
  fps: 16,
  steps: 4,
  numberOfMedia: 1
});
```

CHECK BALANCE:

```javascript
await sogni.account.refreshBalance();
const balance = sogni.account.currentAccount.balance;
console.log(balance.sogni, balance.spark);
```

================================================================================ 16. ERROR HANDLING
================================================================================

TRY-CATCH PATTERN:

```javascript
try {
  const project = await sogni.projects.create({ ... });
  const urls = await project.waitForCompletion();
} catch (error) {
  if (error.code) {
    // API error with code
    console.error(`Error ${error.code}: ${error.message}`);
  } else {
    // Network or other error
    console.error(error.message);
  }
}
```

EVENT-BASED ERROR HANDLING:

```javascript
project.on('failed', (error) => {
  console.error('Project failed:', error.code, error.message);
});

project.on('jobFailed', (job) => {
  console.error('Job failed:', job.error);
});
```

CANCEL A PROJECT:

```javascript
await sogni.projects.cancel(project.id);
// or
await project.cancel();
```

COMMON ERROR SCENARIOS:

- Insufficient balance
- Invalid model ID
- Missing required parameters (e.g., referenceImage for i2v)
- Network unavailable
- NSFW content detected (if filter enabled)
- Invalid dimensions or parameters

================================================================================ 17. ADVANCED PATTERNS
================================================================================

BATCH PROCESSING:

```javascript
const images = ['image1.jpg', 'image2.jpg', 'image3.jpg'];
const projects = [];

for (const img of images) {
  const project = await sogni.projects.create({
    type: 'video',
    network: 'fast',
    modelId: 'wan_v2.2-14b-fp8_i2v_lightx2v',
    referenceImage: fs.readFileSync(img),
    positivePrompt: 'camera slowly zooms in',
    numberOfMedia: 1,
    duration: 5,
    fps: 16
  });
  projects.push(project);
}

const results = await Promise.all(projects.map((p) => p.waitForCompletion()));
```

IMAGE ENHANCEMENT:

```javascript
// After a job completes, enhance it
const enhancedProject = await job.enhance('medium', {
  // Optional overrides
  steps: 30
});
const enhancedUrls = await enhancedProject.waitForCompletion();
```

TOKEN MANAGEMENT:

```javascript
// Use Spark tokens instead of Sogni
const project = await sogni.projects.create({
  type: 'image',
  tokenType: 'spark'
  // ... other params
});
```

USING LORAS:

```javascript
const project = await sogni.projects.create({
  type: 'image',
  modelId: 'flux1-schnell-fp8',
  positivePrompt: 'Portrait from multiple angles',
  loras: ['multiple_angles'],
  loraStrengths: [0.9],
  numberOfMedia: 1
});
```

================================================================================ 18. TYPE DEFINITIONS
================================================================================

PROJECT PARAMS:

```typescript
type ProjectParams = ImageProjectParams | VideoProjectParams;

interface BaseProjectParams {
  modelId: string;
  numberOfMedia: number;
  positivePrompt: string;
  negativePrompt?: string;
  stylePrompt?: string;
  steps?: number;
  guidance?: number;
  network?: 'fast' | 'relaxed';
  disableNSFWFilter?: boolean;
  seed?: number;
  tokenType?: 'sogni' | 'spark';
  loras?: string[];
  loraStrengths?: number[];
}

interface ImageProjectParams extends BaseProjectParams {
  type: 'image';
  numberOfPreviews?: number;
  startingImage?: File | Buffer | Blob;
  startingImageStrength?: number;
  contextImages?: (File | Buffer | Blob)[];
  sampler?: string;
  scheduler?: string;
  sizePreset?: string | 'custom';
  width?: number;
  height?: number;
  controlNet?: ControlNetParams;
  outputFormat?: 'png' | 'jpg' | 'webp';
}

interface VideoProjectParams extends BaseProjectParams {
  type: 'video';
  duration?: number;
  fps?: number;
  shift?: number;
  teacacheThreshold?: number;
  referenceImage?: File | Buffer | Blob;
  referenceImageEnd?: File | Buffer | Blob;
  referenceAudio?: File | Buffer | Blob;
  referenceVideo?: File | Buffer | Blob;
  audioStart?: number;
  audioDuration?: number;
  videoStart?: number;
  trimEndFrame?: boolean;
  width?: number;
  height?: number;
  sampler?: string;
  scheduler?: string;
  firstFrameStrength?: number;
  lastFrameStrength?: number;
  outputFormat?: 'mp4';
  controlNet?: VideoControlNetParams;
}
```

CONTROLNET TYPES:

```typescript
type ControlNetName =
  | 'canny'
  | 'depth'
  | 'inpaint'
  | 'instrp2p'
  | 'lineart'
  | 'lineartanime'
  | 'mlsd'
  | 'normalbae'
  | 'openpose'
  | 'scribble'
  | 'segmentation'
  | 'shuffle'
  | 'softedge'
  | 'tile'
  | 'instantid';

type ControlNetMode = 'balanced' | 'prompt_priority' | 'cn_priority';

interface ControlNetParams {
  name: ControlNetName;
  image?: File | Buffer | Blob;
  strength?: number;
  mode?: ControlNetMode;
  guidanceStart?: number;
  guidanceEnd?: number;
}

type VideoControlNetName = 'canny' | 'pose' | 'depth' | 'detailer';

interface VideoControlNetParams {
  name: VideoControlNetName;
  strength?: number;
}
```

MODEL TYPES:

```typescript
interface AvailableModel {
  id: string;
  name: string;
  workerCount: number;
  media: 'image' | 'video';
}

interface SupportedModel extends AvailableModel {
  SID: number;
  tier: string;
}

interface SizePreset {
  id: string;
  label: string;
  width: number;
  height: number;
  ratio: string;
  aspect: string;
}
```

STATUS TYPES:

```typescript
type ProjectStatus = 'pending' | 'queued' | 'processing' | 'completed' | 'failed' | 'canceled';

type JobStatus = 'pending' | 'initiating' | 'processing' | 'completed' | 'failed' | 'canceled';
```

ERROR TYPE:

```typescript
interface ErrorData {
  code: number;
  message: string;
}
```

================================================================================
END OF DOCUMENT
================================================================================

For additional resources:

- Examples: https://github.com/Sogni-AI/sogni-client/tree/main/examples
- API Docs: https://sdk-docs.sogni.ai
- Support: https://www.sogni.ai/support
