genmagic-ai-provider
v0.1.0
Published
GenMagic provider for the Vercel AI SDK: one API key for text, image, speech and video models (Veo, Kling, Seedance, Wan, FLUX, Nano Banana, GPT Image and more) with generateText, generateImage, generateSpeech and generateVideo.
Maintainers
Readme
genmagic-ai-provider
GenMagic provider for the Vercel AI SDK. One API key and one pay-as-you-go balance for more than 450 models: text, images, speech and video, including Veo, Kling, Seedance, Wan, FLUX, Nano Banana and GPT Image. Each model's price is public before you run it.
It works with generateText, streamText, generateImage, generateSpeech and experimental_generateVideo.
Install
npm install genmagic-ai-provider aiNeeds ai 7.0.50 or newer and Node 22 or newer. The package is ESM.
Get a key (a free key with starter credit takes a minute): https://genmagic.co/get-api-key?utm_source=ai-sdk&utm_medium=package
export GENMAGIC_API_KEY=...Or pass it yourself. The key is read when a call is made, so importing the package never fails without one.
import { createGenMagic } from 'genmagic-ai-provider';
const genmagic = createGenMagic({ apiKey: process.env.MY_KEY });Text
import { generateText } from 'ai';
import { genmagic } from 'genmagic-ai-provider';
const { text } = await generateText({
model: genmagic('anthropic/claude-haiku-5.5'),
prompt: 'Write a one-line tagline for a coffee subscription.',
});Text goes through GenMagic's OpenAI-compatible /chat/completions, built on @ai-sdk/openai-compatible, so streaming, tools, reasoning and usage work as they do with any OpenAI-style model. Structured output (Output.object) is sent as a JSON schema; set supportsStructuredOutputs: false in createGenMagic to use plain JSON mode instead.
Images
import { writeFile } from 'node:fs/promises';
import { generateImage } from 'ai';
import { genmagic } from 'genmagic-ai-provider';
const { image } = await generateImage({
model: genmagic.image('google/gemini-nano-banana-2.1'),
prompt: 'A red fox in snow, 35mm photo',
aspectRatio: '16:9',
});
await writeFile('fox.png', image.uint8Array);GenMagic images come in 1024x1024, 1792x1024 and 1024x1792. aspectRatio is mapped to the nearest one and you get a warning when it is not exact; size is sent as you give it. Pass a picture in the prompt (prompt: { text, images: [...] }) to transform it. Up to four images per call.
Speech
import { writeFile } from 'node:fs/promises';
import { generateSpeech } from 'ai';
import { genmagic } from 'genmagic-ai-provider';
const { audio } = await generateSpeech({
model: genmagic.speech('openai/gpt-audio-mini'),
text: 'Welcome to the show.',
voice: 'alloy',
});
await writeFile(`welcome.${audio.format}`, audio.uint8Array);GenMagic voices the text exactly as written. speed, instructions, language and outputFormat are not supported and come back as warnings.
Video
import { writeFile } from 'node:fs/promises';
import { experimental_generateVideo as generateVideo } from 'ai';
import { genmagic } from 'genmagic-ai-provider';
const { video } = await generateVideo({
model: genmagic.video('alibaba/wan-3.0'),
prompt: 'Waves at sunset, slow drone shot',
aspectRatio: '16:9',
duration: 5,
poll: { intervalMs: 5000, timeoutMs: 15 * 60_000 },
});
await writeFile('waves.mp4', video.uint8Array);A video is a job. The provider starts it (POST /videos answers at once), then the AI SDK polls it until it is done. Nothing is charged until the clip completes. To start now and collect later from another process, use experimental_startVideo and experimental_getVideoStatus; the operation is plain JSON ({ "id": "..." }).
Supported: aspectRatio (16:9, 9:16, 1:1, or adaptive to follow the opening image), duration in whole seconds, resolution (1280x720 becomes 720p), generateAudio, an opening image (prompt: { image, text } or a first_frame in frameImages), a last_frame, and image inputReferences. fps, seed and video references are not supported and come back as warnings. Which model supports which setting is in the catalog below.
A failed start is not retried, except a 429. GenMagic does not deduplicate starts, so a retry after a lost connection could start and bill a second clip.
Find models and prices
Browse https://genmagic.co/models, or ask the API. The catalog needs no key.
curl -s "https://genmagic.co/api/v1/models?category=video" \
| jq -r '.data[] | "\(.id) \(.pricing.video.usd_per_second)"'
curl -s "https://genmagic.co/api/v1/models?category=image" \
| jq -r '.data[] | "\(.id) $\(.pricing.est_per_generation_usd)"'category is text, image, audio or video. Speech models are the audio ones with audio_kind speech. Each entry lists its capabilities (reasoning, vision, tools, frame_images, references) and its pricing. The full API is described at https://genmagic.co/openapi.json.
What a call cost
GenMagic returns the cost of each image, speech and video call in US cents, and the balance left.
// Image: per call
result.calls[0].providerMetadata.genmagic; // { images: [...], costCents: 5.8, creditsRemainingCents: 194.2 }
// Speech
speech.providerMetadata.genmagic; // { mediaUrl, costCents, creditsRemainingCents }
// Video: when the clip is done
video.providerMetadata.genmagic; // { videoId, videos: [{ url }], costCents, creditsRemainingCents }A field is left out when GenMagic did not send it. Text is billed per token at the catalog price and GenMagic sends no per-call cost on chat; token counts are in usage. GET /api/v1/usage returns your balance and spend.
Errors
When GenMagic refuses a call, you get an APICallError with GenMagic's own message: a wrong key, too little balance (it says how to top up), a model's content refusal, a rate limit. The status is on error.statusCode and GenMagic's code on error.data.error.code. Without a key, the call fails before any request is sent and the message says where to get one.
A failed video render arrives as a normal error from generateVideo with the reason GenMagic gave.
Provider options
Everything under providerOptions.genmagic that this package does not know is sent to GenMagic unchanged, so a field GenMagic adds later works without an upgrade.
| Model | Option | Use |
| --- | --- | --- |
| text | reasoningEffort | Thinking effort for a reasoning model |
| image | type: 'logo' | Steer toward a brand mark |
| image | responseFormat: 'url' | Download a hosted image instead of receiving base64. The hosted link is kept in image.providerMetadata.genmagic.url |
| video | resolution: '720p' | GenMagic's own resolution name, wins over the SDK resolution |
Provider registry
import { createProviderRegistry } from 'ai';
import { genmagic } from 'genmagic-ai-provider';
const registry = createProviderRegistry({ genmagic });
registry.languageModel('genmagic:anthropic/claude-haiku-5.5');
registry.imageModel('genmagic:google/gemini-nano-banana-2.1');
registry.speechModel('genmagic:openai/gpt-audio-mini');
registry.videoModel('genmagic:alibaba/wan-3.0');GenMagic has no embedding models.
Settings
createGenMagic({ apiKey, baseURL, headers, fetch, supportsStructuredOutputs })
apiKey: defaults toGENMAGIC_API_KEY. A string, or a function that returns one.baseURL: defaults tohttps://genmagic.co/api/v1.headers: extra headers for every call.fetch: your own fetch, for logging or tests.
Every call carries X-GenMagic-Client: ai-sdk-provider/<version> so GenMagic can tell traffic through this package apart from your other API use.
AI SDK 6
This package targets AI SDK 7 (provider specification v4). On AI SDK 6, @ai-sdk/openai-compatible 2 works for text and images:
import { createOpenAICompatible } from '@ai-sdk/openai-compatible';
const genmagic = createOpenAICompatible({
name: 'genmagic',
baseURL: 'https://genmagic.co/api/v1',
apiKey: process.env.GENMAGIC_API_KEY,
includeUsage: true,
});Links
- Developer docs: https://genmagic.co/developers?utm_source=ai-sdk&utm_medium=package
- Models and prices: https://genmagic.co/models
- API reference: https://genmagic.co/openapi.json
- Support: [email protected]
MIT
