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 cursor;
33#[allow(dead_code)]
34pub mod discovery;
35pub mod ollama;
36pub mod openai_compat;
37#[allow(dead_code)]
38pub mod session_format;
39
40#[allow(unused_imports)]
41pub use cloud::{CloudConversation, CloudMessage, CloudProvider, FetchOptions};
42pub use config::ProviderType;
43#[allow(unused_imports)]
44pub use config::{CsmConfig, ProviderConfig};
45#[allow(unused_imports)]
46pub use discovery::discover_all_providers;
47#[allow(unused_imports)]
48pub use session_format::{GenericMessage, GenericSession};
49
50use crate::models::ChatSession;
51use anyhow::Result;
52use std::path::PathBuf;
53
54/// Trait for LLM chat providers
55pub trait ChatProvider: Send + Sync {
56    /// Get the provider type
57    fn provider_type(&self) -> ProviderType;
58
59    /// Get the provider name for display
60    fn name(&self) -> &str;
61
62    /// Check if this provider is available/configured
63    fn is_available(&self) -> bool;
64
65    /// Get the base path where sessions are stored
66    fn sessions_path(&self) -> Option<PathBuf>;
67
68    /// List all chat sessions from this provider
69    fn list_sessions(&self) -> Result<Vec<ChatSession>>;
70
71    /// Import a session from this provider into CSM format
72    fn import_session(&self, session_id: &str) -> Result<ChatSession>;
73
74    /// Export a CSM session to this provider's format
75    #[allow(dead_code)]
76    fn export_session(&self, session: &ChatSession) -> Result<()>;
77}
78
79/// Registry of available providers
80pub struct ProviderRegistry {
81    providers: Vec<Box<dyn ChatProvider>>,
82}
83
84impl ProviderRegistry {
85    /// Create a new provider registry with auto-discovered providers
86    pub fn new() -> Self {
87        let mut registry = Self {
88            providers: Vec::new(),
89        };
90        registry.discover_providers();
91        registry
92    }
93
94    /// Discover and register available providers
95    fn discover_providers(&mut self) {
96        // Add Cursor provider
97        if let Some(provider) = cursor::CursorProvider::discover() {
98            self.providers.push(Box::new(provider));
99        }
100
101        // Add Ollama provider
102        if let Some(provider) = ollama::OllamaProvider::discover() {
103            self.providers.push(Box::new(provider));
104        }
105
106        // Add OpenAI-compatible providers (vLLM, LM Studio, LocalAI, etc.)
107        for provider in openai_compat::discover_openai_compatible_providers() {
108            self.providers.push(Box::new(provider));
109        }
110    }
111
112    /// Get all registered providers
113    pub fn providers(&self) -> &[Box<dyn ChatProvider>] {
114        &self.providers
115    }
116
117    /// Get available (configured and working) providers
118    pub fn available_providers(&self) -> Vec<&dyn ChatProvider> {
119        self.providers
120            .iter()
121            .filter(|p| p.is_available())
122            .map(|p| p.as_ref())
123            .collect()
124    }
125
126    /// Get a provider by type
127    pub fn get_provider(&self, provider_type: ProviderType) -> Option<&dyn ChatProvider> {
128        self.providers
129            .iter()
130            .find(|p| p.provider_type() == provider_type)
131            .map(|p| p.as_ref())
132    }
133
134    /// List all sessions from all providers
135    #[allow(dead_code)]
136    pub fn list_all_sessions(&self) -> Result<Vec<(ProviderType, ChatSession)>> {
137        let mut all_sessions = Vec::new();
138
139        for provider in &self.providers {
140            if provider.is_available() {
141                if let Ok(sessions) = provider.list_sessions() {
142                    for session in sessions {
143                        all_sessions.push((provider.provider_type(), session));
144                    }
145                }
146            }
147        }
148
149        Ok(all_sessions)
150    }
151}
152
153impl Default for ProviderRegistry {
154    fn default() -> Self {
155        Self::new()
156    }
157}