assistant: Stream tool uses as structured data (#17322)
This PR adjusts the approach we use to encoding tool uses in the completion response to use a structured format rather than simply injecting it into the response stream as text. In #17170 we would encode the tool uses as XML and insert them as text. This would require then re-parsing the tool uses out of the buffer in order to use them. The approach taken in this PR is to make `stream_completion` return a stream of `LanguageModelCompletionEvent`s. Each of these events can be either text, or a tool use. A new `stream_completion_text` method has been added to `LanguageModel` for scenarios where we only care about textual content (currently, everywhere that isn't the Assistant context editor). Release Notes: - N/A
This commit is contained in:
parent
132e8e8064
commit
452272e5df
14 changed files with 235 additions and 83 deletions
|
@ -33,7 +33,10 @@ use std::{
|
|||
use strum::IntoEnumIterator;
|
||||
use ui::{prelude::*, TintColor};
|
||||
|
||||
use crate::{LanguageModelAvailability, LanguageModelProvider};
|
||||
use crate::{
|
||||
LanguageModelAvailability, LanguageModelCompletionEvent, LanguageModelProvider,
|
||||
LanguageModelToolUse,
|
||||
};
|
||||
|
||||
use super::anthropic::count_anthropic_tokens;
|
||||
|
||||
|
@ -496,7 +499,7 @@ impl LanguageModel for CloudLanguageModel {
|
|||
&self,
|
||||
request: LanguageModelRequest,
|
||||
_cx: &AsyncAppContext,
|
||||
) -> BoxFuture<'static, Result<BoxStream<'static, Result<String>>>> {
|
||||
) -> BoxFuture<'static, Result<BoxStream<'static, Result<LanguageModelCompletionEvent>>>> {
|
||||
match &self.model {
|
||||
CloudModel::Anthropic(model) => {
|
||||
let request = request.into_anthropic(model.id().into(), model.max_output_tokens());
|
||||
|
@ -522,7 +525,20 @@ impl LanguageModel for CloudLanguageModel {
|
|||
async move {
|
||||
Ok(future
|
||||
.await?
|
||||
.map(|result| result.map_err(|err| anyhow!(err)))
|
||||
.map(|result| {
|
||||
result
|
||||
.map(|content| match content {
|
||||
anthropic::ResponseContent::Text { text } => {
|
||||
LanguageModelCompletionEvent::Text(text)
|
||||
}
|
||||
anthropic::ResponseContent::ToolUse { id, name, input } => {
|
||||
LanguageModelCompletionEvent::ToolUse(
|
||||
LanguageModelToolUse { id, name, input },
|
||||
)
|
||||
}
|
||||
})
|
||||
.map_err(|err| anyhow!(err))
|
||||
})
|
||||
.boxed())
|
||||
}
|
||||
.boxed()
|
||||
|
@ -546,7 +562,13 @@ impl LanguageModel for CloudLanguageModel {
|
|||
.await?;
|
||||
Ok(open_ai::extract_text_from_events(response_lines(response)))
|
||||
});
|
||||
async move { Ok(future.await?.boxed()) }.boxed()
|
||||
async move {
|
||||
Ok(future
|
||||
.await?
|
||||
.map(|result| result.map(LanguageModelCompletionEvent::Text))
|
||||
.boxed())
|
||||
}
|
||||
.boxed()
|
||||
}
|
||||
CloudModel::Google(model) => {
|
||||
let client = self.client.clone();
|
||||
|
@ -569,7 +591,13 @@ impl LanguageModel for CloudLanguageModel {
|
|||
response,
|
||||
)))
|
||||
});
|
||||
async move { Ok(future.await?.boxed()) }.boxed()
|
||||
async move {
|
||||
Ok(future
|
||||
.await?
|
||||
.map(|result| result.map(LanguageModelCompletionEvent::Text))
|
||||
.boxed())
|
||||
}
|
||||
.boxed()
|
||||
}
|
||||
CloudModel::Zed(model) => {
|
||||
let client = self.client.clone();
|
||||
|
@ -591,7 +619,13 @@ impl LanguageModel for CloudLanguageModel {
|
|||
.await?;
|
||||
Ok(open_ai::extract_text_from_events(response_lines(response)))
|
||||
});
|
||||
async move { Ok(future.await?.boxed()) }.boxed()
|
||||
async move {
|
||||
Ok(future
|
||||
.await?
|
||||
.map(|result| result.map(LanguageModelCompletionEvent::Text))
|
||||
.boxed())
|
||||
}
|
||||
.boxed()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue