Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 17 additions & 3 deletions src/ai-sdk/providers/varg.ts
Original file line number Diff line number Diff line change
Expand Up @@ -382,9 +382,23 @@ class VargVideoModel implements VideoModelV3 {
}

if (options.providerOptions?.varg) {
params.provider_options = checkVargProviderOptions(
options.providerOptions.varg as Record<string, unknown>,
);
const vargOpts = {
...(options.providerOptions.varg as Record<string, unknown>),
};
// Unified varg-API fields riding the `varg` namespace are lifted to the
// request body top level — they are NOT provider_options. `audio` is the
// unified native-audio request (audio: "native" sugar): the API maps it
// per model via mapping_rules (rename audio → generate_audio) and models
// without the rule silently ignore it. Sending it inside
// provider_options.fal instead used to rely on the API silently
// stripping unknown keys, which now 422s.
if (vargOpts.audio !== undefined) {
params.audio = vargOpts.audio;
delete vargOpts.audio;
}
if (Object.keys(vargOpts).length > 0) {
params.provider_options = checkVargProviderOptions(vargOpts);
}
}

const result = await executeJob(this.baseUrl, this.apiKey, "video", params);
Expand Down
21 changes: 12 additions & 9 deletions src/react/elements.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,10 +132,15 @@ function attachAudioGetter<
* IGNORED_PROPS_BY_TYPE) — the expanded providerOptions carry the
* semantic difference, so pre-existing configurations keep their keys.
*
* Provider mapping (verified against the varg API deep-merge behavior):
* Provider mapping:
* - direct fal models → `providerOptions.fal.generate_audio`
* - varg gateway models → `providerOptions.varg.fal.generate_audio`
* (the API forwards `provider_options.fal.*` to the fal payload)
* - varg gateway models → `providerOptions.varg.audio` (the UNIFIED field —
* lifted to the request body top level by VargVideoModel and mapped per
* model by the API's mapping_rules; models without native-audio support
* silently ignore it). Do NOT inject provider_options.fal.generate_audio
* here: the API rejects unknown provider_options keys with a 422, so the
* old blind injection broke every fal video model whose schema lacks
* generate_audio.
*/
function expandNativeAudio(props: VideoProps): VideoProps {
if (props.audio !== "native") return props;
Expand All @@ -155,12 +160,10 @@ function expandNativeAudio(props: VideoProps): VideoProps {
string,
unknown
>;
const falOpts = { ...((vargOpts.fal as object) ?? {}) } as Record<
string,
unknown
>;
if (falOpts.generate_audio === undefined) falOpts.generate_audio = true;
vargOpts.fal = falOpts;
// Unified native-audio request — best-effort by design: the varg API
// maps it to the provider's own flag on models that support it and
// silently drops it elsewhere (mapping_rules, migration 0147).
if (vargOpts.audio === undefined) vargOpts.audio = true;
providerOptions.varg = vargOpts as Record<string, unknown>;
} else {
// Default to the fal namespace (fal is the only provider with a
Expand Down
12 changes: 8 additions & 4 deletions src/react/tests/audio-element.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,12 @@ describe("audio: native sugar", () => {
expect(po.fal?.generate_audio).toBe(true);
});

test("expands into varg.fal namespace for varg gateway models", () => {
test("expands to the unified varg.audio field for varg gateway models", () => {
// NOT provider_options.fal.generate_audio: the varg API rejects unknown
// provider_options keys with a 422 on models whose fal schema lacks the
// flag. The unified `audio` field is mapped per model by the API's
// mapping_rules (rename audio → generate_audio) and silently ignored by
// models without native-audio support.
const vid = Video({
prompt: "sunset",
audio: "native",
Expand All @@ -213,9 +218,8 @@ describe("audio: native sugar", () => {
string,
Record<string, unknown>
>;
expect((po.varg?.fal as Record<string, unknown>)?.generate_audio).toBe(
true,
);
expect(po.varg?.audio).toBe(true);
expect(po.varg?.fal).toBeUndefined();
expect(vid.props.keepAudio).toBe(true);
});

Expand Down
Loading