Skip to main content
Everything AI-related goes through one indirection: ask the factory for a capability and a model, and get back a service that supports it. Providers are interchangeable, and a plugin can add more.

Resolving a service

The factory walks its registered services, resolves each from the container, and returns the first that implements the requested interface and reports supportsModel($model). If nothing matches, it throws. Every service implements Ai\Domain\Services\AiServiceInterface:

Capabilities

Bundled providers

Registered during boot: OpenAI, Anthropic, Cohere, xAI, Google, Azure, ElevenLabs, Speechify, Stability AI, Clipdrop, Fal.ai, Luma, Ollama, and a generic adapter for OpenAI-compatible servers. Each implements the capabilities its API supports. Video generation is asynchronous at several providers, which is why the application exposes provider-specific webhook endpoints that complete a generation when the provider calls back.

The model registry

What users can select comes from the registry, not from the services: At boot, base.json is loaded and registry.json is merged over it, matching services and models by key.
Services and models that don’t exist in base.json survive the merge only when marked "custom": true. That flag is what lets an administrator’s custom server, or a plugin’s models, persist.
A model entry carries its key, type, name, a cost multiplier, modalities, capability specs, the rate keys it bills under, and UI configuration such as prompt length and accepted images.

Chat generation

The handler returns a generator, and the request handler streams it. Parts are also folded into the stored message, so reloading the conversation shows the same content.

Stream parts

Only some of these are forwarded to the browser; the rest are internal. See the REST API overview for the client-visible events.

Tools

Tools are what a model can call mid-answer: web search, page fetching, YouTube lookups, media generation, transcription, knowledge base and embedding search, memories and chat history, canvas documents, follow-up questions and voice selection. They’re held in a collection, and filtered per message: the tool must be enabled, allowed by the workspace’s plan, and not disabled by the user. Temporary conversations exclude the tools that would leave state behind. A tool returns its textual result plus its cost, which is added to the generation’s total. See AI tools.

Embeddings and knowledge bases

Uploaded documents and links are chunked, embedded, and stored in a vector store. The default store keeps vectors as files in the configured storage; an alternative can be selected from the registered stores. Search is scoped by namespace and by the dataset units attached to the conversation. See Vector stores. Documents are parsed by a reader stack that handles PDF, Word, HTML, XML, JSON, YAML, CSV and plain text.

Credits

Ai\Infrastructure\Services\CostCalculator converts provider usage into credits using the installation’s configured rates, which are keyed per model and per rate type such as input, output or image. The lifecycle is:
1

Estimate

estimate($model) returns the model’s multiplier as a pre-flight figure.
2

Reserve

BillingService::reserve() holds the estimate so concurrent work can’t overspend.
3

Generate

The provider call runs.
4

Release and consume

The reservation is released, and the real cost is consumed, which dispatches a credit usage event.
When a workspace brings its own provider key, the billing service skips deduction, which is why the model is always passed alongside the workspace.