Skip to content

AI Model Router

The AI model router is a shared module that centralizes all AI model configuration. Every AI-powered feature (chatbot, tagging, title generation, embeddings) routes through this module. This allows configuring providers, models, and task assignments in one place, swapping models without changing business logic, and using different models for different tasks.

┌─────────────────────────────────────────────────┐
│ AI Config │
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ Provider │ │ Provider │ │ Provider │ │
│ │ google │ │ openai │ │ local │ │
│ └────┬─────┘ └────┬─────┘ └────┬─────┘ │
│ │ │ │ │
│ ┌────┴─────┐ ┌────┴─────┐ ┌───┴──────┐ │
│ │ Model │ │ Model │ │ Model │ │
│ │ gemini- │ │ text- │ │ custom │ │
│ │ 2.0-flash│ │ embed-3 │ │ reranker │ │
│ └────┬─────┘ └────┬─────┘ └────┬─────┘ │
│ │ │ │ │
│ ┌────┴─────┐ ┌────┴─────┐ ┌───┴──────┐ │
│ │ Tasks │ │ Tasks │ │ Tasks │ │
│ │ chatbot │ │ embed │ │ rerank │ │
│ │ tagging │ │ │ │ │ │
│ │ title-gen│ │ │ │ │ │
│ └──────────┘ └──────────┘ └──────────┘ │
└─────────────────────────────────────────────────┘

A provider represents an AI service endpoint. One provider can serve multiple models.

FieldTypeNotes
idStringUnique identifier (e.g., google, openai)
typeProviderTypeGOOGLE, OPENAI, ANTHROPIC, CUSTOM
apiKeyStringSecret, loaded from environment variable
baseUrlString?Override for custom/self-hosted endpoints
rateLimitInt?Max requests per minute (optional)

The initial deployment uses Google as the sole provider. The system is designed to support additional providers without code changes — only configuration changes.

A model is a specific AI model available through a provider. One model can be assigned to multiple tasks.

FieldTypeNotes
idStringUnique identifier (e.g., gemini-flash, text-embed)
providerIdStringFK → Provider
modelIdStringThe provider’s model identifier (e.g., gemini-2.0-flash, text-embedding-004)
typeModelTypeGENERATIVE or EMBEDDING
maxTokensInt?Max output tokens for generative models
dimensionsInt?Embedding dimensions for embedding models
idProvidermodelIdTypePurpose
gemini-flashgooglegemini-2.0-flashGENERATIVEFast generative tasks
gemini-progooglegemini-2.0-proGENERATIVEComplex generative tasks
text-embedgoogletext-embedding-004EMBEDDINGEvent and query embeddings

A task maps a specific AI use case to a model. Each task has its own configuration (system prompt, temperature, etc.).

FieldTypeNotes
idStringTask identifier (e.g., chatbot, tagging)
modelIdStringFK → Model
systemPromptString?Task-specific system instructions
temperatureFloat?0.0–2.0
maxOutputTokensInt?Per-task override
Task IDModelPurposeTemperature
chatbotgemini-proAgentic event discovery chatbot0.7
tagginggemini-flashAuto-generate tags, category, summary for events0.3
title-generationgemini-flashAuto-generate conversation titles0.5
embeddingtext-embedGenerate embeddings for events and search queriesN/A

The Provider → Model → Task configuration is stored as a typed configuration object in the API codebase, loaded from environment variables for secrets and a config file for structure. It is not stored in the database.

// Example structure (not prescriptive)
interface AIConfig {
providers: Record<string, ProviderConfig>
models: Record<string, ModelConfig>
tasks: Record<string, TaskConfig>
}

When a feature needs AI (e.g., event creation triggers tagging), it calls the model router with the task ID. The router resolves: task → model → provider, builds the appropriate client, and executes the request.

Changing which model handles a task requires only updating the task’s modelId in config. No business logic changes needed. Example: switching chatbot from gemini-pro to an OpenAI model requires adding an openai provider, adding the model, and pointing the chatbot task to it.

If a provider is unavailable, the router returns an error to the caller. Each caller handles AI failure according to its own spec (e.g., tagging → proceed with empty tags, chatbot → return error message). The model router does not implement retries or fallback chains — that is the caller’s responsibility.

The model router produces provider-specific client instances compatible with the Vercel AI SDK (ai package). Generative tasks use generateText() or streamText(). Embedding tasks use embedMany() or embed().

S-AI-1: Resolve task to model and provider

Section titled “S-AI-1: Resolve task to model and provider”
GIVEN task "tagging" is configured with modelId "gemini-flash"
AND model "gemini-flash" is configured with providerId "google"
WHEN the tagging feature requests the model router for task "tagging"
THEN the router returns a Google Gemini client configured with model "gemini-2.0-flash"
GIVEN task "chatbot" is configured with modelId "gemini-pro"
WHEN the config is updated to set task "chatbot" modelId to "gpt-4o"
AND model "gpt-4o" is configured with providerId "openai"
THEN the chatbot feature now uses OpenAI GPT-4o without any code changes
GIVEN the Google provider's API is down
WHEN the tagging feature requests the model router for task "tagging"
THEN the router throws a provider-unavailable error
AND the tagging caller handles it by proceeding with empty tags
GIVEN no task is configured with id "nonexistent"
WHEN a feature requests the model router for task "nonexistent"
THEN the router throws a configuration error
GIVEN task "embedding" is configured with modelId "text-embed"
WHEN the embedding feature sends text "Jazz Night at the Union"
THEN the router returns a vector of the configured dimensions