Connection strings for LLMs. Like database URLs, but for AI.
Parse, normalize, validate, and build portable llm:// URLs across AI providers.
Install · Quick Start · Examples · Supported Providers · API
llm://openai/gpt-5.5?effort=medium&maxTokens=2000
llm://anthropic/claude-opus-4-8?cache=5m&effort=max
llm://bedrock/anthropic.claude-sonnet-4-5-20250929-v1:0?temp=0.5&max=4096
llm://openrouter/anthropic/claude-sonnet-4-5?temp=0.7&max=2000Every LLM provider invented slightly different names for the same knobs:
max_tokens vs maxOutputTokens vs maxTokens, top_p vs topP vs p,
stop vs stop_sequences vs stopSequences.
llm-strings gives you one portable format for model configuration. Put the whole config in an env var, normalize it to the provider's API shape, validate it before you spend tokens, and build UI controls from the same metadata.
Based on the LLM Connection Strings
proposal by Dan Levy. See the draft IETF RFC for llm://.
- One config string for host, model, credentials, and generation params.
- Provider-native output from provider-agnostic input like
temp=0.7&max=2000. - Early validation for ranges, mutual exclusions, Bedrock model-family rules, and OpenAI reasoning-family normalization.
- Short aliases like
openai,anthropic,google,bedrock,groq, andopenrouter, with env overrides for private or regional endpoints. - AI SDK providerOptions generation for provider-specific settings.
- Zero runtime dependencies, ESM + CJS, full TypeScript declarations, and sub-path imports for smaller bundles.
npm install llm-stringspnpm add llm-stringsyarn add llm-stringsbun add llm-stringsimport { build, normalize, parse, validate } from "llm-strings";
const input = "llm://openai/gpt-4o?temp=0.7&max=2000&topp=0.9";
const parsed = parse(input);
// {
// raw: "llm://openai/gpt-4o?temp=0.7&max=2000&topp=0.9",
// host: "api.openai.com",
// hostAlias: "openai",
// model: "gpt-4o",
// params: { temp: "0.7", max: "2000", topp: "0.9" }
// }
const { config, provider } = normalize(parsed);
// provider: "openai"
// config.params: { temperature: "0.7", max_tokens: "2000", top_p: "0.9" }
const issues = validate("llm://anthropic/claude-sonnet-4-5?temp=0.7&top_p=0.9");
// [
// {
// param: "temperature",
// value: "0.7",
// severity: "error",
// message: "Cannot specify both \"temperature\" and \"top_p\" for Anthropic models."
// }
// ]
const url = build({
host: "anthropic",
model: "claude-sonnet-4-5",
params: { temp: "0.7", max: "4096" },
});
// "llm://api.anthropic.com/claude-sonnet-4-5?temp=0.7&max=4096"llm://[label[:apiKey]@]host/model[?params]| Part | Required | Description | Example |
|---|---|---|---|
label |
No | App name or environment label | worker |
apiKey |
No | API key in the password position | sk-proj-abc123 |
host |
Yes | Provider host or short alias | api.openai.com, openai |
model |
Yes | Model name, route, or provider ID | gpt-5.5 |
params |
No | Query-string generation settings | effort=medium&max=2000 |
Connection strings can include secrets, so treat values containing apiKey like
credentials: store them in secret managers or env vars, and avoid logging them.
LLM_URL="llm://worker:sk-proj-abc123@openai/gpt-4o?temp=0.7&max=2000"import { normalize, parse } from "llm-strings";
const { config, provider } = normalize(parse(process.env.LLM_URL!));
await fetch(`https://${config.host}/v1/chat/completions`, {
method: "POST",
headers: {
Authorization: `Bearer ${config.apiKey}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
model: config.model,
messages: [{ role: "user", content: "Hello!" }],
...Object.fromEntries(
Object.entries(config.params).map(([key, value]) => [
key,
Number.isNaN(Number(value)) ? value : Number(value),
]),
),
}),
});
console.log(provider); // "openai"# OpenAI
LLM_URL="llm://openai/gpt-4o?temp=0.7&max=2000"
# Anthropic
LLM_URL="llm://anthropic/claude-sonnet-4-5?temp=0.7&max=2000"
# Google
LLM_URL="llm://google/gemini-3.5-flash?temp=0.7&max=2000"
# Bedrock
LLM_URL="llm://bedrock/us.anthropic.claude-sonnet-4-5-20250929-v1:0?temp=0.7&max=2000"import { normalize, parse } from "llm-strings";
for (const value of [
"llm://openai/gpt-4o?temp=0.7&max=2000",
"llm://anthropic/claude-sonnet-4-5?temp=0.7&max=2000",
"llm://google/gemini-3.5-flash?temp=0.7&max=2000",
]) {
const { config, provider } = normalize(parse(value));
console.log(provider, config.params);
}
// openai { temperature: "0.7", max_tokens: "2000" }
// anthropic { temperature: "0.7", max_tokens: "2000" }
// google { temperature: "0.7", maxOutputTokens: "2000" }import { normalize, parse } from "llm-strings";
const { config, provider } = normalize(
parse("llm://groq/llama-3.3-70b?max=1000"),
);
config.host; // "api.groq.com"
provider; // "groq"Override any alias at deploy time:
LLM_STRINGS_OPENAI_HOST="regional.openai.example.com"
LLM_STRINGS_BEDROCK_HOST="https://bedrock-runtime.us-west-2.amazonaws.com/model"The alternate form LLM_STRINGS_HOST_OPENAI is also supported. Overrides may
include a scheme or path; only the host portion is used.
import { validate } from "llm-strings";
validate("llm://openai/gpt-4o?temp=3.0");
// [{ param: "temperature", message: "\"temperature\" must be <= 2, got 3", ... }]
validate("llm://openai/gpt-5.5?temp=0.7&max=2000");
// [] — temperature is a known unsupported reasoning-family param, so normalize() drops it
validate("llm://fal/fal-ai/flux-pro?future_model_param=1");
// [] — unknown params pass through by default for model-specific schemas
validate("llm://fal/fal-ai/flux-pro?future_model_param=1", { strict: true });
// [{ param: "future_model_param", severity: "error", message: "Unknown param..." }]import { normalize, parse } from "llm-strings";
const { changes } = normalize(
parse("llm://google/gemini-3.5-flash?temp=0.7&max=2000&topp=0.9"),
{ verbose: true },
);
for (const change of changes) {
console.log(`${change.from} -> ${change.to} (${change.reason})`);
}
// temp -> temperature (alias: "temp" -> "temperature")
// max -> max_tokens (alias: "max" -> "max_tokens")
// max_tokens -> maxOutputTokens (google uses "maxOutputTokens" instead of "max_tokens")
// topp -> top_p (alias: "topp" -> "top_p")
// top_p -> topP (google uses "topP" instead of "top_p")The AI SDK adapter lives on a separate sub-path so you only load it when you need it:
const { createAiSdkProviderOptions } = await import("llm-strings/ai-sdk");
const { providerOptions } = createAiSdkProviderOptions(
"llm://anthropic/claude-sonnet-4-5?cache=1h&effort=max",
);
// {
// anthropic: {
// cacheControl: { type: "ephemeral", ttl: "1h" },
// effort: "max"
// }
// }Common generation settings like temperature, top-p, and max output tokens
belong on the AI SDK call itself. The helper emits provider-specific options
such as Anthropic cache control, Bedrock cache points, OpenAI reasoning options,
Mistral safePrompt, OpenRouter routing, and Vercel AI Gateway routing,
including options such as order, sort=ttft, and caching=auto.
import { CANONICAL_PARAM_SPECS, PROVIDER_META } from "llm-strings/providers";
PROVIDER_META.map(({ id, name, host, color }) => ({ id, name, host, color }));
// [{ id: "openai", name: "OpenAI", host: "api.openai.com", color: "#10a37f" }, ...]
CANONICAL_PARAM_SPECS.anthropic.temperature;
// {
// type: "number",
// min: 0,
// max: 1,
// default: 0.7,
// description: "Controls randomness"
// }llm-strings ships provider detection, host aliases, metadata, and parameter
normalization for the major LLM provider shapes. Chat-compatible providers get
canonical parameter mapping and validation; media and audio providers are
available for detection, metadata, aliases, and flexible AI SDK providerOptions.
| Category | Providers |
|---|---|
| Core chat + reasoning | OpenAI, Azure OpenAI, Anthropic, Google AI Studio, Google Vertex AI, Mistral, Cohere, AWS Bedrock, OpenRouter, Vercel AI Gateway |
| OpenAI-compatible APIs | xAI, Groq, DeepInfra, Together.ai, Fireworks, DeepSeek, Moonshot AI, Perplexity, Alibaba DashScope, Cerebras, Baseten, Hugging Face |
| Media, audio, and flexible | Fal, Black Forest Labs, Replicate, Prodia, Luma, ByteDance, Kling AI, ElevenLabs, AssemblyAI, Deepgram, Gladia, LMNT, Hume, Rev.ai |
| Extra aliases and endpoints | aistudio, vertex, grok, bfl, dashscope, alibabacloud, togetherai, fireworksai, moonshot, wandb, weightsandbiases, baidu, qianfan, venice, parasail, novita, atlascloud, xiaomi, minimax |
| Provider ID | Default host | Param style |
|---|---|---|
openai |
api.openai.com |
snake_case |
azure |
models.inference.ai.azure.com |
OpenAI-compatible |
anthropic |
api.anthropic.com |
snake_case |
google |
generativelanguage.googleapis.com |
camelCase |
google-vertex |
aiplatform.googleapis.com |
camelCase |
mistral |
api.mistral.ai |
snake_case |
cohere |
api.cohere.com |
mixed |
bedrock |
bedrock-runtime.us-east-1.amazonaws.com |
camelCase |
openrouter |
openrouter.ai |
OpenAI-compatible |
vercel |
gateway.ai.vercel.app |
OpenAI-compatible |
xai |
api.x.ai |
OpenAI-compatible |
groq |
api.groq.com |
OpenAI-compatible |
fal |
fal.run |
flexible |
deepinfra |
api.deepinfra.com |
OpenAI-compatible |
black-forest-labs |
api.bfl.ai |
flexible |
together |
api.together.xyz |
OpenAI-compatible |
fireworks |
api.fireworks.ai |
OpenAI-compatible |
deepseek |
api.deepseek.com |
OpenAI-compatible |
moonshotai |
api.moonshot.ai |
OpenAI-compatible |
perplexity |
api.perplexity.ai |
OpenAI-compatible |
alibaba |
dashscope-intl.aliyuncs.com |
OpenAI-compatible |
cerebras |
api.cerebras.ai |
OpenAI-compatible |
replicate |
api.replicate.com |
flexible |
prodia |
api.prodia.com |
flexible |
luma |
api.lumalabs.ai |
flexible |
bytedance |
ark.cn-beijing.volces.com |
flexible |
kling |
api.klingai.com |
flexible |
elevenlabs |
api.elevenlabs.io |
flexible |
assemblyai |
api.assemblyai.com |
flexible |
deepgram |
api.deepgram.com |
flexible |
gladia |
api.gladia.io |
flexible |
lmnt |
api.lmnt.com |
flexible |
hume |
api.hume.ai |
flexible |
revai |
api.rev.ai |
flexible |
baseten |
api.baseten.co |
OpenAI-compatible |
huggingface |
api-inference.huggingface.co |
OpenAI-compatible |
Use short, memorable query params. normalize() expands them first, then maps
them to provider-native names.
| Shorthand | Canonical |
|---|---|
temp |
temperature |
max, max_out, max_output, max_output_tokens, maxTokens, maxOutputTokens, max_completion_tokens |
max_tokens |
topp, topP, nucleus |
top_p |
topk, topK |
top_k |
freq, freq_penalty, frequencyPenalty, repetition_penalty |
frequency_penalty |
pres, pres_penalty, presencePenalty |
presence_penalty |
stop_sequences, stopSequences, stop_sequence |
stop |
random_seed, randomSeed |
seed |
candidateCount, candidate_count, num_completions |
n |
reasoning, reasoning_effort |
effort |
cache_control, cacheControl, cachePoint, cache_point |
cache |
import { normalize, parse } from "llm-strings";
normalize(parse("llm://anthropic/claude-sonnet-4-5?max=4096&cache=true")).config
.params;
// { max_tokens: "4096", cache_control: "ephemeral" }
normalize(parse("llm://anthropic/claude-sonnet-4-5?max=4096&cache=5m")).config
.params;
// { max_tokens: "4096", cache_control: "ephemeral", cache_ttl: "5m" }
normalize(
parse("llm://bedrock/anthropic.claude-sonnet-4-5-20250929-v1:0?cache=1h"),
).config.params;
// { cache_control: "ephemeral", cache_ttl: "1h" }Caching currently normalizes for Anthropic and supported Bedrock models. For
Anthropic, cache=5m and cache=1h are both supported. For Bedrock, support is
model-family specific and currently covers Claude and Amazon Nova models. For
providers where caching is automatic, unsupported, or provider-specific in a way
that should not be represented as a generation param, cache is dropped during
normalization.
import { build, parse } from "llm-strings/parse";
import { normalize } from "llm-strings/normalize";
import { validate } from "llm-strings/validate";
import { createAiSdkProviderOptions } from "llm-strings/ai-sdk";
import {
ALIASES,
CANONICAL_PARAM_SPECS,
HOST_ALIASES,
PARAM_SPECS,
PROVIDER_META,
PROVIDER_PARAMS,
detectProvider,
resolveHostAlias,
} from "llm-strings/providers";All sub-paths ship ESM + CJS with full type declarations.
Parses an llm:// connection string into structured config. Throws when the
scheme is not llm://.
Builds an llm:// connection string from a config object. This is the inverse
of parse().
Normalizes params for the detected provider:
- Expands shorthand aliases such as
temp->temperature. - Maps canonical names to provider-specific names such as
max_tokens->maxOutputTokensfor Google. - Normalizes cache values such as
cache=5m->cache_control=ephemeralandcache_ttl=5m. - Adjusts OpenAI reasoning-family params such as
max_tokens->max_completion_tokensand drops known unsupported sampling params.
Pass { verbose: true } to get a changes array that documents each
transformation.
Parses, normalizes, and validates a connection string. Returns [] when the
config is valid. Checks include type correctness, numeric ranges, enum values,
unknown providers, Anthropic temperature + top_p mutual exclusion,
OpenAI reasoning-family normalization, and Bedrock model-family rules. Unknown
params are allowed by default so new model-specific schemas can pass through.
Pass { strict: true } to report unknown providers or unknown params as
errors.
Creates AI SDK providerOptions from a string, parsed config, or normalize
result. Import from llm-strings/ai-sdk.
Import from llm-strings/providers:
| Export | Description |
|---|---|
detectProvider(host) |
Detects provider from hostname. |
resolveHostAlias(host) |
Expands aliases and applies LLM_STRINGS_*_HOST overrides. |
detectBedrockModelFamily() |
Detects Anthropic, Meta, Amazon, Mistral, Cohere, or AI21 Bedrock families. |
detectGatewaySubProvider() |
Extracts provider prefix from gateway models like anthropic/claude.... |
isReasoningModel(model) |
Detects OpenAI GPT-5 and o-series reasoning models, including gateway-prefixed names. |
isGatewayProvider(provider) |
Returns true for openrouter and vercel. |
canHostOpenAIModels(provider) |
Returns true for providers that need OpenAI reasoning-model checks. |
bedrockSupportsCaching(model) |
Returns true for Bedrock Claude and Nova prompt caching support. |
| Export | Description |
|---|---|
ALIASES |
Shorthand -> canonical param name mapping. |
HOST_ALIASES |
Short provider host aliases -> canonical API hosts. |
PROVIDER_PARAMS |
Canonical -> provider-specific param names, per provider. |
PARAM_SPECS |
Validation rules keyed by provider-specific param name. |
REASONING_MODEL_UNSUPPORTED |
Canonical params unsupported by OpenAI reasoning models. |
PROVIDER_META |
Provider metadata for UI integrations. |
CANONICAL_PARAM_SPECS |
Canonical param specs per provider, useful for building forms and settings UIs. |
import type {
LlmConnectionConfig,
NormalizeChange,
NormalizeOptions,
NormalizeResult,
ValidateOptions,
ValidationIssue,
} from "llm-strings";
import type {
BedrockModelFamily,
CanonicalParamSpec,
ParamSpec,
Provider,
ProviderMeta,
} from "llm-strings/providers";pnpm install
pnpm test
pnpm run build
pnpm run lintThis package is intentionally small: pure TypeScript, zero runtime dependencies, and focused tests for parsing, normalization, validation, provider metadata, Bedrock behavior, gateway behavior, and AI SDK providerOptions.
Issues and pull requests are welcome. Good contributions include new provider aliases, provider-specific validation rules, improved normalization mappings, AI SDK providerOptions coverage, docs fixes, and real-world edge cases.
MIT © Dan Levy