use std::pin::Pin;use std::str::FromStr as _;use std::sync::Arc; use anthropic::AnthropicModelMode;use anyhow::{Result, anyhow};use collections::HashMap;use copilot::{GlobalCopilotAuth, Status};use copilot_chat::responses as copilot_responses;use copilot_chat::{ ChatLocation, ChatMessage, ChatMessageContent, ChatMessagePart, CopilotChat, CopilotChatConfiguration, Function, FunctionContent, ImageUrl, Model as CopilotChatModel, ModelVendor, Request as CopilotChatRequest, ResponseEvent, Tool, ToolCall, ToolCallContent, ToolChoice,};use futures::future::BoxFuture;use futures::stream::BoxStream;use futures::{FutureExt, Stream, StreamExt};use gpui::{App, AsyncApp, Entity, Subscription, Task};use http_client::StatusCode;use language::language_settings::all_language_settings;use language_model::{ AuthenticateError, CompletionIntent, IconOrSvg, LanguageModel, LanguageModelCompletionError, LanguageModelCompletionEvent, LanguageModelCostInfo, LanguageModelEffortLevel, LanguageModelId, LanguageModelName, LanguageModelProvider, LanguageModelProviderId, LanguageModelProviderName, LanguageModelProviderState, LanguageModelRequest, LanguageModelRequestMessage, LanguageModelToolChoice, LanguageModelToolResultContent, LanguageModelToolSchemaFormat, LanguageModelToolUse, MessageContent, ProviderSettingsView, RateLimiter, Role, StopReason, TokenUsage,};use settings::SettingsStore;use ui::prelude::*;use util::debug_panic; use crate::provider::anthropic::{AnthropicEventMapper, AnthropicPromptCacheMode, into_anthropic};use language_model::util::{fix_streamed_json, parse_tool_arguments}; const PROVIDER_ID: LanguageModelProviderId = LanguageModelProviderId::new("copilot_chat");const PROVIDER_NAME: LanguageModelProviderName = LanguageModelProviderName::new("GitHub Copilot Chat"); pub struct CopilotChatLanguageModelProvider { state: Entity<State>,} pub struct State { _copilot_chat_subscription: Option<Subscription>, _settings_subscription: Subscription,} impl State { fn is_authenticated(&self, cx: &App) -> bool { CopilotChat::global(cx) .map(|m| m.read(cx).is_authenticated()) .unwrap_or(false) }} impl CopilotChatLanguageModelProvider { pub fn new(cx: &mut App) -> Self { let state = cx.new(|cx| { let copilot_chat_subscription = CopilotChat::global(cx) .map(|copilot_chat| cx.observe(&copilot_chat, |_, _, cx| cx.notify())); State { _copilot_chat_subscription: copilot_chat_subscription, _settings_subscription: cx.observe_global::<SettingsStore>(|_, cx| { if let Some(copilot_chat) = CopilotChat::global(cx) { let language_settings = all_language_settings(None, cx); let configuration = CopilotChatConfiguration { enterprise_uri: language_settings .edit_predictions .copilot .enterprise_uri .clone(), }; copilot_chat.update(cx, |chat, cx| { chat.set_configuration(configuration, cx); }); } cx.notify(); }), } }); Self { state } } fn create_language_model(&self, model: CopilotChatModel) -> Arc<dyn LanguageModel> { Arc::new(CopilotChatLanguageModel { model, request_limiter: RateLimiter::new(4), }) }} impl LanguageModelProviderState for CopilotChatLanguageModelProvider { type ObservableEntity = State; fn observable_entity(&self) -> Option<Entity<Self::ObservableEntity>> { Some(self.state.clone()) }} impl LanguageModelProvider for CopilotChatLanguageModelProvider { fn id(&self) -> LanguageModelProviderId { PROVIDER_ID } fn name(&self) -> LanguageModelProviderName { PROVIDER_NAME } fn icon(&self) -> IconOrSvg { IconOrSvg::Icon(IconName::Copilot) } fn default_model(&self, cx: &App) -> Option<Arc<dyn LanguageModel>> { let models = CopilotChat::global(cx).and_then(|m| m.read(cx).models())?; models .first() .map(|model| self.create_language_model(model.clone())) } fn default_fast_model(&self, cx: &App) -> Option<Arc<dyn LanguageModel>> { // The default model should be Copilot Chat's 'base model', which is likely a relatively fast // model (e.g. 4o) and a sensible choice when considering premium requests self.default_model(cx) } fn provided_models(&self, cx: &App) -> Vec<Arc<dyn LanguageModel>> { let Some(models) = CopilotChat::global(cx).and_then(|m| m.read(cx).models()) else { return Vec::new(); }; models .iter() .map(|model| self.create_language_model(model.clone())) .collect() } fn is_authenticated(&self, cx: &App) -> bool { self.state.read(cx).is_authenticated(cx) } fn authenticate(&self, cx: &mut App) -> Task<Result<(), AuthenticateError>> { if self.is_authenticated(cx) { return Task::ready(Ok(())); }; let Some(copilot) = GlobalCopilotAuth::try_global(cx).cloned() else { return Task::ready(Err(anyhow!(concat!( "Copilot must be enabled for Copilot Chat to work. ", "Please enable Copilot and try again." )) .into())); }; let err = match copilot.0.read(cx).status() { Status::Authorized => return Task::ready(Ok(())), Status::Disabled => anyhow!( "Copilot must be enabled for Copilot Chat to work. Please enable Copilot and try again." ), Status::Error(err) => anyhow!(format!( "Received the following error while signing into Copilot: {err}" )), Status::Starting { task: _ } => anyhow!( "Copilot is still starting, please wait for Copilot to start then try again" ), Status::Unauthorized => anyhow!( "Unable to authorize with Copilot. Please make sure that you have an active Copilot and Copilot Chat subscription." ), Status::SignedOut { .. } => { anyhow!("You have signed out of Copilot. Please sign in to Copilot and try again.") } Status::SigningIn { prompt: _ } => anyhow!("Still signing into Copilot..."), }; Task::ready(Err(err.into())) } fn settings_view(&self, cx: &mut App) -> Option<ProviderSettingsView> { let is_authenticated = self.state.read(cx).is_authenticated(cx); let title = if is_authenticated { None } else { Some("Configure Copilot".into()) }; let description = if is_authenticated { None } else { Some(language_model::InlineDescription::Text( "Requires an active GitHub Copilot subscription.".into(), )) }; Some(ProviderSettingsView::Inline( language_model::InlineProviderSettings { title, description, create_view: Arc::new(|_window, cx| { cx.new(|cx| { copilot_ui::ConfigurationView::new( |cx| { CopilotChat::global(cx) .map(|m| m.read(cx).is_authenticated()) .unwrap_or(false) }, copilot_ui::ConfigurationMode::Chat, cx, ) .compact() }) .into() }), }, )) }} pub struct CopilotChatLanguageModel { model: CopilotChatModel, request_limiter: RateLimiter,} impl LanguageModel for CopilotChatLanguageModel { fn id(&self) -> LanguageModelId { LanguageModelId::from(self.model.id().to_string()) } fn name(&self) -> LanguageModelName { LanguageModelName::from(self.model.display_name().to_string()) } fn provider_id(&self) -> LanguageModelProviderId { PROVIDER_ID } fn provider_name(&self) -> LanguageModelProviderName { PROVIDER_NAME } fn supports_tools(&self) -> bool { self.model.supports_tools() } fn supports_streaming_tools(&self) -> bool { true } fn supports_images(&self) -> bool { self.model.supports_vision() } fn supports_thinking(&self) -> bool { self.model.can_think() } fn supported_effort_levels(&self) -> Vec<LanguageModelEffortLevel> { let levels = self.model.reasoning_effort_levels(); if levels.is_empty() { return vec![]; } levels .iter() .map(|level| { let name = match level.as_str() { "low" => "Low".into(), "medium" => "Medium".into(), "high" => "High".into(), "xhigh" => "Extra High".into(), _ => language_model::SharedString::from(level.clone()), }; LanguageModelEffortLevel { name, value: language_model::SharedString::from(level.clone()), is_default: level == "high", } }) .collect() } fn tool_input_format(&self) -> LanguageModelToolSchemaFormat { match self.model.vendor() { ModelVendor::OpenAI | ModelVendor::Anthropic => { LanguageModelToolSchemaFormat::JsonSchema } ModelVendor::Google | ModelVendor::XAI | ModelVendor::Unknown => { LanguageModelToolSchemaFormat::JsonSchemaSubset } } } fn supports_tool_choice(&self, choice: LanguageModelToolChoice) -> bool { match choice { LanguageModelToolChoice::Auto | LanguageModelToolChoice::Any | LanguageModelToolChoice::None => self.supports_tools(), } } fn model_cost_info(&self) -> Option<LanguageModelCostInfo> { LanguageModelCostInfo::RequestCost { cost_per_request: self.model.multiplier(), } .into() } fn telemetry_id(&self) -> String { format!("copilot_chat/{}", self.model.id()) } fn max_token_count(&self) -> u64 { self.model.max_token_count() } fn stream_completion( &self, request: LanguageModelRequest, cx: &AsyncApp, ) -> BoxFuture< 'static, Result< BoxStream<'static, Result<LanguageModelCompletionEvent, LanguageModelCompletionError>>, LanguageModelCompletionError, >, > { let is_user_initiated = request.intent.is_none_or(|intent| match intent { CompletionIntent::UserPrompt | CompletionIntent::ThreadContextSummarization | CompletionIntent::InlineAssist | CompletionIntent::TerminalInlineAssist | CompletionIntent::GenerateGitCommitMessage => true, CompletionIntent::Subagent | CompletionIntent::ToolResults | CompletionIntent::ThreadSummarization | CompletionIntent::CreateFile | CompletionIntent::EditFile => false, }); if self.model.supports_messages() { let location = intent_to_chat_location(request.intent); let model = self.model.clone(); let request_limiter = self.request_limiter.clone(); let future = cx.spawn(async move |cx| { let effort = request .thinking_effort .as_ref() .and_then(|e| anthropic::Effort::from_str(e).ok()); let mut anthropic_request = into_anthropic( request, model.id().to_string(), 0.0, model.max_output_tokens() as u64, if model.supports_adaptive_thinking() { AnthropicModelMode::Thinking { budget_tokens: None, } } else if model.supports_thinking() { AnthropicModelMode::Thinking { budget_tokens: compute_thinking_budget( model.min_thinking_budget(), model.max_thinking_budget(), model.max_output_tokens() as u32, ), } } else { AnthropicModelMode::Default }, AnthropicPromptCacheMode::Legacy, &PROVIDER_ID, )?; anthropic_request.temperature = None; // The Copilot proxy doesn't support eager_input_streaming on tools. for tool in &mut anthropic_request.tools { tool.eager_input_streaming = false; } if model.supports_adaptive_thinking() { if anthropic_request.thinking.is_some() { anthropic_request.thinking = Some(anthropic::Thinking::Adaptive { display: Some(anthropic::AdaptiveThinkingDisplay::Summarized), }); anthropic_request.output_config = effort.map(|effort| anthropic::OutputConfig { effort: Some(effort), }); } } let anthropic_beta = if !model.supports_adaptive_thinking() && model.supports_thinking() { Some("interleaved-thinking-2025-05-14".to_string()) } else { None }; let body = serde_json::to_string(&anthropic::StreamingRequest { base: anthropic_request, stream: true, }) .map_err(|e| anyhow::anyhow!(e))?; let stream = CopilotChat::stream_messages( body, location, is_user_initiated, anthropic_beta, cx.clone(), ); request_limiter .stream(async move { let events = stream.await?; let mapper = AnthropicEventMapper::new(PROVIDER_NAME, PROVIDER_ID); Ok(mapper.map_stream(events).boxed()) }) .await }); return async move { Ok(future.await?.boxed()) }.boxed(); } if self.model.supports_response() { let location = intent_to_chat_location(request.intent); let responses_request = match into_copilot_responses(&self.model, request) { Ok(request) => request, Err(error) => return async move { Err(error.into()) }.boxed(), }; let request_limiter = self.request_limiter.clone(); let future = cx.spawn(async move |cx| { let request = CopilotChat::stream_response( responses_request, location, is_user_initiated, cx.clone(), ); request_limiter .stream(async move { let stream = request.await?; let mapper = CopilotResponsesEventMapper::new(); Ok(mapper.map_stream(stream).boxed()) }) .await }); return async move { Ok(future.await?.boxed()) }.boxed(); } let location = intent_to_chat_location(request.intent); let copilot_request = match into_copilot_chat(&self.model, request) { Ok(request) => request, Err(err) => return futures::future::ready(Err(err.into())).boxed(), }; let is_streaming = copilot_request.stream; let request_limiter = self.request_limiter.clone(); let future = cx.spawn(async move |cx| { let request = CopilotChat::stream_completion( copilot_request, location, is_user_initiated, cx.clone(), ); request_limiter .stream(async move { let response = request.await?; Ok(map_to_language_model_completion_events( response, is_streaming, )) }) .await }); async move { Ok(future.await?.boxed()) }.boxed() }} pub fn map_to_language_model_completion_events( events: Pin<Box<dyn Send + Stream<Item = Result<ResponseEvent>>>>, is_streaming: bool,) -> impl Stream<Item = Result<LanguageModelCompletionEvent, LanguageModelCompletionError>> { #[derive(Default)] struct RawToolCall { id: String, name: String, arguments: String, thought_signature: Option<String>, } struct State { events: Pin<Box<dyn Send + Stream<Item = Result<ResponseEvent>>>>, tool_calls_by_index: HashMap<usize, RawToolCall>, reasoning_opaque: Option<String>, reasoning_text: Option<String>, } futures::stream::unfold( State { events,Lines 1–500 of 1950. Load more
use std::pin::Pin; use std::str::FromStr as _; use std::sync::Arc; use anthropic::AnthropicModelMode; use anyhow::{Result, anyhow}; use collections::HashMap; use copilot::{GlobalCopilotAuth, Status}; use copilot_chat::responses as copilot_responses; use copilot_chat::{ ChatLocation, ChatMessage, ChatMessageContent, ChatMessagePart, CopilotChat, CopilotChatConfiguration, Function, FunctionContent, ImageUrl, Model as CopilotChatModel, ModelVendor, Request as CopilotChatRequest, ResponseEvent, Tool, ToolCall, ToolCallContent, ToolChoice, }; use futures::future::BoxFuture; use futures::stream::BoxStream; use futures::{FutureExt, Stream, StreamExt}; use gpui::{App, AsyncApp, Entity, Subscription, Task}; use http_client::StatusCode; use language::language_settings::all_language_settings; use language_model::{ AuthenticateError, CompletionIntent, IconOrSvg, LanguageModel, LanguageModelCompletionError, LanguageModelCompletionEvent, LanguageModelCostInfo, LanguageModelEffortLevel, LanguageModelId, LanguageModelName, LanguageModelProvider, LanguageModelProviderId, LanguageModelProviderName, LanguageModelProviderState, LanguageModelRequest, LanguageModelRequestMessage, LanguageModelToolChoice, LanguageModelToolResultContent, LanguageModelToolSchemaFormat, LanguageModelToolUse, MessageContent, ProviderSettingsView, RateLimiter, Role, StopReason, TokenUsage, }; use settings::SettingsStore; use ui::prelude::*; use util::debug_panic; use crate::provider::anthropic::{AnthropicEventMapper, AnthropicPromptCacheMode, into_anthropic}; use language_model::util::{fix_streamed_json, parse_tool_arguments}; const PROVIDER_ID: LanguageModelProviderId = LanguageModelProviderId::new("copilot_chat"); const PROVIDER_NAME: LanguageModelProviderName = LanguageModelProviderName::new("GitHub Copilot Chat"); pub struct CopilotChatLanguageModelProvider { state: Entity<State>, } pub struct State { _copilot_chat_subscription: Option<Subscription>, _settings_subscription: Subscription, } impl State { fn is_authenticated(&self, cx: &App) -> bool { CopilotChat::global(cx) .map(|m| m.read(cx).is_authenticated()) .unwrap_or(false) } } impl CopilotChatLanguageModelProvider { pub fn new(cx: &mut App) -> Self { let state = cx.new(|cx| { let copilot_chat_subscription = CopilotChat::global(cx) .map(|copilot_chat| cx.observe(&copilot_chat, |_, _, cx| cx.notify())); State { _copilot_chat_subscription: copilot_chat_subscription, _settings_subscription: cx.observe_global::<SettingsStore>(|_, cx| { if let Some(copilot_chat) = CopilotChat::global(cx) { let language_settings = all_language_settings(None, cx); let configuration = CopilotChatConfiguration { enterprise_uri: language_settings .edit_predictions .copilot .enterprise_uri .clone(), }; copilot_chat.update(cx, |chat, cx| { chat.set_configuration(configuration, cx); }); } cx.notify(); }), } }); Self { state } } fn create_language_model(&self, model: CopilotChatModel) -> Arc<dyn LanguageModel> { Arc::new(CopilotChatLanguageModel { model, request_limiter: RateLimiter::new(4), }) } } impl LanguageModelProviderState for CopilotChatLanguageModelProvider { type ObservableEntity = State; fn observable_entity(&self) -> Option<Entity<Self::ObservableEntity>> { Some(self.state.clone()) } } impl LanguageModelProvider for CopilotChatLanguageModelProvider { fn id(&self) -> LanguageModelProviderId { PROVIDER_ID } fn name(&self) -> LanguageModelProviderName { PROVIDER_NAME } fn icon(&self) -> IconOrSvg { IconOrSvg::Icon(IconName::Copilot) } fn default_model(&self, cx: &App) -> Option<Arc<dyn LanguageModel>> { let models = CopilotChat::global(cx).and_then(|m| m.read(cx).models())?; models .first() .map(|model| self.create_language_model(model.clone())) } fn default_fast_model(&self, cx: &App) -> Option<Arc<dyn LanguageModel>> { // The default model should be Copilot Chat's 'base model', which is likely a relatively fast // model (e.g. 4o) and a sensible choice when considering premium requests self.default_model(cx) } fn provided_models(&self, cx: &App) -> Vec<Arc<dyn LanguageModel>> { let Some(models) = CopilotChat::global(cx).and_then(|m| m.read(cx).models()) else { return Vec::new(); }; models .iter() .map(|model| self.create_language_model(model.clone())) .collect() } fn is_authenticated(&self, cx: &App) -> bool { self.state.read(cx).is_authenticated(cx) } fn authenticate(&self, cx: &mut App) -> Task<Result<(), AuthenticateError>> { if self.is_authenticated(cx) { return Task::ready(Ok(())); }; let Some(copilot) = GlobalCopilotAuth::try_global(cx).cloned() else { return Task::ready(Err(anyhow!(concat!( "Copilot must be enabled for Copilot Chat to work. ", "Please enable Copilot and try again." )) .into())); }; let err = match copilot.0.read(cx).status() { Status::Authorized => return Task::ready(Ok(())), Status::Disabled => anyhow!( "Copilot must be enabled for Copilot Chat to work. Please enable Copilot and try again." ), Status::Error(err) => anyhow!(format!( "Received the following error while signing into Copilot: {err}" )), Status::Starting { task: _ } => anyhow!( "Copilot is still starting, please wait for Copilot to start then try again" ), Status::Unauthorized => anyhow!( "Unable to authorize with Copilot. Please make sure that you have an active Copilot and Copilot Chat subscription." ), Status::SignedOut { .. } => { anyhow!("You have signed out of Copilot. Please sign in to Copilot and try again.") } Status::SigningIn { prompt: _ } => anyhow!("Still signing into Copilot..."), }; Task::ready(Err(err.into())) } fn settings_view(&self, cx: &mut App) -> Option<ProviderSettingsView> { let is_authenticated = self.state.read(cx).is_authenticated(cx); let title = if is_authenticated { None } else { Some("Configure Copilot".into()) }; let description = if is_authenticated { None } else { Some(language_model::InlineDescription::Text( "Requires an active GitHub Copilot subscription.".into(), )) }; Some(ProviderSettingsView::Inline( language_model::InlineProviderSettings { title, description, create_view: Arc::new(|_window, cx| { cx.new(|cx| { copilot_ui::ConfigurationView::new( |cx| { CopilotChat::global(cx) .map(|m| m.read(cx).is_authenticated()) .unwrap_or(false) }, copilot_ui::ConfigurationMode::Chat, cx, ) .compact() }) .into() }), }, )) } } pub struct CopilotChatLanguageModel { model: CopilotChatModel, request_limiter: RateLimiter, } impl LanguageModel for CopilotChatLanguageModel { fn id(&self) -> LanguageModelId { LanguageModelId::from(self.model.id().to_string()) } fn name(&self) -> LanguageModelName { LanguageModelName::from(self.model.display_name().to_string()) } fn provider_id(&self) -> LanguageModelProviderId { PROVIDER_ID } fn provider_name(&self) -> LanguageModelProviderName { PROVIDER_NAME } fn supports_tools(&self) -> bool { self.model.supports_tools() } fn supports_streaming_tools(&self) -> bool { true } fn supports_images(&self) -> bool { self.model.supports_vision() } fn supports_thinking(&self) -> bool { self.model.can_think() } fn supported_effort_levels(&self) -> Vec<LanguageModelEffortLevel> { let levels = self.model.reasoning_effort_levels(); if levels.is_empty() { return vec![]; } levels .iter() .map(|level| { let name = match level.as_str() { "low" => "Low".into(), "medium" => "Medium".into(), "high" => "High".into(), "xhigh" => "Extra High".into(), _ => language_model::SharedString::from(level.clone()), }; LanguageModelEffortLevel { name, value: language_model::SharedString::from(level.clone()), is_default: level == "high", } }) .collect() } fn tool_input_format(&self) -> LanguageModelToolSchemaFormat { match self.model.vendor() { ModelVendor::OpenAI | ModelVendor::Anthropic => { LanguageModelToolSchemaFormat::JsonSchema } ModelVendor::Google | ModelVendor::XAI | ModelVendor::Unknown => { LanguageModelToolSchemaFormat::JsonSchemaSubset } } } fn supports_tool_choice(&self, choice: LanguageModelToolChoice) -> bool { match choice { LanguageModelToolChoice::Auto | LanguageModelToolChoice::Any | LanguageModelToolChoice::None => self.supports_tools(), } } fn model_cost_info(&self) -> Option<LanguageModelCostInfo> { LanguageModelCostInfo::RequestCost { cost_per_request: self.model.multiplier(), } .into() } fn telemetry_id(&self) -> String { format!("copilot_chat/{}", self.model.id()) } fn max_token_count(&self) -> u64 { self.model.max_token_count() } fn stream_completion( &self, request: LanguageModelRequest, cx: &AsyncApp, ) -> BoxFuture< 'static, Result< BoxStream<'static, Result<LanguageModelCompletionEvent, LanguageModelCompletionError>>, LanguageModelCompletionError, >, > { let is_user_initiated = request.intent.is_none_or(|intent| match intent { CompletionIntent::UserPrompt | CompletionIntent::ThreadContextSummarization | CompletionIntent::InlineAssist | CompletionIntent::TerminalInlineAssist | CompletionIntent::GenerateGitCommitMessage => true, CompletionIntent::Subagent | CompletionIntent::ToolResults | CompletionIntent::ThreadSummarization | CompletionIntent::CreateFile | CompletionIntent::EditFile => false, }); if self.model.supports_messages() { let location = intent_to_chat_location(request.intent); let model = self.model.clone(); let request_limiter = self.request_limiter.clone(); let future = cx.spawn(async move |cx| { let effort = request .thinking_effort .as_ref() .and_then(|e| anthropic::Effort::from_str(e).ok()); let mut anthropic_request = into_anthropic( request, model.id().to_string(), 0.0, model.max_output_tokens() as u64, if model.supports_adaptive_thinking() { AnthropicModelMode::Thinking { budget_tokens: None, } } else if model.supports_thinking() { AnthropicModelMode::Thinking { budget_tokens: compute_thinking_budget( model.min_thinking_budget(), model.max_thinking_budget(), model.max_output_tokens() as u32, ), } } else { AnthropicModelMode::Default }, AnthropicPromptCacheMode::Legacy, &PROVIDER_ID, )?; anthropic_request.temperature = None; // The Copilot proxy doesn't support eager_input_streaming on tools. for tool in &mut anthropic_request.tools { tool.eager_input_streaming = false; } if model.supports_adaptive_thinking() { if anthropic_request.thinking.is_some() { anthropic_request.thinking = Some(anthropic::Thinking::Adaptive { display: Some(anthropic::AdaptiveThinkingDisplay::Summarized), }); anthropic_request.output_config = effort.map(|effort| anthropic::OutputConfig { effort: Some(effort), }); } } let anthropic_beta = if !model.supports_adaptive_thinking() && model.supports_thinking() { Some("interleaved-thinking-2025-05-14".to_string()) } else { None }; let body = serde_json::to_string(&anthropic::StreamingRequest { base: anthropic_request, stream: true, }) .map_err(|e| anyhow::anyhow!(e))?; let stream = CopilotChat::stream_messages( body, location, is_user_initiated, anthropic_beta, cx.clone(), ); request_limiter .stream(async move { let events = stream.await?; let mapper = AnthropicEventMapper::new(PROVIDER_NAME, PROVIDER_ID); Ok(mapper.map_stream(events).boxed()) }) .await }); return async move { Ok(future.await?.boxed()) }.boxed(); } if self.model.supports_response() { let location = intent_to_chat_location(request.intent); let responses_request = match into_copilot_responses(&self.model, request) { Ok(request) => request, Err(error) => return async move { Err(error.into()) }.boxed(), }; let request_limiter = self.request_limiter.clone(); let future = cx.spawn(async move |cx| { let request = CopilotChat::stream_response( responses_request, location, is_user_initiated, cx.clone(), ); request_limiter .stream(async move { let stream = request.await?; let mapper = CopilotResponsesEventMapper::new(); Ok(mapper.map_stream(stream).boxed()) }) .await }); return async move { Ok(future.await?.boxed()) }.boxed(); } let location = intent_to_chat_location(request.intent); let copilot_request = match into_copilot_chat(&self.model, request) { Ok(request) => request, Err(err) => return futures::future::ready(Err(err.into())).boxed(), }; let is_streaming = copilot_request.stream; let request_limiter = self.request_limiter.clone(); let future = cx.spawn(async move |cx| { let request = CopilotChat::stream_completion( copilot_request, location, is_user_initiated, cx.clone(), ); request_limiter .stream(async move { let response = request.await?; Ok(map_to_language_model_completion_events( response, is_streaming, )) }) .await }); async move { Ok(future.await?.boxed()) }.boxed() } } pub fn map_to_language_model_completion_events( events: Pin<Box<dyn Send + Stream<Item = Result<ResponseEvent>>>>, is_streaming: bool, ) -> impl Stream<Item = Result<LanguageModelCompletionEvent, LanguageModelCompletionError>> { #[derive(Default)] struct RawToolCall { id: String, name: String, arguments: String, thought_signature: Option<String>, } struct State { events: Pin<Box<dyn Send + Stream<Item = Result<ResponseEvent>>>>, tool_calls_by_index: HashMap<usize, RawToolCall>, reasoning_opaque: Option<String>, reasoning_text: Option<String>, } futures::stream::unfold( State { events,