cd /news/artificial-intelligence/how-i-built-a-provider-agnostic-ai-a… Β· home β€Ί topics β€Ί artificial-intelligence β€Ί article
[ARTICLE Β· art-61560] src=dev.to β†— pub= topic=artificial-intelligence verified=true sentiment=↑ positive

How I built a provider-agnostic AI architecture that automatically switches between Groq, OpenRouter, Ollama, and Gemini.

A developer built a provider-agnostic AI architecture for CVForbes that automatically switches between Groq, OpenRouter, Ollama, and Gemini without changing any service code. The system uses a router layer that handles failover and keeps the application decoupled from specific LLM providers.

read4 min views1 publishedJul 16, 2026

How I designed CVForbes to automatically switch between Groq, OpenRouter, Ollama, and Gemini without changing a single service.

                    Client
                       β”‚
                       β–Ό
             FastAPI Endpoint
                       β”‚
                       β–Ό
             Dependency Injection
                       β”‚
                       β–Ό
               LLMRouter Service
      β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
      β”‚            β”‚              β”‚
      β–Ό            β–Ό              β–Ό
  GroqProvider  OpenRouter   OllamaProvider
      β”‚            β”‚              β”‚
      β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                 β”‚
            GeminiProvider
                 β”‚
                 β–Ό
          LangChain Models
                 β”‚
                 β–Ό
         Structured Output

When building ** CVForbes**, I quickly realized that depending on a single LLM provider wasn't a great long-term strategy.

What if Groq goes down?

What if another provider becomes cheaper or faster?

What if I want to test a new model without touching every service in my application?

Instead of tightly coupling my business logic to one provider, I designed a provider-agnostic routing layer that sits between my application and the LLMs.

Now, every AI featureβ€”resume tailoring, parsing, cover letter generation, and future modulesβ€”talks to a single router. The router decides which provider should handle the request, performs automatic failover when necessary, and keeps the rest of the application completely unaware of what's happening behind the scenes.

This article explains the architecture behind that system and the design decisions that made it scalable.

A lot of AI projects start like this:

llm = ChatGroq(...)
response = llm.invoke(prompt)

It works.

Until it doesn't.

Once your application grows, changing providers means editing multiple services, updating imports, and introducing provider-specific logic across the codebase.

I wanted to avoid that entirely.

Instead of allowing services to communicate directly with an LLM provider, every request goes through a single router.

                    Client
                       β”‚
                       β–Ό
             FastAPI Endpoint
                       β”‚
                       β–Ό
             Dependency Injection
                       β”‚
                       β–Ό
               LLMRouter Service
      β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
      β”‚            β”‚              β”‚
      β–Ό            β–Ό              β–Ό
  GroqProvider  OpenRouter   OllamaProvider
      β”‚            β”‚              β”‚
      β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                 β”‚
            GeminiProvider
                 β”‚
                 β–Ό
          LangChain Models
                 β”‚
                 β–Ό
         Structured Output

Every service simply asks for "an LLM."

The router decides which one.

The router has one job:

That's it.

Everything else stays where it belongs.

This keeps the application clean and makes providers interchangeable.

Each provider follows the same interface.

class BaseLLMProvider:
    def get_llm(self):
        ...

Whether it's Groq, Gemini, Ollama, or OpenRouter doesn't matter.

The router simply calls:

provider.get_llm()

Adding a new provider later becomes incredibly straightforward.

Every AI request follows the same journey.

Incoming Request
       β”‚
       β–Ό
Need LLM?
       β”‚
       β–Ό
Router receives prompt
       β”‚
       β–Ό
Is Groq healthy?
      / \
    Yes  No
    β”‚      β”‚
    β–Ό      β–Ό
 Use Groq  Try OpenRouter
               β”‚
               β–Ό
        Healthy?
          / \
       Yes   No
       β”‚      β”‚
       β–Ό      β–Ό
Use OpenRouter Try Ollama
                     β”‚
                     β–Ό
               Healthy?
                  β”‚
                  β–Ό
              Use Ollama
                  β”‚
                  β–Ό
              Last fallback
                Gemini

Notice something missing?

Nowhere in the application do we reference Groq or Gemini directly.

That's intentional.

One of my biggest goals was resilience.

Instead of immediately failing when a provider becomes unavailable, the router simply tries the next one.

Groq
  β”‚
  ❌
  β–Ό
OpenRouter
  β”‚
  ❌
  β–Ό
Ollama
  β”‚
  βœ…
  β–Ό
Return Response

The user doesn't know a provider failed.

And honestly, they shouldn't have to.

Trying the same failed provider on every request wastes time.

Instead, the router keeps lightweight health information.

Groq        βœ…
OpenRouter  βœ…
Ollama      ❌
Gemini      βœ…

If a provider repeatedly fails, it's temporarily skipped until it becomes healthy again.

This reduces unnecessary delays during outages.

Another design choice was using FastAPI's dependency injection.

Instead of creating providers inside every endpoint, a shared router instance is injected wherever it's needed.

Endpoint
    β”‚
Depends()
    β”‚
LLM Router
    β”‚
Providers

This keeps endpoints focused on business logic rather than infrastructure.

This approach gave CVForbes several advantages:

Perhaps my favorite part is that adding another provider requires almost no changes to the rest of the application.

The architecture grows without becoming more complicated.

Building AI applications isn't just about choosing the best model.

It's about designing systems that continue working when models, providers, or APIs inevitably change.

A small investment in abstraction early on saved me from coupling my entire application to a single vendor.

Looking back, that decision has probably been one of the most valuable architectural choices in CVForbes.

The router already supports multiple providers with automatic failover, but there's plenty of room for future improvements:

The exciting part is that the architecture already supports these ideas without requiring a redesign.

When people think about AI architecture, they often focus on which model to use.

I think the better question is:

How easily can your application switch models tomorrow?

Designing around abstractions instead of vendors made CVForbes significantly more maintainable, resilient, and scalable. Providers may change, APIs may evolve, and new models will continue to emergeβ€”but the rest of the application won't need to know.

That's the kind of architecture I aim for: one that's built around software engineering principles rather than a single AI provider.

The complete implementation, including the router, providers, health management, and FastAPI integration, is available in the accompanying GitHub repository.

Feel free to explore it, suggest improvements, or adapt the architecture for your own AI applications.

── more in #artificial-intelligence 4 stories Β· sorted by recency
── more on @cvforbes 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain β€” perfect for shipping the agent you just read about.

$git push zahid main
β†’ Live at https://your-agent.zahid.host βœ“
Get free account β†’ Pricing
from €0/mo Β· no card required
LIVE [news/how-i-built-a-provid…] indexed:0 read:4min 2026-07-16 Β· β€”