ADR-0020: LLM Component Provider Adapter Boundary
Date: 2026-06-13 Status: Accepted
Decision
Define a project-owned LlmProvider trait with Camel-shaped request/response types. Confine all production siumai imports to the adapter file (provider/siumai_adapter.rs) plus one test-only file (provider/siumai_adapter_tests.rs, gated by #[cfg(all(test, feature = "openai"))]). provider_factory.rs selects and dispatches to adapter constructors without importing siumai directly.
The original boundary included two production files. Refactoring provider_factory.rs to delegate narrowed it to one production file without changing the architectural decision.
Test fixtures legitimately need siumai types (StubChat, StubEmbed implement siumai traits) and cannot be expressed through the public adapter API.
No other file in the crate may import siumai. A test (tests/boundary.rs) scans all .rs files for direct siumai references outside its allowlist. The allowlist still permits provider_factory.rs for historical compatibility, although that file has no direct siumai imports.
Context
The camel-component-llm component needs an LLM provider abstraction to support multiple backends (OpenAI, Ollama, etc.). Two approaches were considered:
- Depend on siumai types directly — Similar to how
camel-sqldepends onsqlxtypes directly in its producer. - Define a project-owned
LlmProvidertrait — Isolate siumai behind a strict adapter boundary.
The SQL precedent (direct sqlx dependency) does not transfer because:
sqlxis mature (0.8 stable) with a stable API surface.siumaiis0.11.0-beta.9— beta quality, API may churn.- Database concepts (connection, pool, query) are stable and well-understood.
- LLM API semantics (chat, streaming, tool calling, structured output) are still rapidly evolving.
Considered Options
Depend on siumai types directly
Rejected. The beta status of siumai (0.11.0-beta.9) means API churn is likely. Direct dependency would spread siumai types across the component, producer, endpoint, and config — making every breaking change a crate-wide refactor. The SQL precedent does not apply because sqlx is stable and its domain concepts are well-established.
Define a project-owned trait (Accepted)
Accepted. A project-owned LlmProvider trait with Camel-shaped types isolates production siumai imports to one adapter file. Mock provider works without siumai at all (--features mock only). If siumai breaks, only the adapter file changes. Future non-siumai providers are possible without breaking the component API.
Consequences
Positive:
- siumai API churn is confined to one production adapter file.
- Mock provider works without siumai dependency (
--features mockonly). - Testing is deterministic without network.
- Future non-siumai providers are possible without breaking the component API.
- Hot-reload, multi-context, and test isolation are safe (own provider map, not global registry).
Negative:
- Boilerplate: own request/response types that don't mirror siumai.
- Manual translation between Camel types and siumai types in the adapter.
Failure mode: If production siumai imports or siumai-shaped public types leak past provider/siumai_adapter.rs, the design has failed.