Skip to main content

chasm_cli/providers/
mod.rs

1// Copyright (c) 2024-2026 Nervosys LLC
2// SPDX-License-Identifier: Apache-2.0
3//! LLM Provider integrations for Chat System Manager
4//!
5//! Supports multiple chat providers:
6//!
7//! ## Local Providers
8//! - VS Code Copilot Chat (default)
9//! - Cursor
10//! - Ollama
11//! - vLLM
12//! - Azure AI Foundry (Foundry Local)
13//! - OpenAI API compatible servers
14//! - LM Studio
15//! - LocalAI
16//!
17//! ## Cloud Providers (conversation history import)
18//! - ChatGPT (OpenAI)
19//! - Claude (Anthropic)
20//! - Perplexity
21//! - DeepSeek
22//! - Gemini (Google)
23//! - Qwen (Alibaba)
24//! - Mistral
25//! - Cohere
26//! - Groq
27//! - Together AI
28
29#[allow(dead_code)]
30pub mod cloud;
31pub mod config;
32pub mod continuedev;
33pub mod cursor;
34#[allow(dead_code)]
35pub mod discovery;
36pub mod ollama;
37pub mod openai_compat;
38#[allow(dead_code)]
39pub mod session_format;
40
41#[allow(unused_imports)]
42pub use cloud::{CloudConversation, CloudMessage, CloudProvider, FetchOptions};
43pub use config::ProviderType;
44#[allow(unused_imports)]
45pub use config::{CsmConfig, ProviderConfig};
46#[allow(unused_imports)]
47pub use discovery::discover_all_providers;
48#[allow(unused_imports)]
49pub use session_format::{GenericMessage, GenericSession};
50
51use crate::models::ChatSession;
52use anyhow::Result;
53use std::path::PathBuf;
54
55/// Trait for LLM chat providers
56pub trait ChatProvider: Send + Sync {
57    /// Get the provider type
58    fn provider_type(&self) -> ProviderType;
59
60    /// Get the provider name for display
61    fn name(&self) -> &str;
62
63    /// Check if this provider is available/configured
64    fn is_available(&self) -> bool;
65
66    /// Get the base path where sessions are stored
67    fn sessions_path(&self) -> Option<PathBuf>;
68
69    /// List all chat sessions from this provider
70    fn list_sessions(&self) -> Result<Vec<ChatSession>>;
71
72    /// Import a session from this provider into CSM format
73    fn import_session(&self, session_id: &str) -> Result<ChatSession>;
74
75    /// Export a CSM session to this provider's format
76    #[allow(dead_code)]
77    fn export_session(&self, session: &ChatSession) -> Result<()>;
78}
79
80/// Registry of available providers
81pub struct ProviderRegistry {
82    providers: Vec<Box<dyn ChatProvider>>,
83}
84
85impl ProviderRegistry {
86    /// Create a new provider registry with auto-discovered providers
87    pub fn new() -> Self {
88        let mut registry = Self {
89            providers: Vec::new(),
90        };
91        registry.discover_providers();
92        registry
93    }
94
95    /// Discover and register available providers
96    fn discover_providers(&mut self) {
97        // Add Cursor provider
98        if let Some(provider) = cursor::CursorProvider::discover() {
99            self.providers.push(Box::new(provider));
100        }
101
102        // Add Ollama provider
103        if let Some(provider) = ollama::OllamaProvider::discover() {
104            self.providers.push(Box::new(provider));
105        }
106
107        // Add OpenAI-compatible providers (vLLM, LM Studio, LocalAI, etc.)
108        for provider in openai_compat::discover_openai_compatible_providers() {
109            self.providers.push(Box::new(provider));
110        }
111    }
112
113    /// Get all registered providers
114    pub fn providers(&self) -> &[Box<dyn ChatProvider>] {
115        &self.providers
116    }
117
118    /// Get available (configured and working) providers
119    pub fn available_providers(&self) -> Vec<&dyn ChatProvider> {
120        self.providers
121            .iter()
122            .filter(|p| p.is_available())
123            .map(|p| p.as_ref())
124            .collect()
125    }
126
127    /// Get a provider by type
128    pub fn get_provider(&self, provider_type: ProviderType) -> Option<&dyn ChatProvider> {
129        self.providers
130            .iter()
131            .find(|p| p.provider_type() == provider_type)
132            .map(|p| p.as_ref())
133    }
134
135    /// List all sessions from all providers
136    #[allow(dead_code)]
137    pub fn list_all_sessions(&self) -> Result<Vec<(ProviderType, ChatSession)>> {
138        let mut all_sessions = Vec::new();
139
140        for provider in &self.providers {
141            if provider.is_available() {
142                if let Ok(sessions) = provider.list_sessions() {
143                    for session in sessions {
144                        all_sessions.push((provider.provider_type(), session));
145                    }
146                }
147            }
148        }
149
150        Ok(all_sessions)
151    }
152}
153
154impl Default for ProviderRegistry {
155    fn default() -> Self {
156        Self::new()
157    }
158}