How Lega's AI Adapter System Works
A conceptual look at the adapter-pattern architecture behind Lega's pluggable AI providers, why it exists, and what it protects against as the AI landscape keeps shifting.

Every AI-first product makes a quiet architectural bet the day it picks a model provider. Bind your product's core logic directly to one vendor's API, and you inherit that vendor's outages, pricing changes, rate limits, and model deprecations as your own. The bet Lega made instead was to never let a specific AI provider's shape leak into the rest of the product. That bet is implemented as an adapter system, and it is worth understanding both how it works and why it exists.
The Problem with Calling a Model Directly
Imagine every part of Lega that needs AI, layout generation, style suggestions, sticker creation, refine-and-polish requests, called a specific provider's SDK directly. That seems fine until you consider what actually happens over a product's lifetime: providers change their response formats, a cheaper or faster model becomes available for a specific task, a provider has a multi-hour outage, or you want to run automated tests without spending real API budget on every CI run.
If provider-specific code is scattered across the codebase, every one of those events becomes a multi-file refactor performed under time pressure. That is precisely the situation an adapter pattern is designed to prevent.
The Adapter Boundary
Lega's @kit/ai package defines a small, stable interface that describes what the rest of the product needs from "an AI provider": given a structured request (a content brief, a layout intent, a style constraint), return a structured, validated response (a layout suggestion, a style system, a generated asset). Everything downstream of that interface, the editor, the API routes, the entitlement and usage-tracking logic, only ever talks to the interface. It never imports a provider SDK directly.
import { getAdapter, registerAdapter } from '@kit/ai';
import { LayoutSuggestionSchema } from '@kit/ai/validation';
getAdapter resolves to whichever concrete implementation is configured, currently a Gemini-backed adapter for production traffic and a mock adapter for deterministic testing. registerAdapter is how a new provider implementation gets plugged in without touching call sites. Responses are validated against schemas like LayoutSuggestionSchema before anything downstream trusts them, so a malformed or unexpected model response fails loudly at the boundary instead of corrupting a user's document three layers deeper in the stack.
Config-Driven Model Tiering, Not Hardcoded Model Names
A subtler benefit shows up once you have more than one task calling AI. Layout generation, style suggestion, and sticker generation do not have the same latency, cost, or quality requirements. A naive implementation hardcodes a model name per call site, which means every model swap is a grep-and-replace across the codebase, and it is easy to miss one.
Lega's adapter system instead drives model selection from configuration: which model tier handles which task, and what the fallback chain looks like if a preferred model is unavailable or a request fails. When a new model tier becomes worth adopting, or a provider deprecates a model, that is a configuration change, not a code change scattered across every AI-calling route. The fallback executor means a transient failure on a preferred model degrades gracefully to a fallback rather than surfacing an error to the user mid-generation.
Why Mock Adapters Matter More Than They Sound Like They Should
Testing AI-driven features is genuinely hard. You cannot run your CI suite against a live model API: it is slow, it costs money at scale, and it is non-deterministic, which makes assertions fragile. The adapter boundary makes a mock adapter possible: same interface, deterministic canned responses, zero network calls. Most of the editor's AI-dependent test coverage runs against the mock adapter, and it is only the small, explicitly env-gated evaluation and live-smoke test suites that spend real tokens against the live provider.
One honest caveat worth stating plainly: not every AI-calling route in the codebase is wired through the adapter registry yet. The intelligence API routes currently call Gemini directly in a few places, which means AI end-to-end tests for those specific paths cannot be fully mocked until they are rewired through the adapter. This is a known gap, not a hidden one, and it is the kind of thing an adapter architecture makes easy to fix incrementally, one call site at a time, rather than requiring a rewrite.
What This Buys the Product, Concretely
Three things fall out of this architecture that matter for how Lega actually ships features:
Provider resilience. If a model provider has an outage or deprecates an endpoint, the blast radius is the adapter implementation, not the product surface.
Model experimentation without churn. Trying a new model for layout generation is a config and adapter-level change. It does not require touching the editor commands, the entitlement logic, or the API routes that call it, which is described in more detail in AI Layout Generation.
Honest testability. Deterministic mock responses mean the majority of the AI-dependent test suite is fast, free, and reliable, with real-model evaluation reserved for the harnesses that are explicitly designed to spend budget on it.
How This Connects to the Rest of the Editor
The adapter system answers "what does the AI decide." It deliberately does not answer "how does that decision reach the document." That second question is handled by Legon's command-based state architecture, covered in How Legon's Plugin Architecture Works: AI output gets normalized into the same typed layers and commands a human editing session produces, then flows through one apply pipeline. Keeping "what to generate" and "how to apply it" as separate architectural concerns, each swappable and testable independently, is what lets Lega absorb a fast-moving AI landscape without the rest of the product shaking every time a model provider ships a change. For the wider picture of how this fits into the editor as a whole, see Editor Architecture Overview.
Writing prompts that get the most out of this system is its own skill, covered in AI Design Prompts That Work.