@picsart/ai-sdk 6.55.0 → 7.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -22,7 +22,7 @@ npm install @picsart/ai-sdk
22
22
  ```
23
23
 
24
24
  ```typescript
25
- import { createClient, Models, Model, catalog } from '@picsart/ai-sdk'
25
+ import { createClient, Model, catalog } from '@picsart/ai-sdk'
26
26
 
27
27
  // Create a client — pass your Picsart API key
28
28
  const ai = createClient({
@@ -30,25 +30,38 @@ const ai = createClient({
30
30
  apiUrl: 'https://api.picsart.com',
31
31
  })
32
32
 
33
- // Generate (Models.* are typed model-id constants)
34
- const result = await ai.generate(Models.Flux2Pro, { prompt: 'a cat on mars' })
33
+ // Generate — model ids are plain strings, resolved server-side.
34
+ // Generation works immediately; no catalog load required.
35
+ const result = await ai.generate('flux-2-pro', { prompt: 'a cat on mars' })
35
36
  console.log(result.url)
36
37
 
38
+ // The local catalog accessors (Model / catalog / ai.validate) read a catalog
39
+ // loaded from models-hub. Load it once, then use them:
40
+ await ai.initModelCatalog()
41
+
37
42
  // Browse models — the `catalog` accessor
38
43
  catalog.all() // every model
39
44
  catalog.find({ output: 'video' }) // video models only
40
45
  catalog.search('kling') // search by name/id/provider
41
46
 
42
47
  // Model metadata & params — the `Model` accessor
43
- Model(Models.Flux2Pro).name // 'Flux 2 Pro'
44
- Model(Models.Flux2Pro).meta() // mode, provider, badges, …
45
- Model(Models.Flux2Pro).params() // accepted parameters
46
- Model(Models.Flux2Pro).params().toSchema() // param schema
48
+ Model('flux-2-pro').name // 'Flux 2 Pro'
49
+ Model('flux-2-pro').meta() // mode, provider, badges, …
50
+ Model('flux-2-pro').params() // accepted parameters
51
+ Model('flux-2-pro').params().toSchema() // param schema
47
52
 
48
53
  // Validate input (never throws) → { valid, errors? }
49
- Model(Models.Flux2Pro).validate({ prompt: 'a cat' })
54
+ Model('flux-2-pro').validate({ prompt: 'a cat' })
50
55
  ```
51
56
 
57
+ > **Dynamic-only (v7).** The SDK no longer bundles a model catalog or builds
58
+ > vendor payloads locally. Every generation rides the models-hub `v1/models`
59
+ > workflow (the server resolves the model, builds the payload, and normalizes
60
+ > the result). Model ids are plain strings. The catalog accessors
61
+ > (`Model()`, `catalog.*`, `ai.validate()`) require `await ai.initModelCatalog()`
62
+ > first — until then they throw `ApiError` 409 `catalog_not_loaded`.
63
+ > `generate()`/`generateText()`/`run()`/`submit()` never wait on it.
64
+
52
65
  ## Authentication
53
66
 
54
67
  Pass `apiKey` and the SDK sends `Authorization: Bearer <apiKey>` on every request
@@ -141,12 +154,12 @@ const ai = createClient({
141
154
  })
142
155
 
143
156
  // Generates and auto-saves to the root folder
144
- const result = await ai.generate(Models.Flux2Pro, { prompt: 'a cat' })
157
+ const result = await ai.generate('flux-2-pro', { prompt: 'a cat' })
145
158
  // result.drive = { uid: '...', folder: { name: 'AI Playground', uid: '...' } }
146
159
 
147
160
  // Save to a subfolder
148
161
  const board = await ai.drive.ensureFolder('Cats')
149
- const result = await ai.generate(Models.Flux2Pro, { prompt: 'a cat' }, { folder: board })
162
+ const result = await ai.generate('flux-2-pro', { prompt: 'a cat' }, { folder: board })
150
163
 
151
164
  // Browse Drive
152
165
  const folders = await ai.drive.folders()
@@ -200,16 +213,16 @@ as `mode: 'text'`. Each vendor uses its own native workflow, so capabilities are
200
213
  — Gemini accepts video input, Claude uses the Anthropic-native messages API.
201
214
 
202
215
  ```typescript
203
- import { createClient, Models } from '@picsart/ai-sdk'
216
+ import { createClient } from '@picsart/ai-sdk'
204
217
 
205
218
  const ai = createClient({ apiKey: process.env.PICSART_API_KEY, apiUrl: 'https://api.picsart.com' })
206
219
 
207
220
  // Text in, text out
208
- const { text } = await ai.generateText(Models.ClaudeOpus48, { prompt: 'Explain RAG in one line.' })
221
+ const { text } = await ai.generateText('claude-opus-5', { prompt: 'Explain RAG in one line.' })
209
222
  console.log(text)
210
223
 
211
224
  // Optional vision input + reasoning level (OpenAI / Gemini)
212
- const res = await ai.generateText(Models.Gpt55, {
225
+ const res = await ai.generateText('gpt-5-5', {
213
226
  prompt: 'What is in this image?',
214
227
  imageUrls: ['https://cdn.example.com/photo.jpg'],
215
228
  thinking: 'high', // 'off' | 'low' | 'medium' | 'high'
@@ -218,7 +231,7 @@ console.log(res.text)
218
231
  console.log(res.raw) // full backend response — usage, finish_reason, etc.
219
232
 
220
233
  // Gemini accepts video input
221
- await ai.generateText(Models.Gemini3Pro, {
234
+ await ai.generateText('gemini-3-pro', {
222
235
  prompt: 'Summarize this clip',
223
236
  videoUrl: 'https://cdn.example.com/clip.mp4',
224
237
  })
@@ -233,9 +246,10 @@ catalog.find({ output: 'text' })
233
246
  > thinking knob. Claude’s `claude/v1/messages`
234
247
  > workflow exposes no thinking knob, so Claude models omit `thinking`.
235
248
 
236
- `generateText()` is type-narrowed to text models (`TextModelId`); calling it with an
237
- image/video model throws, and `generate()` throws on a text model — use the matching
238
- method for each.
249
+ Use `generateText()` for text models and `generate()` for media models. In
250
+ dynamic mode ids are plain strings with no compile-time narrowing — the server
251
+ validates the model/mode and answers a 400 `wrong_model_mode` if you call the
252
+ wrong method for a model.
239
253
 
240
254
  ## Generic Execution — ai.run()
241
255
 
@@ -250,14 +264,14 @@ task result — and for `mode: 'json'` models, `run()` is the only surface:
250
264
  structured-output models (judges, evaluators, classifiers) are rejected by
251
265
  `generate()`, `generateText()`, and the async lifecycle.
252
266
 
253
- The first json model is `typesafe-evaluate` (`Models.TypesafeEvaluate`) — LLM-judge
254
- evaluation of any content against named questions. Type the result via the `R`
255
- generic, same as `ai.apis.run()`:
267
+ The first json model is `typesafe-evaluate` — LLM-judge evaluation of any
268
+ content against named questions. Type the result via the `R` generic, same as
269
+ `ai.apis.run()`:
256
270
 
257
271
  ```typescript
258
272
  interface EvaluateResult { model: string; answers: Record<string, unknown>; usage: unknown }
259
273
 
260
- const { result } = await ai.run<EvaluateResult>(Models.TypesafeEvaluate, {
274
+ const { result } = await ai.run<EvaluateResult>('typesafe-evaluate', {
261
275
  state: 'The weather is lovely and everyone is having a great time.',
262
276
  questions: {
263
277
  is_safe: { type: 'noul', instructions: 'Is this content safe for all audiences?' },
@@ -304,21 +318,21 @@ For progress tracking and job recovery:
304
318
 
305
319
  ```typescript
306
320
  // Submit without waiting — returns the generation id (a string)
307
- const generationId = await ai.submit(Models.KlingV3, { prompt: 'a sunset' })
321
+ const generationId = await ai.submit('kling-v3', { prompt: 'a sunset' })
308
322
 
309
323
  // Subscribe to updates — the terminal generation.completed event carries the
310
324
  // parsed result, so no follow-up result() call is needed
311
- for await (const e of ai.subscribe(Models.KlingV3, generationId)) {
325
+ for await (const e of ai.subscribe('kling-v3', generationId)) {
312
326
  if (e.type === 'generation.progress') console.log(e.progress?.percent)
313
327
  if (e.type === 'generation.completed') console.log(e.result.url)
314
328
  if (e.type === 'generation.failed') console.error(e.error.message)
315
329
  }
316
330
 
317
331
  // Or just wait for the parsed result (throws on failure/cancel)
318
- const result = await ai.result(Models.KlingV3, generationId, { intervalMs: 2000 })
332
+ const result = await ai.result('kling-v3', generationId, { intervalMs: 2000 })
319
333
 
320
334
  // One-shot snapshot of a stored id (e.g. after a page reload)
321
- const { value: snapshot } = await ai.subscribe(Models.KlingV3, generationId).next()
335
+ const { value: snapshot } = await ai.subscribe('kling-v3', generationId).next()
322
336
  ```
323
337
 
324
338
  Generation ids are plain strings — store them and recover jobs later with
@@ -331,10 +345,10 @@ is an `ApiError` with the same four fields, so you can branch on the error
331
345
  instead of pattern-matching its message:
332
346
 
333
347
  ```typescript
334
- import { createClient, Models, ApiError } from '@picsart/ai-sdk'
348
+ import { createClient, ApiError } from '@picsart/ai-sdk'
335
349
 
336
350
  try {
337
- const result = await ai.generate(Models.Flux2Pro, { prompt: 'a cat on mars' })
351
+ const result = await ai.generate('flux-2-pro', { prompt: 'a cat on mars' })
338
352
  } catch (err) {
339
353
  if (err instanceof ApiError) {
340
354
  err.status // 402 — HTTP status, or its synthesized equivalent
@@ -387,8 +401,7 @@ Key exports (see `src/index.ts` for the full list):
387
401
  | Export | Type | Description |
388
402
  |--------|------|-------------|
389
403
  | `createClient` | function | Create an AI client from an API key (or a custom authenticated fetch) |
390
- | `Models` | object | Typed model-id constants (`Models.Flux2Pro` → `'flux-2-pro'`) |
391
- | `Model`, `catalog` | function / object | Model metadata, params, validation, discovery |
404
+ | `Model`, `catalog` | function / object | Model metadata, params, validation, discovery (after `initModelCatalog()`) |
392
405
  | `GenerateResult` | type | `{ url, items: [{ url, metadata? }], generationId, usage?, drive? }` |
393
406
  | `GenerationEvent` | type | One `ai.subscribe()` update — `generation.progress` / `.completed` / `.failed` |
394
407
  | `GenerationEventType` | const | Named event-type constants (`GenerationEventType.Completed` === `'generation.completed'`) |
@@ -410,66 +423,37 @@ packages/ai-sdk/
410
423
  client/
411
424
  types.ts # ClientConfig, GenerateResult, DriveConfig
412
425
  transport.ts # Default SdkTransport, over @picsart/workflows-client
413
- prepare.ts # Validate input, build payload, parse result
426
+ prepare.ts # Build the v1/models command + parse its result
414
427
  drive.ts # Drive folder management + file saving
415
- index.ts # createClient() factory
428
+ index.ts # createClient() factory (dynamic-only)
416
429
  core/
417
430
  types.ts # ModelDefinition, ParamConfig, GenerationContext
418
431
  workflow.ts # Task envelope types + the SdkTransport contract
419
- contracts.ts # Runtime input validation
432
+ active-catalog.ts # The process-global catalog installed by initModelCatalog()
433
+ catalog-store.ts # Loads the catalog from models-hub (/v1/models-catalog)
434
+ catalog-wire.ts # Wire ⇄ ModelDefinition (de)serialization
420
435
  schema.ts # ParamConfig → JSON Schema
421
436
  response.ts # Vendor-agnostic result extraction
422
- pricing.ts # ToolId resolution
423
- model-registry.ts # Model lookup indexes
437
+ model-registry.ts # Model lookup indexes over the active catalog
424
438
  providers.ts # Provider colors, labels, names
425
439
  voices.ts # Voice catalogs (ElevenLabs, OpenAI, Gemini)
426
- helpers.ts # Vendor utilities
427
- generated/
428
- model-constants.ts # AUTO-GENERATED: Models object + id constants
429
- model-input-types.ts # AUTO-GENERATED: per-model TypeScript input types
430
- vendors/
431
- define.ts # defineModels() framework + params.* helpers
432
- presets.ts # Reusable paramConfig factories
433
- catalog/
434
- index.ts # Aggregation: ALL_MODELS, VENDOR_CATALOGS
435
- kling.ts # One file per vendor (31 total)
436
- flux.ts
437
- ...
440
+ descriptors/ # Model()/catalog accessors, param descriptors, pricing
438
441
  __tests__/ # SDK tests
439
442
  scripts/ # Build scripts
440
443
  ```
441
444
 
442
- ## Adding a New Model
443
-
444
- ### Standard flow (pass-through payload)
445
-
446
- When the backend accepts param values as-is (no field renaming needed):
447
-
448
- 1. Add config in `src/vendors/catalog/{vendor}.ts` via `defineModels()`:
449
- - `buildPayload` is **optional** — omit it and param values pass through as-is
450
- 2. Run `npm run build:model-constants` to regenerate constants
451
- 3. Run `npm run build:model-input-types` to regenerate TypeScript types
452
- 4. Model automatically appears in `catalog.all()` and as the `Models.NewModel` id constant
453
-
454
- ### With payload transforms
455
-
456
- When the vendor API uses different field names or value formats:
457
-
458
- 1. Define the model config as above (no `buildPayload`)
459
- 2. Run `npm run build:model-input-types` — generates typed input for your model
460
- 3. Create `src/vendors/catalog/{vendor}.payloads.ts`:
461
- ```ts
462
- import type { ModelInput } from '../../generated/model-input-types.ts';
463
- import { registerPayloads } from '../define.ts';
464
- import { SPECS, MODELS } from './{vendor}.ts';
465
-
466
- registerPayloads({ SPECS, MODELS }, {
467
- 'model-id': (input: ModelInput<'model-id'>) => ({
468
- prompt: input.prompt,
469
- aspect_ratio: input.aspectRatio, // rename for vendor API
470
- }),
471
- });
472
- ```
473
- 4. Import the `.payloads.ts` file in `src/vendors/catalog/index.ts` (after the vendor import)
474
-
475
- See `src/vendors/catalog/luma.ts` + `src/vendors/catalog/luma.payloads.ts` for a working example.
445
+ ## The Model Catalog
446
+
447
+ The SDK is **dynamic-only**: it ships with no bundled catalog and builds no
448
+ vendor payloads locally. Model definitions (parameter schemas, payload shapes,
449
+ and which workflow serves each model) live in **models-hub**, and every
450
+ generation rides its `v1/models` workflow — the server resolves the model,
451
+ builds the vendor payload, and normalizes the result.
452
+
453
+ - **Add or change a model** in the models-hub service
454
+ ([`pa-models-hub-pluggable-worker`](https://gitlab.com/picsart/backend/Editor/external-ai-integrations/pa-models-hub-pluggable-worker));
455
+ it takes effect without an SDK release.
456
+ - **Load the catalog** into the SDK with `await ai.initModelCatalog()` — it
457
+ fetches `/v1/models-catalog` from models-hub and installs it as the active
458
+ catalog, so `Model()`, `catalog.*` and `ai.validate()` serve the loaded data.
459
+ Generation (`generate`/`generateText`/`run`/`submit`) does **not** require it.