3287 lines · 122.5 KB · Rust
RawDownload
use agent_settings::{AgentSettings, SUMMARIZE_THREAD_PROMPT};use anyhow::{Context as _, Result, bail};use assistant_slash_command::{    SlashCommandContent, SlashCommandEvent, SlashCommandLine, SlashCommandOutputSection,    SlashCommandResult, SlashCommandWorkingSet,};use client::{self, proto};use clock::ReplicaId;use cloud_llm_client::CompletionIntent;use collections::{HashMap, HashSet};use fs::{Fs, RenameOptions}; use futures::{FutureExt, StreamExt, future::Shared};use gpui::{    App, AppContext as _, Context, Entity, EventEmitter, RenderImage, SharedString, Subscription,    Task,};use itertools::Itertools as _;use language::{AnchorRangeExt, Bias, Buffer, LanguageRegistry, OffsetRangeExt, Point, ToOffset};use language_model::{    AnthropicCompletionType, AnthropicEventData, AnthropicEventType, LanguageModel,    LanguageModelCacheConfiguration, LanguageModelCompletionEvent, LanguageModelImage,    LanguageModelRegistry, LanguageModelRequest, LanguageModelRequestMessage,    LanguageModelToolUseId, MessageContent, PaymentRequiredError, Role, StopReason,    report_anthropic_event,};use open_ai::Model as OpenAiModel;use paths::text_threads_dir;use prompt_store::PromptBuilder;use serde::{Deserialize, Serialize};use smallvec::SmallVec;use std::{    cmp::{Ordering, max},    fmt::{Debug, Write as _},    iter, mem,    ops::Range,    path::Path,    sync::Arc,    time::{Duration, Instant},}; use text::{BufferSnapshot, ToPoint};use ui::IconName;use util::{ResultExt, TryFutureExt, post_inc};use uuid::Uuid; #[derive(Clone, Debug, Eq, PartialEq, Hash, PartialOrd, Ord, Serialize, Deserialize)]pub struct TextThreadId(String); impl TextThreadId {    pub fn new() -> Self {        Self(Uuid::new_v4().to_string())    }     pub fn from_proto(id: String) -> Self {        Self(id)    }     pub fn to_proto(&self) -> String {        self.0.clone()    }} #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Hash, Serialize, Deserialize)]pub struct MessageId(pub clock::Lamport); impl MessageId {    pub fn as_u64(self) -> u64 {        self.0.as_u64()    }} #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]pub enum MessageStatus {    Pending,    Done,    Error(SharedString),    Canceled,} impl MessageStatus {    pub fn from_proto(status: proto::ContextMessageStatus) -> MessageStatus {        match status.variant {            Some(proto::context_message_status::Variant::Pending(_)) => MessageStatus::Pending,            Some(proto::context_message_status::Variant::Done(_)) => MessageStatus::Done,            Some(proto::context_message_status::Variant::Error(error)) => {                MessageStatus::Error(error.message.into())            }            Some(proto::context_message_status::Variant::Canceled(_)) => MessageStatus::Canceled,            None => MessageStatus::Pending,        }    }     pub fn to_proto(&self) -> proto::ContextMessageStatus {        match self {            MessageStatus::Pending => proto::ContextMessageStatus {                variant: Some(proto::context_message_status::Variant::Pending(                    proto::context_message_status::Pending {},                )),            },            MessageStatus::Done => proto::ContextMessageStatus {                variant: Some(proto::context_message_status::Variant::Done(                    proto::context_message_status::Done {},                )),            },            MessageStatus::Error(message) => proto::ContextMessageStatus {                variant: Some(proto::context_message_status::Variant::Error(                    proto::context_message_status::Error {                        message: message.to_string(),                    },                )),            },            MessageStatus::Canceled => proto::ContextMessageStatus {                variant: Some(proto::context_message_status::Variant::Canceled(                    proto::context_message_status::Canceled {},                )),            },        }    }} #[derive(Clone, Debug)]pub enum TextThreadOperation {    InsertMessage {        anchor: MessageAnchor,        metadata: MessageMetadata,        version: clock::Global,    },    UpdateMessage {        message_id: MessageId,        metadata: MessageMetadata,        version: clock::Global,    },    UpdateSummary {        summary: TextThreadSummaryContent,        version: clock::Global,    },    SlashCommandStarted {        id: InvokedSlashCommandId,        output_range: Range<language::Anchor>,        name: String,        version: clock::Global,    },    SlashCommandFinished {        id: InvokedSlashCommandId,        timestamp: clock::Lamport,        error_message: Option<String>,        version: clock::Global,    },    SlashCommandOutputSectionAdded {        timestamp: clock::Lamport,        section: SlashCommandOutputSection<language::Anchor>,        version: clock::Global,    },    ThoughtProcessOutputSectionAdded {        timestamp: clock::Lamport,        section: ThoughtProcessOutputSection<language::Anchor>,        version: clock::Global,    },    BufferOperation(language::Operation),} impl TextThreadOperation {    pub fn from_proto(op: proto::ContextOperation) -> Result<Self> {        match op.variant.context("invalid variant")? {            proto::context_operation::Variant::InsertMessage(insert) => {                let message = insert.message.context("invalid message")?;                let id = MessageId(language::proto::deserialize_timestamp(                    message.id.context("invalid id")?,                ));                Ok(Self::InsertMessage {                    anchor: MessageAnchor {                        id,                        start: language::proto::deserialize_anchor(                            message.start.context("invalid anchor")?,                        )                        .context("invalid anchor")?,                    },                    metadata: MessageMetadata {                        role: Role::from_proto(message.role),                        status: MessageStatus::from_proto(                            message.status.context("invalid status")?,                        ),                        timestamp: id.0,                        cache: None,                    },                    version: language::proto::deserialize_version(&insert.version),                })            }            proto::context_operation::Variant::UpdateMessage(update) => Ok(Self::UpdateMessage {                message_id: MessageId(language::proto::deserialize_timestamp(                    update.message_id.context("invalid message id")?,                )),                metadata: MessageMetadata {                    role: Role::from_proto(update.role),                    status: MessageStatus::from_proto(update.status.context("invalid status")?),                    timestamp: language::proto::deserialize_timestamp(                        update.timestamp.context("invalid timestamp")?,                    ),                    cache: None,                },                version: language::proto::deserialize_version(&update.version),            }),            proto::context_operation::Variant::UpdateSummary(update) => Ok(Self::UpdateSummary {                summary: TextThreadSummaryContent {                    text: update.summary,                    done: update.done,                    timestamp: language::proto::deserialize_timestamp(                        update.timestamp.context("invalid timestamp")?,                    ),                },                version: language::proto::deserialize_version(&update.version),            }),            proto::context_operation::Variant::SlashCommandStarted(message) => {                Ok(Self::SlashCommandStarted {                    id: InvokedSlashCommandId(language::proto::deserialize_timestamp(                        message.id.context("invalid id")?,                    )),                    output_range: language::proto::deserialize_anchor_range(                        message.output_range.context("invalid range")?,                    )?,                    name: message.name,                    version: language::proto::deserialize_version(&message.version),                })            }            proto::context_operation::Variant::SlashCommandOutputSectionAdded(message) => {                let section = message.section.context("missing section")?;                Ok(Self::SlashCommandOutputSectionAdded {                    timestamp: language::proto::deserialize_timestamp(                        message.timestamp.context("missing timestamp")?,                    ),                    section: SlashCommandOutputSection {                        range: language::proto::deserialize_anchor_range(                            section.range.context("invalid range")?,                        )?,                        icon: section.icon_name.parse()?,                        label: section.label.into(),                        metadata: section                            .metadata                            .and_then(|metadata| serde_json::from_str(&metadata).log_err()),                    },                    version: language::proto::deserialize_version(&message.version),                })            }            proto::context_operation::Variant::SlashCommandCompleted(message) => {                Ok(Self::SlashCommandFinished {                    id: InvokedSlashCommandId(language::proto::deserialize_timestamp(                        message.id.context("invalid id")?,                    )),                    timestamp: language::proto::deserialize_timestamp(                        message.timestamp.context("missing timestamp")?,                    ),                    error_message: message.error_message,                    version: language::proto::deserialize_version(&message.version),                })            }            proto::context_operation::Variant::ThoughtProcessOutputSectionAdded(message) => {                let section = message.section.context("missing section")?;                Ok(Self::ThoughtProcessOutputSectionAdded {                    timestamp: language::proto::deserialize_timestamp(                        message.timestamp.context("missing timestamp")?,                    ),                    section: ThoughtProcessOutputSection {                        range: language::proto::deserialize_anchor_range(                            section.range.context("invalid range")?,                        )?,                    },                    version: language::proto::deserialize_version(&message.version),                })            }            proto::context_operation::Variant::BufferOperation(op) => Ok(Self::BufferOperation(                language::proto::deserialize_operation(                    op.operation.context("invalid buffer operation")?,                )?,            )),        }    }     pub fn to_proto(&self) -> proto::ContextOperation {        match self {            Self::InsertMessage {                anchor,                metadata,                version,            } => proto::ContextOperation {                variant: Some(proto::context_operation::Variant::InsertMessage(                    proto::context_operation::InsertMessage {                        message: Some(proto::ContextMessage {                            id: Some(language::proto::serialize_timestamp(anchor.id.0)),                            start: Some(language::proto::serialize_anchor(&anchor.start)),                            role: metadata.role.to_proto() as i32,                            status: Some(metadata.status.to_proto()),                        }),                        version: language::proto::serialize_version(version),                    },                )),            },            Self::UpdateMessage {                message_id,                metadata,                version,            } => proto::ContextOperation {                variant: Some(proto::context_operation::Variant::UpdateMessage(                    proto::context_operation::UpdateMessage {                        message_id: Some(language::proto::serialize_timestamp(message_id.0)),                        role: metadata.role.to_proto() as i32,                        status: Some(metadata.status.to_proto()),                        timestamp: Some(language::proto::serialize_timestamp(metadata.timestamp)),                        version: language::proto::serialize_version(version),                    },                )),            },            Self::UpdateSummary { summary, version } => proto::ContextOperation {                variant: Some(proto::context_operation::Variant::UpdateSummary(                    proto::context_operation::UpdateSummary {                        summary: summary.text.clone(),                        done: summary.done,                        timestamp: Some(language::proto::serialize_timestamp(summary.timestamp)),                        version: language::proto::serialize_version(version),                    },                )),            },            Self::SlashCommandStarted {                id,                output_range,                name,                version,            } => proto::ContextOperation {                variant: Some(proto::context_operation::Variant::SlashCommandStarted(                    proto::context_operation::SlashCommandStarted {                        id: Some(language::proto::serialize_timestamp(id.0)),                        output_range: Some(language::proto::serialize_anchor_range(                            output_range.clone(),                        )),                        name: name.clone(),                        version: language::proto::serialize_version(version),                    },                )),            },            Self::SlashCommandOutputSectionAdded {                timestamp,                section,                version,            } => proto::ContextOperation {                variant: Some(                    proto::context_operation::Variant::SlashCommandOutputSectionAdded(                        proto::context_operation::SlashCommandOutputSectionAdded {                            timestamp: Some(language::proto::serialize_timestamp(*timestamp)),                            section: Some({                                let icon_name: &'static str = section.icon.into();                                proto::SlashCommandOutputSection {                                    range: Some(language::proto::serialize_anchor_range(                                        section.range.clone(),                                    )),                                    icon_name: icon_name.to_string(),                                    label: section.label.to_string(),                                    metadata: section.metadata.as_ref().and_then(|metadata| {                                        serde_json::to_string(metadata).log_err()                                    }),                                }                            }),                            version: language::proto::serialize_version(version),                        },                    ),                ),            },            Self::SlashCommandFinished {                id,                timestamp,                error_message,                version,            } => proto::ContextOperation {                variant: Some(proto::context_operation::Variant::SlashCommandCompleted(                    proto::context_operation::SlashCommandCompleted {                        id: Some(language::proto::serialize_timestamp(id.0)),                        timestamp: Some(language::proto::serialize_timestamp(*timestamp)),                        error_message: error_message.clone(),                        version: language::proto::serialize_version(version),                    },                )),            },            Self::ThoughtProcessOutputSectionAdded {                timestamp,                section,                version,            } => proto::ContextOperation {                variant: Some(                    proto::context_operation::Variant::ThoughtProcessOutputSectionAdded(                        proto::context_operation::ThoughtProcessOutputSectionAdded {                            timestamp: Some(language::proto::serialize_timestamp(*timestamp)),                            section: Some({                                proto::ThoughtProcessOutputSection {                                    range: Some(language::proto::serialize_anchor_range(                                        section.range.clone(),                                    )),                                }                            }),                            version: language::proto::serialize_version(version),                        },                    ),                ),            },            Self::BufferOperation(operation) => proto::ContextOperation {                variant: Some(proto::context_operation::Variant::BufferOperation(                    proto::context_operation::BufferOperation {                        operation: Some(language::proto::serialize_operation(operation)),                    },                )),            },        }    }     fn timestamp(&self) -> clock::Lamport {        match self {            Self::InsertMessage { anchor, .. } => anchor.id.0,            Self::UpdateMessage { metadata, .. } => metadata.timestamp,            Self::UpdateSummary { summary, .. } => summary.timestamp,            Self::SlashCommandStarted { id, .. } => id.0,            Self::SlashCommandOutputSectionAdded { timestamp, .. }            | Self::SlashCommandFinished { timestamp, .. }            | Self::ThoughtProcessOutputSectionAdded { timestamp, .. } => *timestamp,            Self::BufferOperation(_) => {                panic!("reading the timestamp of a buffer operation is not supported")            }        }    }     /// Returns the current version of the context operation.    pub fn version(&self) -> &clock::Global {        match self {            Self::InsertMessage { version, .. }            | Self::UpdateMessage { version, .. }            | Self::UpdateSummary { version, .. }            | Self::SlashCommandStarted { version, .. }            | Self::SlashCommandOutputSectionAdded { version, .. }            | Self::SlashCommandFinished { version, .. }            | Self::ThoughtProcessOutputSectionAdded { version, .. } => version,            Self::BufferOperation(_) => {                panic!("reading the version of a buffer operation is not supported")            }        }    }} #[derive(Debug, Clone)]pub enum TextThreadEvent {    ShowAssistError(SharedString),    ShowPaymentRequiredError,    MessagesEdited,    SummaryChanged,    SummaryGenerated,    PathChanged {        old_path: Option<Arc<Path>>,        new_path: Arc<Path>,    },    StreamedCompletion,    StartedThoughtProcess(Range<language::Anchor>),    EndedThoughtProcess(language::Anchor),    InvokedSlashCommandChanged {        command_id: InvokedSlashCommandId,    },    ParsedSlashCommandsUpdated {        removed: Vec<Range<language::Anchor>>,        updated: Vec<ParsedSlashCommand>,    },    SlashCommandOutputSectionAdded {        section: SlashCommandOutputSection<language::Anchor>,    },    Operation(TextThreadOperation),} #[derive(Clone, Debug, Eq, PartialEq)]pub enum TextThreadSummary {    Pending,    Content(TextThreadSummaryContent),    Error,} #[derive(Clone, Debug, Eq, PartialEq)]pub struct TextThreadSummaryContent {    pub text: String,    pub done: bool,    pub timestamp: clock::Lamport,} impl TextThreadSummary {    pub const DEFAULT: &str = "New Text Thread";     pub fn or_default(&self) -> SharedString {        self.unwrap_or(Self::DEFAULT)    }     pub fn unwrap_or(&self, message: impl Into<SharedString>) -> SharedString {        self.content()            .map_or_else(|| message.into(), |content| content.text.clone().into())    }     pub fn content(&self) -> Option<&TextThreadSummaryContent> {        match self {            TextThreadSummary::Content(content) => Some(content),

Lines 1–500 of 3287.