@picsart/ai-sdk 6.54.0 → 6.99.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()
@@ -194,22 +207,22 @@ names or catalog model IDs from an arbitrary vendor request.
194
207
 
195
208
  ## Text Generation (LLMs)
196
209
 
197
- Claude, GPT, Gemini, Kimi and Grok text models are called with `generateText()`. Single-shot:
210
+ Claude, GPT, Gemini, Kimi, Grok and MiniMax text models are called with `generateText()`. Single-shot:
198
211
  pass a prompt and optional image(s)/video, get text back. They surface in the catalog
199
212
  as `mode: 'text'`. Each vendor uses its own native workflow, so capabilities aren't lost
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
  })
@@ -227,14 +240,16 @@ await ai.generateText(Models.Gemini3Pro, {
227
240
  catalog.find({ output: 'text' })
228
241
  ```
229
242
 
230
- > Thinking level maps per vendor: OpenAI, Kimi and Grok → `reasoning_effort`, Gemini →
231
- > `thinkingConfig.thinkingLevel` (LOW/HIGH). Kimi K3 and Grok 4.7 always reason, so
232
- > they have no `off`. Claude’s `claude/v1/messages`
243
+ > Thinking level maps per vendor: OpenAI, Kimi, Grok and MiniMax M3.1 Flash → `reasoning_effort`, Gemini →
244
+ > `thinkingConfig.thinkingLevel` (LOW/HIGH). Kimi K3, Grok 4.7 and MiniMax M3.1 Flash
245
+ > always reason, so they have no `off`. MiniMax M3 reasons adaptively and has no
246
+ > thinking knob. Claude’s `claude/v1/messages`
233
247
  > workflow exposes no thinking knob, so Claude models omit `thinking`.
234
248
 
235
- `generateText()` is type-narrowed to text models (`TextModelId`); calling it with an
236
- image/video model throws, and `generate()` throws on a text model — use the matching
237
- 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.
238
253
 
239
254
  ## Generic Execution — ai.run()
240
255
 
@@ -249,14 +264,14 @@ task result — and for `mode: 'json'` models, `run()` is the only surface:
249
264
  structured-output models (judges, evaluators, classifiers) are rejected by
250
265
  `generate()`, `generateText()`, and the async lifecycle.
251
266
 
252
- The first json model is `typesafe-evaluate` (`Models.TypesafeEvaluate`) — LLM-judge
253
- evaluation of any content against named questions. Type the result via the `R`
254
- 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()`:
255
270
 
256
271
  ```typescript
257
272
  interface EvaluateResult { model: string; answers: Record<string, unknown>; usage: unknown }
258
273
 
259
- const { result } = await ai.run<EvaluateResult>(Models.TypesafeEvaluate, {
274
+ const { result } = await ai.run<EvaluateResult>('typesafe-evaluate', {
260
275
  state: 'The weather is lovely and everyone is having a great time.',
261
276
  questions: {
262
277
  is_safe: { type: 'noul', instructions: 'Is this content safe for all audiences?' },
@@ -303,21 +318,21 @@ For progress tracking and job recovery:
303
318
 
304
319
  ```typescript
305
320
  // Submit without waiting — returns the generation id (a string)
306
- const generationId = await ai.submit(Models.KlingV3, { prompt: 'a sunset' })
321
+ const generationId = await ai.submit('kling-v3', { prompt: 'a sunset' })
307
322
 
308
323
  // Subscribe to updates — the terminal generation.completed event carries the
309
324
  // parsed result, so no follow-up result() call is needed
310
- for await (const e of ai.subscribe(Models.KlingV3, generationId)) {
325
+ for await (const e of ai.subscribe('kling-v3', generationId)) {
311
326
  if (e.type === 'generation.progress') console.log(e.progress?.percent)
312
327
  if (e.type === 'generation.completed') console.log(e.result.url)
313
328
  if (e.type === 'generation.failed') console.error(e.error.message)
314
329
  }
315
330
 
316
331
  // Or just wait for the parsed result (throws on failure/cancel)
317
- const result = await ai.result(Models.KlingV3, generationId, { intervalMs: 2000 })
332
+ const result = await ai.result('kling-v3', generationId, { intervalMs: 2000 })
318
333
 
319
334
  // One-shot snapshot of a stored id (e.g. after a page reload)
320
- const { value: snapshot } = await ai.subscribe(Models.KlingV3, generationId).next()
335
+ const { value: snapshot } = await ai.subscribe('kling-v3', generationId).next()
321
336
  ```
322
337
 
323
338
  Generation ids are plain strings — store them and recover jobs later with
@@ -330,10 +345,10 @@ is an `ApiError` with the same four fields, so you can branch on the error
330
345
  instead of pattern-matching its message:
331
346
 
332
347
  ```typescript
333
- import { createClient, Models, ApiError } from '@picsart/ai-sdk'
348
+ import { createClient, ApiError } from '@picsart/ai-sdk'
334
349
 
335
350
  try {
336
- 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' })
337
352
  } catch (err) {
338
353
  if (err instanceof ApiError) {
339
354
  err.status // 402 — HTTP status, or its synthesized equivalent
@@ -386,8 +401,7 @@ Key exports (see `src/index.ts` for the full list):
386
401
  | Export | Type | Description |
387
402
  |--------|------|-------------|
388
403
  | `createClient` | function | Create an AI client from an API key (or a custom authenticated fetch) |
389
- | `Models` | object | Typed model-id constants (`Models.Flux2Pro` → `'flux-2-pro'`) |
390
- | `Model`, `catalog` | function / object | Model metadata, params, validation, discovery |
404
+ | `Model`, `catalog` | function / object | Model metadata, params, validation, discovery (after `initModelCatalog()`) |
391
405
  | `GenerateResult` | type | `{ url, items: [{ url, metadata? }], generationId, usage?, drive? }` |
392
406
  | `GenerationEvent` | type | One `ai.subscribe()` update — `generation.progress` / `.completed` / `.failed` |
393
407
  | `GenerationEventType` | const | Named event-type constants (`GenerationEventType.Completed` === `'generation.completed'`) |
@@ -409,66 +423,37 @@ packages/ai-sdk/
409
423
  client/
410
424
  types.ts # ClientConfig, GenerateResult, DriveConfig
411
425
  transport.ts # Default SdkTransport, over @picsart/workflows-client
412
- prepare.ts # Validate input, build payload, parse result
426
+ prepare.ts # Build the v1/models command + parse its result
413
427
  drive.ts # Drive folder management + file saving
414
- index.ts # createClient() factory
428
+ index.ts # createClient() factory (dynamic-only)
415
429
  core/
416
430
  types.ts # ModelDefinition, ParamConfig, GenerationContext
417
431
  workflow.ts # Task envelope types + the SdkTransport contract
418
- 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
419
435
  schema.ts # ParamConfig → JSON Schema
420
436
  response.ts # Vendor-agnostic result extraction
421
- pricing.ts # ToolId resolution
422
- model-registry.ts # Model lookup indexes
437
+ model-registry.ts # Model lookup indexes over the active catalog
423
438
  providers.ts # Provider colors, labels, names
424
439
  voices.ts # Voice catalogs (ElevenLabs, OpenAI, Gemini)
425
- helpers.ts # Vendor utilities
426
- generated/
427
- model-constants.ts # AUTO-GENERATED: Models object + id constants
428
- model-input-types.ts # AUTO-GENERATED: per-model TypeScript input types
429
- vendors/
430
- define.ts # defineModels() framework + params.* helpers
431
- presets.ts # Reusable paramConfig factories
432
- catalog/
433
- index.ts # Aggregation: ALL_MODELS, VENDOR_CATALOGS
434
- kling.ts # One file per vendor (31 total)
435
- flux.ts
436
- ...
440
+ descriptors/ # Model()/catalog accessors, param descriptors, pricing
437
441
  __tests__/ # SDK tests
438
442
  scripts/ # Build scripts
439
443
  ```
440
444
 
441
- ## Adding a New Model
442
-
443
- ### Standard flow (pass-through payload)
444
-
445
- When the backend accepts param values as-is (no field renaming needed):
446
-
447
- 1. Add config in `src/vendors/catalog/{vendor}.ts` via `defineModels()`:
448
- - `buildPayload` is **optional** — omit it and param values pass through as-is
449
- 2. Run `npm run build:model-constants` to regenerate constants
450
- 3. Run `npm run build:model-input-types` to regenerate TypeScript types
451
- 4. Model automatically appears in `catalog.all()` and as the `Models.NewModel` id constant
452
-
453
- ### With payload transforms
454
-
455
- When the vendor API uses different field names or value formats:
456
-
457
- 1. Define the model config as above (no `buildPayload`)
458
- 2. Run `npm run build:model-input-types` — generates typed input for your model
459
- 3. Create `src/vendors/catalog/{vendor}.payloads.ts`:
460
- ```ts
461
- import type { ModelInput } from '../../generated/model-input-types.ts';
462
- import { registerPayloads } from '../define.ts';
463
- import { SPECS, MODELS } from './{vendor}.ts';
464
-
465
- registerPayloads({ SPECS, MODELS }, {
466
- 'model-id': (input: ModelInput<'model-id'>) => ({
467
- prompt: input.prompt,
468
- aspect_ratio: input.aspectRatio, // rename for vendor API
469
- }),
470
- });
471
- ```
472
- 4. Import the `.payloads.ts` file in `src/vendors/catalog/index.ts` (after the vendor import)
473
-
474
- 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.