Multi-Provider AI App: OpenAI + Anthropic + Google in One SDK
In January 2026, OpenAI announced DALL-E 3 deprecation with a short migration window.
Teams that had built image generation into their products — sometimes deeply, sometimes across multiple services — scrambled. Not because switching models is technically hard. Because they had to update imports, rewrite API calls, adjust response parsing, update tests, and redeploy — for every service that touched image generation.
One provider change. Multiple engineering weeks of work. That's the real cost of vendor lock-in.
The analysts have a name for it now: "emergency portability work." Enterprises that built single-provider AI features spent 40-60% of Q1 2026 engineering cycles on this. And it will happen again — because model deprecations are not a rare event. They're a regular part of the AI industry lifecycle.
The architectural answer is clear: make model choice configuration, not code. NeuroLink is built around this principle.
The Fragmentation Problem
Here's what a "typical" multi-provider AI codebase looks like today, before any abstraction:
// openai-service.ts
import OpenAI from "openai";
const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
export async function generateWithOpenAI(prompt: string) {
const response = await openai.chat.completions.create({
model: "gpt-4o",
messages: [{ role: "user", content: prompt }],
});
return response.choices[0].message.content;
}
// anthropic-service.ts
import Anthropic from "@anthropic-ai/sdk";
const anthropic = new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY });
export async function generateWithAnthropic(prompt: string) {
const response = await anthropic.messages.create({
model: "claude-3-5-sonnet-20241022",
max_tokens: 1024,
messages: [{ role: "user", content: prompt }],
});
return response.content[0].type === "text" ? response.content[0].text : "";
}
// google-service.ts
import { GoogleGenerativeAI } from "@google/generative-ai";
const genAI = new GoogleGenerativeAI(process.env.GOOGLE_API_KEY!);
export async function generateWithGoogle(prompt: string) {
const model = genAI.getGenerativeModel({ model: "gemini-2.0-flash" });
const result = await model.generateContent(prompt);
return result.response.text();
}
Three files. Three import patterns. Three response shapes. Three authentication schemes. Three error types to catch. And none of this handles failover — if OpenAI goes down, you need more code to route to Anthropic.
This is not a hypothetical. This is what production AI codebases look like.
The NeuroLink Approach: One Function, Any Provider
With NeuroLink, the same generate() call works across all 13 supported providers. The provider is a parameter, not an architectural decision:
import { NeuroLink } from "@juspay/neurolink";
const neurolink = new NeuroLink();
// OpenAI
const openAIResult = await neurolink.generate({
input: { text: "Explain transformer architecture" },
provider: "openai",
model: "gpt-4o",
});
// Anthropic — identical call pattern
const anthropicResult = await neurolink.generate({
input: { text: "Explain transformer architecture" },
provider: "anthropic",
model: "claude-3-5-sonnet-20241022",
});
// Google AI Studio — identical call pattern
const googleResult = await neurolink.generate({
input: { text: "Explain transformer architecture" },
provider: "google-ai",
model: "gemini-2.0-flash",
});
// All three results have the same shape:
// { content, provider, model, usage, responseTime, analytics }
console.log(openAIResult.content); // string
console.log(anthropicResult.content); // string
console.log(googleResult.content); // string
Switching from OpenAI to Anthropic is changing one string. No import changes. No response parsing updates. No error handling rewrites.
13 Providers, One Import
NeuroLink supports 13 AI providers out of the box:
import {
GoogleVertexAI,
AmazonBedrock,
AmazonSageMaker,
OpenAI,
OpenAICompatible,
AnthropicProvider,
AzureOpenAIProvider,
GoogleAIStudio,
HuggingFace,
Ollama,
MistralAI,
LiteLLM,
OpenRouter,
} from "@juspay/neurolink";
That includes every major cloud provider (AWS Bedrock, Azure OpenAI, Google Vertex AI), every major model lab (OpenAI, Anthropic, Google AI Studio, Mistral, HuggingFace), and local inference (Ollama). OpenRouter integration alone adds 200+ models through a single provider entry.
For an application that needs to stay flexible — or that needs different providers for different tasks — this is the foundation.
Intelligent Failover with the Workflow Engine
Provider abstraction handles the "normal" case. But production systems need to handle the abnormal case: what happens when a provider is down, rate-limited, or too slow?
NeuroLink's workflow engine handles this with predefined failover patterns. The simplest is a fallback workflow:
import { NeuroLink, registerWorkflow, runWorkflow } from "@juspay/neurolink";
// Define a fallback chain: try OpenAI first, then Anthropic, then Google
const failoverWorkflow = registerWorkflow({
type: "fallback",
steps: [
{ provider: "openai", model: "gpt-4o" },
{ provider: "anthropic", model: "claude-3-5-sonnet-20241022" },
{ provider: "google-ai", model: "gemini-2.0-flash" },
],
});
const neurolink = new NeuroLink();
const result = await neurolink.generate({
input: { text: "Summarize the quarterly earnings report" },
workflowConfig: failoverWorkflow,
});
// result.provider tells you which provider actually handled the request
console.log(`Handled by: ${result.provider}`);
If OpenAI returns an error or times out, the workflow automatically tries Anthropic. If Anthropic fails, it tries Google. The calling code doesn't change at all.
Multi-Model Consensus: Getting the Best Answer
Failover solves reliability. But there's a more interesting use case: using multiple providers simultaneously to get a better answer than any single model would give.
NeuroLink ships with predefined ensemble workflows:
import { NeuroLink, CONSENSUS_3_WORKFLOW } from "@juspay/neurolink";
const neurolink = new NeuroLink();
// CONSENSUS_3_WORKFLOW:
// 1. GPT-4o generates a response
// 2. Claude 3.5 Sonnet generates a response
// 3. Gemini 2.0 Flash generates a response
// 4. GPT-4o acts as judge, scoring all three responses
// 5. The highest-scoring response is returned
const result = await neurolink.generate({
input: {
text: "What are the key risks in this contract clause? " +
"Be specific and cite legal precedents where relevant.",
},
workflowConfig: CONSENSUS_3_WORKFLOW,
});
// Inspect the workflow results
console.log(`Winner: ${result.workflow.selectedModel}`);
console.log(`Judge scores:`, result.workflow.judgeScores);
console.log(`Total time: ${result.workflow.metrics.totalTime}ms`);
// See all three responses if you want to compare
result.workflow.ensembleResponses.forEach(r => {
console.log(`[${r.model}]: ${r.content.substring(0, 200)}...`);
});
// The final answer — the one the judge ranked highest
console.log(result.content);
For high-stakes decisions — contract analysis, medical triage support, financial recommendations — running multiple models and selecting the best-scored response materially reduces error rates.
The predefined workflows available:
-
CONSENSUS_3_WORKFLOW— 3 models in parallel, judge picks best -
MULTI_JUDGE_5_WORKFLOW— 5 models + 3 judges vote by consensus -
QUALITY_MAX_WORKFLOW— adaptive 3-tier quality optimization
Provider-Specific Features Still Work
The abstraction doesn't mean losing access to provider-specific capabilities. Extended thinking is a good example — Anthropic and Google implement it differently, and NeuroLink handles both through the same typed interface:
import { NeuroLink } from "@juspay/neurolink";
const neurolink = new NeuroLink();
// Anthropic extended thinking — configure with budget tokens
const anthropicDeep = await neurolink.generate({
input: { text: "Solve this multi-step logic puzzle: ..." },
provider: "anthropic",
model: "claude-3-7-sonnet-20250219",
thinkingConfig: {
enabled: true,
budgetTokens: 10000, // 5k-100k range
},
});
// Google Gemini extended thinking — configure with level
const googleDeep = await neurolink.generate({
input: { text: "Solve this multi-step logic puzzle: ..." },
provider: "vertex",
model: "gemini-3-pro-preview",
thinkingConfig: {
thinkingLevel: "high", // minimal | low | medium | high
},
});
The thinkingConfig type is smart enough to accept the right parameters for each provider. TypeScript will tell you at compile time if you pass budgetTokens to a Gemini model that doesn't support it that way.
There's also a subtlety worth knowing for structured output with Google:
// Google Gemini: cannot combine function calling with structured output
// NeuroLink documents this in the type comments
const structuredResult = await neurolink.generate({
input: { text: "Extract the product details from this description" },
schema: ProductSchema,
provider: "vertex",
disableTools: true, // required when using schema with Google providers
});
This constraint is documented in the TypeScript types — not discovered at runtime.
Cost Tracking Across Providers
One practical benefit of multi-provider architecture that's easy to overlook: consolidated cost tracking. When each provider has its own SDK, cost tracking requires integrating with each provider's billing API separately.
NeuroLink tracks costs per call, across all providers, in USD:
import { NeuroLink } from "@juspay/neurolink";
const neurolink = new NeuroLink();
const result = await neurolink.generate({
input: { text: "Analyze this large document..." },
provider: "anthropic",
model: "claude-opus-4-6",
maxBudgetUsd: 2.00, // stop if session cost exceeds $2
});
// Cost is in the result, regardless of provider
console.log(`Input tokens: ${result.usage.input}`);
console.log(`Output tokens: ${result.usage.output}`);
console.log(`Cost this call: $${result.analytics?.cost?.toFixed(4)}`);
The built-in pricing table covers all major providers and models, updated to February 2026 pricing. And maxBudgetUsd throws a typed BudgetExceededError before making the API call if the accumulated session cost would exceed your limit:
try {
const result = await neurolink.generate({
input: { text: "Process this huge dataset..." },
provider: "openai",
model: "gpt-4o",
maxBudgetUsd: 5.00,
});
} catch (err) {
if (err.name === "BudgetExceededError") {
// Switch to a cheaper model automatically
const cheapResult = await neurolink.generate({
input: { text: "Process this huge dataset..." },
provider: "openai",
model: "gpt-4o-mini", // 10x cheaper
});
}
}
The Operational Case for Multi-Provider
Here's the honest summary of why multi-provider architecture matters, from an operational perspective:
Model deprecation. DALL-E 3 won't be the last. Models get deprecated. Versions get sunset. With a provider-abstracted SDK, you change one string in your configuration. Without it, you rewrite services.
Provider outages. OpenAI has had notable outages. So has Anthropic. A failover workflow means your application degrades gracefully instead of going down.
Cost optimization. Different providers have different price/performance points for different tasks. Using gpt-4o-mini for simple classification and claude-opus-4-6 for complex analysis — through the same API — is trivially easy with provider abstraction.
Model capability matching. Some tasks genuinely work better on specific models. Anthropic's models tend to perform better on instruction-following. Google's models handle very long contexts well. OpenAI's models have broad general capability. Being able to route different task types to appropriate providers — without architectural friction — is a real advantage.
Regulatory flexibility. The EU AI Act and emerging regulations may require specific data residency or audit requirements. A provider-abstracted architecture means you can adjust where computation happens without rewriting application code.
Getting Started
npm install @juspay/neurolink
import { NeuroLink, CONSENSUS_3_WORKFLOW } from "@juspay/neurolink";
const neurolink = new NeuroLink();
// Single provider
const simple = await neurolink.generate({
input: { text: "Hello, world" },
provider: "anthropic",
});
// Multi-model consensus
const ensemble = await neurolink.generate({
input: { text: "What is the optimal database indexing strategy for this schema?" },
workflowConfig: CONSENSUS_3_WORKFLOW,
});
console.log(`Answered by: ${ensemble.workflow.selectedModel}`);
console.log(ensemble.content);
- GitHub: github.com/juspay/neurolink
- Discord: Join the NeuroLink community to discuss multi-provider patterns
- Docs: Full provider documentation and workflow engine reference in the repository
Stop writing adapters. Stop maintaining five SDKs. Pick the right model for each task, not the one you're locked into.
Top comments (0)