Provider abstraction

Hard-coding one provider is fine for a hackathon. The moment your company has an OpenAI contract or wants to try Claude for a specific task, the flat import becomes a liability. Extract model selection into a getModel helper behind an env var and the whole codebase stops caring which provider is active.

lib/get-model.ts
typescript
import { createOpenRouter } from '@openrouter/ai-sdk-provider';
import { createOpenAI } from '@ai-sdk/openai';

export function getModel() {
  const provider = process.env.LLM_PROVIDER ?? 'openrouter';

  if (provider === 'openai') {
    const openai = createOpenAI({
      apiKey: process.env.OPENAI_API_KEY ?? '',
    });
    return openai.chat(process.env.OPENAI_MODEL ?? 'gpt-4o-mini');
  }

  const openrouter = createOpenRouter({
    apiKey: process.env.OPENROUTER_API_KEY ?? '',
  });
  return openrouter.chat(
    process.env.OPENROUTER_MODEL ?? 'google/gemma-3-12b-it',
  );
}

One helper reads LLM_PROVIDER and returns a ready model. The route handler, tools, and generators all import getModel. Swapping providers is one env var away.

app/api/chat/route.ts
typescript
import { getModel } from '@/lib/get-model';

export async function POST(req: Request) {
  // ... validation as before

  const result = streamText({
    model: getModel(),
    system: SYSTEM_PROMPT,
    messages: convertToModelMessages(parsed.data.messages),
    tools: agentTools,
    maxSteps: 5,
  });

  return result.toUIMessageStreamResponse();
}

Route handler no longer knows about OpenRouter or OpenAI. It just asks for a model and streams. Every caller in the codebase does the same thing.

Provider abstraction

One feature flag switches the whole app. No file outside lib/get-model.ts knows which provider is active.