Deuz SDK 2.0 est sorti — stores, guardrails, handoffs et MCP zéro-config. Nouveautés de la 2.0
Deuz SDK
Providers

OpenAI-compat hosts

Groq, Mistral, DeepSeek, Perplexity, Cohere, Ollama and the other Chat Completions hosts, plus createProviderRegistry.

Le contenu des pages de documentation est en anglais. La navigation, la recherche et l’interface suivent la langue choisie.

Every factory below speaks the OpenAI Chat Completions wire (surface: 'chat_completions'). Import from @deuz-sdk/core/providers (or the dedicated azure / bedrock subpaths).

Factories at a glance

FactoryProvider idDefault base URLNotes
createGroqgroqhttps://api.groq.com/openai/v1LPU cloud
createMistralmistralhttps://api.mistral.ai/v1La Plateforme
createDeepSeekdeepseekhttps://api.deepseek.com/v1V3 / R1 / V4 — see the V4 note
createTogethertogetherhttps://api.together.xyz/v1Open models
createOpenRouteropenrouterhttps://openrouter.ai/api/v1Multi-provider router
createCerebrascerebrashttps://api.cerebras.ai/v1Wafer-scale
createFireworksfireworkshttps://api.fireworks.ai/inference/v1Open models
createMoonshot / createKimimoonshothttps://api.moonshot.ai/v1Kimi K2 family (createKimi is an alias)
createQwenqwenhttps://dashscope.aliyuncs.com/compatible-mode/v1DashScope compatible-mode
createGLMglmhttps://open.bigmodel.cn/api/paas/v4Zhipu BigModel
createMiniMaxminimaxhttps://api.minimax.io/v1M2 family
createPerplexityperplexityhttps://api.perplexity.aiSonar — search-grounded. No /v1, and no client tool calling
createCoherecoherehttps://api.cohere.ai/compatibility/v1Command via the OpenAI compatibility endpoint
createDeepInfradeepinfrahttps://api.deepinfra.com/v1/openaiOpen models (org/Model slugs)
createNvidianvidiahttps://integrate.api.nvidia.com/v1NVIDIA NIM
createSambaNovasambanovahttps://api.sambanova.ai/v1RDU inference
createHyperbolichyperbolichttps://api.hyperbolic.xyz/v1Open models
createOllamaollamahttp://localhost:11434/v1Keyless — see Local models
createLMStudiolmstudiohttp://localhost:1234/v1Keyless — see Local models
createAzureazuredeployment-scopedSee Azure OpenAIapi-key + api-version
createBedrockbedrockMantle …/openai/v1See Amazon Bedrock — Bearer API key

Lowercase singletons (mistral, deepseek, kimi, qwen, perplexity, ollama, …) are exported unbound — prefer the create* factories with an explicit apiKey in apps.

Every factory takes the same CompatSettings: apiKey, baseURL, fetch, headers, and (2.0) capabilities — capability overrides applied to every slug that factory mints, for a host whose slugs the registry does not pin.

Which model slugs work

Any slug the host serves. A factory is a two-line descriptor builder: createGroq({...})('whatever') produces { provider: 'groq', modelId: 'whatever', surface: 'chat_completions' } and the string goes to the wire verbatim. There is no allow-list, nothing throws locally, and a slug the host does not have comes back as that host's own 404ModelNotFoundError.

What the slug does change is which row of the capability registry the call picks up. These are the slugs pinned in 2.0 — the flagship of each host, chosen so that the numbers that reach the wire (max_tokens, the reasoning switch, the structured-output strategy) are right without configuration:

Provider idPinned slugsNotes on the row
groqllama-4-maverickvision: true, 131k context
deepseekdeepseek-v3.2, deepseek-v4-flash, deepseek-v4-proV4 rows set reasoning: truesee below
mistralmistral-large-latestvision: true, 256k context
moonshotkimi-k2131k context
qwenqwen3-max262k context
glmglm-4.6200k context
minimaxminimax-m2200k context
perplexitysonar, sonar-pro, sonar-reasoning-protools: falsesee below
coherecommand-a-03-2025256k context
deepinframeta-llama/Llama-4-Maverick-17B-128E-Instruct, deepseek-ai/DeepSeek-V3.2org/Model casing is part of the slug
nvidiameta/llama-4-maverick-17b-128e-instruct, nvidia/llama-3.3-nemotron-super-49b-v1.5NIM slugs are lower-cased vendor/model
sambanovaLlama-4-Maverick-17B-128E-Instruct, Meta-Llama-3.3-70B-Instructbare CamelCase, no vendor prefix
hyperbolicQwen/Qwen3-235B-A22B-Instruct, moonshotai/Kimi-K2-Instructorg/Model
together, openrouter, cerebras, fireworksnoneRouter/aggregator catalogs turn over too fast to pin honestly
ollama, lmstudionone, deliberatelyThe slug is whatever you pulled — see Local models

Every row on the four 2.0 hosts (deepinfra, nvidia, sambanova, hyperbolic) carries a verify slug at publish marker in the source, for exactly the reason stated there: these vendors rename often, a stale key costs nothing (it simply stops matching and you get the fallback), but a wrong key would hand out capabilities the model does not have.

What an unpinned slug gets

Not an error — new models ship constantly and a registry that threw would be a release blocker for every one of them. An unknown slug takes the conservative fallback row and one warning:

const result = await generateText({ model: together('some-new-model'), prompt: 'hi' });
result.warnings; // [{ type: 'unknown-model', message: "Unknown model 'some-new-model' — …" }]
FieldFallback valueWhat it costs you
maxOutput4096This is the one that matters. It becomes the request's max_tokens, so a long answer is truncated.
contextWindow128_000Feeds compaction sizing — too small truncates history early, too large summarizes too late.
toolsfalseReported only. No adapter reads it; tool calling works regardless.
reasoningfalseeffort is dropped rather than sent. A reasoning model behaves as its own default.
structuredOutputfalsegenerateObject uses the tool strategy instead of json_schema.
vision, caching, …falseReported only.

The fix is one line, and it is the same line whether the host is Together, an internal gateway, or Ollama — state what you know at the factory:

const together = createTogether({
  apiKey: process.env.TOGETHER_API_KEY!,
  capabilities: { maxOutput: 32_000, contextWindow: 131_072, reasoning: true },
});

Nothing is validated: an override is a claim about your model, not a request the SDK can check. See capabilities for the per-factory/per-call precedence.

The registry is keyed by slug alone, not by (provider, slug)

getCapabilities looks the model id up in one flat table. Two hosts serving the same string therefore share one row — which is usually exactly right on an aggregator (openrouter('kimi-k2') inherits the pinned Moonshot row instead of the 4096-token fallback), and occasionally is not.

The case to watch is a slug pinned for a first-party provider being served by someone else. together('gpt-5.5') would pick up the OpenAI row and send max_tokens: 128000; a slug pinned on the OpenAI Responses surface (gpt-5.4, o4-mini) carries samplingRestrictions: true, which makes the Chat Completions adapter send max_completion_tokens and silently drop temperature/topP — while the call itself still goes out on the Chat Completions wire, because dispatch reads the descriptor's surface, never the row's.

In practice most aggregators prefix their slugs (openai/gpt-5.5, meta-llama/…), and a prefixed string can never collide. When it does collide and the row is wrong, a per-factory capabilities override fixes it — that merge happens after the row is chosen.

DeepSeek V4 always thinks

deepseek-v4-flash and deepseek-v4-pro reason on every call — there is no switch — and that has two consequences worth knowing before you debug something that is not a bug. Both were confirmed against the live API; the assertions live in test/live/deepseek.live.test.ts.

Reasoning shares the output budget. reasoning_content is billed and counted as output, so a maxOutputTokens sized for the visible reply returns an empty string rather than an error — the thinking pass consumed the allowance. Give it room:

const result = await generateText({
  model: deepseek('deepseek-v4-flash'),
  prompt: 'Reply with the single word: pong',
  maxOutputTokens: 512, // 16 would come back empty
});
// result.usage.reasoningTokens tells you what the thinking cost

Reasoning arrives as reasoning-delta parts, never spliced into textStream — the two channels stay apart.

generateObject does not work on V4. It has two strategies and V4 rejects both: json sends response_format: json_schema, which V4 answers with "This response_format type is unavailable now", and tool forces tool_choice, which it answers with "Thinking mode does not support this tool_choice".

Ordinary (unforced) tool calling is unaffected, so the agentic loop works normally — only the forced-coercion path is closed. Until a json_object strategy exists, ask for the shape in the prompt:

const { text } = await generateText({
  model: deepseek('deepseek-v4-flash'),
  prompt: 'Return ONLY JSON matching {"capital": string} for the capital of Turkey.',
  maxOutputTokens: 512,
});
const parsed = JSON.parse(text);

The wire accepts max_tokens up to 65,536, but the registry keeps the default at 8,192: one runaway thinking pass at the ceiling is a real bill. Raise it per call when an answer needs the room.

Perplexity Sonar does no client tool calling

Perplexity does no client tool calling

Sonar's search runs server-side; the API has no tools array, so the registry rows pin tools: false and structuredOutput: false, and a tool loop against sonar* will not call anything. Perplexity's citations array is a top-level response field that the Chat Completions wire has no slot for, so it is not surfaced as a canonical part. Its base URL is also the one exception with no /v1 segment — the wire path is https://api.perplexity.ai/chat/completions.

Examples

import { streamChat } from '@deuz-sdk/core';
import {
  createMistral,
  createDeepSeek,
  createQwen,
  createKimi,
  createProviderRegistry,
} from '@deuz-sdk/core/providers';

const mistral = createMistral({ apiKey: process.env.MISTRAL_API_KEY! });
const deepseek = createDeepSeek({ apiKey: process.env.DEEPSEEK_API_KEY! });
const qwen = createQwen({ apiKey: process.env.DASHSCOPE_API_KEY! });
const kimi = createKimi({ apiKey: process.env.MOONSHOT_API_KEY! }); // same as createMoonshot

const registry = createProviderRegistry({
  mistral,
  deepseek,
  qwen,
  moonshot: kimi,
});

const model = registry.model('mistral:mistral-large-latest');
// or: deepseek:deepseek-v3.2 · qwen:qwen3-max · moonshot:kimi-k2

const result = streamChat({
  model,
  messages: [{ role: 'user', content: 'ping' }],
});

Pinned flagship slugs (tools / context) live in the model registry: e.g. mistral-large-latest, deepseek-v3.2, qwen3-max, kimi-k2, and 2.0's sonar / sonar-pro / sonar-reasoning-pro and command-a-03-2025. Slugs on DeepInfra, NVIDIA NIM, SambaNova and Hyperbolic rename often, so only one or two representative rows are pinned per host — everything else takes the conservative fallback, which capabilities (below) corrects.

createOpenAICompatible()

For any host that speaks the OpenAI wire but has no named factory above — a self-hosted vLLM or llama.cpp, an internal company gateway. Before 1.9 the workaround was to point an unrelated factory somewhere else (createGroq({ baseURL: 'https://llm.internal/v1' })), which then resolved keys, pricing, registry rows and every log line under the wrong provider id.

gateway.ts
import { createOpenAICompatible } from '@deuz-sdk/core/providers';

const gateway = createOpenAICompatible({ id: 'vllm', baseURL: 'http://gpu-box:8000/v1' });
const model = gateway('gpt-oss-120b');
// → { provider: 'vllm', modelId: 'gpt-oss-120b', surface: 'chat_completions' }
interface OpenAICompatibleSettings extends CompatSettings {
  /** Provider id used for key/baseURL lookup, the registry, pricing and every log line. Required. */
  id: string;
  /** Wire dialect. Default 'chat_completions'. */
  surface?: 'chat_completions' | 'responses';
  /** Auth header style. Default 'bearer'. */
  authHeader?: 'bearer' | 'api-key';
  /** Keyless host: substitute a placeholder instead of throwing when no key is found (2.0). */
  apiKeyOptional?: boolean;
  // plus CompatSettings: apiKey, baseURL, fetch, headers, capabilities
}

function createOpenAICompatible(settings: OpenAICompatibleSettings): Provider;

Key and base-URL resolution use the same precedence chain as every named factory — deps.keyProvider > factory apiKey > createClient({ apiKeys: { [id]: … } }) > AuthenticationError. id creates no bypass.

apiKeyOptional (2.0) changes only the last step: when every link came up empty, a placeholder bearer token is sent instead of throwing. A real key from any link still wins. Use it for an unauthenticated server on your own network — Local models covers the whole story, including createOllama / createLMStudio, which set it for you.

Two eager errors instead of a confusing 401 later:

  • an empty id, and
  • surface: 'responses' combined with authHeader: 'api-key' — the Responses adapter always sends Authorization: Bearer. Use surface: 'chat_completions', or pass the header yourself via headers: { 'api-key': … }.

A custom `id` has no default base URL

Core never reads env vars, so there is no OPENAI_BASE_URL-style fallback. Pass baseURL here or via createClient({ baseUrls: { [id]: … } }), otherwise the call fails with an InvalidRequestErrorbaseURL is effectively required.

capabilities for a host the registry does not know

An unknown slug does not throw; it falls back to a conservative row whose maxOutput is 4096, reasoning is false and structuredOutput is false. Set what you know, per factory (this option is on CompatSettings, so every named factory takes it too) or per call:

const gateway = createOpenAICompatible({
  id: 'internal-gateway',
  baseURL: 'https://llm.internal/v1',
  apiKey: process.env.GATEWAY_KEY!,
  capabilities: { maxOutput: 32_000, reasoning: true, structuredOutput: true },
});

The per-call capabilities option wins over the factory value. See capabilities for what it can and cannot change.

Sur cette page