Architecture
The architecture separates the public contract from provider selection and vendor-specific transport code.
The boundary is intentionally narrower than any one vendor API. It captures the operations consumers can rely on across providers, while request-option metadata gives provider packages room to expose useful differences without forcing those differences into every consumer.
| Layer | Responsibility |
|---|---|
| Core | Defines capability contracts and provider-neutral errors |
| Registry | Accepts provider registrations and selects active services |
| Vendor plugin | Resolves vendor configuration and performs API calls |
| Consumer plugin | Uses the selected service without depending on a vendor |
Provider lifecycle
A vendor plugin registers an AiProvider with ctx.aiProviders. A provider has a stable ID, a display label, optional settings metadata, and factories for its supported capabilities. Registration is scoped through Cordis, so unloading the plugin removes the provider.
The runtime exposes service facades for the selected providers:
| Context service | Capability |
|---|---|
ctx.embeddings | Embeddings |
ctx.rerankers | Reranking |
ctx.textToSpeech | Text-to-speech |
ctx.apiSpeechToText | File transcription |
ctx.streamingSpeechToText | Live transcription, when supported |
ctx.imageGeneration | Image generation |
ctx.decisions | Decisions |
The facades resolve the current provider on each call. Configuration changes therefore take effect without rebuilding dependent plugins.
One call through the system
- A consumer calls a capability service from the Cordis context.
- The runtime reads the current provider selection and asks that provider's factory for a service.
- The concurrency gate admits the call according to its capability, route, and priority.
- The vendor service resolves its current connection and credential reference, then performs the request.
- The implementation validates the response and returns the provider-neutral result, or throws
AiError.
Credentials remain in DSH's credential service. Profile configuration stores references, so neither consumer plugins nor these provider packages need to persist secret values in their own configuration.
Language-model routes
Language models are registered as DSH model routes rather than as a single selected service. This lets multiple configured routes coexist. Vendor-level request options can be described to the UI with localized labels and defaults.
Error model
Provider failures are normalized as AiError values with one of six stable codes: not-configured, unavailable, rejected, invalid-input, unsupported, or cancelled. Consumers can handle these codes without parsing vendor response text. The message and preserved cause remain diagnostic context, not part of the compatibility contract.
Compatibility boundary
The core package is the dependency boundary for consumers and third-party providers. The runtime and project-maintained provider packages can evolve behind it, but changing a core method, result shape, error code, or service name affects every implementation and consumer. Keep vendor-only controls in provider request-option metadata unless their behavior can be defined consistently across implementations.