Open source · MIT · TypeScript
AI Bridge
Call any model provider through one provider-neutral format. OpenAI, Anthropic and Ollama are built in, and your own providers and dialects plug in beside them. Write against one baseline, swap providers without rewriting call sites, and get responses back in whatever shape your code expects.
What It Does
Every model provider ships its own SDK with its own request and response types. Code that starts on one provider ends up shaped around it, and adding a second one means a translation layer that nobody wants to own. AI Bridge is that layer, written once and typed end to end.
Providers speak only a baseline format called the bridge dialect. Every completion comes back as a canonical BridgeResponse, carrying the full output, inclusive token usage, timing, and the untouched native response. When a caller needs a specific shape, res.toDialect("anthropic") or res.toDialect("openai") projects it, with the return type inferred from the dialects you registered.
AI Bridge does not choose models for you. You name the provider and model on each call, and the bridge handles the translation. Nothing is global: each bridge instance owns its providers and dialects, both are injected at startup, and provider names are inferred from the objects you pass in. Hosted SDKs load lazily on first use.
Quick Example
Register the providers you use, send canonical input, and convert the result as needed.
import {
createBridge,
createBuiltInProviders,
openaiDialect,
anthropicDialect,
} from "@productivehub/ai-bridge";
const bridge = createBridge({
providers: createBuiltInProviders({
ollama: { baseURL: "http://localhost:11434" },
}),
dialects: { openai: openaiDialect, anthropic: anthropicDialect },
});
const res = await bridge.complete({
provider: "ollama",
model: "llama3.2",
input: {
maxOutputTokens: 1024,
messages: [{ role: "user", content: "Summarise this repo in one line." }],
},
});
res.output; // canonical response
res.usage; // token counts, cache and reasoning details
res.raw; // untouched provider response
res.toDialect("anthropic"); // Anthropic.Messages.Message
res.toDialect("openai"); // OpenAI ChatCompletion The OpenAI and Anthropic dialects also accept native input, so existing request payloads can be sent to a different provider without being rewritten first. The response stays canonical regardless of the input dialect.
The package is ESM, requires Node.js 22 or later, and is not yet published to npm. For now, build it from a checkout of the repository.
Providers and Model Discovery
Four providers ship with AI Bridge. Each one is registered only when its key or base URL is configured, and each supports bridge.listModels() for querying its catalog.
| Provider | Credential | Model discovery |
|---|---|---|
| OpenAI | OPENAI_API_KEY | Native models endpoint |
| Anthropic | ANTHROPIC_API_KEY | Native models endpoint, paged automatically |
| Ollama | None required locally | Installed models via /api/tags |
| Ollama Cloud | OLLAMA_CLOUD_API_KEY | Cloud catalog via /api/tags |
Model entries can carry optional cost metadata (input, output, cached input, cache write, and per-request rates) stored as integer amounts in the currency's smallest unit. Unknown rates are omitted rather than guessed.
Extend It: Your Own Providers and Dialects
The built-ins are a starting point, not a boundary. AI Bridge has no central list of providers or dialects to edit and no global registry to patch. You pass your own implementations to createBridge, and their names and types flow through the rest of the API.
Custom providers
A provider implements one method: take a canonical request, call your API, and return canonical output alongside the native response. Model discovery is optional through listModels().
class MyProvider implements ProviderAdapter {
async complete(req: ProviderRequest): Promise<ProviderResponse> {
// Call your API and map its reply into BridgeOutput.
return callMyAPI(req);
}
}
const bridge = createBridge({
providers: { "my-server": new MyProvider() },
});
You can also register a built-in adapter under a name of your choosing, for example pointing the OpenAI provider at any OpenAI-compatible server, and inject your own fetch for custom transports.
Custom dialects
A dialect is a response shape. It needs only a fromBaseline converter, and res.toDialect() returns your type without a cast. Add toBaseline and the dialect also accepts requests in that shape.
const bridge = createBridge({
providers: { "my-server": new MyProvider() },
dialects: {
summary: {
fromBaseline(output: BridgeOutput) {
return {
id: output.id,
choices: output.choices,
tokens: output.usage.totalTokens,
};
},
},
},
});
const res = await bridge.complete({
provider: "my-server",
model: "my-model",
input,
});
const summary = res.toDialect("summary"); // inferred type, no cast Need an HTTP Endpoint?
AI Bridge is a library that runs inside your process. If you want one HTTP endpoint for every provider, named accounts, model aliases, or a command-line client, use AI Gateway. It wraps AI Bridge, so any custom providers and dialects you build work there unchanged.
Contributing
AI Bridge is MIT-licensed and maintained by Segev Shmueli as part of ProductiveHub. The tests run on injected transports and make no external API calls, so the suite runs offline. Start with the architecture notes and the contributing guide, and open an issue on GitHub for bugs or provider requests.
Building on More Than One Model?
We build multi-model agent systems for clients, and AI Bridge is the kind of plumbing they depend on. If you are designing one and want a second opinion on the architecture, the first conversation is free.