rig_core/providers/copilot/
wire.rs1use crate::completion::CompletionRequest;
18use crate::providers::openai::responses_api::SystemInstructionsPlacement;
19use crate::providers::openai::wire::{
20 Dialect, DialectHooks, EmbeddingQuirks, OpenAIConfig, Quirks, ResponsesQuirks, Route,
21};
22use crate::wire::Body;
23
24use super::PROVIDER_NAME;
25
26pub(super) const REQUEST_ID_HEADER: Option<&str> = Some("x-request-id");
29
30pub const DIALECT: Dialect = Dialect {
34 base_url_env: Some("GITHUB_COPILOT_API_BASE"),
35 request_id_header: REQUEST_ID_HEADER,
36 quirks: Quirks {
37 hooks: Some(&HOOKS),
38 verify_path: "",
39 catalog_priced: false,
41 base_url_env_alias: Some("COPILOT_BASE_URL"),
42 accepts_file_ids: false,
44 short_lived_key: true,
45 embedding: EmbeddingQuirks {
46 requires_usage: false,
47 ..EmbeddingQuirks::openai()
48 },
49 responses: ResponsesQuirks {
50 strict_tools_by_default: true,
51 system_instructions: SystemInstructionsPlacement::InputSystemMessages,
52 ..ResponsesQuirks::openai()
53 },
54 ..Quirks::openai()
55 },
56 ..Dialect::gateway(
57 PROVIDER_NAME,
58 super::GITHUB_COPILOT_API_BASE_URL,
59 "GITHUB_COPILOT_API_KEY",
60 )
61};
62
63static HOOKS: DialectHooks = DialectHooks {
64 default_endpoint: Some(super::auth::base_url_from_token),
65 model_route: Some(|model| {
66 if routes_through_responses(model) {
67 Route::Responses
68 } else {
69 Route::Chat
70 }
71 }),
72 completion_envelope: Some(completion_envelope),
73 modality_envelope: Some(|provider, request| {
75 stamp(request, provider.api_key.expose(), "user", false)
76 }),
77};
78
79fn completion_envelope(
82 provider: &OpenAIConfig,
83 request: &CompletionRequest,
84 mut builder: http::request::Builder,
85) -> http::request::Builder {
86 for (name, value) in super::default_headers(
87 provider.api_key.expose(),
88 super::request_initiator(request),
89 super::request_has_vision(request),
90 ) {
91 if let Some(headers) = builder.headers_mut() {
92 headers.remove(name);
93 }
94 builder = builder.header(name, value);
95 }
96 builder
97}
98
99pub fn routes_through_responses(model: &str) -> bool {
101 model.to_ascii_lowercase().contains("codex")
102}
103
104pub(super) fn stamp(
110 request: &mut http::Request<Body>,
111 api_key: &str,
112 initiator: &'static str,
113 has_vision: bool,
114) -> Result<(), http::Error> {
115 let map = request.headers_mut();
116 for (name, value) in super::default_headers(api_key, initiator, has_vision) {
117 map.insert(
118 http::HeaderName::from_bytes(name.as_bytes())?,
119 http::HeaderValue::from_str(&value)?,
120 );
121 }
122 Ok(())
123}
124
125#[cfg(test)]
126mod tests;