Engine Abstraction Layer Architecture
Overviewβ
The Engine Abstraction Layer provides a unified interface for interacting with multiple AI providers (Claude, OpenAI, Gemini, etc.) through a pluggable, trait-based architecture. This design enables users to switch between providers seamlessly while maintaining a consistent API.
Core Componentsβ
Engine Traitβ
The Engine trait (crates/radium-core/src/engines/engine_trait.rs) defines the interface that all AI providers must implement:
#[async_trait]
pub trait Engine: Send + Sync {
fn metadata(&self) -> &EngineMetadata;
async fn is_available(&self) -> bool;
async fn is_authenticated(&self) -> Result<bool>;
async fn execute(&self, request: ExecutionRequest) -> Result<ExecutionResponse>;
fn default_model(&self) -> String;
fn available_models(&self) -> Vec<String>;
}
Key Responsibilities:
- Metadata: Provides engine identification and capabilities
- Availability: Checks if the engine is ready to use (binary exists, API accessible)
- Authentication: Verifies credentials are configured
- Execution: Processes requests and returns responses
- Model Management: Lists available models and provides defaults
Engine Metadataβ
EngineMetadata contains static information about an engine:
id: Unique identifier (e.g., "claude", "openai", "gemini")name: Human-readable namedescription: Brief description of the enginecli_command: Optional CLI binary name (for CLI-based engines)models: List of supported model identifiersrequires_auth: Whether authentication is requiredversion: Optional version string
Execution Request/Responseβ
ExecutionRequest contains:
model: Model identifier to useprompt: User prompt or messagesystem: Optional system messagetemperature: Optional temperature (0.0-1.0)max_tokens: Optional maximum tokens to generateparams: Additional provider-specific parameters
ExecutionResponse contains:
content: Generated text contentusage: Optional token usage informationmodel: Model identifier usedraw: Optional raw response for debugging
Engine Registryβ
The EngineRegistry (crates/radium-core/src/engines/registry.rs) manages engine instances and provides:
Core Operations:
register(engine): Register a new engine instanceget(id): Retrieve an engine by IDlist(): List all registered enginesset_default(id): Set the default engineget_default(): Get the default enginecheck_health(timeout): Check health of all engines
Configuration Management:
load_config(): Load configuration from.radium/config.tomlsave_config(): Persist configuration to diskwith_config_path(path): Create registry with config path
Thread Safety:
- Uses
Arc<RwLock<>>for thread-safe concurrent access - Engines are wrapped in
Arc<dyn Engine>for shared ownership
Architecture Patternsβ
Registry Patternβ
The registry pattern centralizes engine management:
βββββββββββββββββββ
β EngineRegistry β
β β
β βββββββββββββββ β
β β Engines β ββββ HashMap<String, Arc<dyn Engine>>
β β (HashMap) β β
β βββββββββββββββ β
β β
β βββββββββββββββ β
β β Config β ββββ .radium/config.toml
β β (TOML) β β
β βββββββββββββββ β
βββββββββββββββββββ
Provider Patternβ
Each provider implements the Engine trait:
ββββββββββββββββ
β Engine Trait β
ββββββββ¬ββββββββ
β
ββββ MockEngine
ββββ ClaudeEngine
ββββ OpenAIEngine
ββββ GeminiEngine
Configuration Hierarchyβ
Configuration follows a precedence order:
- Workspace Config (
.radium/config.toml) - Highest priority - Global Config (
~/.radium/config.toml) - Fallback - Defaults - Built-in defaults
Lifecycle Managementβ
Engine Registrationβ
- Engine instances are created (e.g.,
ClaudeEngine::new()) - Engines are registered with the registry:
registry.register(Arc::new(engine)) - Configuration is loaded from disk
- Default engine is set (if configured)
Request Execution Flowβ
CLI/API Request
β
ββ> EngineRegistry::get_default()
β β
β ββ> Engine::execute(request)
β β
β ββ> Check authentication
β ββ> Validate request
β ββ> Call provider API
β ββ> Return ExecutionResponse
β
ββ> Response returned to caller
Health Monitoringβ
Health checks verify engine availability and authentication:
pub async fn check_health(&self, timeout_secs: u64) -> Vec<EngineHealth>
Each health check:
- Verifies engine availability (
is_available()) - Checks authentication status (
is_authenticated()) - Returns
HealthStatus(Healthy, Warning, Failed) - Respects timeout for slow checks
Error Handlingβ
The engine system uses a unified error type (EngineError):
NotFound: Engine not found in registryAuthenticationFailed: Credential issuesExecutionError: Request/response errorsInvalidConfig: Configuration parsing errorsRegistryError: Registry operation failuresIo: File system errors
Configuration Formatβ
Engine configuration is stored in TOML format:
[engines]
default = "gemini"
[engines.gemini]
default_model = "gemini-2.0-flash-exp"
temperature = 0.7
max_tokens = 4096
[engines.openai]
default_model = "gpt-4-turbo"
temperature = 0.8
Authenticationβ
Engines use the CredentialStore for API key management:
- Credentials stored in
~/.radium/credentials.json - Provider-specific keys (Claude, OpenAI, Gemini)
- Secure storage with encryption support
- Environment variable fallback (future)
Current Providersβ
Mock Engineβ
- ID:
mock - Purpose: Testing and development
- Auth: Not required
- Models:
mock-model-1,mock-model-2
Claude Engineβ
- ID:
claude - Provider: Anthropic
- Auth: Required (API key)
- Models:
claude-3-opus-20240229,claude-3-sonnet-20240229,claude-3-haiku-20240307 - API: REST API (https://api.anthropic.com/v1/messages)
OpenAI Engineβ
- ID:
openai - Provider: OpenAI
- Auth: Required (API key)
- Models:
gpt-4,gpt-4-turbo,gpt-3.5-turbo - API: Uses
radium-models::OpenAIModel
Gemini Engineβ
- ID:
gemini - Provider: Google
- Auth: Required (API key)
- Models:
gemini-pro,gemini-pro-vision,gemini-2.0-flash-exp - API: Uses
radium-models::GeminiModel
Extension Pointsβ
Adding New Providersβ
To add a new provider:
- Implement the
Enginetrait - Create provider struct with metadata
- Implement
execute()method - Register in CLI initialization
- Add authentication support (if needed)
See docs/guides/adding-new-engine-provider.md for detailed instructions.
Custom Configurationβ
Engines can extend configuration by:
- Adding custom fields to
ExecutionRequest.params - Implementing provider-specific config sections
- Using environment variables for sensitive data
Performance Considerationsβ
- Concurrent Access: Registry uses
RwLockfor read-heavy workloads - Health Checks: Timeout-based to prevent blocking
- Caching: Engine instances are reused (Arc-based)
- Async Execution: All I/O operations are async
Future Enhancementsβ
- Streaming response support
- Vision/multimodal capabilities
- Function calling/tool use
- Advanced metrics and monitoring
- Cost tracking per engine
- Provider-specific optimizations