1#[cfg(test)]
2mod context_tests;
3
4use agent_settings::AgentSettings;
5use anyhow::{Context as _, Result, bail};
6use assistant_slash_command::{
7 SlashCommandContent, SlashCommandEvent, SlashCommandLine, SlashCommandOutputSection,
8 SlashCommandResult, SlashCommandWorkingSet,
9};
10use assistant_slash_commands::FileCommandMetadata;
11use client::{self, proto, telemetry::Telemetry};
12use clock::ReplicaId;
13use collections::{HashMap, HashSet};
14use fs::{Fs, RemoveOptions};
15use futures::{FutureExt, StreamExt, future::Shared};
16use gpui::{
17 App, AppContext as _, Context, Entity, EventEmitter, RenderImage, SharedString, Subscription,
18 Task,
19};
20use language::{AnchorRangeExt, Bias, Buffer, LanguageRegistry, OffsetRangeExt, Point, ToOffset};
21use language_model::{
22 LanguageModel, LanguageModelCacheConfiguration, LanguageModelCompletionEvent,
23 LanguageModelImage, LanguageModelRegistry, LanguageModelRequest, LanguageModelRequestMessage,
24 LanguageModelToolUseId, MessageContent, PaymentRequiredError, Role, StopReason,
25 report_assistant_event,
26};
27use open_ai::Model as OpenAiModel;
28use paths::contexts_dir;
29use project::Project;
30use prompt_store::PromptBuilder;
31use serde::{Deserialize, Serialize};
32use settings::Settings;
33use smallvec::SmallVec;
34use std::{
35 cmp::{Ordering, max},
36 fmt::{Debug, Write as _},
37 iter, mem,
38 ops::Range,
39 path::Path,
40 sync::Arc,
41 time::{Duration, Instant},
42};
43use telemetry_events::{AssistantEventData, AssistantKind, AssistantPhase};
44use text::{BufferSnapshot, ToPoint};
45use ui::IconName;
46use util::{ResultExt, TryFutureExt, post_inc};
47use uuid::Uuid;
48use zed_llm_client::CompletionIntent;
49
50#[derive(Clone, Debug, Eq, PartialEq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
51pub struct ContextId(String);
52
53impl ContextId {
54 pub fn new() -> Self {
55 Self(Uuid::new_v4().to_string())
56 }
57
58 pub fn from_proto(id: String) -> Self {
59 Self(id)
60 }
61
62 pub fn to_proto(&self) -> String {
63 self.0.clone()
64 }
65}
66
67#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
68pub struct MessageId(pub clock::Lamport);
69
70impl MessageId {
71 pub fn as_u64(self) -> u64 {
72 self.0.as_u64()
73 }
74}
75
76#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
77pub enum MessageStatus {
78 Pending,
79 Done,
80 Error(SharedString),
81 Canceled,
82}
83
84impl MessageStatus {
85 pub fn from_proto(status: proto::ContextMessageStatus) -> MessageStatus {
86 match status.variant {
87 Some(proto::context_message_status::Variant::Pending(_)) => MessageStatus::Pending,
88 Some(proto::context_message_status::Variant::Done(_)) => MessageStatus::Done,
89 Some(proto::context_message_status::Variant::Error(error)) => {
90 MessageStatus::Error(error.message.into())
91 }
92 Some(proto::context_message_status::Variant::Canceled(_)) => MessageStatus::Canceled,
93 None => MessageStatus::Pending,
94 }
95 }
96
97 pub fn to_proto(&self) -> proto::ContextMessageStatus {
98 match self {
99 MessageStatus::Pending => proto::ContextMessageStatus {
100 variant: Some(proto::context_message_status::Variant::Pending(
101 proto::context_message_status::Pending {},
102 )),
103 },
104 MessageStatus::Done => proto::ContextMessageStatus {
105 variant: Some(proto::context_message_status::Variant::Done(
106 proto::context_message_status::Done {},
107 )),
108 },
109 MessageStatus::Error(message) => proto::ContextMessageStatus {
110 variant: Some(proto::context_message_status::Variant::Error(
111 proto::context_message_status::Error {
112 message: message.to_string(),
113 },
114 )),
115 },
116 MessageStatus::Canceled => proto::ContextMessageStatus {
117 variant: Some(proto::context_message_status::Variant::Canceled(
118 proto::context_message_status::Canceled {},
119 )),
120 },
121 }
122 }
123}
124
125#[derive(Clone, Debug)]
126pub enum ContextOperation {
127 InsertMessage {
128 anchor: MessageAnchor,
129 metadata: MessageMetadata,
130 version: clock::Global,
131 },
132 UpdateMessage {
133 message_id: MessageId,
134 metadata: MessageMetadata,
135 version: clock::Global,
136 },
137 UpdateSummary {
138 summary: ContextSummaryContent,
139 version: clock::Global,
140 },
141 SlashCommandStarted {
142 id: InvokedSlashCommandId,
143 output_range: Range<language::Anchor>,
144 name: String,
145 version: clock::Global,
146 },
147 SlashCommandFinished {
148 id: InvokedSlashCommandId,
149 timestamp: clock::Lamport,
150 error_message: Option<String>,
151 version: clock::Global,
152 },
153 SlashCommandOutputSectionAdded {
154 timestamp: clock::Lamport,
155 section: SlashCommandOutputSection<language::Anchor>,
156 version: clock::Global,
157 },
158 ThoughtProcessOutputSectionAdded {
159 timestamp: clock::Lamport,
160 section: ThoughtProcessOutputSection<language::Anchor>,
161 version: clock::Global,
162 },
163 BufferOperation(language::Operation),
164}
165
166impl ContextOperation {
167 pub fn from_proto(op: proto::ContextOperation) -> Result<Self> {
168 match op.variant.context("invalid variant")? {
169 proto::context_operation::Variant::InsertMessage(insert) => {
170 let message = insert.message.context("invalid message")?;
171 let id = MessageId(language::proto::deserialize_timestamp(
172 message.id.context("invalid id")?,
173 ));
174 Ok(Self::InsertMessage {
175 anchor: MessageAnchor {
176 id,
177 start: language::proto::deserialize_anchor(
178 message.start.context("invalid anchor")?,
179 )
180 .context("invalid anchor")?,
181 },
182 metadata: MessageMetadata {
183 role: Role::from_proto(message.role),
184 status: MessageStatus::from_proto(
185 message.status.context("invalid status")?,
186 ),
187 timestamp: id.0,
188 cache: None,
189 },
190 version: language::proto::deserialize_version(&insert.version),
191 })
192 }
193 proto::context_operation::Variant::UpdateMessage(update) => Ok(Self::UpdateMessage {
194 message_id: MessageId(language::proto::deserialize_timestamp(
195 update.message_id.context("invalid message id")?,
196 )),
197 metadata: MessageMetadata {
198 role: Role::from_proto(update.role),
199 status: MessageStatus::from_proto(update.status.context("invalid status")?),
200 timestamp: language::proto::deserialize_timestamp(
201 update.timestamp.context("invalid timestamp")?,
202 ),
203 cache: None,
204 },
205 version: language::proto::deserialize_version(&update.version),
206 }),
207 proto::context_operation::Variant::UpdateSummary(update) => Ok(Self::UpdateSummary {
208 summary: ContextSummaryContent {
209 text: update.summary,
210 done: update.done,
211 timestamp: language::proto::deserialize_timestamp(
212 update.timestamp.context("invalid timestamp")?,
213 ),
214 },
215 version: language::proto::deserialize_version(&update.version),
216 }),
217 proto::context_operation::Variant::SlashCommandStarted(message) => {
218 Ok(Self::SlashCommandStarted {
219 id: InvokedSlashCommandId(language::proto::deserialize_timestamp(
220 message.id.context("invalid id")?,
221 )),
222 output_range: language::proto::deserialize_anchor_range(
223 message.output_range.context("invalid range")?,
224 )?,
225 name: message.name,
226 version: language::proto::deserialize_version(&message.version),
227 })
228 }
229 proto::context_operation::Variant::SlashCommandOutputSectionAdded(message) => {
230 let section = message.section.context("missing section")?;
231 Ok(Self::SlashCommandOutputSectionAdded {
232 timestamp: language::proto::deserialize_timestamp(
233 message.timestamp.context("missing timestamp")?,
234 ),
235 section: SlashCommandOutputSection {
236 range: language::proto::deserialize_anchor_range(
237 section.range.context("invalid range")?,
238 )?,
239 icon: section.icon_name.parse()?,
240 label: section.label.into(),
241 metadata: section
242 .metadata
243 .and_then(|metadata| serde_json::from_str(&metadata).log_err()),
244 },
245 version: language::proto::deserialize_version(&message.version),
246 })
247 }
248 proto::context_operation::Variant::SlashCommandCompleted(message) => {
249 Ok(Self::SlashCommandFinished {
250 id: InvokedSlashCommandId(language::proto::deserialize_timestamp(
251 message.id.context("invalid id")?,
252 )),
253 timestamp: language::proto::deserialize_timestamp(
254 message.timestamp.context("missing timestamp")?,
255 ),
256 error_message: message.error_message,
257 version: language::proto::deserialize_version(&message.version),
258 })
259 }
260 proto::context_operation::Variant::ThoughtProcessOutputSectionAdded(message) => {
261 let section = message.section.context("missing section")?;
262 Ok(Self::ThoughtProcessOutputSectionAdded {
263 timestamp: language::proto::deserialize_timestamp(
264 message.timestamp.context("missing timestamp")?,
265 ),
266 section: ThoughtProcessOutputSection {
267 range: language::proto::deserialize_anchor_range(
268 section.range.context("invalid range")?,
269 )?,
270 },
271 version: language::proto::deserialize_version(&message.version),
272 })
273 }
274 proto::context_operation::Variant::BufferOperation(op) => Ok(Self::BufferOperation(
275 language::proto::deserialize_operation(
276 op.operation.context("invalid buffer operation")?,
277 )?,
278 )),
279 }
280 }
281
282 pub fn to_proto(&self) -> proto::ContextOperation {
283 match self {
284 Self::InsertMessage {
285 anchor,
286 metadata,
287 version,
288 } => proto::ContextOperation {
289 variant: Some(proto::context_operation::Variant::InsertMessage(
290 proto::context_operation::InsertMessage {
291 message: Some(proto::ContextMessage {
292 id: Some(language::proto::serialize_timestamp(anchor.id.0)),
293 start: Some(language::proto::serialize_anchor(&anchor.start)),
294 role: metadata.role.to_proto() as i32,
295 status: Some(metadata.status.to_proto()),
296 }),
297 version: language::proto::serialize_version(version),
298 },
299 )),
300 },
301 Self::UpdateMessage {
302 message_id,
303 metadata,
304 version,
305 } => proto::ContextOperation {
306 variant: Some(proto::context_operation::Variant::UpdateMessage(
307 proto::context_operation::UpdateMessage {
308 message_id: Some(language::proto::serialize_timestamp(message_id.0)),
309 role: metadata.role.to_proto() as i32,
310 status: Some(metadata.status.to_proto()),
311 timestamp: Some(language::proto::serialize_timestamp(metadata.timestamp)),
312 version: language::proto::serialize_version(version),
313 },
314 )),
315 },
316 Self::UpdateSummary { summary, version } => proto::ContextOperation {
317 variant: Some(proto::context_operation::Variant::UpdateSummary(
318 proto::context_operation::UpdateSummary {
319 summary: summary.text.clone(),
320 done: summary.done,
321 timestamp: Some(language::proto::serialize_timestamp(summary.timestamp)),
322 version: language::proto::serialize_version(version),
323 },
324 )),
325 },
326 Self::SlashCommandStarted {
327 id,
328 output_range,
329 name,
330 version,
331 } => proto::ContextOperation {
332 variant: Some(proto::context_operation::Variant::SlashCommandStarted(
333 proto::context_operation::SlashCommandStarted {
334 id: Some(language::proto::serialize_timestamp(id.0)),
335 output_range: Some(language::proto::serialize_anchor_range(
336 output_range.clone(),
337 )),
338 name: name.clone(),
339 version: language::proto::serialize_version(version),
340 },
341 )),
342 },
343 Self::SlashCommandOutputSectionAdded {
344 timestamp,
345 section,
346 version,
347 } => proto::ContextOperation {
348 variant: Some(
349 proto::context_operation::Variant::SlashCommandOutputSectionAdded(
350 proto::context_operation::SlashCommandOutputSectionAdded {
351 timestamp: Some(language::proto::serialize_timestamp(*timestamp)),
352 section: Some({
353 let icon_name: &'static str = section.icon.into();
354 proto::SlashCommandOutputSection {
355 range: Some(language::proto::serialize_anchor_range(
356 section.range.clone(),
357 )),
358 icon_name: icon_name.to_string(),
359 label: section.label.to_string(),
360 metadata: section.metadata.as_ref().and_then(|metadata| {
361 serde_json::to_string(metadata).log_err()
362 }),
363 }
364 }),
365 version: language::proto::serialize_version(version),
366 },
367 ),
368 ),
369 },
370 Self::SlashCommandFinished {
371 id,
372 timestamp,
373 error_message,
374 version,
375 } => proto::ContextOperation {
376 variant: Some(proto::context_operation::Variant::SlashCommandCompleted(
377 proto::context_operation::SlashCommandCompleted {
378 id: Some(language::proto::serialize_timestamp(id.0)),
379 timestamp: Some(language::proto::serialize_timestamp(*timestamp)),
380 error_message: error_message.clone(),
381 version: language::proto::serialize_version(version),
382 },
383 )),
384 },
385 Self::ThoughtProcessOutputSectionAdded {
386 timestamp,
387 section,
388 version,
389 } => proto::ContextOperation {
390 variant: Some(
391 proto::context_operation::Variant::ThoughtProcessOutputSectionAdded(
392 proto::context_operation::ThoughtProcessOutputSectionAdded {
393 timestamp: Some(language::proto::serialize_timestamp(*timestamp)),
394 section: Some({
395 proto::ThoughtProcessOutputSection {
396 range: Some(language::proto::serialize_anchor_range(
397 section.range.clone(),
398 )),
399 }
400 }),
401 version: language::proto::serialize_version(version),
402 },
403 ),
404 ),
405 },
406 Self::BufferOperation(operation) => proto::ContextOperation {
407 variant: Some(proto::context_operation::Variant::BufferOperation(
408 proto::context_operation::BufferOperation {
409 operation: Some(language::proto::serialize_operation(operation)),
410 },
411 )),
412 },
413 }
414 }
415
416 fn timestamp(&self) -> clock::Lamport {
417 match self {
418 Self::InsertMessage { anchor, .. } => anchor.id.0,
419 Self::UpdateMessage { metadata, .. } => metadata.timestamp,
420 Self::UpdateSummary { summary, .. } => summary.timestamp,
421 Self::SlashCommandStarted { id, .. } => id.0,
422 Self::SlashCommandOutputSectionAdded { timestamp, .. }
423 | Self::SlashCommandFinished { timestamp, .. }
424 | Self::ThoughtProcessOutputSectionAdded { timestamp, .. } => *timestamp,
425 Self::BufferOperation(_) => {
426 panic!("reading the timestamp of a buffer operation is not supported")
427 }
428 }
429 }
430
431 /// Returns the current version of the context operation.
432 pub fn version(&self) -> &clock::Global {
433 match self {
434 Self::InsertMessage { version, .. }
435 | Self::UpdateMessage { version, .. }
436 | Self::UpdateSummary { version, .. }
437 | Self::SlashCommandStarted { version, .. }
438 | Self::SlashCommandOutputSectionAdded { version, .. }
439 | Self::SlashCommandFinished { version, .. }
440 | Self::ThoughtProcessOutputSectionAdded { version, .. } => version,
441 Self::BufferOperation(_) => {
442 panic!("reading the version of a buffer operation is not supported")
443 }
444 }
445 }
446}
447
448#[derive(Debug, Clone)]
449pub enum ContextEvent {
450 ShowAssistError(SharedString),
451 ShowPaymentRequiredError,
452 MessagesEdited,
453 SummaryChanged,
454 SummaryGenerated,
455 StreamedCompletion,
456 StartedThoughtProcess(Range<language::Anchor>),
457 EndedThoughtProcess(language::Anchor),
458 InvokedSlashCommandChanged {
459 command_id: InvokedSlashCommandId,
460 },
461 ParsedSlashCommandsUpdated {
462 removed: Vec<Range<language::Anchor>>,
463 updated: Vec<ParsedSlashCommand>,
464 },
465 SlashCommandOutputSectionAdded {
466 section: SlashCommandOutputSection<language::Anchor>,
467 },
468 Operation(ContextOperation),
469}
470
471#[derive(Clone, Debug, Eq, PartialEq)]
472pub enum ContextSummary {
473 Pending,
474 Content(ContextSummaryContent),
475 Error,
476}
477
478#[derive(Default, Clone, Debug, Eq, PartialEq)]
479pub struct ContextSummaryContent {
480 pub text: String,
481 pub done: bool,
482 pub timestamp: clock::Lamport,
483}
484
485impl ContextSummary {
486 pub const DEFAULT: &str = "New Text Thread";
487
488 pub fn or_default(&self) -> SharedString {
489 self.unwrap_or(Self::DEFAULT)
490 }
491
492 pub fn unwrap_or(&self, message: impl Into<SharedString>) -> SharedString {
493 self.content()
494 .map_or_else(|| message.into(), |content| content.text.clone().into())
495 }
496
497 pub fn content(&self) -> Option<&ContextSummaryContent> {
498 match self {
499 ContextSummary::Content(content) => Some(content),
500 ContextSummary::Pending | ContextSummary::Error => None,
501 }
502 }
503
504 fn content_as_mut(&mut self) -> Option<&mut ContextSummaryContent> {
505 match self {
506 ContextSummary::Content(content) => Some(content),
507 ContextSummary::Pending | ContextSummary::Error => None,
508 }
509 }
510
511 fn content_or_set_empty(&mut self) -> &mut ContextSummaryContent {
512 match self {
513 ContextSummary::Content(content) => content,
514 ContextSummary::Pending | ContextSummary::Error => {
515 let content = ContextSummaryContent::default();
516 *self = ContextSummary::Content(content);
517 self.content_as_mut().unwrap()
518 }
519 }
520 }
521
522 pub fn is_pending(&self) -> bool {
523 matches!(self, ContextSummary::Pending)
524 }
525
526 fn timestamp(&self) -> Option<clock::Lamport> {
527 match self {
528 ContextSummary::Content(content) => Some(content.timestamp),
529 ContextSummary::Pending | ContextSummary::Error => None,
530 }
531 }
532}
533
534impl PartialOrd for ContextSummary {
535 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
536 self.timestamp().partial_cmp(&other.timestamp())
537 }
538}
539
540#[derive(Clone, Debug, Eq, PartialEq)]
541pub struct MessageAnchor {
542 pub id: MessageId,
543 pub start: language::Anchor,
544}
545
546#[derive(Clone, Debug, Eq, PartialEq)]
547pub enum CacheStatus {
548 Pending,
549 Cached,
550}
551
552#[derive(Clone, Debug, Eq, PartialEq)]
553pub struct MessageCacheMetadata {
554 pub is_anchor: bool,
555 pub is_final_anchor: bool,
556 pub status: CacheStatus,
557 pub cached_at: clock::Global,
558}
559
560#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
561pub struct MessageMetadata {
562 pub role: Role,
563 pub status: MessageStatus,
564 pub timestamp: clock::Lamport,
565 #[serde(skip)]
566 pub cache: Option<MessageCacheMetadata>,
567}
568
569impl From<&Message> for MessageMetadata {
570 fn from(message: &Message) -> Self {
571 Self {
572 role: message.role,
573 status: message.status.clone(),
574 timestamp: message.id.0,
575 cache: message.cache.clone(),
576 }
577 }
578}
579
580impl MessageMetadata {
581 pub fn is_cache_valid(&self, buffer: &BufferSnapshot, range: &Range<usize>) -> bool {
582 let result = match &self.cache {
583 Some(MessageCacheMetadata { cached_at, .. }) => !buffer.has_edits_since_in_range(
584 &cached_at,
585 Range {
586 start: buffer.anchor_at(range.start, Bias::Right),
587 end: buffer.anchor_at(range.end, Bias::Left),
588 },
589 ),
590 _ => false,
591 };
592 result
593 }
594}
595
596#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
597pub struct ThoughtProcessOutputSection<T> {
598 pub range: Range<T>,
599}
600
601impl ThoughtProcessOutputSection<language::Anchor> {
602 pub fn is_valid(&self, buffer: &language::TextBuffer) -> bool {
603 self.range.start.is_valid(buffer) && !self.range.to_offset(buffer).is_empty()
604 }
605}
606
607#[derive(Clone, Debug)]
608pub struct Message {
609 pub offset_range: Range<usize>,
610 pub index_range: Range<usize>,
611 pub anchor_range: Range<language::Anchor>,
612 pub id: MessageId,
613 pub role: Role,
614 pub status: MessageStatus,
615 pub cache: Option<MessageCacheMetadata>,
616}
617
618#[derive(Debug, Clone)]
619pub enum Content {
620 Image {
621 anchor: language::Anchor,
622 image_id: u64,
623 render_image: Arc<RenderImage>,
624 image: Shared<Task<Option<LanguageModelImage>>>,
625 },
626}
627
628impl Content {
629 fn range(&self) -> Range<language::Anchor> {
630 match self {
631 Self::Image { anchor, .. } => *anchor..*anchor,
632 }
633 }
634
635 fn cmp(&self, other: &Self, buffer: &BufferSnapshot) -> Ordering {
636 let self_range = self.range();
637 let other_range = other.range();
638 if self_range.end.cmp(&other_range.start, buffer).is_lt() {
639 Ordering::Less
640 } else if self_range.start.cmp(&other_range.end, buffer).is_gt() {
641 Ordering::Greater
642 } else {
643 Ordering::Equal
644 }
645 }
646}
647
648struct PendingCompletion {
649 id: usize,
650 assistant_message_id: MessageId,
651 _task: Task<()>,
652}
653
654#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
655pub struct InvokedSlashCommandId(clock::Lamport);
656
657pub struct AssistantContext {
658 id: ContextId,
659 timestamp: clock::Lamport,
660 version: clock::Global,
661 pending_ops: Vec<ContextOperation>,
662 operations: Vec<ContextOperation>,
663 buffer: Entity<Buffer>,
664 parsed_slash_commands: Vec<ParsedSlashCommand>,
665 invoked_slash_commands: HashMap<InvokedSlashCommandId, InvokedSlashCommand>,
666 edits_since_last_parse: language::Subscription,
667 slash_commands: Arc<SlashCommandWorkingSet>,
668 slash_command_output_sections: Vec<SlashCommandOutputSection<language::Anchor>>,
669 thought_process_output_sections: Vec<ThoughtProcessOutputSection<language::Anchor>>,
670 message_anchors: Vec<MessageAnchor>,
671 contents: Vec<Content>,
672 messages_metadata: HashMap<MessageId, MessageMetadata>,
673 summary: ContextSummary,
674 summary_task: Task<Option<()>>,
675 completion_count: usize,
676 pending_completions: Vec<PendingCompletion>,
677 token_count: Option<usize>,
678 pending_token_count: Task<Option<()>>,
679 pending_save: Task<Result<()>>,
680 pending_cache_warming_task: Task<Option<()>>,
681 path: Option<Arc<Path>>,
682 _subscriptions: Vec<Subscription>,
683 telemetry: Option<Arc<Telemetry>>,
684 language_registry: Arc<LanguageRegistry>,
685 project: Option<Entity<Project>>,
686 prompt_builder: Arc<PromptBuilder>,
687 completion_mode: agent_settings::CompletionMode,
688}
689
690trait ContextAnnotation {
691 fn range(&self) -> &Range<language::Anchor>;
692}
693
694impl ContextAnnotation for ParsedSlashCommand {
695 fn range(&self) -> &Range<language::Anchor> {
696 &self.source_range
697 }
698}
699
700impl EventEmitter<ContextEvent> for AssistantContext {}
701
702impl AssistantContext {
703 pub fn local(
704 language_registry: Arc<LanguageRegistry>,
705 project: Option<Entity<Project>>,
706 telemetry: Option<Arc<Telemetry>>,
707 prompt_builder: Arc<PromptBuilder>,
708 slash_commands: Arc<SlashCommandWorkingSet>,
709 cx: &mut Context<Self>,
710 ) -> Self {
711 Self::new(
712 ContextId::new(),
713 ReplicaId::default(),
714 language::Capability::ReadWrite,
715 language_registry,
716 prompt_builder,
717 slash_commands,
718 project,
719 telemetry,
720 cx,
721 )
722 }
723
724 pub fn completion_mode(&self) -> agent_settings::CompletionMode {
725 self.completion_mode
726 }
727
728 pub fn set_completion_mode(&mut self, completion_mode: agent_settings::CompletionMode) {
729 self.completion_mode = completion_mode;
730 }
731
732 pub fn new(
733 id: ContextId,
734 replica_id: ReplicaId,
735 capability: language::Capability,
736 language_registry: Arc<LanguageRegistry>,
737 prompt_builder: Arc<PromptBuilder>,
738 slash_commands: Arc<SlashCommandWorkingSet>,
739 project: Option<Entity<Project>>,
740 telemetry: Option<Arc<Telemetry>>,
741 cx: &mut Context<Self>,
742 ) -> Self {
743 let buffer = cx.new(|_cx| {
744 let buffer = Buffer::remote(
745 language::BufferId::new(1).unwrap(),
746 replica_id,
747 capability,
748 "",
749 );
750 buffer.set_language_registry(language_registry.clone());
751 buffer
752 });
753 let edits_since_last_slash_command_parse =
754 buffer.update(cx, |buffer, _| buffer.subscribe());
755 let mut this = Self {
756 id,
757 timestamp: clock::Lamport::new(replica_id),
758 version: clock::Global::new(),
759 pending_ops: Vec::new(),
760 operations: Vec::new(),
761 message_anchors: Default::default(),
762 contents: Default::default(),
763 messages_metadata: Default::default(),
764 parsed_slash_commands: Vec::new(),
765 invoked_slash_commands: HashMap::default(),
766 slash_command_output_sections: Vec::new(),
767 thought_process_output_sections: Vec::new(),
768 edits_since_last_parse: edits_since_last_slash_command_parse,
769 summary: ContextSummary::Pending,
770 summary_task: Task::ready(None),
771 completion_count: Default::default(),
772 pending_completions: Default::default(),
773 token_count: None,
774 pending_token_count: Task::ready(None),
775 pending_cache_warming_task: Task::ready(None),
776 _subscriptions: vec![cx.subscribe(&buffer, Self::handle_buffer_event)],
777 pending_save: Task::ready(Ok(())),
778 completion_mode: AgentSettings::get_global(cx).preferred_completion_mode,
779 path: None,
780 buffer,
781 telemetry,
782 project,
783 language_registry,
784 slash_commands,
785 prompt_builder,
786 };
787
788 let first_message_id = MessageId(clock::Lamport {
789 replica_id: 0,
790 value: 0,
791 });
792 let message = MessageAnchor {
793 id: first_message_id,
794 start: language::Anchor::MIN,
795 };
796 this.messages_metadata.insert(
797 first_message_id,
798 MessageMetadata {
799 role: Role::User,
800 status: MessageStatus::Done,
801 timestamp: first_message_id.0,
802 cache: None,
803 },
804 );
805 this.message_anchors.push(message);
806
807 this.set_language(cx);
808 this.count_remaining_tokens(cx);
809 this
810 }
811
812 pub(crate) fn serialize(&self, cx: &App) -> SavedContext {
813 let buffer = self.buffer.read(cx);
814 SavedContext {
815 id: Some(self.id.clone()),
816 zed: "context".into(),
817 version: SavedContext::VERSION.into(),
818 text: buffer.text(),
819 messages: self
820 .messages(cx)
821 .map(|message| SavedMessage {
822 id: message.id,
823 start: message.offset_range.start,
824 metadata: self.messages_metadata[&message.id].clone(),
825 })
826 .collect(),
827 summary: self
828 .summary
829 .content()
830 .map(|summary| summary.text.clone())
831 .unwrap_or_default(),
832 slash_command_output_sections: self
833 .slash_command_output_sections
834 .iter()
835 .filter_map(|section| {
836 if section.is_valid(buffer) {
837 let range = section.range.to_offset(buffer);
838 Some(assistant_slash_command::SlashCommandOutputSection {
839 range,
840 icon: section.icon,
841 label: section.label.clone(),
842 metadata: section.metadata.clone(),
843 })
844 } else {
845 None
846 }
847 })
848 .collect(),
849 thought_process_output_sections: self
850 .thought_process_output_sections
851 .iter()
852 .filter_map(|section| {
853 if section.is_valid(buffer) {
854 let range = section.range.to_offset(buffer);
855 Some(ThoughtProcessOutputSection { range })
856 } else {
857 None
858 }
859 })
860 .collect(),
861 }
862 }
863
864 pub fn deserialize(
865 saved_context: SavedContext,
866 path: Arc<Path>,
867 language_registry: Arc<LanguageRegistry>,
868 prompt_builder: Arc<PromptBuilder>,
869 slash_commands: Arc<SlashCommandWorkingSet>,
870 project: Option<Entity<Project>>,
871 telemetry: Option<Arc<Telemetry>>,
872 cx: &mut Context<Self>,
873 ) -> Self {
874 let id = saved_context.id.clone().unwrap_or_else(ContextId::new);
875 let mut this = Self::new(
876 id,
877 ReplicaId::default(),
878 language::Capability::ReadWrite,
879 language_registry,
880 prompt_builder,
881 slash_commands,
882 project,
883 telemetry,
884 cx,
885 );
886 this.path = Some(path);
887 this.buffer.update(cx, |buffer, cx| {
888 buffer.set_text(saved_context.text.as_str(), cx)
889 });
890 let operations = saved_context.into_ops(&this.buffer, cx);
891 this.apply_ops(operations, cx);
892 this
893 }
894
895 pub fn id(&self) -> &ContextId {
896 &self.id
897 }
898
899 pub fn replica_id(&self) -> ReplicaId {
900 self.timestamp.replica_id
901 }
902
903 pub fn version(&self, cx: &App) -> ContextVersion {
904 ContextVersion {
905 context: self.version.clone(),
906 buffer: self.buffer.read(cx).version(),
907 }
908 }
909
910 pub fn slash_commands(&self) -> &Arc<SlashCommandWorkingSet> {
911 &self.slash_commands
912 }
913
914 pub fn set_capability(&mut self, capability: language::Capability, cx: &mut Context<Self>) {
915 self.buffer
916 .update(cx, |buffer, cx| buffer.set_capability(capability, cx));
917 }
918
919 fn next_timestamp(&mut self) -> clock::Lamport {
920 let timestamp = self.timestamp.tick();
921 self.version.observe(timestamp);
922 timestamp
923 }
924
925 pub fn serialize_ops(
926 &self,
927 since: &ContextVersion,
928 cx: &App,
929 ) -> Task<Vec<proto::ContextOperation>> {
930 let buffer_ops = self
931 .buffer
932 .read(cx)
933 .serialize_ops(Some(since.buffer.clone()), cx);
934
935 let mut context_ops = self
936 .operations
937 .iter()
938 .filter(|op| !since.context.observed(op.timestamp()))
939 .cloned()
940 .collect::<Vec<_>>();
941 context_ops.extend(self.pending_ops.iter().cloned());
942
943 cx.background_spawn(async move {
944 let buffer_ops = buffer_ops.await;
945 context_ops.sort_unstable_by_key(|op| op.timestamp());
946 buffer_ops
947 .into_iter()
948 .map(|op| proto::ContextOperation {
949 variant: Some(proto::context_operation::Variant::BufferOperation(
950 proto::context_operation::BufferOperation {
951 operation: Some(op),
952 },
953 )),
954 })
955 .chain(context_ops.into_iter().map(|op| op.to_proto()))
956 .collect()
957 })
958 }
959
960 pub fn apply_ops(
961 &mut self,
962 ops: impl IntoIterator<Item = ContextOperation>,
963 cx: &mut Context<Self>,
964 ) {
965 let mut buffer_ops = Vec::new();
966 for op in ops {
967 match op {
968 ContextOperation::BufferOperation(buffer_op) => buffer_ops.push(buffer_op),
969 op @ _ => self.pending_ops.push(op),
970 }
971 }
972 self.buffer
973 .update(cx, |buffer, cx| buffer.apply_ops(buffer_ops, cx));
974 self.flush_ops(cx);
975 }
976
977 fn flush_ops(&mut self, cx: &mut Context<AssistantContext>) {
978 let mut changed_messages = HashSet::default();
979 let mut summary_generated = false;
980
981 self.pending_ops.sort_unstable_by_key(|op| op.timestamp());
982 for op in mem::take(&mut self.pending_ops) {
983 if !self.can_apply_op(&op, cx) {
984 self.pending_ops.push(op);
985 continue;
986 }
987
988 let timestamp = op.timestamp();
989 match op.clone() {
990 ContextOperation::InsertMessage {
991 anchor, metadata, ..
992 } => {
993 if self.messages_metadata.contains_key(&anchor.id) {
994 // We already applied this operation.
995 } else {
996 changed_messages.insert(anchor.id);
997 self.insert_message(anchor, metadata, cx);
998 }
999 }
1000 ContextOperation::UpdateMessage {
1001 message_id,
1002 metadata: new_metadata,
1003 ..
1004 } => {
1005 let metadata = self.messages_metadata.get_mut(&message_id).unwrap();
1006 if new_metadata.timestamp > metadata.timestamp {
1007 *metadata = new_metadata;
1008 changed_messages.insert(message_id);
1009 }
1010 }
1011 ContextOperation::UpdateSummary {
1012 summary: new_summary,
1013 ..
1014 } => {
1015 if self.summary.timestamp().map_or(true, |current_timestamp| {
1016 new_summary.timestamp > current_timestamp
1017 }) {
1018 self.summary = ContextSummary::Content(new_summary);
1019 summary_generated = true;
1020 }
1021 }
1022 ContextOperation::SlashCommandStarted {
1023 id,
1024 output_range,
1025 name,
1026 ..
1027 } => {
1028 self.invoked_slash_commands.insert(
1029 id,
1030 InvokedSlashCommand {
1031 name: name.into(),
1032 range: output_range,
1033 run_commands_in_ranges: Vec::new(),
1034 status: InvokedSlashCommandStatus::Running(Task::ready(())),
1035 transaction: None,
1036 timestamp: id.0,
1037 },
1038 );
1039 cx.emit(ContextEvent::InvokedSlashCommandChanged { command_id: id });
1040 }
1041 ContextOperation::SlashCommandOutputSectionAdded { section, .. } => {
1042 let buffer = self.buffer.read(cx);
1043 if let Err(ix) = self
1044 .slash_command_output_sections
1045 .binary_search_by(|probe| probe.range.cmp(§ion.range, buffer))
1046 {
1047 self.slash_command_output_sections
1048 .insert(ix, section.clone());
1049 cx.emit(ContextEvent::SlashCommandOutputSectionAdded { section });
1050 }
1051 }
1052 ContextOperation::ThoughtProcessOutputSectionAdded { section, .. } => {
1053 let buffer = self.buffer.read(cx);
1054 if let Err(ix) = self
1055 .thought_process_output_sections
1056 .binary_search_by(|probe| probe.range.cmp(§ion.range, buffer))
1057 {
1058 self.thought_process_output_sections
1059 .insert(ix, section.clone());
1060 }
1061 }
1062 ContextOperation::SlashCommandFinished {
1063 id,
1064 error_message,
1065 timestamp,
1066 ..
1067 } => {
1068 if let Some(slash_command) = self.invoked_slash_commands.get_mut(&id) {
1069 if timestamp > slash_command.timestamp {
1070 slash_command.timestamp = timestamp;
1071 match error_message {
1072 Some(message) => {
1073 slash_command.status =
1074 InvokedSlashCommandStatus::Error(message.into());
1075 }
1076 None => {
1077 slash_command.status = InvokedSlashCommandStatus::Finished;
1078 }
1079 }
1080 cx.emit(ContextEvent::InvokedSlashCommandChanged { command_id: id });
1081 }
1082 }
1083 }
1084 ContextOperation::BufferOperation(_) => unreachable!(),
1085 }
1086
1087 self.version.observe(timestamp);
1088 self.timestamp.observe(timestamp);
1089 self.operations.push(op);
1090 }
1091
1092 if !changed_messages.is_empty() {
1093 self.message_roles_updated(changed_messages, cx);
1094 cx.emit(ContextEvent::MessagesEdited);
1095 cx.notify();
1096 }
1097
1098 if summary_generated {
1099 cx.emit(ContextEvent::SummaryChanged);
1100 cx.emit(ContextEvent::SummaryGenerated);
1101 cx.notify();
1102 }
1103 }
1104
1105 fn can_apply_op(&self, op: &ContextOperation, cx: &App) -> bool {
1106 if !self.version.observed_all(op.version()) {
1107 return false;
1108 }
1109
1110 match op {
1111 ContextOperation::InsertMessage { anchor, .. } => self
1112 .buffer
1113 .read(cx)
1114 .version
1115 .observed(anchor.start.timestamp),
1116 ContextOperation::UpdateMessage { message_id, .. } => {
1117 self.messages_metadata.contains_key(message_id)
1118 }
1119 ContextOperation::UpdateSummary { .. } => true,
1120 ContextOperation::SlashCommandStarted { output_range, .. } => {
1121 self.has_received_operations_for_anchor_range(output_range.clone(), cx)
1122 }
1123 ContextOperation::SlashCommandOutputSectionAdded { section, .. } => {
1124 self.has_received_operations_for_anchor_range(section.range.clone(), cx)
1125 }
1126 ContextOperation::ThoughtProcessOutputSectionAdded { section, .. } => {
1127 self.has_received_operations_for_anchor_range(section.range.clone(), cx)
1128 }
1129 ContextOperation::SlashCommandFinished { .. } => true,
1130 ContextOperation::BufferOperation(_) => {
1131 panic!("buffer operations should always be applied")
1132 }
1133 }
1134 }
1135
1136 fn has_received_operations_for_anchor_range(
1137 &self,
1138 range: Range<text::Anchor>,
1139 cx: &App,
1140 ) -> bool {
1141 let version = &self.buffer.read(cx).version;
1142 let observed_start = range.start == language::Anchor::MIN
1143 || range.start == language::Anchor::MAX
1144 || version.observed(range.start.timestamp);
1145 let observed_end = range.end == language::Anchor::MIN
1146 || range.end == language::Anchor::MAX
1147 || version.observed(range.end.timestamp);
1148 observed_start && observed_end
1149 }
1150
1151 fn push_op(&mut self, op: ContextOperation, cx: &mut Context<Self>) {
1152 self.operations.push(op.clone());
1153 cx.emit(ContextEvent::Operation(op));
1154 }
1155
1156 pub fn buffer(&self) -> &Entity<Buffer> {
1157 &self.buffer
1158 }
1159
1160 pub fn language_registry(&self) -> Arc<LanguageRegistry> {
1161 self.language_registry.clone()
1162 }
1163
1164 pub fn project(&self) -> Option<Entity<Project>> {
1165 self.project.clone()
1166 }
1167
1168 pub fn prompt_builder(&self) -> Arc<PromptBuilder> {
1169 self.prompt_builder.clone()
1170 }
1171
1172 pub fn path(&self) -> Option<&Arc<Path>> {
1173 self.path.as_ref()
1174 }
1175
1176 pub fn summary(&self) -> &ContextSummary {
1177 &self.summary
1178 }
1179
1180 pub fn parsed_slash_commands(&self) -> &[ParsedSlashCommand] {
1181 &self.parsed_slash_commands
1182 }
1183
1184 pub fn invoked_slash_command(
1185 &self,
1186 command_id: &InvokedSlashCommandId,
1187 ) -> Option<&InvokedSlashCommand> {
1188 self.invoked_slash_commands.get(command_id)
1189 }
1190
1191 pub fn slash_command_output_sections(&self) -> &[SlashCommandOutputSection<language::Anchor>] {
1192 &self.slash_command_output_sections
1193 }
1194
1195 pub fn thought_process_output_sections(
1196 &self,
1197 ) -> &[ThoughtProcessOutputSection<language::Anchor>] {
1198 &self.thought_process_output_sections
1199 }
1200
1201 pub fn contains_files(&self, cx: &App) -> bool {
1202 let buffer = self.buffer.read(cx);
1203 self.slash_command_output_sections.iter().any(|section| {
1204 section.is_valid(buffer)
1205 && section
1206 .metadata
1207 .as_ref()
1208 .and_then(|metadata| {
1209 serde_json::from_value::<FileCommandMetadata>(metadata.clone()).ok()
1210 })
1211 .is_some()
1212 })
1213 }
1214
1215 fn set_language(&mut self, cx: &mut Context<Self>) {
1216 let markdown = self.language_registry.language_for_name("Markdown");
1217 cx.spawn(async move |this, cx| {
1218 let markdown = markdown.await?;
1219 this.update(cx, |this, cx| {
1220 this.buffer
1221 .update(cx, |buffer, cx| buffer.set_language(Some(markdown), cx));
1222 })
1223 })
1224 .detach_and_log_err(cx);
1225 }
1226
1227 fn handle_buffer_event(
1228 &mut self,
1229 _: Entity<Buffer>,
1230 event: &language::BufferEvent,
1231 cx: &mut Context<Self>,
1232 ) {
1233 match event {
1234 language::BufferEvent::Operation {
1235 operation,
1236 is_local: true,
1237 } => cx.emit(ContextEvent::Operation(ContextOperation::BufferOperation(
1238 operation.clone(),
1239 ))),
1240 language::BufferEvent::Edited => {
1241 self.count_remaining_tokens(cx);
1242 self.reparse(cx);
1243 cx.emit(ContextEvent::MessagesEdited);
1244 }
1245 _ => {}
1246 }
1247 }
1248
1249 pub fn token_count(&self) -> Option<usize> {
1250 self.token_count
1251 }
1252
1253 pub(crate) fn count_remaining_tokens(&mut self, cx: &mut Context<Self>) {
1254 // Assume it will be a Chat request, even though that takes fewer tokens (and risks going over the limit),
1255 // because otherwise you see in the UI that your empty message has a bunch of tokens already used.
1256 let Some(model) = LanguageModelRegistry::read_global(cx).default_model() else {
1257 return;
1258 };
1259 let request = self.to_completion_request(Some(&model.model), cx);
1260 let debounce = self.token_count.is_some();
1261 self.pending_token_count = cx.spawn(async move |this, cx| {
1262 async move {
1263 if debounce {
1264 cx.background_executor()
1265 .timer(Duration::from_millis(200))
1266 .await;
1267 }
1268
1269 let token_count = cx
1270 .update(|cx| model.model.count_tokens(request, cx))?
1271 .await?;
1272 this.update(cx, |this, cx| {
1273 this.token_count = Some(token_count);
1274 this.start_cache_warming(&model.model, cx);
1275 cx.notify()
1276 })
1277 }
1278 .log_err()
1279 .await
1280 });
1281 }
1282
1283 pub fn mark_cache_anchors(
1284 &mut self,
1285 cache_configuration: &Option<LanguageModelCacheConfiguration>,
1286 speculative: bool,
1287 cx: &mut Context<Self>,
1288 ) -> bool {
1289 let cache_configuration =
1290 cache_configuration
1291 .as_ref()
1292 .unwrap_or(&LanguageModelCacheConfiguration {
1293 max_cache_anchors: 0,
1294 should_speculate: false,
1295 min_total_token: 0,
1296 });
1297
1298 let messages: Vec<Message> = self.messages(cx).collect();
1299
1300 let mut sorted_messages = messages.clone();
1301 if speculative {
1302 // Avoid caching the last message if this is a speculative cache fetch as
1303 // it's likely to change.
1304 sorted_messages.pop();
1305 }
1306 sorted_messages.retain(|m| m.role == Role::User);
1307 sorted_messages.sort_by(|a, b| b.offset_range.len().cmp(&a.offset_range.len()));
1308
1309 let cache_anchors = if self.token_count.unwrap_or(0) < cache_configuration.min_total_token {
1310 // If we have't hit the minimum threshold to enable caching, don't cache anything.
1311 0
1312 } else {
1313 // Save 1 anchor for the inline assistant to use.
1314 max(cache_configuration.max_cache_anchors, 1) - 1
1315 };
1316 sorted_messages.truncate(cache_anchors);
1317
1318 let anchors: HashSet<MessageId> = sorted_messages
1319 .into_iter()
1320 .map(|message| message.id)
1321 .collect();
1322
1323 let buffer = self.buffer.read(cx).snapshot();
1324 let invalidated_caches: HashSet<MessageId> = messages
1325 .iter()
1326 .scan(false, |encountered_invalid, message| {
1327 let message_id = message.id;
1328 let is_invalid = self
1329 .messages_metadata
1330 .get(&message_id)
1331 .map_or(true, |metadata| {
1332 !metadata.is_cache_valid(&buffer, &message.offset_range)
1333 || *encountered_invalid
1334 });
1335 *encountered_invalid |= is_invalid;
1336 Some(if is_invalid { Some(message_id) } else { None })
1337 })
1338 .flatten()
1339 .collect();
1340
1341 let last_anchor = messages.iter().rev().find_map(|message| {
1342 if anchors.contains(&message.id) {
1343 Some(message.id)
1344 } else {
1345 None
1346 }
1347 });
1348
1349 let mut new_anchor_needs_caching = false;
1350 let current_version = &buffer.version;
1351 // If we have no anchors, mark all messages as not being cached.
1352 let mut hit_last_anchor = last_anchor.is_none();
1353
1354 for message in messages.iter() {
1355 if hit_last_anchor {
1356 self.update_metadata(message.id, cx, |metadata| metadata.cache = None);
1357 continue;
1358 }
1359
1360 if let Some(last_anchor) = last_anchor {
1361 if message.id == last_anchor {
1362 hit_last_anchor = true;
1363 }
1364 }
1365
1366 new_anchor_needs_caching = new_anchor_needs_caching
1367 || (invalidated_caches.contains(&message.id) && anchors.contains(&message.id));
1368
1369 self.update_metadata(message.id, cx, |metadata| {
1370 let cache_status = if invalidated_caches.contains(&message.id) {
1371 CacheStatus::Pending
1372 } else {
1373 metadata
1374 .cache
1375 .as_ref()
1376 .map_or(CacheStatus::Pending, |cm| cm.status.clone())
1377 };
1378 metadata.cache = Some(MessageCacheMetadata {
1379 is_anchor: anchors.contains(&message.id),
1380 is_final_anchor: hit_last_anchor,
1381 status: cache_status,
1382 cached_at: current_version.clone(),
1383 });
1384 });
1385 }
1386 new_anchor_needs_caching
1387 }
1388
1389 fn start_cache_warming(&mut self, model: &Arc<dyn LanguageModel>, cx: &mut Context<Self>) {
1390 let cache_configuration = model.cache_configuration();
1391
1392 if !self.mark_cache_anchors(&cache_configuration, true, cx) {
1393 return;
1394 }
1395 if !self.pending_completions.is_empty() {
1396 return;
1397 }
1398 if let Some(cache_configuration) = cache_configuration {
1399 if !cache_configuration.should_speculate {
1400 return;
1401 }
1402 }
1403
1404 let request = {
1405 let mut req = self.to_completion_request(Some(&model), cx);
1406 // Skip the last message because it's likely to change and
1407 // therefore would be a waste to cache.
1408 req.messages.pop();
1409 req.messages.push(LanguageModelRequestMessage {
1410 role: Role::User,
1411 content: vec!["Respond only with OK, nothing else.".into()],
1412 cache: false,
1413 });
1414 req
1415 };
1416
1417 let model = Arc::clone(model);
1418 self.pending_cache_warming_task = cx.spawn(async move |this, cx| {
1419 async move {
1420 match model.stream_completion(request, &cx).await {
1421 Ok(mut stream) => {
1422 stream.next().await;
1423 log::info!("Cache warming completed successfully");
1424 }
1425 Err(e) => {
1426 log::warn!("Cache warming failed: {}", e);
1427 }
1428 };
1429 this.update(cx, |this, cx| {
1430 this.update_cache_status_for_completion(cx);
1431 })
1432 .ok();
1433 anyhow::Ok(())
1434 }
1435 .log_err()
1436 .await
1437 });
1438 }
1439
1440 pub fn update_cache_status_for_completion(&mut self, cx: &mut Context<Self>) {
1441 let cached_message_ids: Vec<MessageId> = self
1442 .messages_metadata
1443 .iter()
1444 .filter_map(|(message_id, metadata)| {
1445 metadata.cache.as_ref().and_then(|cache| {
1446 if cache.status == CacheStatus::Pending {
1447 Some(*message_id)
1448 } else {
1449 None
1450 }
1451 })
1452 })
1453 .collect();
1454
1455 for message_id in cached_message_ids {
1456 self.update_metadata(message_id, cx, |metadata| {
1457 if let Some(cache) = &mut metadata.cache {
1458 cache.status = CacheStatus::Cached;
1459 }
1460 });
1461 }
1462 cx.notify();
1463 }
1464
1465 pub fn reparse(&mut self, cx: &mut Context<Self>) {
1466 let buffer = self.buffer.read(cx).text_snapshot();
1467 let mut row_ranges = self
1468 .edits_since_last_parse
1469 .consume()
1470 .into_iter()
1471 .map(|edit| {
1472 let start_row = buffer.offset_to_point(edit.new.start).row;
1473 let end_row = buffer.offset_to_point(edit.new.end).row + 1;
1474 start_row..end_row
1475 })
1476 .peekable();
1477
1478 let mut removed_parsed_slash_command_ranges = Vec::new();
1479 let mut updated_parsed_slash_commands = Vec::new();
1480 while let Some(mut row_range) = row_ranges.next() {
1481 while let Some(next_row_range) = row_ranges.peek() {
1482 if row_range.end >= next_row_range.start {
1483 row_range.end = next_row_range.end;
1484 row_ranges.next();
1485 } else {
1486 break;
1487 }
1488 }
1489
1490 let start = buffer.anchor_before(Point::new(row_range.start, 0));
1491 let end = buffer.anchor_after(Point::new(
1492 row_range.end - 1,
1493 buffer.line_len(row_range.end - 1),
1494 ));
1495
1496 self.reparse_slash_commands_in_range(
1497 start..end,
1498 &buffer,
1499 &mut updated_parsed_slash_commands,
1500 &mut removed_parsed_slash_command_ranges,
1501 cx,
1502 );
1503 self.invalidate_pending_slash_commands(&buffer, cx);
1504 }
1505
1506 if !updated_parsed_slash_commands.is_empty()
1507 || !removed_parsed_slash_command_ranges.is_empty()
1508 {
1509 cx.emit(ContextEvent::ParsedSlashCommandsUpdated {
1510 removed: removed_parsed_slash_command_ranges,
1511 updated: updated_parsed_slash_commands,
1512 });
1513 }
1514 }
1515
1516 fn reparse_slash_commands_in_range(
1517 &mut self,
1518 range: Range<text::Anchor>,
1519 buffer: &BufferSnapshot,
1520 updated: &mut Vec<ParsedSlashCommand>,
1521 removed: &mut Vec<Range<text::Anchor>>,
1522 cx: &App,
1523 ) {
1524 let old_range = self.pending_command_indices_for_range(range.clone(), cx);
1525
1526 let mut new_commands = Vec::new();
1527 let mut lines = buffer.text_for_range(range).lines();
1528 let mut offset = lines.offset();
1529 while let Some(line) = lines.next() {
1530 if let Some(command_line) = SlashCommandLine::parse(line) {
1531 let name = &line[command_line.name.clone()];
1532 let arguments = command_line
1533 .arguments
1534 .iter()
1535 .filter_map(|argument_range| {
1536 if argument_range.is_empty() {
1537 None
1538 } else {
1539 line.get(argument_range.clone())
1540 }
1541 })
1542 .map(ToOwned::to_owned)
1543 .collect::<SmallVec<_>>();
1544 if let Some(command) = self.slash_commands.command(name, cx) {
1545 if !command.requires_argument() || !arguments.is_empty() {
1546 let start_ix = offset + command_line.name.start - 1;
1547 let end_ix = offset
1548 + command_line
1549 .arguments
1550 .last()
1551 .map_or(command_line.name.end, |argument| argument.end);
1552 let source_range =
1553 buffer.anchor_after(start_ix)..buffer.anchor_after(end_ix);
1554 let pending_command = ParsedSlashCommand {
1555 name: name.to_string(),
1556 arguments,
1557 source_range,
1558 status: PendingSlashCommandStatus::Idle,
1559 };
1560 updated.push(pending_command.clone());
1561 new_commands.push(pending_command);
1562 }
1563 }
1564 }
1565
1566 offset = lines.offset();
1567 }
1568
1569 let removed_commands = self.parsed_slash_commands.splice(old_range, new_commands);
1570 removed.extend(removed_commands.map(|command| command.source_range));
1571 }
1572
1573 fn invalidate_pending_slash_commands(
1574 &mut self,
1575 buffer: &BufferSnapshot,
1576 cx: &mut Context<Self>,
1577 ) {
1578 let mut invalidated_command_ids = Vec::new();
1579 for (&command_id, command) in self.invoked_slash_commands.iter_mut() {
1580 if !matches!(command.status, InvokedSlashCommandStatus::Finished)
1581 && (!command.range.start.is_valid(buffer) || !command.range.end.is_valid(buffer))
1582 {
1583 command.status = InvokedSlashCommandStatus::Finished;
1584 cx.emit(ContextEvent::InvokedSlashCommandChanged { command_id });
1585 invalidated_command_ids.push(command_id);
1586 }
1587 }
1588
1589 for command_id in invalidated_command_ids {
1590 let version = self.version.clone();
1591 let timestamp = self.next_timestamp();
1592 self.push_op(
1593 ContextOperation::SlashCommandFinished {
1594 id: command_id,
1595 timestamp,
1596 error_message: None,
1597 version: version.clone(),
1598 },
1599 cx,
1600 );
1601 }
1602 }
1603
1604 pub fn pending_command_for_position(
1605 &mut self,
1606 position: language::Anchor,
1607 cx: &mut Context<Self>,
1608 ) -> Option<&mut ParsedSlashCommand> {
1609 let buffer = self.buffer.read(cx);
1610 match self
1611 .parsed_slash_commands
1612 .binary_search_by(|probe| probe.source_range.end.cmp(&position, buffer))
1613 {
1614 Ok(ix) => Some(&mut self.parsed_slash_commands[ix]),
1615 Err(ix) => {
1616 let cmd = self.parsed_slash_commands.get_mut(ix)?;
1617 if position.cmp(&cmd.source_range.start, buffer).is_ge()
1618 && position.cmp(&cmd.source_range.end, buffer).is_le()
1619 {
1620 Some(cmd)
1621 } else {
1622 None
1623 }
1624 }
1625 }
1626 }
1627
1628 pub fn pending_commands_for_range(
1629 &self,
1630 range: Range<language::Anchor>,
1631 cx: &App,
1632 ) -> &[ParsedSlashCommand] {
1633 let range = self.pending_command_indices_for_range(range, cx);
1634 &self.parsed_slash_commands[range]
1635 }
1636
1637 fn pending_command_indices_for_range(
1638 &self,
1639 range: Range<language::Anchor>,
1640 cx: &App,
1641 ) -> Range<usize> {
1642 self.indices_intersecting_buffer_range(&self.parsed_slash_commands, range, cx)
1643 }
1644
1645 fn indices_intersecting_buffer_range<T: ContextAnnotation>(
1646 &self,
1647 all_annotations: &[T],
1648 range: Range<language::Anchor>,
1649 cx: &App,
1650 ) -> Range<usize> {
1651 let buffer = self.buffer.read(cx);
1652 let start_ix = match all_annotations
1653 .binary_search_by(|probe| probe.range().end.cmp(&range.start, &buffer))
1654 {
1655 Ok(ix) | Err(ix) => ix,
1656 };
1657 let end_ix = match all_annotations
1658 .binary_search_by(|probe| probe.range().start.cmp(&range.end, &buffer))
1659 {
1660 Ok(ix) => ix + 1,
1661 Err(ix) => ix,
1662 };
1663 start_ix..end_ix
1664 }
1665
1666 pub fn insert_command_output(
1667 &mut self,
1668 command_source_range: Range<language::Anchor>,
1669 name: &str,
1670 output: Task<SlashCommandResult>,
1671 ensure_trailing_newline: bool,
1672 cx: &mut Context<Self>,
1673 ) {
1674 let version = self.version.clone();
1675 let command_id = InvokedSlashCommandId(self.next_timestamp());
1676
1677 const PENDING_OUTPUT_END_MARKER: &str = "…";
1678
1679 let (command_range, command_source_range, insert_position, first_transaction) =
1680 self.buffer.update(cx, |buffer, cx| {
1681 let command_source_range = command_source_range.to_offset(buffer);
1682 let mut insertion = format!("\n{PENDING_OUTPUT_END_MARKER}");
1683 if ensure_trailing_newline {
1684 insertion.push('\n');
1685 }
1686
1687 buffer.finalize_last_transaction();
1688 buffer.start_transaction();
1689 buffer.edit(
1690 [(
1691 command_source_range.end..command_source_range.end,
1692 insertion,
1693 )],
1694 None,
1695 cx,
1696 );
1697 let first_transaction = buffer.end_transaction(cx).unwrap();
1698 buffer.finalize_last_transaction();
1699
1700 let insert_position = buffer.anchor_after(command_source_range.end + 1);
1701 let command_range = buffer.anchor_after(command_source_range.start)
1702 ..buffer.anchor_before(
1703 command_source_range.end + 1 + PENDING_OUTPUT_END_MARKER.len(),
1704 );
1705 let command_source_range = buffer.anchor_before(command_source_range.start)
1706 ..buffer.anchor_before(command_source_range.end + 1);
1707 (
1708 command_range,
1709 command_source_range,
1710 insert_position,
1711 first_transaction,
1712 )
1713 });
1714 self.reparse(cx);
1715
1716 let insert_output_task = cx.spawn(async move |this, cx| {
1717 let run_command = async {
1718 let mut stream = output.await?;
1719
1720 struct PendingSection {
1721 start: language::Anchor,
1722 icon: IconName,
1723 label: SharedString,
1724 metadata: Option<serde_json::Value>,
1725 }
1726
1727 let mut pending_section_stack: Vec<PendingSection> = Vec::new();
1728 let mut last_role: Option<Role> = None;
1729 let mut last_section_range = None;
1730
1731 while let Some(event) = stream.next().await {
1732 let event = event?;
1733 this.update(cx, |this, cx| {
1734 this.buffer.update(cx, |buffer, _cx| {
1735 buffer.finalize_last_transaction();
1736 buffer.start_transaction()
1737 });
1738
1739 match event {
1740 SlashCommandEvent::StartMessage {
1741 role,
1742 merge_same_roles,
1743 } => {
1744 if !merge_same_roles && Some(role) != last_role {
1745 let buffer = this.buffer.read(cx);
1746 let offset = insert_position.to_offset(buffer);
1747 this.insert_message_at_offset(
1748 offset,
1749 role,
1750 MessageStatus::Pending,
1751 cx,
1752 );
1753 }
1754
1755 last_role = Some(role);
1756 }
1757 SlashCommandEvent::StartSection {
1758 icon,
1759 label,
1760 metadata,
1761 } => {
1762 this.buffer.update(cx, |buffer, cx| {
1763 let insert_point = insert_position.to_point(buffer);
1764 if insert_point.column > 0 {
1765 buffer.edit([(insert_point..insert_point, "\n")], None, cx);
1766 }
1767
1768 pending_section_stack.push(PendingSection {
1769 start: buffer.anchor_before(insert_position),
1770 icon,
1771 label,
1772 metadata,
1773 });
1774 });
1775 }
1776 SlashCommandEvent::Content(SlashCommandContent::Text {
1777 text,
1778 run_commands_in_text,
1779 }) => {
1780 let start = this.buffer.read(cx).anchor_before(insert_position);
1781
1782 this.buffer.update(cx, |buffer, cx| {
1783 buffer.edit(
1784 [(insert_position..insert_position, text)],
1785 None,
1786 cx,
1787 )
1788 });
1789
1790 let end = this.buffer.read(cx).anchor_before(insert_position);
1791 if run_commands_in_text {
1792 if let Some(invoked_slash_command) =
1793 this.invoked_slash_commands.get_mut(&command_id)
1794 {
1795 invoked_slash_command
1796 .run_commands_in_ranges
1797 .push(start..end);
1798 }
1799 }
1800 }
1801 SlashCommandEvent::EndSection => {
1802 if let Some(pending_section) = pending_section_stack.pop() {
1803 let offset_range = (pending_section.start..insert_position)
1804 .to_offset(this.buffer.read(cx));
1805 if !offset_range.is_empty() {
1806 let range = this.buffer.update(cx, |buffer, _cx| {
1807 buffer.anchor_after(offset_range.start)
1808 ..buffer.anchor_before(offset_range.end)
1809 });
1810 this.insert_slash_command_output_section(
1811 SlashCommandOutputSection {
1812 range: range.clone(),
1813 icon: pending_section.icon,
1814 label: pending_section.label,
1815 metadata: pending_section.metadata,
1816 },
1817 cx,
1818 );
1819 last_section_range = Some(range);
1820 }
1821 }
1822 }
1823 }
1824
1825 this.buffer.update(cx, |buffer, cx| {
1826 if let Some(event_transaction) = buffer.end_transaction(cx) {
1827 buffer.merge_transactions(event_transaction, first_transaction);
1828 }
1829 });
1830 })?;
1831 }
1832
1833 this.update(cx, |this, cx| {
1834 this.buffer.update(cx, |buffer, cx| {
1835 buffer.finalize_last_transaction();
1836 buffer.start_transaction();
1837
1838 let mut deletions = vec![(command_source_range.to_offset(buffer), "")];
1839 let insert_position = insert_position.to_offset(buffer);
1840 let command_range_end = command_range.end.to_offset(buffer);
1841
1842 if buffer.contains_str_at(insert_position, PENDING_OUTPUT_END_MARKER) {
1843 deletions.push((
1844 insert_position..insert_position + PENDING_OUTPUT_END_MARKER.len(),
1845 "",
1846 ));
1847 }
1848
1849 if ensure_trailing_newline
1850 && buffer.contains_str_at(command_range_end, "\n")
1851 {
1852 let newline_offset = insert_position.saturating_sub(1);
1853 if buffer.contains_str_at(newline_offset, "\n")
1854 && last_section_range.map_or(true, |last_section_range| {
1855 !last_section_range
1856 .to_offset(buffer)
1857 .contains(&newline_offset)
1858 })
1859 {
1860 deletions.push((command_range_end..command_range_end + 1, ""));
1861 }
1862 }
1863
1864 buffer.edit(deletions, None, cx);
1865
1866 if let Some(deletion_transaction) = buffer.end_transaction(cx) {
1867 buffer.merge_transactions(deletion_transaction, first_transaction);
1868 }
1869 });
1870 })?;
1871
1872 debug_assert!(pending_section_stack.is_empty());
1873
1874 anyhow::Ok(())
1875 };
1876
1877 let command_result = run_command.await;
1878
1879 this.update(cx, |this, cx| {
1880 let version = this.version.clone();
1881 let timestamp = this.next_timestamp();
1882 let Some(invoked_slash_command) = this.invoked_slash_commands.get_mut(&command_id)
1883 else {
1884 return;
1885 };
1886 let mut error_message = None;
1887 match command_result {
1888 Ok(()) => {
1889 invoked_slash_command.status = InvokedSlashCommandStatus::Finished;
1890 }
1891 Err(error) => {
1892 let message = error.to_string();
1893 invoked_slash_command.status =
1894 InvokedSlashCommandStatus::Error(message.clone().into());
1895 error_message = Some(message);
1896 }
1897 }
1898
1899 cx.emit(ContextEvent::InvokedSlashCommandChanged { command_id });
1900 this.push_op(
1901 ContextOperation::SlashCommandFinished {
1902 id: command_id,
1903 timestamp,
1904 error_message,
1905 version,
1906 },
1907 cx,
1908 );
1909 })
1910 .ok();
1911 });
1912
1913 self.invoked_slash_commands.insert(
1914 command_id,
1915 InvokedSlashCommand {
1916 name: name.to_string().into(),
1917 range: command_range.clone(),
1918 run_commands_in_ranges: Vec::new(),
1919 status: InvokedSlashCommandStatus::Running(insert_output_task),
1920 transaction: Some(first_transaction),
1921 timestamp: command_id.0,
1922 },
1923 );
1924 cx.emit(ContextEvent::InvokedSlashCommandChanged { command_id });
1925 self.push_op(
1926 ContextOperation::SlashCommandStarted {
1927 id: command_id,
1928 output_range: command_range,
1929 name: name.to_string(),
1930 version,
1931 },
1932 cx,
1933 );
1934 }
1935
1936 fn insert_slash_command_output_section(
1937 &mut self,
1938 section: SlashCommandOutputSection<language::Anchor>,
1939 cx: &mut Context<Self>,
1940 ) {
1941 let buffer = self.buffer.read(cx);
1942 let insertion_ix = match self
1943 .slash_command_output_sections
1944 .binary_search_by(|probe| probe.range.cmp(§ion.range, buffer))
1945 {
1946 Ok(ix) | Err(ix) => ix,
1947 };
1948 self.slash_command_output_sections
1949 .insert(insertion_ix, section.clone());
1950 cx.emit(ContextEvent::SlashCommandOutputSectionAdded {
1951 section: section.clone(),
1952 });
1953 let version = self.version.clone();
1954 let timestamp = self.next_timestamp();
1955 self.push_op(
1956 ContextOperation::SlashCommandOutputSectionAdded {
1957 timestamp,
1958 section,
1959 version,
1960 },
1961 cx,
1962 );
1963 }
1964
1965 fn insert_thought_process_output_section(
1966 &mut self,
1967 section: ThoughtProcessOutputSection<language::Anchor>,
1968 cx: &mut Context<Self>,
1969 ) {
1970 let buffer = self.buffer.read(cx);
1971 let insertion_ix = match self
1972 .thought_process_output_sections
1973 .binary_search_by(|probe| probe.range.cmp(§ion.range, buffer))
1974 {
1975 Ok(ix) | Err(ix) => ix,
1976 };
1977 self.thought_process_output_sections
1978 .insert(insertion_ix, section.clone());
1979 // cx.emit(ContextEvent::ThoughtProcessOutputSectionAdded {
1980 // section: section.clone(),
1981 // });
1982 let version = self.version.clone();
1983 let timestamp = self.next_timestamp();
1984 self.push_op(
1985 ContextOperation::ThoughtProcessOutputSectionAdded {
1986 timestamp,
1987 section,
1988 version,
1989 },
1990 cx,
1991 );
1992 }
1993
1994 pub fn completion_provider_changed(&mut self, cx: &mut Context<Self>) {
1995 self.count_remaining_tokens(cx);
1996 }
1997
1998 fn get_last_valid_message_id(&self, cx: &Context<Self>) -> Option<MessageId> {
1999 self.message_anchors.iter().rev().find_map(|message| {
2000 message
2001 .start
2002 .is_valid(self.buffer.read(cx))
2003 .then_some(message.id)
2004 })
2005 }
2006
2007 pub fn assist(&mut self, cx: &mut Context<Self>) -> Option<MessageAnchor> {
2008 let model_registry = LanguageModelRegistry::read_global(cx);
2009 let model = model_registry.default_model()?;
2010 let last_message_id = self.get_last_valid_message_id(cx)?;
2011
2012 if !model.provider.is_authenticated(cx) {
2013 log::info!("completion provider has no credentials");
2014 return None;
2015 }
2016
2017 let model = model.model;
2018
2019 // Compute which messages to cache, including the last one.
2020 self.mark_cache_anchors(&model.cache_configuration(), false, cx);
2021
2022 let request = self.to_completion_request(Some(&model), cx);
2023
2024 let assistant_message = self
2025 .insert_message_after(last_message_id, Role::Assistant, MessageStatus::Pending, cx)
2026 .unwrap();
2027
2028 // Queue up the user's next reply.
2029 let user_message = self
2030 .insert_message_after(assistant_message.id, Role::User, MessageStatus::Done, cx)
2031 .unwrap();
2032
2033 let pending_completion_id = post_inc(&mut self.completion_count);
2034
2035 let task = cx.spawn({
2036 async move |this, cx| {
2037 let stream = model.stream_completion(request, &cx);
2038 let assistant_message_id = assistant_message.id;
2039 let mut response_latency = None;
2040 let stream_completion = async {
2041 let request_start = Instant::now();
2042 let mut events = stream.await?;
2043 let mut stop_reason = StopReason::EndTurn;
2044 let mut thought_process_stack = Vec::new();
2045
2046 const THOUGHT_PROCESS_START_MARKER: &str = "<think>\n";
2047 const THOUGHT_PROCESS_END_MARKER: &str = "\n</think>";
2048
2049 while let Some(event) = events.next().await {
2050 if response_latency.is_none() {
2051 response_latency = Some(request_start.elapsed());
2052 }
2053 let event = event?;
2054
2055 let mut context_event = None;
2056 let mut thought_process_output_section = None;
2057
2058 this.update(cx, |this, cx| {
2059 let message_ix = this
2060 .message_anchors
2061 .iter()
2062 .position(|message| message.id == assistant_message_id)?;
2063 this.buffer.update(cx, |buffer, cx| {
2064 let message_old_end_offset = this.message_anchors[message_ix + 1..]
2065 .iter()
2066 .find(|message| message.start.is_valid(buffer))
2067 .map_or(buffer.len(), |message| {
2068 message.start.to_offset(buffer).saturating_sub(1)
2069 });
2070
2071 match event {
2072 LanguageModelCompletionEvent::StatusUpdate { .. } => {}
2073 LanguageModelCompletionEvent::StartMessage { .. } => {}
2074 LanguageModelCompletionEvent::Stop(reason) => {
2075 stop_reason = reason;
2076 }
2077 LanguageModelCompletionEvent::Thinking { text: chunk, .. } => {
2078 if thought_process_stack.is_empty() {
2079 let start =
2080 buffer.anchor_before(message_old_end_offset);
2081 thought_process_stack.push(start);
2082 let chunk =
2083 format!("{THOUGHT_PROCESS_START_MARKER}{chunk}{THOUGHT_PROCESS_END_MARKER}");
2084 let chunk_len = chunk.len();
2085 buffer.edit(
2086 [(
2087 message_old_end_offset..message_old_end_offset,
2088 chunk,
2089 )],
2090 None,
2091 cx,
2092 );
2093 let end = buffer
2094 .anchor_before(message_old_end_offset + chunk_len);
2095 context_event = Some(
2096 ContextEvent::StartedThoughtProcess(start..end),
2097 );
2098 } else {
2099 // This ensures that all the thinking chunks are inserted inside the thinking tag
2100 let insertion_position =
2101 message_old_end_offset - THOUGHT_PROCESS_END_MARKER.len();
2102 buffer.edit(
2103 [(insertion_position..insertion_position, chunk)],
2104 None,
2105 cx,
2106 );
2107 }
2108 }
2109 LanguageModelCompletionEvent::Text(mut chunk) => {
2110 if let Some(start) = thought_process_stack.pop() {
2111 let end = buffer.anchor_before(message_old_end_offset);
2112 context_event =
2113 Some(ContextEvent::EndedThoughtProcess(end));
2114 thought_process_output_section =
2115 Some(ThoughtProcessOutputSection {
2116 range: start..end,
2117 });
2118 chunk.insert_str(0, "\n\n");
2119 }
2120
2121 buffer.edit(
2122 [(
2123 message_old_end_offset..message_old_end_offset,
2124 chunk,
2125 )],
2126 None,
2127 cx,
2128 );
2129 }
2130 LanguageModelCompletionEvent::ToolUse(_) |
2131 LanguageModelCompletionEvent::UsageUpdate(_) => {}
2132 }
2133 });
2134
2135 if let Some(section) = thought_process_output_section.take() {
2136 this.insert_thought_process_output_section(section, cx);
2137 }
2138 if let Some(context_event) = context_event.take() {
2139 cx.emit(context_event);
2140 }
2141
2142 cx.emit(ContextEvent::StreamedCompletion);
2143
2144 Some(())
2145 })?;
2146 smol::future::yield_now().await;
2147 }
2148 this.update(cx, |this, cx| {
2149 this.pending_completions
2150 .retain(|completion| completion.id != pending_completion_id);
2151 this.summarize(false, cx);
2152 this.update_cache_status_for_completion(cx);
2153 })?;
2154
2155 anyhow::Ok(stop_reason)
2156 };
2157
2158 let result = stream_completion.await;
2159
2160 this.update(cx, |this, cx| {
2161 let error_message = if let Some(error) = result.as_ref().err() {
2162 if error.is::<PaymentRequiredError>() {
2163 cx.emit(ContextEvent::ShowPaymentRequiredError);
2164 this.update_metadata(assistant_message_id, cx, |metadata| {
2165 metadata.status = MessageStatus::Canceled;
2166 });
2167 Some(error.to_string())
2168 } else {
2169 let error_message = error
2170 .chain()
2171 .map(|err| err.to_string())
2172 .collect::<Vec<_>>()
2173 .join("\n");
2174 cx.emit(ContextEvent::ShowAssistError(SharedString::from(
2175 error_message.clone(),
2176 )));
2177 this.update_metadata(assistant_message_id, cx, |metadata| {
2178 metadata.status =
2179 MessageStatus::Error(SharedString::from(error_message.clone()));
2180 });
2181 Some(error_message)
2182 }
2183 } else {
2184 this.update_metadata(assistant_message_id, cx, |metadata| {
2185 metadata.status = MessageStatus::Done;
2186 });
2187 None
2188 };
2189
2190 let language_name = this
2191 .buffer
2192 .read(cx)
2193 .language()
2194 .map(|language| language.name());
2195 report_assistant_event(
2196 AssistantEventData {
2197 conversation_id: Some(this.id.0.clone()),
2198 kind: AssistantKind::Panel,
2199 phase: AssistantPhase::Response,
2200 message_id: None,
2201 model: model.telemetry_id(),
2202 model_provider: model.provider_id().to_string(),
2203 response_latency,
2204 error_message,
2205 language_name: language_name.map(|name| name.to_proto()),
2206 },
2207 this.telemetry.clone(),
2208 cx.http_client(),
2209 model.api_key(cx),
2210 cx.background_executor(),
2211 );
2212
2213 if let Ok(stop_reason) = result {
2214 match stop_reason {
2215 StopReason::ToolUse => {}
2216 StopReason::EndTurn => {}
2217 StopReason::MaxTokens => {}
2218 StopReason::Refusal => {}
2219 }
2220 }
2221 })
2222 .ok();
2223 }
2224 });
2225
2226 self.pending_completions.push(PendingCompletion {
2227 id: pending_completion_id,
2228 assistant_message_id: assistant_message.id,
2229 _task: task,
2230 });
2231
2232 Some(user_message)
2233 }
2234
2235 pub fn to_xml(&self, cx: &App) -> String {
2236 let mut output = String::new();
2237 let buffer = self.buffer.read(cx);
2238 for message in self.messages(cx) {
2239 if message.status != MessageStatus::Done {
2240 continue;
2241 }
2242
2243 writeln!(&mut output, "<{}>", message.role).unwrap();
2244 for chunk in buffer.text_for_range(message.offset_range) {
2245 output.push_str(chunk);
2246 }
2247 if !output.ends_with('\n') {
2248 output.push('\n');
2249 }
2250 writeln!(&mut output, "</{}>", message.role).unwrap();
2251 }
2252 output
2253 }
2254
2255 pub fn to_completion_request(
2256 &self,
2257 model: Option<&Arc<dyn LanguageModel>>,
2258 cx: &App,
2259 ) -> LanguageModelRequest {
2260 let buffer = self.buffer.read(cx);
2261
2262 let mut contents = self.contents(cx).peekable();
2263
2264 fn collect_text_content(buffer: &Buffer, range: Range<usize>) -> Option<String> {
2265 let text: String = buffer.text_for_range(range.clone()).collect();
2266 if text.trim().is_empty() {
2267 None
2268 } else {
2269 Some(text)
2270 }
2271 }
2272
2273 let mut completion_request = LanguageModelRequest {
2274 thread_id: None,
2275 prompt_id: None,
2276 intent: Some(CompletionIntent::UserPrompt),
2277 mode: None,
2278 messages: Vec::new(),
2279 tools: Vec::new(),
2280 tool_choice: None,
2281 stop: Vec::new(),
2282 temperature: model.and_then(|model| AgentSettings::temperature_for_model(model, cx)),
2283 };
2284 for message in self.messages(cx) {
2285 if message.status != MessageStatus::Done {
2286 continue;
2287 }
2288
2289 let mut offset = message.offset_range.start;
2290 let mut request_message = LanguageModelRequestMessage {
2291 role: message.role,
2292 content: Vec::new(),
2293 cache: message
2294 .cache
2295 .as_ref()
2296 .map_or(false, |cache| cache.is_anchor),
2297 };
2298
2299 while let Some(content) = contents.peek() {
2300 if content
2301 .range()
2302 .end
2303 .cmp(&message.anchor_range.end, buffer)
2304 .is_lt()
2305 {
2306 let content = contents.next().unwrap();
2307 let range = content.range().to_offset(buffer);
2308 request_message.content.extend(
2309 collect_text_content(buffer, offset..range.start).map(MessageContent::Text),
2310 );
2311
2312 match content {
2313 Content::Image { image, .. } => {
2314 if let Some(image) = image.clone().now_or_never().flatten() {
2315 request_message
2316 .content
2317 .push(language_model::MessageContent::Image(image));
2318 }
2319 }
2320 }
2321
2322 offset = range.end;
2323 } else {
2324 break;
2325 }
2326 }
2327
2328 request_message.content.extend(
2329 collect_text_content(buffer, offset..message.offset_range.end)
2330 .map(MessageContent::Text),
2331 );
2332
2333 if !request_message.contents_empty() {
2334 completion_request.messages.push(request_message);
2335 }
2336 }
2337 let supports_max_mode = if let Some(model) = model {
2338 model.supports_max_mode()
2339 } else {
2340 false
2341 };
2342
2343 if supports_max_mode {
2344 completion_request.mode = Some(self.completion_mode.into());
2345 }
2346 completion_request
2347 }
2348
2349 pub fn cancel_last_assist(&mut self, cx: &mut Context<Self>) -> bool {
2350 if let Some(pending_completion) = self.pending_completions.pop() {
2351 self.update_metadata(pending_completion.assistant_message_id, cx, |metadata| {
2352 if metadata.status == MessageStatus::Pending {
2353 metadata.status = MessageStatus::Canceled;
2354 }
2355 });
2356 true
2357 } else {
2358 false
2359 }
2360 }
2361
2362 pub fn cycle_message_roles(&mut self, ids: HashSet<MessageId>, cx: &mut Context<Self>) {
2363 for id in &ids {
2364 if let Some(metadata) = self.messages_metadata.get(id) {
2365 let role = metadata.role.cycle();
2366 self.update_metadata(*id, cx, |metadata| metadata.role = role);
2367 }
2368 }
2369
2370 self.message_roles_updated(ids, cx);
2371 }
2372
2373 fn message_roles_updated(&mut self, ids: HashSet<MessageId>, cx: &mut Context<Self>) {
2374 let mut ranges = Vec::new();
2375 for message in self.messages(cx) {
2376 if ids.contains(&message.id) {
2377 ranges.push(message.anchor_range.clone());
2378 }
2379 }
2380 }
2381
2382 pub fn update_metadata(
2383 &mut self,
2384 id: MessageId,
2385 cx: &mut Context<Self>,
2386 f: impl FnOnce(&mut MessageMetadata),
2387 ) {
2388 let version = self.version.clone();
2389 let timestamp = self.next_timestamp();
2390 if let Some(metadata) = self.messages_metadata.get_mut(&id) {
2391 f(metadata);
2392 metadata.timestamp = timestamp;
2393 let operation = ContextOperation::UpdateMessage {
2394 message_id: id,
2395 metadata: metadata.clone(),
2396 version,
2397 };
2398 self.push_op(operation, cx);
2399 cx.emit(ContextEvent::MessagesEdited);
2400 cx.notify();
2401 }
2402 }
2403
2404 pub fn insert_message_after(
2405 &mut self,
2406 message_id: MessageId,
2407 role: Role,
2408 status: MessageStatus,
2409 cx: &mut Context<Self>,
2410 ) -> Option<MessageAnchor> {
2411 if let Some(prev_message_ix) = self
2412 .message_anchors
2413 .iter()
2414 .position(|message| message.id == message_id)
2415 {
2416 // Find the next valid message after the one we were given.
2417 let mut next_message_ix = prev_message_ix + 1;
2418 while let Some(next_message) = self.message_anchors.get(next_message_ix) {
2419 if next_message.start.is_valid(self.buffer.read(cx)) {
2420 break;
2421 }
2422 next_message_ix += 1;
2423 }
2424
2425 let buffer = self.buffer.read(cx);
2426 let offset = self
2427 .message_anchors
2428 .get(next_message_ix)
2429 .map_or(buffer.len(), |message| {
2430 buffer.clip_offset(message.start.to_offset(buffer) - 1, Bias::Left)
2431 });
2432 Some(self.insert_message_at_offset(offset, role, status, cx))
2433 } else {
2434 None
2435 }
2436 }
2437
2438 fn insert_message_at_offset(
2439 &mut self,
2440 offset: usize,
2441 role: Role,
2442 status: MessageStatus,
2443 cx: &mut Context<Self>,
2444 ) -> MessageAnchor {
2445 let start = self.buffer.update(cx, |buffer, cx| {
2446 buffer.edit([(offset..offset, "\n")], None, cx);
2447 buffer.anchor_before(offset + 1)
2448 });
2449
2450 let version = self.version.clone();
2451 let anchor = MessageAnchor {
2452 id: MessageId(self.next_timestamp()),
2453 start,
2454 };
2455 let metadata = MessageMetadata {
2456 role,
2457 status,
2458 timestamp: anchor.id.0,
2459 cache: None,
2460 };
2461 self.insert_message(anchor.clone(), metadata.clone(), cx);
2462 self.push_op(
2463 ContextOperation::InsertMessage {
2464 anchor: anchor.clone(),
2465 metadata,
2466 version,
2467 },
2468 cx,
2469 );
2470 anchor
2471 }
2472
2473 pub fn insert_content(&mut self, content: Content, cx: &mut Context<Self>) {
2474 let buffer = self.buffer.read(cx);
2475 let insertion_ix = match self
2476 .contents
2477 .binary_search_by(|probe| probe.cmp(&content, buffer))
2478 {
2479 Ok(ix) => {
2480 self.contents.remove(ix);
2481 ix
2482 }
2483 Err(ix) => ix,
2484 };
2485 self.contents.insert(insertion_ix, content);
2486 cx.emit(ContextEvent::MessagesEdited);
2487 }
2488
2489 pub fn contents<'a>(&'a self, cx: &'a App) -> impl 'a + Iterator<Item = Content> {
2490 let buffer = self.buffer.read(cx);
2491 self.contents
2492 .iter()
2493 .filter(|content| {
2494 let range = content.range();
2495 range.start.is_valid(buffer) && range.end.is_valid(buffer)
2496 })
2497 .cloned()
2498 }
2499
2500 pub fn split_message(
2501 &mut self,
2502 range: Range<usize>,
2503 cx: &mut Context<Self>,
2504 ) -> (Option<MessageAnchor>, Option<MessageAnchor>) {
2505 let start_message = self.message_for_offset(range.start, cx);
2506 let end_message = self.message_for_offset(range.end, cx);
2507 if let Some((start_message, end_message)) = start_message.zip(end_message) {
2508 // Prevent splitting when range spans multiple messages.
2509 if start_message.id != end_message.id {
2510 return (None, None);
2511 }
2512
2513 let message = start_message;
2514 let role = message.role;
2515 let mut edited_buffer = false;
2516
2517 let mut suffix_start = None;
2518
2519 // TODO: why did this start panicking?
2520 if range.start > message.offset_range.start
2521 && range.end < message.offset_range.end.saturating_sub(1)
2522 {
2523 if self.buffer.read(cx).chars_at(range.end).next() == Some('\n') {
2524 suffix_start = Some(range.end + 1);
2525 } else if self.buffer.read(cx).reversed_chars_at(range.end).next() == Some('\n') {
2526 suffix_start = Some(range.end);
2527 }
2528 }
2529
2530 let version = self.version.clone();
2531 let suffix = if let Some(suffix_start) = suffix_start {
2532 MessageAnchor {
2533 id: MessageId(self.next_timestamp()),
2534 start: self.buffer.read(cx).anchor_before(suffix_start),
2535 }
2536 } else {
2537 self.buffer.update(cx, |buffer, cx| {
2538 buffer.edit([(range.end..range.end, "\n")], None, cx);
2539 });
2540 edited_buffer = true;
2541 MessageAnchor {
2542 id: MessageId(self.next_timestamp()),
2543 start: self.buffer.read(cx).anchor_before(range.end + 1),
2544 }
2545 };
2546
2547 let suffix_metadata = MessageMetadata {
2548 role,
2549 status: MessageStatus::Done,
2550 timestamp: suffix.id.0,
2551 cache: None,
2552 };
2553 self.insert_message(suffix.clone(), suffix_metadata.clone(), cx);
2554 self.push_op(
2555 ContextOperation::InsertMessage {
2556 anchor: suffix.clone(),
2557 metadata: suffix_metadata,
2558 version,
2559 },
2560 cx,
2561 );
2562
2563 let new_messages =
2564 if range.start == range.end || range.start == message.offset_range.start {
2565 (None, Some(suffix))
2566 } else {
2567 let mut prefix_end = None;
2568 if range.start > message.offset_range.start
2569 && range.end < message.offset_range.end - 1
2570 {
2571 if self.buffer.read(cx).chars_at(range.start).next() == Some('\n') {
2572 prefix_end = Some(range.start + 1);
2573 } else if self.buffer.read(cx).reversed_chars_at(range.start).next()
2574 == Some('\n')
2575 {
2576 prefix_end = Some(range.start);
2577 }
2578 }
2579
2580 let version = self.version.clone();
2581 let selection = if let Some(prefix_end) = prefix_end {
2582 MessageAnchor {
2583 id: MessageId(self.next_timestamp()),
2584 start: self.buffer.read(cx).anchor_before(prefix_end),
2585 }
2586 } else {
2587 self.buffer.update(cx, |buffer, cx| {
2588 buffer.edit([(range.start..range.start, "\n")], None, cx)
2589 });
2590 edited_buffer = true;
2591 MessageAnchor {
2592 id: MessageId(self.next_timestamp()),
2593 start: self.buffer.read(cx).anchor_before(range.end + 1),
2594 }
2595 };
2596
2597 let selection_metadata = MessageMetadata {
2598 role,
2599 status: MessageStatus::Done,
2600 timestamp: selection.id.0,
2601 cache: None,
2602 };
2603 self.insert_message(selection.clone(), selection_metadata.clone(), cx);
2604 self.push_op(
2605 ContextOperation::InsertMessage {
2606 anchor: selection.clone(),
2607 metadata: selection_metadata,
2608 version,
2609 },
2610 cx,
2611 );
2612
2613 (Some(selection), Some(suffix))
2614 };
2615
2616 if !edited_buffer {
2617 cx.emit(ContextEvent::MessagesEdited);
2618 }
2619 new_messages
2620 } else {
2621 (None, None)
2622 }
2623 }
2624
2625 fn insert_message(
2626 &mut self,
2627 new_anchor: MessageAnchor,
2628 new_metadata: MessageMetadata,
2629 cx: &mut Context<Self>,
2630 ) {
2631 cx.emit(ContextEvent::MessagesEdited);
2632
2633 self.messages_metadata.insert(new_anchor.id, new_metadata);
2634
2635 let buffer = self.buffer.read(cx);
2636 let insertion_ix = self
2637 .message_anchors
2638 .iter()
2639 .position(|anchor| {
2640 let comparison = new_anchor.start.cmp(&anchor.start, buffer);
2641 comparison.is_lt() || (comparison.is_eq() && new_anchor.id > anchor.id)
2642 })
2643 .unwrap_or(self.message_anchors.len());
2644 self.message_anchors.insert(insertion_ix, new_anchor);
2645 }
2646
2647 pub fn summarize(&mut self, mut replace_old: bool, cx: &mut Context<Self>) {
2648 let Some(model) = LanguageModelRegistry::read_global(cx).default_model() else {
2649 return;
2650 };
2651
2652 if replace_old || (self.message_anchors.len() >= 2 && self.summary.is_pending()) {
2653 if !model.provider.is_authenticated(cx) {
2654 return;
2655 }
2656
2657 let mut request = self.to_completion_request(Some(&model.model), cx);
2658 request.messages.push(LanguageModelRequestMessage {
2659 role: Role::User,
2660 content: vec![
2661 "Generate a concise 3-7 word title for this conversation, omitting punctuation. Go straight to the title, without any preamble and prefix like `Here's a concise suggestion:...` or `Title:`"
2662 .into(),
2663 ],
2664 cache: false,
2665 });
2666
2667 // If there is no summary, it is set with `done: false` so that "Loading Summary…" can
2668 // be displayed.
2669 match self.summary {
2670 ContextSummary::Pending | ContextSummary::Error => {
2671 self.summary = ContextSummary::Content(ContextSummaryContent {
2672 text: "".to_string(),
2673 done: false,
2674 timestamp: clock::Lamport::default(),
2675 });
2676 replace_old = true;
2677 }
2678 ContextSummary::Content(_) => {}
2679 }
2680
2681 self.summary_task = cx.spawn(async move |this, cx| {
2682 let result = async {
2683 let stream = model.model.stream_completion_text(request, &cx);
2684 let mut messages = stream.await?;
2685
2686 let mut replaced = !replace_old;
2687 while let Some(message) = messages.stream.next().await {
2688 let text = message?;
2689 let mut lines = text.lines();
2690 this.update(cx, |this, cx| {
2691 let version = this.version.clone();
2692 let timestamp = this.next_timestamp();
2693 let summary = this.summary.content_or_set_empty();
2694 if !replaced && replace_old {
2695 summary.text.clear();
2696 replaced = true;
2697 }
2698 summary.text.extend(lines.next());
2699 summary.timestamp = timestamp;
2700 let operation = ContextOperation::UpdateSummary {
2701 summary: summary.clone(),
2702 version,
2703 };
2704 this.push_op(operation, cx);
2705 cx.emit(ContextEvent::SummaryChanged);
2706 cx.emit(ContextEvent::SummaryGenerated);
2707 })?;
2708
2709 // Stop if the LLM generated multiple lines.
2710 if lines.next().is_some() {
2711 break;
2712 }
2713 }
2714
2715 this.read_with(cx, |this, _cx| {
2716 if let Some(summary) = this.summary.content() {
2717 if summary.text.is_empty() {
2718 bail!("Model generated an empty summary");
2719 }
2720 }
2721 Ok(())
2722 })??;
2723
2724 this.update(cx, |this, cx| {
2725 let version = this.version.clone();
2726 let timestamp = this.next_timestamp();
2727 if let Some(summary) = this.summary.content_as_mut() {
2728 summary.done = true;
2729 summary.timestamp = timestamp;
2730 let operation = ContextOperation::UpdateSummary {
2731 summary: summary.clone(),
2732 version,
2733 };
2734 this.push_op(operation, cx);
2735 cx.emit(ContextEvent::SummaryChanged);
2736 cx.emit(ContextEvent::SummaryGenerated);
2737 }
2738 })?;
2739
2740 anyhow::Ok(())
2741 }
2742 .await;
2743
2744 if let Err(err) = result {
2745 this.update(cx, |this, cx| {
2746 this.summary = ContextSummary::Error;
2747 cx.emit(ContextEvent::SummaryChanged);
2748 })
2749 .log_err();
2750 log::error!("Error generating context summary: {}", err);
2751 }
2752
2753 Some(())
2754 });
2755 }
2756 }
2757
2758 fn message_for_offset(&self, offset: usize, cx: &App) -> Option<Message> {
2759 self.messages_for_offsets([offset], cx).pop()
2760 }
2761
2762 pub fn messages_for_offsets(
2763 &self,
2764 offsets: impl IntoIterator<Item = usize>,
2765 cx: &App,
2766 ) -> Vec<Message> {
2767 let mut result = Vec::new();
2768
2769 let mut messages = self.messages(cx).peekable();
2770 let mut offsets = offsets.into_iter().peekable();
2771 let mut current_message = messages.next();
2772 while let Some(offset) = offsets.next() {
2773 // Locate the message that contains the offset.
2774 while current_message.as_ref().map_or(false, |message| {
2775 !message.offset_range.contains(&offset) && messages.peek().is_some()
2776 }) {
2777 current_message = messages.next();
2778 }
2779 let Some(message) = current_message.as_ref() else {
2780 break;
2781 };
2782
2783 // Skip offsets that are in the same message.
2784 while offsets.peek().map_or(false, |offset| {
2785 message.offset_range.contains(offset) || messages.peek().is_none()
2786 }) {
2787 offsets.next();
2788 }
2789
2790 result.push(message.clone());
2791 }
2792 result
2793 }
2794
2795 fn messages_from_anchors<'a>(
2796 &'a self,
2797 message_anchors: impl Iterator<Item = &'a MessageAnchor> + 'a,
2798 cx: &'a App,
2799 ) -> impl 'a + Iterator<Item = Message> {
2800 let buffer = self.buffer.read(cx);
2801
2802 Self::messages_from_iters(buffer, &self.messages_metadata, message_anchors.enumerate())
2803 }
2804
2805 pub fn messages<'a>(&'a self, cx: &'a App) -> impl 'a + Iterator<Item = Message> {
2806 self.messages_from_anchors(self.message_anchors.iter(), cx)
2807 }
2808
2809 pub fn messages_from_iters<'a>(
2810 buffer: &'a Buffer,
2811 metadata: &'a HashMap<MessageId, MessageMetadata>,
2812 messages: impl Iterator<Item = (usize, &'a MessageAnchor)> + 'a,
2813 ) -> impl 'a + Iterator<Item = Message> {
2814 let mut messages = messages.peekable();
2815
2816 iter::from_fn(move || {
2817 if let Some((start_ix, message_anchor)) = messages.next() {
2818 let metadata = metadata.get(&message_anchor.id)?;
2819
2820 let message_start = message_anchor.start.to_offset(buffer);
2821 let mut message_end = None;
2822 let mut end_ix = start_ix;
2823 while let Some((_, next_message)) = messages.peek() {
2824 if next_message.start.is_valid(buffer) {
2825 message_end = Some(next_message.start);
2826 break;
2827 } else {
2828 end_ix += 1;
2829 messages.next();
2830 }
2831 }
2832 let message_end_anchor = message_end.unwrap_or(language::Anchor::MAX);
2833 let message_end = message_end_anchor.to_offset(buffer);
2834
2835 return Some(Message {
2836 index_range: start_ix..end_ix,
2837 offset_range: message_start..message_end,
2838 anchor_range: message_anchor.start..message_end_anchor,
2839 id: message_anchor.id,
2840 role: metadata.role,
2841 status: metadata.status.clone(),
2842 cache: metadata.cache.clone(),
2843 });
2844 }
2845 None
2846 })
2847 }
2848
2849 pub fn save(
2850 &mut self,
2851 debounce: Option<Duration>,
2852 fs: Arc<dyn Fs>,
2853 cx: &mut Context<AssistantContext>,
2854 ) {
2855 if self.replica_id() != ReplicaId::default() {
2856 // Prevent saving a remote context for now.
2857 return;
2858 }
2859
2860 self.pending_save = cx.spawn(async move |this, cx| {
2861 if let Some(debounce) = debounce {
2862 cx.background_executor().timer(debounce).await;
2863 }
2864
2865 let (old_path, summary) = this.read_with(cx, |this, _| {
2866 let path = this.path.clone();
2867 let summary = if let Some(summary) = this.summary.content() {
2868 if summary.done {
2869 Some(summary.text.clone())
2870 } else {
2871 None
2872 }
2873 } else {
2874 None
2875 };
2876 (path, summary)
2877 })?;
2878
2879 if let Some(summary) = summary {
2880 let context = this.read_with(cx, |this, cx| this.serialize(cx))?;
2881 let mut discriminant = 1;
2882 let mut new_path;
2883 loop {
2884 new_path = contexts_dir().join(&format!(
2885 "{} - {}.zed.json",
2886 summary.trim(),
2887 discriminant
2888 ));
2889 if fs.is_file(&new_path).await {
2890 discriminant += 1;
2891 } else {
2892 break;
2893 }
2894 }
2895
2896 fs.create_dir(contexts_dir().as_ref()).await?;
2897 fs.atomic_write(new_path.clone(), serde_json::to_string(&context).unwrap())
2898 .await?;
2899 if let Some(old_path) = old_path {
2900 if new_path.as_path() != old_path.as_ref() {
2901 fs.remove_file(
2902 &old_path,
2903 RemoveOptions {
2904 recursive: false,
2905 ignore_if_not_exists: true,
2906 },
2907 )
2908 .await?;
2909 }
2910 }
2911
2912 this.update(cx, |this, _| this.path = Some(new_path.into()))?;
2913 }
2914
2915 Ok(())
2916 });
2917 }
2918
2919 pub fn set_custom_summary(&mut self, custom_summary: String, cx: &mut Context<Self>) {
2920 let timestamp = self.next_timestamp();
2921 let summary = self.summary.content_or_set_empty();
2922 summary.timestamp = timestamp;
2923 summary.done = true;
2924 summary.text = custom_summary;
2925 cx.emit(ContextEvent::SummaryChanged);
2926 }
2927}
2928
2929#[derive(Debug, Default)]
2930pub struct ContextVersion {
2931 context: clock::Global,
2932 buffer: clock::Global,
2933}
2934
2935impl ContextVersion {
2936 pub fn from_proto(proto: &proto::ContextVersion) -> Self {
2937 Self {
2938 context: language::proto::deserialize_version(&proto.context_version),
2939 buffer: language::proto::deserialize_version(&proto.buffer_version),
2940 }
2941 }
2942
2943 pub fn to_proto(&self, context_id: ContextId) -> proto::ContextVersion {
2944 proto::ContextVersion {
2945 context_id: context_id.to_proto(),
2946 context_version: language::proto::serialize_version(&self.context),
2947 buffer_version: language::proto::serialize_version(&self.buffer),
2948 }
2949 }
2950}
2951
2952#[derive(Debug, Clone)]
2953pub struct ParsedSlashCommand {
2954 pub name: String,
2955 pub arguments: SmallVec<[String; 3]>,
2956 pub status: PendingSlashCommandStatus,
2957 pub source_range: Range<language::Anchor>,
2958}
2959
2960#[derive(Debug)]
2961pub struct InvokedSlashCommand {
2962 pub name: SharedString,
2963 pub range: Range<language::Anchor>,
2964 pub run_commands_in_ranges: Vec<Range<language::Anchor>>,
2965 pub status: InvokedSlashCommandStatus,
2966 pub transaction: Option<language::TransactionId>,
2967 timestamp: clock::Lamport,
2968}
2969
2970#[derive(Debug)]
2971pub enum InvokedSlashCommandStatus {
2972 Running(Task<()>),
2973 Error(SharedString),
2974 Finished,
2975}
2976
2977#[derive(Debug, Clone)]
2978pub enum PendingSlashCommandStatus {
2979 Idle,
2980 Running { _task: Shared<Task<()>> },
2981 Error(String),
2982}
2983
2984#[derive(Debug, Clone)]
2985pub struct PendingToolUse {
2986 pub id: LanguageModelToolUseId,
2987 pub name: String,
2988 pub input: serde_json::Value,
2989 pub status: PendingToolUseStatus,
2990 pub source_range: Range<language::Anchor>,
2991}
2992
2993#[derive(Debug, Clone)]
2994pub enum PendingToolUseStatus {
2995 Idle,
2996 Running { _task: Shared<Task<()>> },
2997 Error(String),
2998}
2999
3000impl PendingToolUseStatus {
3001 pub fn is_idle(&self) -> bool {
3002 matches!(self, PendingToolUseStatus::Idle)
3003 }
3004}
3005
3006#[derive(Serialize, Deserialize)]
3007pub struct SavedMessage {
3008 pub id: MessageId,
3009 pub start: usize,
3010 pub metadata: MessageMetadata,
3011}
3012
3013#[derive(Serialize, Deserialize)]
3014pub struct SavedContext {
3015 pub id: Option<ContextId>,
3016 pub zed: String,
3017 pub version: String,
3018 pub text: String,
3019 pub messages: Vec<SavedMessage>,
3020 pub summary: String,
3021 pub slash_command_output_sections:
3022 Vec<assistant_slash_command::SlashCommandOutputSection<usize>>,
3023 #[serde(default)]
3024 pub thought_process_output_sections: Vec<ThoughtProcessOutputSection<usize>>,
3025}
3026
3027impl SavedContext {
3028 pub const VERSION: &'static str = "0.4.0";
3029
3030 pub fn from_json(json: &str) -> Result<Self> {
3031 let saved_context_json = serde_json::from_str::<serde_json::Value>(json)?;
3032 match saved_context_json
3033 .get("version")
3034 .context("version not found")?
3035 {
3036 serde_json::Value::String(version) => match version.as_str() {
3037 SavedContext::VERSION => {
3038 Ok(serde_json::from_value::<SavedContext>(saved_context_json)?)
3039 }
3040 SavedContextV0_3_0::VERSION => {
3041 let saved_context =
3042 serde_json::from_value::<SavedContextV0_3_0>(saved_context_json)?;
3043 Ok(saved_context.upgrade())
3044 }
3045 SavedContextV0_2_0::VERSION => {
3046 let saved_context =
3047 serde_json::from_value::<SavedContextV0_2_0>(saved_context_json)?;
3048 Ok(saved_context.upgrade())
3049 }
3050 SavedContextV0_1_0::VERSION => {
3051 let saved_context =
3052 serde_json::from_value::<SavedContextV0_1_0>(saved_context_json)?;
3053 Ok(saved_context.upgrade())
3054 }
3055 _ => anyhow::bail!("unrecognized saved context version: {version:?}"),
3056 },
3057 _ => anyhow::bail!("version not found on saved context"),
3058 }
3059 }
3060
3061 fn into_ops(
3062 self,
3063 buffer: &Entity<Buffer>,
3064 cx: &mut Context<AssistantContext>,
3065 ) -> Vec<ContextOperation> {
3066 let mut operations = Vec::new();
3067 let mut version = clock::Global::new();
3068 let mut next_timestamp = clock::Lamport::new(ReplicaId::default());
3069
3070 let mut first_message_metadata = None;
3071 for message in self.messages {
3072 if message.id == MessageId(clock::Lamport::default()) {
3073 first_message_metadata = Some(message.metadata);
3074 } else {
3075 operations.push(ContextOperation::InsertMessage {
3076 anchor: MessageAnchor {
3077 id: message.id,
3078 start: buffer.read(cx).anchor_before(message.start),
3079 },
3080 metadata: MessageMetadata {
3081 role: message.metadata.role,
3082 status: message.metadata.status,
3083 timestamp: message.metadata.timestamp,
3084 cache: None,
3085 },
3086 version: version.clone(),
3087 });
3088 version.observe(message.id.0);
3089 next_timestamp.observe(message.id.0);
3090 }
3091 }
3092
3093 if let Some(metadata) = first_message_metadata {
3094 let timestamp = next_timestamp.tick();
3095 operations.push(ContextOperation::UpdateMessage {
3096 message_id: MessageId(clock::Lamport::default()),
3097 metadata: MessageMetadata {
3098 role: metadata.role,
3099 status: metadata.status,
3100 timestamp,
3101 cache: None,
3102 },
3103 version: version.clone(),
3104 });
3105 version.observe(timestamp);
3106 }
3107
3108 let buffer = buffer.read(cx);
3109 for section in self.slash_command_output_sections {
3110 let timestamp = next_timestamp.tick();
3111 operations.push(ContextOperation::SlashCommandOutputSectionAdded {
3112 timestamp,
3113 section: SlashCommandOutputSection {
3114 range: buffer.anchor_after(section.range.start)
3115 ..buffer.anchor_before(section.range.end),
3116 icon: section.icon,
3117 label: section.label,
3118 metadata: section.metadata,
3119 },
3120 version: version.clone(),
3121 });
3122
3123 version.observe(timestamp);
3124 }
3125
3126 for section in self.thought_process_output_sections {
3127 let timestamp = next_timestamp.tick();
3128 operations.push(ContextOperation::ThoughtProcessOutputSectionAdded {
3129 timestamp,
3130 section: ThoughtProcessOutputSection {
3131 range: buffer.anchor_after(section.range.start)
3132 ..buffer.anchor_before(section.range.end),
3133 },
3134 version: version.clone(),
3135 });
3136
3137 version.observe(timestamp);
3138 }
3139
3140 let timestamp = next_timestamp.tick();
3141 operations.push(ContextOperation::UpdateSummary {
3142 summary: ContextSummaryContent {
3143 text: self.summary,
3144 done: true,
3145 timestamp,
3146 },
3147 version: version.clone(),
3148 });
3149 version.observe(timestamp);
3150
3151 operations
3152 }
3153}
3154
3155#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
3156struct SavedMessageIdPreV0_4_0(usize);
3157
3158#[derive(Serialize, Deserialize)]
3159struct SavedMessagePreV0_4_0 {
3160 id: SavedMessageIdPreV0_4_0,
3161 start: usize,
3162}
3163
3164#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
3165struct SavedMessageMetadataPreV0_4_0 {
3166 role: Role,
3167 status: MessageStatus,
3168}
3169
3170#[derive(Serialize, Deserialize)]
3171struct SavedContextV0_3_0 {
3172 id: Option<ContextId>,
3173 zed: String,
3174 version: String,
3175 text: String,
3176 messages: Vec<SavedMessagePreV0_4_0>,
3177 message_metadata: HashMap<SavedMessageIdPreV0_4_0, SavedMessageMetadataPreV0_4_0>,
3178 summary: String,
3179 slash_command_output_sections: Vec<assistant_slash_command::SlashCommandOutputSection<usize>>,
3180}
3181
3182impl SavedContextV0_3_0 {
3183 const VERSION: &'static str = "0.3.0";
3184
3185 fn upgrade(self) -> SavedContext {
3186 SavedContext {
3187 id: self.id,
3188 zed: self.zed,
3189 version: SavedContext::VERSION.into(),
3190 text: self.text,
3191 messages: self
3192 .messages
3193 .into_iter()
3194 .filter_map(|message| {
3195 let metadata = self.message_metadata.get(&message.id)?;
3196 let timestamp = clock::Lamport {
3197 replica_id: ReplicaId::default(),
3198 value: message.id.0 as u32,
3199 };
3200 Some(SavedMessage {
3201 id: MessageId(timestamp),
3202 start: message.start,
3203 metadata: MessageMetadata {
3204 role: metadata.role,
3205 status: metadata.status.clone(),
3206 timestamp,
3207 cache: None,
3208 },
3209 })
3210 })
3211 .collect(),
3212 summary: self.summary,
3213 slash_command_output_sections: self.slash_command_output_sections,
3214 thought_process_output_sections: Vec::new(),
3215 }
3216 }
3217}
3218
3219#[derive(Serialize, Deserialize)]
3220struct SavedContextV0_2_0 {
3221 id: Option<ContextId>,
3222 zed: String,
3223 version: String,
3224 text: String,
3225 messages: Vec<SavedMessagePreV0_4_0>,
3226 message_metadata: HashMap<SavedMessageIdPreV0_4_0, SavedMessageMetadataPreV0_4_0>,
3227 summary: String,
3228}
3229
3230impl SavedContextV0_2_0 {
3231 const VERSION: &'static str = "0.2.0";
3232
3233 fn upgrade(self) -> SavedContext {
3234 SavedContextV0_3_0 {
3235 id: self.id,
3236 zed: self.zed,
3237 version: SavedContextV0_3_0::VERSION.to_string(),
3238 text: self.text,
3239 messages: self.messages,
3240 message_metadata: self.message_metadata,
3241 summary: self.summary,
3242 slash_command_output_sections: Vec::new(),
3243 }
3244 .upgrade()
3245 }
3246}
3247
3248#[derive(Serialize, Deserialize)]
3249struct SavedContextV0_1_0 {
3250 id: Option<ContextId>,
3251 zed: String,
3252 version: String,
3253 text: String,
3254 messages: Vec<SavedMessagePreV0_4_0>,
3255 message_metadata: HashMap<SavedMessageIdPreV0_4_0, SavedMessageMetadataPreV0_4_0>,
3256 summary: String,
3257 api_url: Option<String>,
3258 model: OpenAiModel,
3259}
3260
3261impl SavedContextV0_1_0 {
3262 const VERSION: &'static str = "0.1.0";
3263
3264 fn upgrade(self) -> SavedContext {
3265 SavedContextV0_2_0 {
3266 id: self.id,
3267 zed: self.zed,
3268 version: SavedContextV0_2_0::VERSION.to_string(),
3269 text: self.text,
3270 messages: self.messages,
3271 message_metadata: self.message_metadata,
3272 summary: self.summary,
3273 }
3274 .upgrade()
3275 }
3276}
3277
3278#[derive(Debug, Clone)]
3279pub struct SavedContextMetadata {
3280 pub title: String,
3281 pub path: Arc<Path>,
3282 pub mtime: chrono::DateTime<chrono::Local>,
3283}