Every model in @dudko.dev/agent-web is an AI SDK LanguageModel. You supply
one either directly (a ready model) or by spec (ProviderModelSpec),
which the registry resolves by dynamically importing the provider's optional
peer package and fetching the API key from your CredentialStore.
{ providerType, model, baseURL?, credentialRef?, apiKey?, headers?, providerOptions? }providerType |
Peer package | Needs key | baseURL |
Notes |
|---|---|---|---|---|
openai |
@ai-sdk/openai |
yes | optional | Direct BYOK works (CORS since 2026). |
anthropic |
@ai-sdk/anthropic |
yes | optional | Browser-access header injected for you. |
google |
@ai-sdk/google |
yes | optional | Gemini; the most reliable direct BYOK path. |
openai-compatible |
@ai-sdk/openai-compatible |
yes | required | vLLM, Ollama, LM Studio, LocalAI, your proxy… |
xai |
@ai-sdk/xai |
yes | optional | Direct BYOK works. |
deepseek |
@ai-sdk/deepseek |
yes | optional | Direct BYOK works; the Flash line reads images. |
gateway |
(ships in ai) |
Vercel key | optional | Vercel AI Gateway — recommended for shared keys. |
web-llm |
@browser-ai/web-llm |
no | — | Local WebGPU model; resolved via createWebLLMModel. |
Passing a direct LanguageModel bypasses the registry entirely — use this
for Chrome/Edge built-in AI (@browser-ai/core, browserAI()),
@browser-ai/transformers-js, or any other AI SDK provider. These also answer a
browser directly with the user's key (checked October 2026): Moonshot Kimi
(@ai-sdk/moonshotai; images, no PDFs), Groq (@ai-sdk/groq), Mistral
(@ai-sdk/mistral; images), OpenRouter (@openrouter/ai-sdk-provider) and
Cerebras (@ai-sdk/cerebras — its error answers lack CORS, so a bad key reads
as a network error).
amazon-bedrock (SigV4 signing), google-vertex (ADC via google-auth-library),
and cloudflare (workers-ai-provider, Worker bindings / process.env) are not
browser-safe. Gemini is covered by google; anything else can be reached through
openai-compatible or gateway pointed at your own endpoint. Azure folds into
openai-compatible.
Two independent facts govern direct BYOK from the browser:
- Key exposure. Any key that reaches the browser is, in principle, extractable by the user and by any script on your origin. That is acceptable for the user's own key (BYOK). It is never acceptable for a shared, app-owned key.
- CORS. Many provider endpoints don't send permissive CORS headers, so a
direct
fetchfrom a browser origin fails regardless of the key.
Practical guidance, exposed programmatically via directBrowserOk(providerType):
- Direct BYOK works (checked October 2026 with a preflight from a page
origin):
google,openai,xai,deepseek,openai-compatible/gateway(your endpoint), andanthropic(the registry addsanthropic-dangerous-direct-browser-access: true). - CORS policies change:
directBrowserOkreflects the last check, and a proxybaseURLis always the fallback.
For shared keys, or any provider that blocks browser CORS, don't ship the
key to the client. Point baseURL at your own server (which injects the real
key server-side), or use the Vercel AI Gateway:
// Your proxy — the key lives on your server, not in the browser.
{ providerType: 'openai-compatible', model: 'gpt-4o-mini', baseURL: 'https://api.myapp.com/llm' }
// Vercel AI Gateway.
{ providerType: 'gateway', model: 'openai/gpt-4o-mini', credentialRef: 'vercel' }model is the default (executor). planner and synthesizer accept either a
full ProviderModelSpec/direct model, or a partial override that inherits
the base provider + credentials and changes only what it sets:
await createAgent({
model: { providerType: 'openai', model: 'gpt-4o', credentialRef: 'openai' },
planner: { model: 'gpt-4o-mini' }, // same provider + key, cheaper model
synthesizer: { // a different provider — must bring its own key
providerType: 'anthropic', model: 'claude-haiku-4-5', credentialRef: 'anthropic',
},
credentials,
})A cross-provider override without its own credentialRef/apiKey throws —
inheriting a key across vendors is almost always a misconfiguration.
Anything not covered by the fields above is spread into the provider factory,
e.g. providerOptions: { fetch: myFetch }, WebLLM engine settings, etc.
Providers are optional peers imported dynamically. If your bundler tries to
eagerly resolve or pre-bundle them, mark the ones you don't install as external
(Vite: optimizeDeps.exclude / build.rollupOptions.external; webpack:
externals). The ./mcp subpath keeps @modelcontextprotocol/sdk out of the
core bundle automatically.