Back to Rig

Rig Provider

.continue/rules/rig-provider.md

latest5.5 KB
Original Source
<!-- This file is generated by scripts/sync_agent_instruction_files.sh. --> <!-- Do not edit this file directly; update AGENTS.md and re-run the sync script. -->

name: Rig Provider Implementation globs: "**/*.rs" alwaysApply: false

Use this checklist for provider and vector-store implementations.

rig-provider-implementation

Reference implementation: rig-core/src/providers/openai/ (Chat Completions API)

When implementing a new provider, study the OpenAI Chat Completions implementation thoroughly. It demonstrates the complete pattern including both completion and embedding models.

Required Components

  1. Provider Extension and Builder Structs

    rust
    #[derive(Debug, Default, Clone, Copy)]
    pub struct MyProviderExt;
    
    #[derive(Debug, Default, Clone, Copy)]
    pub struct MyProviderBuilder;
    
  2. Provider Trait Implementation

    rust
    impl Provider for MyProviderExt {
        type Builder = MyProviderBuilder;
        const VERIFY_PATH: &'static str = "/models";  // Health check endpoint
    
        fn build<H>(...) -> http_client::Result<Self> {
            Ok(Self)
        }
    }
    
  3. Capabilities Declaration Explicitly declare what your provider supports:

    rust
    impl<H> Capabilities<H> for MyProviderExt {
        type Completion = Capable<completion::CompletionModel<H>>;  // Supported
        type Embeddings = Nothing;                                   // Not supported
    }
    

    Use Capable<T> for supported features and Nothing for unsupported ones.

  4. ProviderBuilder Implementation

    rust
    impl ProviderBuilder for MyProviderBuilder {
        type Output = MyProviderExt;
        type ApiKey = BearerAuth;  // Or custom auth type
        const BASE_URL: &'static str = "https://api.provider.com/v1";
    }
    
  5. Client Type Aliases

    rust
    pub type Client<H = reqwest::Client> = client::Client<MyProviderExt, H>;
    
  6. Model Constants

    rust
    pub const MODEL_NAME: &str = "model-name";
    
  7. CompletionModel Implementation Implement the completion::CompletionModel trait with proper:

    • Request conversion (TryFrom<CompletionRequest>)
    • Response conversion (into Rig's standard response types)
    • Streaming support
    • Telemetry/tracing spans
  8. Message Type Conversions Implement TryFrom/From conversions between Rig's message::Message types and your provider's message format.

  9. EmbeddingModel Implementation (if supported) Implement the EmbeddingModel trait:

    • Set MAX_DOCUMENTS for batch embedding limits
    • Implement embed_texts for batch document embedding
    • Map errors to EmbeddingError
    • Provide ndims() returning the embedding vector dimensionality

Critical Requirements

  • Do not diverge from the provider's actual API. If OpenAI's API has a field, implement it as OpenAI defines it. Do not add fields that don't exist in the real API.
  • Implement proper error handling using CompletionError, EmbeddingError, etc.
  • Add telemetry spans following GenAI semantic conventions (see existing providers).
  • Support both streaming and non-streaming where the provider supports it.

rig-vector-store-implementation

Vector stores live in separate companion crates (e.g., rig-mongodb, rig-lancedb).

Core Trait

rust
pub trait VectorStoreIndex: WasmCompatSend + WasmCompatSync {
    type Filter: SearchFilter + WasmCompatSend + WasmCompatSync;

    fn top_n<T: for<'a> Deserialize<'a> + WasmCompatSend>(
        &self,
        req: VectorSearchRequest<Self::Filter>,
    ) -> impl Future<Output = Result<Vec<(f64, String, T)>, VectorStoreError>> + WasmCompatSend;

    fn top_n_ids(
        &self,
        req: VectorSearchRequest<Self::Filter>,
    ) -> impl Future<Output = Result<Vec<(f64, String)>, VectorStoreError>> + WasmCompatSend;
}

Requirements

  • Implement both top_n and top_n_ids
  • Define an appropriate Filter type for your backend
  • Use WasmCompatSend/WasmCompatSync bounds (see below)
  • Return proper VectorStoreError variants

rig-wasm-compat

Rig supports WebAssembly targets. Since WASM is single-threaded, Send/Sync bounds are unnecessary and can prevent compilation.

The Pattern

rust
#[cfg(not(target_family = "wasm"))]
pub trait WasmCompatSend: Send {}  // native

#[cfg(target_family = "wasm")]
pub trait WasmCompatSend {}        // wasm

#[cfg(not(target_family = "wasm"))]
pub trait WasmCompatSync: Sync {}  // native

#[cfg(target_family = "wasm")]
pub trait WasmCompatSync {}        // wasm

Usage Rules

Always use WasmCompatSend and WasmCompatSync instead of raw Send and Sync in trait bounds.

rust
// Correct
pub trait MyTrait: WasmCompatSend + WasmCompatSync {
    fn do_thing(&self) -> impl Future<Output = ()> + WasmCompatSend;
}

// Incorrect - will break WASM builds
pub trait MyTrait: Send + Sync {
    fn do_thing(&self) -> impl Future<Output = ()> + Send;
}

Boxed Futures

Use WasmBoxedFuture for boxed futures:

rust
pub type WasmBoxedFuture<'a, T> = Pin<Box<dyn Future<Output = T> + Send + 'a>>;  // native
pub type WasmBoxedFuture<'a, T> = Pin<Box<dyn Future<Output = T> + 'a>>;         // wasm

Conditional Error Types

Some error types need platform-specific bounds:

rust
#[derive(Debug, thiserror::Error)]
pub enum MyError {
    #[cfg(not(target_family = "wasm"))]
    #[error("Error: {0}")]
    Inner(#[from] Box<dyn std::error::Error + Send + Sync + 'static>),

    #[cfg(target_family = "wasm")]
    #[error("Error: {0}")]
    Inner(#[from] Box<dyn std::error::Error + 'static>),
}