Provider Plugins
A provider plugin turns a model backend into the one abstraction the agent loop understands:
#![allow(unused)]
fn main() {
#[async_trait::async_trait]
pub trait Provider: Send + Sync {
fn name(&self) -> &str;
fn default_model(&self) -> &str;
fn capabilities(&self, model: Option<&str>) -> ModelCapabilities;
async fn complete(
&self,
ctx: &Ctx,
req: &CompletionRequest,
) -> Result<CompletionResponse, ProviderError>;
}
}
The CLI does not construct concrete providers. It passes a provider id and
ProviderConfig to truss_providers::registry::build. Auth resolution belongs
inside the provider’s build() function, not in truss-cli or truss-core.
Add a compile-time provider
- Add a module in
crates/truss-providers/src/. - Implement
Provider. - Add
pub(crate) fn build(cfg: &ProviderConfig) -> Result<Arc<dyn Provider>, BuildError>. - Add a cargo feature in
crates/truss-providers/Cargo.toml. - Add one
available()entry and onebuild()match arm inregistry.rs.
The provider should convert Truss-neutral CompletionRequest, ToolDef,
SystemBlock, ReasoningEffort, and messages into the backend’s wire format.
Keep provider-specific HTTP/auth/client code in truss-providers; truss-core
should stay vendor-free.
Durability and provenance
Providers do not write durable journal entries themselves. The agent/durable loop records model results after the provider returns. That keeps replay centralized: on resume, Truss reuses the recorded result instead of asking the provider again.
Provider authors must:
- honor
ctxcancellation/deadlines; - return typed
ProviderErrorvalues so retry policy can distinguish retryable transport errors from fatal config/model errors; - expose conservative
ModelCapabilities; - keep secret material out of
Debug, events, and errors; - implement
complete_streamonly when the backend has true streaming. The default wrapper is acceptable and still produces correctAgentEvents.
Runnable checks
These checks exercise the provider registry boundary without requiring live credentials:
cargo test -p truss-providers registry
cargo build -p truss-cli --no-default-features --features provider-openai
cargo build -p truss-core
For a provider with live credentials, add a wiremock/unit test first, then smoke the CLI:
truss --provider <id> --model <model> "say hello in one sentence"