Skip to main content

elizaos_plugin_copilot_proxy/
lib.rs

1//! Copilot Proxy model provider plugin for elizaOS.
2//!
3//! This crate provides:
4//! - A typed HTTP client for the Copilot Proxy server (`CopilotProxyClient`)
5//! - A service layer for managing proxy interactions (`CopilotProxyService`)
6//! - A high-level plugin wrapper (`CopilotProxyPlugin`)
7//! - A helper to construct an elizaOS plugin definition
8
9#![warn(missing_docs)]
10
11/// HTTP client for the Copilot Proxy server.
12pub mod client;
13/// Configuration types.
14pub mod config;
15/// Error types and result aliases.
16pub mod error;
17/// Model provider implementations.
18pub mod providers;
19/// Service layer for managing interactions.
20pub mod service;
21/// Type definitions.
22pub mod types;
23
24pub use client::CopilotProxyClient;
25pub use config::{
26    normalize_base_url, CopilotProxyConfig, AVAILABLE_MODELS, DEFAULT_BASE_URL,
27    DEFAULT_CONTEXT_WINDOW, DEFAULT_LARGE_MODEL, DEFAULT_MAX_TOKENS, DEFAULT_SMALL_MODEL,
28    DEFAULT_TIMEOUT_SECONDS,
29};
30pub use error::{CopilotProxyError, Result};
31pub use providers::{
32    get_available_models, get_default_models, is_known_model, CopilotProxyModelProvider,
33    ModelCost, ModelDefinition, ModelProviderConfig,
34};
35pub use service::{get_service, initialize_service, CopilotProxyService};
36pub use types::{
37    ChatCompletionChoice, ChatCompletionRequest, ChatCompletionResponse, ChatMessage, ChatRole,
38    ModelInfo, ModelsResponse, TextGenerationParams, TextGenerationResult, TokenUsage,
39};
40
41use anyhow::Result as AnyhowResult;
42use std::sync::Arc;
43
44/// High-level Copilot Proxy plugin wrapper.
45pub struct CopilotProxyPlugin {
46    provider: CopilotProxyModelProvider,
47}
48
49impl CopilotProxyPlugin {
50    /// Create a new plugin with the given configuration.
51    pub fn new(config: CopilotProxyConfig) -> Result<Self> {
52        config.validate()?;
53        Ok(Self {
54            provider: CopilotProxyModelProvider::new(config),
55        })
56    }
57
58    /// Create a plugin from environment variables.
59    pub fn from_env() -> Result<Self> {
60        let config = CopilotProxyConfig::from_env();
61        Self::new(config)
62    }
63
64    /// Initialize the plugin.
65    pub async fn initialize(&self) -> Result<()> {
66        self.provider.initialize().await
67    }
68
69    /// Check if the plugin is available.
70    pub async fn is_available(&self) -> bool {
71        self.provider.is_available().await
72    }
73
74    /// Generate text using the default (large) model.
75    pub async fn generate_text(&self, prompt: &str) -> Result<String> {
76        self.provider.generate_text_large(prompt).await
77    }
78
79    /// Generate text using the small model.
80    pub async fn generate_text_small(&self, prompt: &str) -> Result<String> {
81        self.provider.generate_text_small(prompt).await
82    }
83
84    /// Generate text using the large model.
85    pub async fn generate_text_large(&self, prompt: &str) -> Result<String> {
86        self.provider.generate_text_large(prompt).await
87    }
88
89    /// Generate a JSON object using the small model.
90    pub async fn generate_object_small(&self, prompt: &str) -> Result<serde_json::Value> {
91        self.provider.generate_object_small(prompt).await
92    }
93
94    /// Generate a JSON object using the large model.
95    pub async fn generate_object_large(&self, prompt: &str) -> Result<serde_json::Value> {
96        self.provider.generate_object_large(prompt).await
97    }
98
99    /// Get the model provider.
100    pub fn provider(&self) -> &CopilotProxyModelProvider {
101        &self.provider
102    }
103
104    /// Shutdown the plugin.
105    pub async fn shutdown(&self) {
106        self.provider.shutdown().await
107    }
108}
109
110/// Construct a Copilot Proxy plugin from environment variables.
111pub fn get_copilot_proxy_plugin() -> AnyhowResult<CopilotProxyPlugin> {
112    CopilotProxyPlugin::from_env()
113        .map_err(|e| anyhow::anyhow!("Failed to create Copilot Proxy plugin: {}", e))
114}
115
116/// Create an elizaOS plugin wired to Copilot Proxy model handlers.
117pub fn create_copilot_proxy_elizaos_plugin() -> AnyhowResult<elizaos::types::Plugin> {
118    use elizaos::types::{Plugin, PluginDefinition};
119    use std::collections::HashMap;
120
121    let plugin = Arc::new(get_copilot_proxy_plugin()?);
122
123    let mut model_handlers: HashMap<String, elizaos::types::ModelHandlerFn> = HashMap::new();
124
125    // TEXT_LARGE handler
126    let plugin_large = plugin.clone();
127    model_handlers.insert(
128        "TEXT_LARGE".to_string(),
129        Box::new(move |params: serde_json::Value| {
130            let plugin = plugin_large.clone();
131            Box::pin(async move {
132                let prompt = params
133                    .get("prompt")
134                    .and_then(|v| v.as_str())
135                    .unwrap_or("");
136                plugin
137                    .generate_text_large(prompt)
138                    .await
139                    .map_err(|e| anyhow::anyhow!("Copilot Proxy error: {}", e))
140            })
141        }),
142    );
143
144    // TEXT_SMALL handler
145    let plugin_small = plugin.clone();
146    model_handlers.insert(
147        "TEXT_SMALL".to_string(),
148        Box::new(move |params: serde_json::Value| {
149            let plugin = plugin_small.clone();
150            Box::pin(async move {
151                let prompt = params
152                    .get("prompt")
153                    .and_then(|v| v.as_str())
154                    .unwrap_or("");
155                plugin
156                    .generate_text_small(prompt)
157                    .await
158                    .map_err(|e| anyhow::anyhow!("Copilot Proxy error: {}", e))
159            })
160        }),
161    );
162
163    // OBJECT_LARGE handler
164    let plugin_obj_large = plugin.clone();
165    model_handlers.insert(
166        "OBJECT_LARGE".to_string(),
167        Box::new(move |params: serde_json::Value| {
168            let plugin = plugin_obj_large.clone();
169            Box::pin(async move {
170                let prompt = params
171                    .get("prompt")
172                    .and_then(|v| v.as_str())
173                    .unwrap_or("");
174                let result = plugin
175                    .generate_object_large(prompt)
176                    .await
177                    .map_err(|e| anyhow::anyhow!("Copilot Proxy error: {}", e))?;
178                Ok(serde_json::to_string(&result).unwrap_or_default())
179            })
180        }),
181    );
182
183    // OBJECT_SMALL handler
184    let plugin_obj_small = plugin.clone();
185    model_handlers.insert(
186        "OBJECT_SMALL".to_string(),
187        Box::new(move |params: serde_json::Value| {
188            let plugin = plugin_obj_small.clone();
189            Box::pin(async move {
190                let prompt = params
191                    .get("prompt")
192                    .and_then(|v| v.as_str())
193                    .unwrap_or("");
194                let result = plugin
195                    .generate_object_small(prompt)
196                    .await
197                    .map_err(|e| anyhow::anyhow!("Copilot Proxy error: {}", e))?;
198                Ok(serde_json::to_string(&result).unwrap_or_default())
199            })
200        }),
201    );
202
203    Ok(Plugin {
204        definition: PluginDefinition {
205            name: "copilot-proxy".to_string(),
206            description: "Copilot Proxy model provider for elizaOS".to_string(),
207            ..Default::default()
208        },
209        model_handlers,
210        ..Default::default()
211    })
212}