pub mod auth;
pub mod extension;
pub mod model_listing;
pub mod wire;
pub use crate::client::copilot::Copilot;
pub use wire::CopilotConfig;
use crate::completion;
use crate::providers::openai;
use serde::{Deserialize, Serialize};
pub(crate) const GITHUB_COPILOT_API_BASE_URL: &str = "https://api.githubcopilot.com";
pub(crate) const EDITOR_PLUGIN_VERSION: &str = "copilot-chat/0.35.0";
pub(crate) const USER_AGENT: &str = "GitHubCopilotChat/0.35.0";
pub(crate) const EDITOR_VERSION: &str = "vscode/1.107.0";
const API_VERSION: &str = "2025-04-01";
pub const PROVIDER_NAME: &str = "copilot";
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum CopilotIntent {
#[default]
Panel,
Edits,
}
impl CopilotIntent {
pub fn as_header(self) -> &'static str {
match self {
CopilotIntent::Panel => "conversation-panel",
CopilotIntent::Edits => "conversation-edits",
}
}
}
pub const GPT_4: &str = "gpt-4";
pub const GPT_4O: &str = "gpt-4o";
pub const GPT_4O_MINI: &str = "gpt-4o-mini";
pub const GPT_4_1: &str = "gpt-4.1";
pub const GPT_4_1_MINI: &str = "gpt-4.1-mini";
pub const GPT_4_1_NANO: &str = "gpt-4.1-nano";
pub const GPT_5_3_CODEX: &str = "gpt-5.3-codex";
pub const GPT_5_1_CODEX: &str = "gpt-5.1-codex";
pub const GPT_5_5: &str = "gpt-5.5";
pub const GPT_5_4: &str = "gpt-5.4";
pub const CLAUDE_SONNET_4: &str = "claude-sonnet-4";
pub const CLAUDE_SONNET_4_6: &str = "claude-sonnet-4.6";
pub const CLAUDE_OPUS_4_6: &str = "claude-opus-4.6";
pub const CLAUDE_OPUS_4_7: &str = "claude-opus-4.7";
pub const CLAUDE_3_5_SONNET: &str = "claude-3.5-sonnet";
pub const GEMINI_3_FLASH: &str = "gemini-3-flash-preview";
pub const GEMINI_3_1_PRO_FLASH: &str = "gemini-3.1-pro-preview";
pub const GEMINI_2_0_FLASH: &str = "gemini-2.0-flash-001";
pub const O3_MINI: &str = "o3-mini";
pub const TEXT_EMBEDDING_3_SMALL: &str = "text-embedding-3-small";
pub const TEXT_EMBEDDING_3_LARGE: &str = "text-embedding-3-large";
pub const TEXT_EMBEDDING_ADA_002: &str = "text-embedding-ada-002";
pub use openai::EncodingFormat;
pub(crate) fn default_headers(
api_key: &str,
initiator: &'static str,
has_vision: bool,
intent: CopilotIntent,
) -> Vec<(&'static str, String)> {
let mut headers = vec![
(
http::header::AUTHORIZATION.as_str(),
format!("Bearer {api_key}"),
),
("copilot-integration-id", "vscode-chat".to_string()),
("editor-version", EDITOR_VERSION.to_string()),
("editor-plugin-version", EDITOR_PLUGIN_VERSION.to_string()),
("user-agent", USER_AGENT.to_string()),
("openai-intent", intent.as_header().to_string()),
("x-github-api-version", API_VERSION.to_string()),
("x-request-id", crate::id::generate()),
(
"x-vscode-user-agent-library-version",
"electron-fetch".to_string(),
),
("X-Initiator", initiator.to_string()),
];
if has_vision {
headers.push(("copilot-vision-request", "true".to_string()));
}
headers
}
pub(crate) fn request_initiator(request: &completion::CompletionRequest) -> &'static str {
use crate::completion::Message;
let agent = request.chat_history.iter().any(|message| match message {
Message::Assistant(_) => true,
Message::User { content } => content
.iter()
.any(|item| matches!(item, crate::message::UserContent::ToolResult(_))),
Message::System { .. } => false,
});
if agent { "agent" } else { "user" }
}
pub(crate) fn request_has_vision(request: &completion::CompletionRequest) -> bool {
request.chat_history.iter().any(|message| match message {
crate::completion::Message::User { content } => content
.iter()
.any(|item| matches!(item, crate::message::UserContent::Image(_))),
_ => false,
})
}
#[cfg(test)]
mod tests;