rig_core/providers/copilot/
mod.rs1pub mod auth;
31pub mod extension;
32pub mod model_listing;
33pub mod wire;
34
35pub use crate::client::copilot::Copilot;
36pub use wire::CopilotConfig;
37
38use crate::completion;
39use crate::providers::openai;
40use serde::{Deserialize, Serialize};
41
42pub(crate) const GITHUB_COPILOT_API_BASE_URL: &str = "https://api.githubcopilot.com";
44pub(crate) const EDITOR_PLUGIN_VERSION: &str = "copilot-chat/0.35.0";
45pub(crate) const USER_AGENT: &str = "GitHubCopilotChat/0.35.0";
46pub(crate) const EDITOR_VERSION: &str = "vscode/1.107.0";
47const API_VERSION: &str = "2025-04-01";
48
49pub const PROVIDER_NAME: &str = "copilot";
51
52#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
54#[serde(rename_all = "snake_case")]
55pub enum CopilotIntent {
56 #[default]
57 Panel,
58 Edits,
59}
60
61impl CopilotIntent {
62 pub fn as_header(self) -> &'static str {
63 match self {
64 CopilotIntent::Panel => "conversation-panel",
65 CopilotIntent::Edits => "conversation-edits",
66 }
67 }
68}
69
70pub const GPT_4: &str = "gpt-4";
72pub const GPT_4O: &str = "gpt-4o";
74pub const GPT_4O_MINI: &str = "gpt-4o-mini";
76pub const GPT_4_1: &str = "gpt-4.1";
78pub const GPT_4_1_MINI: &str = "gpt-4.1-mini";
80pub const GPT_4_1_NANO: &str = "gpt-4.1-nano";
82pub const GPT_5_3_CODEX: &str = "gpt-5.3-codex";
84pub const GPT_5_1_CODEX: &str = "gpt-5.1-codex";
86pub const GPT_5_5: &str = "gpt-5.5";
88pub const GPT_5_4: &str = "gpt-5.4";
90pub const CLAUDE_SONNET_4: &str = "claude-sonnet-4";
92pub const CLAUDE_SONNET_4_6: &str = "claude-sonnet-4.6";
94pub const CLAUDE_OPUS_4_6: &str = "claude-opus-4.6";
96pub const CLAUDE_OPUS_4_7: &str = "claude-opus-4.7";
98pub const CLAUDE_3_5_SONNET: &str = "claude-3.5-sonnet";
100pub const GEMINI_3_FLASH: &str = "gemini-3-flash-preview";
102pub const GEMINI_3_1_PRO_FLASH: &str = "gemini-3.1-pro-preview";
104pub const GEMINI_2_0_FLASH: &str = "gemini-2.0-flash-001";
106pub const O3_MINI: &str = "o3-mini";
108pub const TEXT_EMBEDDING_3_SMALL: &str = "text-embedding-3-small";
110pub const TEXT_EMBEDDING_3_LARGE: &str = "text-embedding-3-large";
112pub const TEXT_EMBEDDING_ADA_002: &str = "text-embedding-ada-002";
114
115pub use openai::EncodingFormat;
118
119pub(crate) fn default_headers(
121 api_key: &str,
122 initiator: &'static str,
123 has_vision: bool,
124 intent: CopilotIntent,
125) -> Vec<(&'static str, String)> {
126 let mut headers = vec![
127 (
128 http::header::AUTHORIZATION.as_str(),
129 format!("Bearer {api_key}"),
130 ),
131 ("copilot-integration-id", "vscode-chat".to_string()),
132 ("editor-version", EDITOR_VERSION.to_string()),
133 ("editor-plugin-version", EDITOR_PLUGIN_VERSION.to_string()),
134 ("user-agent", USER_AGENT.to_string()),
135 ("openai-intent", intent.as_header().to_string()),
136 ("x-github-api-version", API_VERSION.to_string()),
137 ("x-request-id", crate::id::generate()),
138 (
139 "x-vscode-user-agent-library-version",
140 "electron-fetch".to_string(),
141 ),
142 ("X-Initiator", initiator.to_string()),
143 ];
144
145 if has_vision {
146 headers.push(("copilot-vision-request", "true".to_string()));
147 }
148
149 headers
150}
151
152pub(crate) fn request_initiator(request: &completion::CompletionRequest) -> &'static str {
155 use crate::completion::Message;
156 let agent = request.chat_history.iter().any(|message| match message {
157 Message::Assistant(_) => true,
158 Message::User { content } => content
159 .iter()
160 .any(|item| matches!(item, crate::message::UserContent::ToolResult(_))),
161 Message::System { .. } => false,
162 });
163 if agent { "agent" } else { "user" }
164}
165
166pub(crate) fn request_has_vision(request: &completion::CompletionRequest) -> bool {
169 request.chat_history.iter().any(|message| match message {
170 crate::completion::Message::User { content } => content
171 .iter()
172 .any(|item| matches!(item, crate::message::UserContent::Image(_))),
173 _ => false,
174 })
175}
176
177#[cfg(test)]
178mod tests;