@picsart/ai-sdk 6.15.0 → 6.16.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.
Files changed (4) hide show
  1. package/README.md +29 -0
  2. package/index.d.ts +51 -12
  3. package/index.js +179 -49
  4. package/package.json +1 -1
package/README.md CHANGED
@@ -153,6 +153,35 @@ const folders = await ai.drive.folders()
153
153
  const items = await ai.drive.list()
154
154
  ```
155
155
 
156
+ Generated Drive assets carry `model` and JSON `aiSDKPayload` automatically. Both
157
+ `generate()` and `submit()` record the original SDK inputs; custom Drive attributes
158
+ cannot replace that provenance. These are requested inputs, not a claim that every
159
+ provider default or rewritten prompt has been recovered. The stored prompt is capped
160
+ at 18,000 characters without changing the prompt sent to generation.
161
+
162
+ `apis.run()` remains a direct vendor API pass-through. It does not apply the client’s
163
+ Drive configuration or derive SDK metadata, even when a vendor payload explicitly
164
+ requests Drive saving. Vendor-shaped inputs must never be stored as `aiSDKPayload`;
165
+ that attribute is reserved for original SDK-shaped inputs.
166
+
167
+ For a separate or delayed save, pass the original generation context:
168
+
169
+ ```typescript
170
+ await ai.drive.save({
171
+ url: generatedUrl,
172
+ name: 'clip.mp4',
173
+ resourceType: 'VIDEO',
174
+ generation: { modelId, params: originalGenerationParams },
175
+ })
176
+ // The existing helper also accepts a full params object instead of just a prompt:
177
+ const saveParams = ai.drive.buildSaveParams(generatedUrl, modelId, modelName, 'video', originalGenerationParams)
178
+ ```
179
+
180
+ Legacy `model`/`prompt`/`textScript` saves are normalized at write time. URL-only
181
+ uploads remain ordinary uploads: without generation context the SDK cannot infer
182
+ model, prompt or settings. Existing Drive files are not backfilled. The backend preserves SDK-supplied metadata; it cannot reconstruct SDK input
183
+ names or catalog model IDs from an arbitrary vendor request.
184
+
156
185
  ## Text Generation (LLMs)
157
186
 
158
187
  Claude, GPT, and Gemini text models are called with `generateText()`. Single-shot:
package/index.d.ts CHANGED
@@ -349,14 +349,30 @@ type ModelInputById = {
349
349
  "gemini-2.5-flash-tts": {
350
350
  language?: string;
351
351
  accent?: string;
352
- prompt: string;
352
+ prompt?: string;
353
353
  voiceId?: string;
354
+ parts?: Array<{
355
+ text: string;
356
+ speaker?: string;
357
+ }>;
358
+ multiSpeakerVoiceConfigs?: Array<{
359
+ speaker: string;
360
+ voiceName: string;
361
+ }>;
354
362
  };
355
363
  "gemini-2.5-pro-tts": {
356
364
  language?: string;
357
365
  accent?: string;
358
- prompt: string;
366
+ prompt?: string;
359
367
  voiceId?: string;
368
+ parts?: Array<{
369
+ text: string;
370
+ speaker?: string;
371
+ }>;
372
+ multiSpeakerVoiceConfigs?: Array<{
373
+ speaker: string;
374
+ voiceName: string;
375
+ }>;
360
376
  };
361
377
  "gemini-3-pro": {
362
378
  prompt: string;
@@ -412,14 +428,34 @@ type ModelInputById = {
412
428
  "gemini-3.8-flash-lite-tts": {
413
429
  language?: string;
414
430
  accent?: string;
415
- prompt: string;
431
+ prompt?: string;
416
432
  voiceId?: string;
433
+ style?: string;
434
+ parts?: Array<{
435
+ text: string;
436
+ speaker?: string;
437
+ style?: string;
438
+ }>;
439
+ multiSpeakerVoiceConfigs?: Array<{
440
+ speaker: string;
441
+ voiceName: string;
442
+ }>;
417
443
  };
418
444
  "gemini-3.8-flash-tts": {
419
445
  language?: string;
420
446
  accent?: string;
421
- prompt: string;
447
+ prompt?: string;
422
448
  voiceId?: string;
449
+ style?: string;
450
+ parts?: Array<{
451
+ text: string;
452
+ speaker?: string;
453
+ style?: string;
454
+ }>;
455
+ multiSpeakerVoiceConfigs?: Array<{
456
+ speaker: string;
457
+ voiceName: string;
458
+ }>;
423
459
  };
424
460
  "gemini-omni-1.1-flash-preview": {
425
461
  prompt: string;
@@ -2668,11 +2704,19 @@ interface ListOptions {
2668
2704
  folder?: DriveFolder;
2669
2705
  type?: MediaTypeFilter;
2670
2706
  }
2707
+ /** Original generation inputs, portable across clients and delayed Drive saves. */
2708
+ interface GenerationProvenance {
2709
+ modelId: string;
2710
+ params: Record<string, unknown>;
2711
+ app?: AppIdentity;
2712
+ }
2671
2713
  interface SaveParams {
2672
2714
  url: string;
2673
2715
  name: string;
2674
2716
  resourceType: 'PHOTO' | 'VIDEO' | 'AUDIO';
2675
2717
  attributes?: Record<string, string>;
2718
+ /** Required to retain full provenance when saving a generated URL separately. */
2719
+ generation?: GenerationProvenance;
2676
2720
  previewUrl?: string;
2677
2721
  }
2678
2722
  /** Target folder for backend Drive save. */
@@ -2714,7 +2758,7 @@ interface DriveClient {
2714
2758
  /** Save an asset to Drive. */
2715
2759
  save(params: SaveParams, folder?: DriveFolder): Promise<DriveSaveResult | null>;
2716
2760
  /** Build the save params for a generation result. */
2717
- buildSaveParams(url: string, modelId: string, modelName: string, mode: string, prompt?: string): SaveParams;
2761
+ buildSaveParams(url: string, modelId: string, modelName: string, mode: string, promptOrParams?: string | Record<string, unknown>): SaveParams;
2718
2762
  /** Set a like/dislike reaction on a file. */
2719
2763
  addReaction(fileUid: string, reaction: UserReaction): Promise<boolean>;
2720
2764
  /** Clear the reaction on a file. */
@@ -2744,12 +2788,7 @@ declare function resolveExtension(mode: string, hints?: FilenameHints): string;
2744
2788
  */
2745
2789
  declare function expectedOutputFormat(model: Pick<ModelDefinition, 'paramConfig' | 'outputExtension'>, params: Record<string, unknown>): string | undefined;
2746
2790
  declare function buildFilename(prompt: string | undefined, mode: string, hints?: FilenameHints): string;
2747
- declare function buildGenerationAttributes(input: {
2748
- modelId: string;
2749
- params: Record<string, unknown>;
2750
- /** TODO(backend-autosave): temporary — remove once the backend stamps appId/appType. */
2751
- app?: AppIdentity;
2752
- }): DriveAttributes;
2791
+ declare function buildGenerationAttributes(input: GenerationProvenance): DriveAttributes;
2753
2792
  declare function parseGeneration(file: DriveFile | Record<string, unknown>): GenerationFile;
2754
2793
 
2755
2794
  /** Result of `ai.apis.run()` — the API result plus optional credit usage. */
@@ -3545,4 +3584,4 @@ declare const getModel: (id: string) => ModelDefinition | undefined;
3545
3584
  */
3546
3585
  declare const findModel: (ref: string) => ModelDefinition | undefined;
3547
3586
 
3548
- export { ALL_MODELS, type AiClient, ApiError, type ApiErrorCode, type ApiErrorInit, type ApiResponse, type ApiRunOptions, type ApiSchemas, type ApisClient, type AppIdentity, type AppType, type AuthenticatedFetch, type AvatarOption, type BooleanDescriptor, type BooleanEntry, type CatalogDescriptor, type CatalogEntry, type CatalogItem, type CatalogPage, type CatalogPageOptions, type CatalogPreview, type CatalogQuery, type CatalogResult, type CatalogSource, type CatalogsClient, type CatalogsOptions, type ClientConfig, type CreditRange, type CreditRangeContext, type CreditTier, type CreditUsage, DEFAULT_VISIBLE_RELEASES, type DeepLinkResult, type DriveAttributes, type DriveClient, type DriveConfig, type DriveDraftInfo, type DriveFile, type DriveFileDetails, type DriveFolder, type DriveMediaItem, type DriveSaveResult, type EntryMeta, type EnumDescriptor, type EnumEntry, type EnumOption, type FileDescriptor, type FileEntry, type FlatParamEntry, type GenerateOptions, type GenerateResult, type GenerateResultItem, type GenerateResultItemMetadata, type GenerateTextResult, type GenerationContext, type GenerationEvent, GenerationEventType, type GenerationFile, type GenerationMode, type GenerationOptions, type GenerationProgress, type ListOptions, type MediaModelId, type MediaTypeFilter, Model, type ModelDefinition, type ModelDescriptor, type ModelFilter, type ModelInput, type ModelInputById, type ModelMeta, type ModelParams, type ModelParamsAccessor, Models, type ObjectDescriptor, type ObjectEntry, type ParamDescriptor, type ParamEntry, type ParamOption, type PayloadDriveFolderOptions, type PayloadDriveOptions, type PayloadInputsTransformationOptions, type PricingOptions, type ProviderInfo, type RangeDescriptor, type RangeEntry, type ReleaseTag, type SaveParams, type SdkPayload, type SdkTransport, type SeedanceDraftTask, type TextDescriptor, type TextEntry, type TextModelId, type TextModelInputById, type ToolUsage, type TransportPollOptions, type TransportResult, type TypedModelId, type UserReaction, type ValidationResult, type VoiceOption, type WorkflowJobHandle, type WorkflowSubmitRequest, buildFilename, buildGenerationAttributes, catalog, createClient, decodeDeepLinkPayload, encodeDeepLinkPayload, expectedOutputFormat, findModel, getModel, getModelsByMode, getVoiceById, inferResourceType, isVisibleForReleases, parseGeneration, releaseOf, resolveExtension, toAvatarOption, toVoiceOption };
3587
+ export { ALL_MODELS, type AiClient, ApiError, type ApiErrorCode, type ApiErrorInit, type ApiResponse, type ApiRunOptions, type ApiSchemas, type ApisClient, type AppIdentity, type AppType, type AuthenticatedFetch, type AvatarOption, type BooleanDescriptor, type BooleanEntry, type CatalogDescriptor, type CatalogEntry, type CatalogItem, type CatalogPage, type CatalogPageOptions, type CatalogPreview, type CatalogQuery, type CatalogResult, type CatalogSource, type CatalogsClient, type CatalogsOptions, type ClientConfig, type CreditRange, type CreditRangeContext, type CreditTier, type CreditUsage, DEFAULT_VISIBLE_RELEASES, type DeepLinkResult, type DriveAttributes, type DriveClient, type DriveConfig, type DriveDraftInfo, type DriveFile, type DriveFileDetails, type DriveFolder, type DriveMediaItem, type DriveSaveResult, type EntryMeta, type EnumDescriptor, type EnumEntry, type EnumOption, type FileDescriptor, type FileEntry, type FlatParamEntry, type GenerateOptions, type GenerateResult, type GenerateResultItem, type GenerateResultItemMetadata, type GenerateTextResult, type GenerationContext, type GenerationEvent, GenerationEventType, type GenerationFile, type GenerationMode, type GenerationOptions, type GenerationProgress, type GenerationProvenance, type ListOptions, type MediaModelId, type MediaTypeFilter, Model, type ModelDefinition, type ModelDescriptor, type ModelFilter, type ModelInput, type ModelInputById, type ModelMeta, type ModelParams, type ModelParamsAccessor, Models, type ObjectDescriptor, type ObjectEntry, type ParamDescriptor, type ParamEntry, type ParamOption, type PayloadDriveFolderOptions, type PayloadDriveOptions, type PayloadInputsTransformationOptions, type PricingOptions, type ProviderInfo, type RangeDescriptor, type RangeEntry, type ReleaseTag, type SaveParams, type SdkPayload, type SdkTransport, type SeedanceDraftTask, type TextDescriptor, type TextEntry, type TextModelId, type TextModelInputById, type ToolUsage, type TransportPollOptions, type TransportResult, type TypedModelId, type UserReaction, type ValidationResult, type VoiceOption, type WorkflowJobHandle, type WorkflowSubmitRequest, buildFilename, buildGenerationAttributes, catalog, createClient, decodeDeepLinkPayload, encodeDeepLinkPayload, expectedOutputFormat, findModel, getModel, getModelsByMode, getVoiceById, inferResourceType, isVisibleForReleases, parseGeneration, releaseOf, resolveExtension, toAvatarOption, toVoiceOption };
package/index.js CHANGED
@@ -6453,11 +6453,6 @@ var buildGemini31FlashLiteImagePayload = (ctx) => ({
6453
6453
  imageSize: ctx.resolution ?? "1K",
6454
6454
  ...buildThinkingConfig(ctx)
6455
6455
  });
6456
- var buildGeminiTTSPayload = (model) => (ctx) => ({
6457
- text: ctx.prompt,
6458
- model,
6459
- voiceName: ctx.voiceId ?? "Kore"
6460
- });
6461
6456
  function inferMimeType2(url) {
6462
6457
  return url.match(/\.png(\?|$)/i) ? "image/png" : "image/jpeg";
6463
6458
  }
@@ -6469,6 +6464,52 @@ var buildGeminiOmniVideoPayload = (ctx) => ({
6469
6464
  ...ctx.imageUrls?.[0] ? { image: { url: ctx.imageUrls[0], mimeType: inferMimeType2(ctx.imageUrls[0]) } } : {},
6470
6465
  ...ctx.videoUrl ? { video: { url: ctx.videoUrl } } : {}
6471
6466
  });
6467
+ var GEMINI_TTS_TEXT_MAX = 6e3;
6468
+ var GEMINI_TTS_STYLE_MAX = 500;
6469
+ var geminiTtsParams = (directable) => ({
6470
+ ...params.language(true),
6471
+ // 6,000 boundary-verified against the live gemini/v1/audios worker
6472
+ // (accepts >5,000; see scripts/api-tests/audio-charlimit-boundary-probe.mjs).
6473
+ ...params.prompt({ required: false, maxLength: GEMINI_TTS_TEXT_MAX }),
6474
+ ...params.voiceId([], GEMINI_DEFAULT_VOICE_ID, { catalog: { workflow: "gemini/v1/catalog/voices" } }),
6475
+ ...directable ? {
6476
+ style: {
6477
+ label: "Style",
6478
+ descriptor: {
6479
+ kind: "text",
6480
+ maxLength: GEMINI_TTS_STYLE_MAX,
6481
+ placeholder: "e.g. warm, unhurried; a bedtime story"
6482
+ }
6483
+ }
6484
+ } : {},
6485
+ // Worker `SpeechPartDto[]`, same names. Used instead of `prompt`.
6486
+ parts: {
6487
+ label: "Spoken Parts",
6488
+ descriptor: {
6489
+ kind: "object",
6490
+ array: { min: 1, max: 200 },
6491
+ fields: {
6492
+ text: { label: "Text", kind: "text", maxLength: GEMINI_TTS_TEXT_MAX },
6493
+ speaker: { label: "Speaker", kind: "text", required: false },
6494
+ ...directable ? { style: { label: "Style", kind: "text", maxLength: GEMINI_TTS_STYLE_MAX, required: false } } : {}
6495
+ }
6496
+ }
6497
+ },
6498
+ // Worker `SpeakerVoiceConfigDto[]`, max 2. Replaces `voiceId`. The voice is
6499
+ // a plain id, not a catalog picker: catalog loading resolves top-level params
6500
+ // only, so a caller lists voices with `ai.catalogs.voices(<model>)`.
6501
+ multiSpeakerVoiceConfigs: {
6502
+ label: "Speakers",
6503
+ descriptor: {
6504
+ kind: "object",
6505
+ array: { min: 1, max: 2 },
6506
+ fields: {
6507
+ speaker: { label: "Speaker", kind: "text", placeholder: "Speaker 1" },
6508
+ voiceName: { label: "Voice", kind: "text", placeholder: GEMINI_DEFAULT_VOICE_ID }
6509
+ }
6510
+ }
6511
+ }
6512
+ });
6472
6513
  var GEMINI_AR_WIDE = ["1:1", "16:9", "9:16", "3:4", "4:3", "2:3", "21:9", "auto"];
6473
6514
  var thinkingLevelParam = {
6474
6515
  thinkingLevel: {
@@ -6586,32 +6627,35 @@ var { MODELS: MODELS22 } = defineModels("google", [
6586
6627
  }
6587
6628
  },
6588
6629
  // ── Audio ─────────────────────────────────────────────────────────
6630
+ // Payload builders live in gemini.payloads.ts (typed against ModelInput).
6631
+ // The wire `model` is fixed per entry there: `modelId` is the pricing key and
6632
+ // never reaches the builder.
6633
+ //
6634
+ // 2.5 and 3.8 take direction and speakers differently, so the builders
6635
+ // resolve them per generation. 3.8 reads `style` and per-part speakers from
6636
+ // `speechMetadata` and speaks a natural-language prefix in the text aloud:
6637
+ // `parts` go through and `language` / `accent` fold into `style`. 2.5
6638
+ // ignores `speechMetadata`: it reads speakers from "Speaker: line" labels
6639
+ // and direction from a "Say …:" prefix, so the builder writes both into
6640
+ // one text.
6589
6641
  {
6590
6642
  id: "gemini-2.5-flash-tts",
6591
6643
  name: "Gemini 2.5 Flash TTS",
6592
6644
  addedAt: "2026-02-15",
6593
6645
  workflow: "gemini/v1/audios",
6594
- buildPayload: buildGeminiTTSPayload("gemini-2.5-flash-tts"),
6595
6646
  estimatedTime: 15,
6596
6647
  mode: "audio",
6597
6648
  inputType: "tts",
6598
6649
  modelId: "gemini-2.5-flash-tts",
6599
6650
  description: "Google Gemini native text-to-speech with expressive multilingual voices.",
6600
- features: [feat("Multilingual", "characteristic"), feat("30 Voices", "characteristic")],
6601
- paramConfig: {
6602
- ...params.language(true),
6603
- // 6,000 boundary-verified against the live gemini/v1/audios worker
6604
- // (accepts >5,000; see scripts/api-tests/audio-charlimit-boundary-probe.mjs).
6605
- ...params.prompt({ maxLength: 6e3 }),
6606
- ...params.voiceId([], GEMINI_DEFAULT_VOICE_ID, { catalog: { workflow: "gemini/v1/catalog/voices" } })
6607
- }
6651
+ features: [feat("Multilingual", "characteristic"), feat("30 Voices", "characteristic"), feat("Multi-Speaker", "characteristic")],
6652
+ paramConfig: geminiTtsParams(false)
6608
6653
  },
6609
6654
  {
6610
6655
  id: "gemini-2.5-pro-tts",
6611
6656
  name: "Gemini 2.5 Pro TTS",
6612
6657
  addedAt: "2026-03-18",
6613
6658
  workflow: "gemini/v1/audios",
6614
- buildPayload: buildGeminiTTSPayload("gemini-2.5-pro-tts"),
6615
6659
  estimatedTime: 20,
6616
6660
  mode: "audio",
6617
6661
  inputType: "tts",
@@ -6619,54 +6663,34 @@ var { MODELS: MODELS22 } = defineModels("google", [
6619
6663
  badge: ["premium"],
6620
6664
  description: "Premium Gemini TTS with richer expressiveness and multi-speaker support.",
6621
6665
  features: [feat("Multilingual", "characteristic"), feat("30 Voices", "characteristic"), feat("Multi-Speaker", "characteristic")],
6622
- paramConfig: {
6623
- ...params.language(true),
6624
- // 6,000 boundary-verified against the live gemini/v1/audios worker
6625
- // (accepts >5,000; see scripts/api-tests/audio-charlimit-boundary-probe.mjs).
6626
- ...params.prompt({ maxLength: 6e3 }),
6627
- ...params.voiceId([], GEMINI_DEFAULT_VOICE_ID, { catalog: { workflow: "gemini/v1/catalog/voices" } })
6628
- }
6666
+ paramConfig: geminiTtsParams(false)
6629
6667
  },
6630
6668
  {
6631
6669
  id: "gemini-3.8-flash-tts",
6632
6670
  name: "Gemini 3.8 Flash TTS",
6633
6671
  addedAt: "2026-09-24",
6634
6672
  workflow: "gemini/v1/audios",
6635
- buildPayload: buildGeminiTTSPayload("gemini-3.8-flash-tts"),
6636
6673
  estimatedTime: 15,
6637
6674
  mode: "audio",
6638
6675
  inputType: "tts",
6639
6676
  modelId: "gemini-3.8-flash-tts",
6640
- description: "Latest Gemini native text-to-speech with expressive multilingual voices.",
6641
- features: [feat("Multilingual", "characteristic"), feat("30 Voices", "characteristic")],
6642
- paramConfig: {
6643
- ...params.language(true),
6644
- // 6,000 boundary-verified against the live gemini/v1/audios worker
6645
- // (accepts >5,000; see scripts/api-tests/audio-charlimit-boundary-probe.mjs).
6646
- ...params.prompt({ maxLength: 6e3 }),
6647
- ...params.voiceId([], GEMINI_DEFAULT_VOICE_ID, { catalog: { workflow: "gemini/v1/catalog/voices" } })
6648
- }
6677
+ description: "Latest Gemini native text-to-speech with directable delivery and inline vocal events like <laugh> and <sigh>.",
6678
+ features: [feat("Multilingual", "characteristic"), feat("30 Voices", "characteristic"), feat("Multi-Speaker", "characteristic"), feat("Style Direction", "characteristic")],
6679
+ paramConfig: geminiTtsParams(true)
6649
6680
  },
6650
6681
  {
6651
6682
  id: "gemini-3.8-flash-lite-tts",
6652
6683
  name: "Gemini 3.8 Flash Lite TTS",
6653
6684
  addedAt: "2026-09-24",
6654
6685
  workflow: "gemini/v1/audios",
6655
- buildPayload: buildGeminiTTSPayload("gemini-3.8-flash-lite-tts"),
6656
6686
  estimatedTime: 12,
6657
6687
  mode: "audio",
6658
6688
  inputType: "tts",
6659
6689
  modelId: "gemini-3.8-flash-lite-tts",
6660
6690
  badge: ["fast"],
6661
- description: "Lightweight Gemini 3.8 TTS variant for faster, high-volume speech synthesis.",
6662
- features: [feat("Multilingual", "characteristic"), feat("30 Voices", "characteristic")],
6663
- paramConfig: {
6664
- ...params.language(true),
6665
- // 6,000 boundary-verified against the live gemini/v1/audios worker
6666
- // (accepts >5,000; see scripts/api-tests/audio-charlimit-boundary-probe.mjs).
6667
- ...params.prompt({ maxLength: 6e3 }),
6668
- ...params.voiceId([], GEMINI_DEFAULT_VOICE_ID, { catalog: { workflow: "gemini/v1/catalog/voices" } })
6669
- }
6691
+ description: "Lightweight Gemini 3.8 TTS with directable delivery for faster, high-volume speech synthesis.",
6692
+ features: [feat("Multilingual", "characteristic"), feat("30 Voices", "characteristic"), feat("Multi-Speaker", "characteristic"), feat("Style Direction", "characteristic")],
6693
+ paramConfig: geminiTtsParams(true)
6670
6694
  },
6671
6695
  // ── Video ─────────────────────────────────────────────────────────
6672
6696
  {
@@ -6733,8 +6757,95 @@ var buildOmniFlash11Payload = (input) => ({
6733
6757
  ...input.videoUrl ? { video: { url: input.videoUrl } } : {},
6734
6758
  ...input.videoUrls?.length ? { referenceVideos: input.videoUrls.map((url) => ({ url })) } : {}
6735
6759
  });
6760
+ var STYLE_MAX = 500;
6761
+ var TEXT_MAX = 6e3;
6762
+ var invalid = (message) => new ApiError(message, { status: 400, code: "validation_error" });
6763
+ var blank = (value) => !value?.trim();
6764
+ function checkInput(input) {
6765
+ const configs = input.multiSpeakerVoiceConfigs ?? [];
6766
+ for (const [i, config] of configs.entries()) {
6767
+ if (blank(config.speaker)) throw invalid(`Gemini TTS: speaker config ${i + 1} has no speaker.`);
6768
+ if (blank(config.voiceName)) throw invalid(`Gemini TTS: speaker config ${i + 1} has no voiceName.`);
6769
+ }
6770
+ const speakers = configs.map((config) => config.speaker);
6771
+ if (new Set(speakers).size !== speakers.length) {
6772
+ throw invalid("Gemini TTS: multiSpeakerVoiceConfigs names the same speaker twice.");
6773
+ }
6774
+ const parts = input.parts?.length ? input.parts : void 0;
6775
+ if (!parts) {
6776
+ if (blank(input.prompt)) throw invalid("Gemini TTS: provide a prompt or parts.");
6777
+ return { speakers };
6778
+ }
6779
+ for (const [i, part] of parts.entries()) {
6780
+ if (blank(part.text)) throw invalid(`Gemini TTS: part ${i + 1} has no text.`);
6781
+ if (speakers.length && (!part.speaker || !speakers.includes(part.speaker))) {
6782
+ throw invalid(
6783
+ `Gemini TTS: part ${i + 1} needs a speaker from multiSpeakerVoiceConfigs (${speakers.join(", ")}).`
6784
+ );
6785
+ }
6786
+ if (!speakers.length && part.speaker) {
6787
+ throw invalid(`Gemini TTS: part ${i + 1} names a speaker but no multiSpeakerVoiceConfigs are set.`);
6788
+ }
6789
+ }
6790
+ return { parts, speakers };
6791
+ }
6792
+ function assertLength(spoken) {
6793
+ if (spoken.length > TEXT_MAX) {
6794
+ throw invalid(`Gemini TTS: the text totals ${spoken.length} characters; the limit is ${TEXT_MAX}.`);
6795
+ }
6796
+ }
6797
+ var voiceFields = (input) => input.multiSpeakerVoiceConfigs?.length ? { multiSpeakerVoiceConfigs: input.multiSpeakerVoiceConfigs } : { voiceName: input.voiceId ?? GEMINI_DEFAULT_VOICE_ID };
6798
+ function composeStyle(input) {
6799
+ const language = input.language?.trim();
6800
+ const accent = input.accent?.trim();
6801
+ const voice = language || accent ? `speak${language ? ` in ${language}` : ""}${accent ? ` with a ${accent} accent` : ""}` : void 0;
6802
+ const composed = [...input.styles.map((style) => style?.trim()), voice].filter(Boolean).join("; ") || void 0;
6803
+ if (composed && composed.length > STYLE_MAX) {
6804
+ throw invalid(`Gemini TTS: style with language/accent exceeds ${STYLE_MAX} characters.`);
6805
+ }
6806
+ return composed;
6807
+ }
6808
+ function directionPrefix(input) {
6809
+ const language = input.language?.trim();
6810
+ const accent = input.accent?.trim();
6811
+ if (!language && !accent) return "";
6812
+ return `Say${language ? ` in ${language}` : ""}${accent ? ` with a ${accent} accent` : ""}: `;
6813
+ }
6814
+ var buildTts25Payload = (model) => (input) => {
6815
+ const { parts, speakers } = checkInput(input);
6816
+ const body = parts ? parts.map((part) => speakers.length ? `${part.speaker}: ${part.text}` : part.text).join("\n") : input.prompt;
6817
+ const text = `${directionPrefix(input)}${body}`;
6818
+ assertLength(text);
6819
+ return { text, model, ...voiceFields(input) };
6820
+ };
6821
+ var buildTts38Payload = (model) => (input) => {
6822
+ const { parts } = checkInput(input);
6823
+ const direction = { language: input.language, accent: input.accent };
6824
+ if (!parts) {
6825
+ assertLength(input.prompt);
6826
+ const style = composeStyle({ styles: [input.style], ...direction });
6827
+ return { text: input.prompt, model, ...voiceFields(input), ...style ? { style } : {} };
6828
+ }
6829
+ assertLength(parts.map((part) => part.text).join("\n"));
6830
+ return {
6831
+ parts: parts.map((part) => {
6832
+ const style = composeStyle({ styles: [input.style, part.style], ...direction });
6833
+ return {
6834
+ text: part.text,
6835
+ ...part.speaker ? { speaker: part.speaker } : {},
6836
+ ...style ? { style } : {}
6837
+ };
6838
+ }),
6839
+ model,
6840
+ ...voiceFields(input)
6841
+ };
6842
+ };
6736
6843
  registerPayloads(MODELS22, {
6737
- "gemini-omni-1.1-flash-preview": buildOmniFlash11Payload
6844
+ "gemini-omni-1.1-flash-preview": buildOmniFlash11Payload,
6845
+ "gemini-2.5-flash-tts": buildTts25Payload("gemini-2.5-flash-tts"),
6846
+ "gemini-2.5-pro-tts": buildTts25Payload("gemini-2.5-pro-tts"),
6847
+ "gemini-3.8-flash-tts": buildTts38Payload("gemini-3.8-flash-tts"),
6848
+ "gemini-3.8-flash-lite-tts": buildTts38Payload("gemini-3.8-flash-lite-tts")
6738
6849
  });
6739
6850
 
6740
6851
  // src/vendors/catalog/openai.ts
@@ -11997,6 +12108,23 @@ function buildGenerationAttributes(input) {
11997
12108
  }
11998
12109
  return attrs;
11999
12110
  }
12111
+ function attributesForSave(params2) {
12112
+ const attributes = { ...params2.attributes };
12113
+ if (params2.generation) {
12114
+ return { ...attributes, ...buildGenerationAttributes(params2.generation) };
12115
+ }
12116
+ if (asString(attributes.model)) {
12117
+ const legacy = adaptLegacyGeneration(attributes);
12118
+ const canonical = parseJsonAttr(attributes.aiSDKPayload);
12119
+ const payload = canonical && typeof canonical.prompt === "string" ? canonical : legacy.aiSDKPayload ?? {};
12120
+ return {
12121
+ ...legacy.appId ? { appId: legacy.appId, appType: legacy.appType } : {},
12122
+ ...attributes,
12123
+ ...buildGenerationAttributes({ modelId: attributes.model, params: payload })
12124
+ };
12125
+ }
12126
+ return attributes;
12127
+ }
12000
12128
  function toMediaItem(file) {
12001
12129
  const url = normalizeUrl(file.sourceUrl);
12002
12130
  if (!url || String(file.name || "").startsWith("__")) return null;
@@ -12332,7 +12460,7 @@ function createDriveClient(f, apiUrl, rootFolderName) {
12332
12460
  width: 1024,
12333
12461
  height: 1024
12334
12462
  },
12335
- attributes: Object.entries(params2.attributes ?? {}).map(([property, value]) => ({
12463
+ attributes: Object.entries(attributesForSave(params2)).map(([property, value]) => ({
12336
12464
  property,
12337
12465
  value
12338
12466
  }))
@@ -12360,15 +12488,17 @@ function createDriveClient(f, apiUrl, rootFolderName) {
12360
12488
  }
12361
12489
  },
12362
12490
  /** Build standard save params from a generation result. */
12363
- buildSaveParams(url, modelId, modelName, mode, prompt) {
12491
+ buildSaveParams(url, modelId, modelName, mode, promptOrParams) {
12492
+ const params2 = typeof promptOrParams === "string" ? { prompt: promptOrParams } : promptOrParams ?? {};
12493
+ const prompt = String(params2.prompt ?? "");
12364
12494
  return {
12365
12495
  url,
12366
12496
  name: buildFilename(prompt, mode, { url }),
12367
12497
  resourceType: inferResourceType(mode),
12368
12498
  attributes: {
12369
12499
  tool: "ai-sdk",
12370
- model: modelId,
12371
- prompt: prompt || "",
12500
+ ...buildGenerationAttributes({ modelId, params: params2 }),
12501
+ prompt,
12372
12502
  service: modelName
12373
12503
  }
12374
12504
  };
@@ -12676,8 +12806,8 @@ function createClient(config) {
12676
12806
  return {
12677
12807
  // Named before the job runs, so the requested/declared format is the best hint.
12678
12808
  name: explicit?.name ?? buildFilename(params2.prompt, model.mode, { format: expectedOutputFormat(model, params2) }),
12679
- // SDK-assembled attributes are the baseline; explicit attributes win per-key.
12680
- attributes: { ...attributes, ...explicit?.attributes ?? {} },
12809
+ // Generation provenance is authoritative; custom attributes cannot replace it.
12810
+ attributes: { ...explicit?.attributes ?? {}, ...attributes },
12681
12811
  folder: explicit?.folder ?? (folderPath ? { path: folderPath } : void 0)
12682
12812
  };
12683
12813
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@picsart/ai-sdk",
3
- "version": "6.15.0",
3
+ "version": "6.16.0",
4
4
  "type": "module",
5
5
  "description": "Type-safe SDK for 100+ AI models — image, video, audio, and text generation with Picsart",
6
6
  "license": "MIT",