Add token usage to LanguageModelTextStream
(#27490)
Release Notes: - N/A --------- Co-authored-by: Michael Sloan <michael@zed.dev>
This commit is contained in:
parent
5f8c53ffe8
commit
1e8b50f471
3 changed files with 49 additions and 13 deletions
|
@ -3712,7 +3712,7 @@ mod tests {
|
||||||
language_settings, tree_sitter_rust, Buffer, Language, LanguageConfig, LanguageMatcher,
|
language_settings, tree_sitter_rust, Buffer, Language, LanguageConfig, LanguageMatcher,
|
||||||
Point,
|
Point,
|
||||||
};
|
};
|
||||||
use language_model::LanguageModelRegistry;
|
use language_model::{LanguageModelRegistry, TokenUsage};
|
||||||
use rand::prelude::*;
|
use rand::prelude::*;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use settings::SettingsStore;
|
use settings::SettingsStore;
|
||||||
|
@ -4091,6 +4091,7 @@ mod tests {
|
||||||
future::ready(Ok(LanguageModelTextStream {
|
future::ready(Ok(LanguageModelTextStream {
|
||||||
message_id: None,
|
message_id: None,
|
||||||
stream: chunks_rx.map(Ok).boxed(),
|
stream: chunks_rx.map(Ok).boxed(),
|
||||||
|
last_token_usage: Arc::new(Mutex::new(TokenUsage::default())),
|
||||||
})),
|
})),
|
||||||
cx,
|
cx,
|
||||||
);
|
);
|
||||||
|
|
|
@ -482,11 +482,17 @@ impl CodegenAlternative {
|
||||||
|
|
||||||
self.generation = cx.spawn(async move |codegen, cx| {
|
self.generation = cx.spawn(async move |codegen, cx| {
|
||||||
let stream = stream.await;
|
let stream = stream.await;
|
||||||
|
let token_usage = stream
|
||||||
|
.as_ref()
|
||||||
|
.ok()
|
||||||
|
.map(|stream| stream.last_token_usage.clone());
|
||||||
let message_id = stream
|
let message_id = stream
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.ok()
|
.ok()
|
||||||
.and_then(|stream| stream.message_id.clone());
|
.and_then(|stream| stream.message_id.clone());
|
||||||
let generate = async {
|
let generate = async {
|
||||||
|
let model_telemetry_id = model_telemetry_id.clone();
|
||||||
|
let model_provider_id = model_provider_id.clone();
|
||||||
let (mut diff_tx, mut diff_rx) = mpsc::channel(1);
|
let (mut diff_tx, mut diff_rx) = mpsc::channel(1);
|
||||||
let executor = cx.background_executor().clone();
|
let executor = cx.background_executor().clone();
|
||||||
let message_id = message_id.clone();
|
let message_id = message_id.clone();
|
||||||
|
@ -596,7 +602,7 @@ impl CodegenAlternative {
|
||||||
kind: AssistantKind::Inline,
|
kind: AssistantKind::Inline,
|
||||||
phase: AssistantPhase::Response,
|
phase: AssistantPhase::Response,
|
||||||
model: model_telemetry_id,
|
model: model_telemetry_id,
|
||||||
model_provider: model_provider_id.to_string(),
|
model_provider: model_provider_id,
|
||||||
response_latency,
|
response_latency,
|
||||||
error_message,
|
error_message,
|
||||||
language_name: language_name.map(|name| name.to_proto()),
|
language_name: language_name.map(|name| name.to_proto()),
|
||||||
|
@ -677,6 +683,16 @@ impl CodegenAlternative {
|
||||||
}
|
}
|
||||||
this.elapsed_time = Some(elapsed_time);
|
this.elapsed_time = Some(elapsed_time);
|
||||||
this.completion = Some(completion.lock().clone());
|
this.completion = Some(completion.lock().clone());
|
||||||
|
if let Some(usage) = token_usage {
|
||||||
|
let usage = usage.lock();
|
||||||
|
telemetry::event!(
|
||||||
|
"Inline Assistant Completion",
|
||||||
|
model = model_telemetry_id,
|
||||||
|
model_provider = model_provider_id,
|
||||||
|
input_tokens = usage.input_tokens,
|
||||||
|
output_tokens = usage.output_tokens,
|
||||||
|
)
|
||||||
|
}
|
||||||
cx.emit(CodegenEvent::Finished);
|
cx.emit(CodegenEvent::Finished);
|
||||||
cx.notify();
|
cx.notify();
|
||||||
})
|
})
|
||||||
|
@ -1021,7 +1037,7 @@ mod tests {
|
||||||
language_settings, tree_sitter_rust, Buffer, Language, LanguageConfig, LanguageMatcher,
|
language_settings, tree_sitter_rust, Buffer, Language, LanguageConfig, LanguageMatcher,
|
||||||
Point,
|
Point,
|
||||||
};
|
};
|
||||||
use language_model::LanguageModelRegistry;
|
use language_model::{LanguageModelRegistry, TokenUsage};
|
||||||
use rand::prelude::*;
|
use rand::prelude::*;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use settings::SettingsStore;
|
use settings::SettingsStore;
|
||||||
|
@ -1405,6 +1421,7 @@ mod tests {
|
||||||
future::ready(Ok(LanguageModelTextStream {
|
future::ready(Ok(LanguageModelTextStream {
|
||||||
message_id: None,
|
message_id: None,
|
||||||
stream: chunks_rx.map(Ok).boxed(),
|
stream: chunks_rx.map(Ok).boxed(),
|
||||||
|
last_token_usage: Arc::new(Mutex::new(TokenUsage::default())),
|
||||||
})),
|
})),
|
||||||
cx,
|
cx,
|
||||||
);
|
);
|
||||||
|
|
|
@ -14,6 +14,7 @@ use futures::FutureExt;
|
||||||
use futures::{future::BoxFuture, stream::BoxStream, StreamExt, TryStreamExt as _};
|
use futures::{future::BoxFuture, stream::BoxStream, StreamExt, TryStreamExt as _};
|
||||||
use gpui::{AnyElement, AnyView, App, AsyncApp, SharedString, Task, Window};
|
use gpui::{AnyElement, AnyView, App, AsyncApp, SharedString, Task, Window};
|
||||||
use icons::IconName;
|
use icons::IconName;
|
||||||
|
use parking_lot::Mutex;
|
||||||
use proto::Plan;
|
use proto::Plan;
|
||||||
use schemars::JsonSchema;
|
use schemars::JsonSchema;
|
||||||
use serde::{de::DeserializeOwned, Deserialize, Serialize};
|
use serde::{de::DeserializeOwned, Deserialize, Serialize};
|
||||||
|
@ -141,6 +142,8 @@ pub struct LanguageModelToolUse {
|
||||||
pub struct LanguageModelTextStream {
|
pub struct LanguageModelTextStream {
|
||||||
pub message_id: Option<String>,
|
pub message_id: Option<String>,
|
||||||
pub stream: BoxStream<'static, Result<String>>,
|
pub stream: BoxStream<'static, Result<String>>,
|
||||||
|
// Has complete token usage after the stream has finished
|
||||||
|
pub last_token_usage: Arc<Mutex<TokenUsage>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for LanguageModelTextStream {
|
impl Default for LanguageModelTextStream {
|
||||||
|
@ -148,6 +151,7 @@ impl Default for LanguageModelTextStream {
|
||||||
Self {
|
Self {
|
||||||
message_id: None,
|
message_id: None,
|
||||||
stream: Box::pin(futures::stream::empty()),
|
stream: Box::pin(futures::stream::empty()),
|
||||||
|
last_token_usage: Arc::new(Mutex::new(TokenUsage::default())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -200,6 +204,7 @@ pub trait LanguageModel: Send + Sync {
|
||||||
let mut events = events.await?.fuse();
|
let mut events = events.await?.fuse();
|
||||||
let mut message_id = None;
|
let mut message_id = None;
|
||||||
let mut first_item_text = None;
|
let mut first_item_text = None;
|
||||||
|
let last_token_usage = Arc::new(Mutex::new(TokenUsage::default()));
|
||||||
|
|
||||||
if let Some(first_event) = events.next().await {
|
if let Some(first_event) = events.next().await {
|
||||||
match first_event {
|
match first_event {
|
||||||
|
@ -214,20 +219,33 @@ pub trait LanguageModel: Send + Sync {
|
||||||
}
|
}
|
||||||
|
|
||||||
let stream = futures::stream::iter(first_item_text.map(Ok))
|
let stream = futures::stream::iter(first_item_text.map(Ok))
|
||||||
.chain(events.filter_map(|result| async move {
|
.chain(events.filter_map({
|
||||||
match result {
|
let last_token_usage = last_token_usage.clone();
|
||||||
Ok(LanguageModelCompletionEvent::StartMessage { .. }) => None,
|
move |result| {
|
||||||
Ok(LanguageModelCompletionEvent::Text(text)) => Some(Ok(text)),
|
let last_token_usage = last_token_usage.clone();
|
||||||
Ok(LanguageModelCompletionEvent::Thinking(_)) => None,
|
async move {
|
||||||
Ok(LanguageModelCompletionEvent::Stop(_)) => None,
|
match result {
|
||||||
Ok(LanguageModelCompletionEvent::ToolUse(_)) => None,
|
Ok(LanguageModelCompletionEvent::StartMessage { .. }) => None,
|
||||||
Ok(LanguageModelCompletionEvent::UsageUpdate(_)) => None,
|
Ok(LanguageModelCompletionEvent::Text(text)) => Some(Ok(text)),
|
||||||
Err(err) => Some(Err(err)),
|
Ok(LanguageModelCompletionEvent::Thinking(_)) => None,
|
||||||
|
Ok(LanguageModelCompletionEvent::Stop(_)) => None,
|
||||||
|
Ok(LanguageModelCompletionEvent::ToolUse(_)) => None,
|
||||||
|
Ok(LanguageModelCompletionEvent::UsageUpdate(token_usage)) => {
|
||||||
|
*last_token_usage.lock() = token_usage;
|
||||||
|
None
|
||||||
|
}
|
||||||
|
Err(err) => Some(Err(err)),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
.boxed();
|
.boxed();
|
||||||
|
|
||||||
Ok(LanguageModelTextStream { message_id, stream })
|
Ok(LanguageModelTextStream {
|
||||||
|
message_id,
|
||||||
|
stream,
|
||||||
|
last_token_usage,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
.boxed()
|
.boxed()
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue