Jangada AIJangada AI

Providers and API keys

Each provider lives in an adapter that translates the normalized types (Message, Completion) to the native SDK. The first LLM argument picks the adapter; the second is the model, with the name the provider uses.

Providernew LLM(...)Environment variableSDK
OpenAI"openai"OPENAI_API_KEYopenai
Azure OpenAI"azure"AZURE_OPENAI_API_KEY + AZURE_OPENAI_ENDPOINTopenai
Groq"groq"GROQ_API_KEYopenai
OpenRouter"openrouter"OPENROUTER_API_KEYopenai
DeepSeek"deepseek"DEEPSEEK_API_KEYopenai
Anthropic"anthropic"ANTHROPIC_API_KEY@anthropic-ai/sdk
Gemini"gemini"GEMINI_API_KEY (or GOOGLE_API_KEY)@google/genai
Vertex AI"vertex"GOOGLE_CLOUD_PROJECT + Google Cloud credentials@google/genai
AWS Bedrock"bedrock"AWS credentials + AWS_REGION@aws-sdk/client-bedrock-runtime
Mistral"mistral"MISTRAL_API_KEY@mistralai/mistralai
Ollama"ollama"OLLAMA_API_KEY (Ollama Cloud only)none

Groq, OpenRouter and DeepSeek speak OpenAI's chat.completions dialect, so they use the openai SDK pointed at another URL. In Python, Groq uses the groq SDK and Ollama uses the ollama SDK; in JS neither needs those packages.

Key and SDK options

Without apiKey, the adapter reads the environment variable from the table. To pass the key directly (from a secrets vault, for example):

import { LLM } from "jangada-ai";

const llm = new LLM("openai", "gpt-4o-mini", { apiKey: "sk-..." });

Precedence is apiKey > environment variable > .env (loaded by import "jangada-ai/node").

clientOptions goes straight to the SDK client constructor. That is where baseURL, timeout, the SDK's own maxRetries, headers and so on belong:

// OpenAI-compatible gateway (LiteLLM, vLLM, internal proxy...)
const local = new LLM("openai", "my-model", {
  apiKey: "gateway-token",
  clientOptions: { baseURL: "http://localhost:4000/v1", timeout: 60_000 },
});

// Anthropic behind a proxy
const claude = new LLM("anthropic", "claude-sonnet-4-5", {
  clientOptions: { baseURL: "https://proxy.company.com/anthropic" },
});

The clientOptions keys are those of each provider's SDK (baseURL in openai and @anthropic-ai/sdk, httpOptions in @google/genai, region and credentials in Bedrock). For retries, prefer the LLM's maxRetries, which understands the normalized errors and falls back afterward.

OpenRouter

A gateway to hundreds of models with one key. The model is qualified by vendor:

new LLM("openrouter", "openai/gpt-4o");
new LLM("openrouter", "anthropic/claude-sonnet-4.6", {
  clientOptions: { defaultHeaders: { "HTTP-Referer": "https://mysite.com", "X-Title": "My App" } },
  extra: { models: ["anthropic/claude-sonnet-4.6", "openai/gpt-4o"] }, // OpenRouter routing
});

Supports text, streaming, structured output, vision, tools, transcription and embeddings. webSearch() becomes OpenRouter's web plugin. Server-side MCP (mcpServers) does not exist here and throws UnsupportedError.

DeepSeek

parse() uses JSON Object mode (the API has no schema mode). There are no native tools and no transcription. Thinking mode goes through extra:

new LLM("deepseek", "deepseek-chat", {
  extra: { thinking: { type: "enabled", reasoning_effort: "high" } },
});

Azure OpenAI

model is the deployment name, not the model name. Endpoint and API version come from the environment or from clientOptions:

AZURE_OPENAI_API_KEY=...
AZURE_OPENAI_ENDPOINT=https://my-resource.openai.azure.com
OPENAI_API_VERSION=2024-10-21   # optional; this is the default
const azure = new LLM("azure", "my-gpt5-deploy", {
  profileModel: "gpt-5", // the deployment runs gpt-5: apply its profile rules
  clientOptions: { endpoint: "https://my-resource.openai.azure.com", apiVersion: "2024-10-21" },
});

To authenticate with Microsoft Entra ID, pass azureADTokenProvider in clientOptions instead of the key. Native tools and mcpServers go through Azure's v1 API (<endpoint>/openai/v1/, Responses API), so they require the endpoint to be set. For profileModel, see Parameters.

Vertex AI

Gemini on Google Cloud, through the same @google/genai with vertexai: true. It uses no API key: authentication is Google Cloud's (Application Default Credentials, gcloud auth application-default login or a service account).

const vertex = new LLM("vertex", "gemini-2.5-flash", {
  clientOptions: { project: "my-project", location: "us-central1" },
});

Without clientOptions, the project comes from GOOGLE_CLOUD_PROJECT and the region from GOOGLE_CLOUD_LOCATION. With no region, gemini-3* models go to global and the others to us-central1. Vertex inherits Gemini's profile rules. It accepts the same native tools, but does not mix a native tool with a function tool in the same call.

AWS Bedrock

Uses the Converse API, with the same payload for Claude, Llama, Mistral and Amazon Nova. Credentials follow the standard AWS chain (AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY, AWS_PROFILE, instance role), resolved by the SDK. The region comes from clientOptions.region, AWS_REGION or AWS_DEFAULT_REGION.

const bedrock = new LLM("bedrock", "us.anthropic.claude-sonnet-4-5-20250929-v1:0", {
  clientOptions: { region: "us-east-1" },
  extra: { guardrailConfig: { guardrailIdentifier: "abc123", guardrailVersion: "1" } },
});

topK and seed do not exist in Converse and are dropped. Top-level Converse fields (additionalModelRequestFields, guardrailConfig, performanceConfig, serviceTier, requestMetadata...) go through extra. Structured output uses tool forcing. Native tools only work on Amazon Nova models (webSearch() becomes nova_grounding, codeExecution() becomes nova_code_interpreter).

Mistral

Mistral's JS SDK uses camelCase and validates the payload: extra params must be camelCase (randomSeed, safePrompt), otherwise the SDK drops them. seed becomes randomSeed and topK is dropped. Besides chat, Mistral does embeddings, transcription (Voxtral) and OCR (llm.ocr(), with mistral-ocr-latest). Native tools (webSearch, codeExecution, imageGeneration, fileSearch) go through the Conversations API.

Ollama

The adapter talks HTTP directly to Ollama's native API (/api/chat, /api/embed), with no SDK. That is why it also runs on edge.

// local: http://localhost:11434, no key
const local = new LLM("ollama", "llama3.2");

// another host
const remote = new LLM("ollama", "qwen3", { clientOptions: { host: "http://gpu-01:11434" } });

// Ollama Cloud (needs OLLAMA_API_KEY)
const cloud = new LLM("ollama", "gpt-oss:120b", { clientOptions: { host: "https://ollama.com" } });

The host can also come from OLLAMA_HOST. maxTokens becomes num_predict; think, keep_alive and format go at the top of the request; other extras (num_ctx, min_p) go in options. A model that was not pulled raises NotFoundError with the hint ollama pull <model>; a stopped server raises APIConnectionError.

webSearch() and webFetch() become function tools that the adapter itself runs through the Ollama Cloud API (they require OLLAMA_API_KEY).

Structured output by provider

ProviderMechanism
OpenAI, Azurestrict json_schema response_format
Groq, OpenRouterjson_schema; if the model does not accept it, JSON Object mode
DeepSeekJSON Object mode
Anthropic, Bedrocktool forcing
Gemini, VertexresponseJsonSchema
Mistralstrict json_schema responseFormat
Ollamaformat with the JSON Schema

Usage is the same everywhere: see Structured output.

Your own provider

registerProvider(name, loader) registers your own adapter (a Provider subclass), loaded on demand like the built-in ones. availableProviders() lists the registered ones. An adapter's structure follows the Python library's: see Extending.

On this page