fix(providers): set X-Initiator header on GitHub Copilot requests (#8809)
Signed-off-by: itscooper <chris.cooper@owasp.org> Signed-off-by: Douwe Osinga <douwe@squareup.com> Co-authored-by: Douwe Osinga <douwe@squareup.com>
This commit is contained in:
@@ -14,8 +14,14 @@ use std::collections::HashMap;
|
|||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
|
// Task-local so complete() and stream() can't race on the same provider instance.
|
||||||
|
tokio::task_local! {
|
||||||
|
static IS_AGENT_CALL: bool;
|
||||||
|
}
|
||||||
|
|
||||||
use super::base::{
|
use super::base::{
|
||||||
Provider, ProviderDef, ProviderMetadata, ProviderUsage, Usage, DEFAULT_PROVIDER_TIMEOUT_SECS,
|
collect_stream, Provider, ProviderDef, ProviderMetadata, ProviderUsage, Usage,
|
||||||
|
DEFAULT_PROVIDER_TIMEOUT_SECS,
|
||||||
};
|
};
|
||||||
use super::errors::ProviderError;
|
use super::errors::ProviderError;
|
||||||
use super::formats::openai::{create_request, get_usage, response_to_message};
|
use super::formats::openai::{create_request, get_usage, response_to_message};
|
||||||
@@ -24,7 +30,7 @@ use super::retry::ProviderRetry;
|
|||||||
use super::utils::{get_model, ImageFormat, RequestLog};
|
use super::utils::{get_model, ImageFormat, RequestLog};
|
||||||
|
|
||||||
use crate::config::{Config, ConfigError};
|
use crate::config::{Config, ConfigError};
|
||||||
use crate::conversation::message::Message;
|
use crate::conversation::message::{Message, MessageContent};
|
||||||
|
|
||||||
use crate::model::ModelConfig;
|
use crate::model::ModelConfig;
|
||||||
use crate::providers::base::{ConfigKey, MessageStream};
|
use crate::providers::base::{ConfigKey, MessageStream};
|
||||||
@@ -244,6 +250,7 @@ impl GithubCopilotProvider {
|
|||||||
async fn post(
|
async fn post(
|
||||||
&self,
|
&self,
|
||||||
session_id: Option<&str>,
|
session_id: Option<&str>,
|
||||||
|
is_user_initiated: bool,
|
||||||
payload: &mut Value,
|
payload: &mut Value,
|
||||||
) -> Result<Response, ProviderError> {
|
) -> Result<Response, ProviderError> {
|
||||||
let (endpoint, token) = self.get_api_info().await?;
|
let (endpoint, token) = self.get_api_info().await?;
|
||||||
@@ -252,6 +259,8 @@ impl GithubCopilotProvider {
|
|||||||
if Self::payload_contains_image(payload) {
|
if Self::payload_contains_image(payload) {
|
||||||
headers.insert("Copilot-Vision-Request", "true".parse().unwrap());
|
headers.insert("Copilot-Vision-Request", "true".parse().unwrap());
|
||||||
}
|
}
|
||||||
|
let initiator = if is_user_initiated { "user" } else { "agent" };
|
||||||
|
headers.insert("X-Initiator", initiator.parse().unwrap());
|
||||||
let api_client = ApiClient::new(endpoint.clone(), auth)?.with_headers(headers)?;
|
let api_client = ApiClient::new(endpoint.clone(), auth)?.with_headers(headers)?;
|
||||||
|
|
||||||
api_client
|
api_client
|
||||||
@@ -407,6 +416,30 @@ impl Provider for GithubCopilotProvider {
|
|||||||
self.model.clone()
|
self.model.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// complete_fast() (compaction, title generation) calls this — always agent-initiated.
|
||||||
|
#[tracing::instrument(
|
||||||
|
skip(self, model_config, session_id, system, messages, tools),
|
||||||
|
fields(session.id = %session_id, gen_ai.request.model = %model_config.model_name)
|
||||||
|
)]
|
||||||
|
async fn complete(
|
||||||
|
&self,
|
||||||
|
model_config: &ModelConfig,
|
||||||
|
session_id: &str,
|
||||||
|
system: &str,
|
||||||
|
messages: &[Message],
|
||||||
|
tools: &[Tool],
|
||||||
|
) -> Result<(Message, ProviderUsage), ProviderError> {
|
||||||
|
IS_AGENT_CALL
|
||||||
|
.scope(true, async {
|
||||||
|
collect_stream(
|
||||||
|
self.stream(model_config, session_id, system, messages, tools)
|
||||||
|
.await?,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
async fn stream(
|
async fn stream(
|
||||||
&self,
|
&self,
|
||||||
model_config: &ModelConfig,
|
model_config: &ModelConfig,
|
||||||
@@ -415,6 +448,14 @@ impl Provider for GithubCopilotProvider {
|
|||||||
messages: &[Message],
|
messages: &[Message],
|
||||||
tools: &[Tool],
|
tools: &[Tool],
|
||||||
) -> Result<MessageStream, ProviderError> {
|
) -> Result<MessageStream, ProviderError> {
|
||||||
|
let is_agent_call = IS_AGENT_CALL.try_with(|&v| v).unwrap_or(false);
|
||||||
|
let last_is_tool_response = messages.last().is_some_and(|m| {
|
||||||
|
m.content
|
||||||
|
.iter()
|
||||||
|
.any(|c| matches!(c, MessageContent::ToolResponse(_)))
|
||||||
|
});
|
||||||
|
let is_user_initiated = !is_agent_call && !last_is_tool_response;
|
||||||
|
|
||||||
// Check if this model supports streaming
|
// Check if this model supports streaming
|
||||||
let supports_streaming = GITHUB_COPILOT_STREAM_MODELS
|
let supports_streaming = GITHUB_COPILOT_STREAM_MODELS
|
||||||
.iter()
|
.iter()
|
||||||
@@ -435,7 +476,9 @@ impl Provider for GithubCopilotProvider {
|
|||||||
let response = self
|
let response = self
|
||||||
.with_retry(|| async {
|
.with_retry(|| async {
|
||||||
let mut payload_clone = payload.clone();
|
let mut payload_clone = payload.clone();
|
||||||
let resp = self.post(Some(session_id), &mut payload_clone).await?;
|
let resp = self
|
||||||
|
.post(Some(session_id), is_user_initiated, &mut payload_clone)
|
||||||
|
.await?;
|
||||||
handle_status(resp).await
|
handle_status(resp).await
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
@@ -465,7 +508,8 @@ impl Provider for GithubCopilotProvider {
|
|||||||
let response = self
|
let response = self
|
||||||
.with_retry(|| async {
|
.with_retry(|| async {
|
||||||
let mut payload_clone = payload.clone();
|
let mut payload_clone = payload.clone();
|
||||||
self.post(session_id_opt, &mut payload_clone).await
|
self.post(session_id_opt, is_user_initiated, &mut payload_clone)
|
||||||
|
.await
|
||||||
})
|
})
|
||||||
.await?;
|
.await?;
|
||||||
let response = handle_response_openai_compat(response).await?;
|
let response = handle_response_openai_compat(response).await?;
|
||||||
|
|||||||
Reference in New Issue
Block a user