@thinwrap/llm
@thinwrap/llm for TypeScript — one typed Chat facade over 18 LLM providers (15 OpenAI-compatible + native Anthropic, Bedrock, Gemini), plus embeddings on 11. Stateless, zero deps, BYO fetch — the in-process complement to an LLM gateway.
TypeScript constraints
- Runtime dependencies
- Zero runtime dependencies
- BYO HTTP client
- injectable fetch parameter (Node 18+ native fetch)
- Provenance
- npm provenance + Sigstore
- Minimum runtime
- Node 18.20+
- Node 18 and 20 emit a one-time ExperimentalWarning on the first fetch() invocation: "ExperimentalWarning: The Fetch API is an experimental feature. This feature could change at any time."
- The warning is informational only — fetch() in Node 18.20+ and Node 20.x is functionally identical to the stable fetch in Node 21+. Suppress with the NODE_NO_WARNINGS=1 env var or the --no-warnings flag if it clutters CI logs.
- Thinwrap does not patch around the warning, does not silence it on the consumer's behalf, and does not require a polyfill. It is surfaced exactly as Node's runtime surfaces it.
30-second install
npm install @thinwrap/llm2-minute end-to-end example
One Chat facade, every provider. Switching vendor is the provider id plus the model — the complete(input) call and theChatResult shape stay identical.
import { Chat } from '@thinwrap/llm';
const chat = new Chat('openai', { apiKey: process.env.OPENAI_API_KEY! });
const res = await chat.complete({
model: 'gpt-4o-mini',
messages: [{ role: 'user', content: 'Say hi in one word.' }],
});
console.log(res.message.content, res.usage);Switch vendor by changing the provider id + model — swap to 'anthropic', 'gemini', 'bedrock','groq', or any of the 15 OpenAI-compatible providers and pass the matching config. Native adapters normalize Anthropic, Bedrock, and Gemini onto the same ChatResult.
const chat = new Chat('anthropic', { apiKey: process.env.ANTHROPIC_API_KEY! });
// same .complete(input) shape, same ChatResultSupported providers
| Provider | Operations | Auth | README |
|---|---|---|---|
openai OpenAI | chat, embeddings | bearer | docs |
azure-openai Azure OpenAI | chat, embeddings | header | docs |
anthropic Anthropic | chat | header | docs |
gemini Google Gemini | chat | header | docs |
bedrock AWS Bedrock | chat | aws-sig-v4 | docs |
mistral Mistral | chat, embeddings | bearer | docs |
cloudflare Cloudflare Workers AI | chat, embeddings | bearer | docs |
together Together AI | chat, embeddings | bearer | docs |
fireworks Fireworks AI | chat, embeddings | bearer | docs |
deepinfra DeepInfra | chat, embeddings | bearer | docs |
openrouter OpenRouter | chat, embeddings | bearer | docs |
groq Groq | chat | bearer | docs |
deepseek DeepSeek | chat | bearer | docs |
xai xAI (Grok) | chat | bearer | docs |
perplexity Perplexity | chat | bearer | docs |
vllm vLLM | chat, embeddings | bearer | docs |
ollama Ollama | chat, embeddings | bearer | docs |
lmstudio LM Studio | chat, embeddings | bearer | docs |
Migration paths
The in-process complement to a gateway
Thinwrap LLM is a thin, in-process, zero-egress facade — it does not proxy your traffic. It normalizes the request/response shape across vendors so your code stays vendor-neutral; routing, budgets, and observability stay wherever you already run them (your own gateway or none). Provider-specific request fields ride through _passthrough; the decoded vendor body is always on result.raw.
Streaming
for await (const delta of chat.stream(input)) {
if (delta.contentDelta) process.stdout.write(delta.contentDelta);
}Embeddings
The OpenAI-float subset (11 of the 18 providers — see the table above) also exposes an Embeddings facade with the same provider-switching model.
Timeouts & cancellation
The wrapper holds no timeout of its own — it awaits whatever your injectedfetch does. Bound a hung provider by passing afetch that sets AbortSignal.timeout(…); an aborted request surfaces as a ConnectorError, same as every other failure.
Cross-language siblings
The PHP (/packages/llm-php) and Go (/packages/llm-go) variants ship the same facade shape and provider set — cross-language parity is a design goal, not a hard release gate.