Architecture: AI Facade
Status: Active
Last updated: 2026-05-01
Audience: AI agents and engineers debugging AI provider routing, model
selection, structured output, or streaming chat.
1. Purpose
AiFacadeService is the single platform-side entry point for every
LLM call the platform makes — generation pipelines, AI conversations,
content extraction validation, comparison generation, item enrichment,
and dashboard test calls. It resolves which AI provider plugin should
handle the call, picks the right model tier for the task, applies
provider-specific routing (reasoning, JSON mode, embedding shape), and
provides graceful degradation across plugins.
Plugins themselves are OpenAI-shape-compatible — every AI provider
plugin (openai, anthropic, google, groq, ollama, mistral,
openrouter, vercel-ai-gateway, plus the various aggregator
implementations) wraps a LangChain client behind the same
IAiProviderPlugin contract. The facade handles which plugin and
which model; the plugin handles the wire protocol.
2. Public Surface (IAiFacade)
interface IAiFacade {
chatCompletion(opts: ChatCompletionOptions): Promise<ChatCompletionResponse>;
streamChatCompletion(opts: ChatCompletionOptions): AsyncIterable<ChatCompletionChunk>;
askJson<T>(opts: AskJsonOptions<T>): Promise<AskJsonResponse<T>>;
askJsonCompletion<T>(opts: AskJsonOptions<T>): Promise<AskJsonCompletionResponse<T>>;
embeddings(opts: EmbeddingsOptions): Promise<EmbeddingsResponse>;
listModels(opts: { providerId?: string }): Promise<AiModel[]>;
getProviderConfig(providerId: string): Promise<AiProviderConfig>;
validateConnection(providerId: string): Promise<ConnectionValidationResult>;
}
Every method accepts a FacadeOptions block carrying userId,
workId?, optional explicit providerId, and routing overrides.
Without these the facade falls back to the cascade (§3).
3. Provider Resolution Cascade
The facade resolves a request to a plugin in this order:
- Explicit
providerIdin the options (admin tool calls, "test this provider" button). - Per-work binding —
work_plugins.aiProviderrow. - Per-user binding —
user_plugins.aiProviderrow. defaultFor: 'ai-provider'plugin — today:openrouter.- Throw
AiFacadeError("no provider")— caller handles.
The cascade is the same one defined in Plugin SDK §11 and shared with every other facade.
4. Model Tier Routing
Every AI provider plugin declares four model aliases in its settings schema:
| Alias | Typical use | Default cost target |
|---|---|---|
simpleModel | Tags, slugs, short labels, classifications | Cheapest tier |
mediumModel | Item summaries, descriptions, reformatting | Mid tier |
complexModel | Full-page generation, multi-step analysis, reasoning | Top tier |
defaultModel | Used when no complexity is specified | Mid tier (typical) |
The facade accepts a complexity: TaskComplexity hint
('simple' | 'medium' | 'complex') and resolves the right alias from
the plugin's resolved settings. The plugin substitutes the alias for
the actual model id (e.g. simpleModel: 'gpt-5-nano') before calling
LangChain.
Pipelines pick complexity per step:
- Domain detection →
simple - Item summary →
medium - Whole-page generation →
complex - Reasoning (deep analysis) →
complexwithrouting.reasoningEffort