1use crate::message::{Content, Message};
5
6pub struct ConversationView<'a> {
7 messages: &'a [Message],
8}
9
10impl<'a> ConversationView<'a> {
11 pub fn new(messages: &'a [Message]) -> Self {
12 Self { messages }
13 }
14
15 pub fn messages(&self) -> &[Message] {
16 self.messages
17 }
18
19 pub fn last_assistant(&self) -> Option<&Message> {
20 self.messages
21 .iter()
22 .rev()
23 .find(|m| matches!(m, Message::Assistant { .. }))
24 }
25
26 pub fn user_visible(&self) -> Vec<Message> {
29 self.messages
30 .iter()
31 .filter_map(|m| match m {
32 Message::System { .. } => None,
33 Message::User { content, meta } => {
34 let filtered: Vec<Content> = content
35 .iter()
36 .filter(|c| {
37 !matches!(c, Content::ToolUse { .. } | Content::ToolResult { .. })
38 })
39 .cloned()
40 .collect();
41 if filtered.is_empty() {
42 None
43 } else {
44 Some(Message::User {
45 content: filtered,
46 meta: meta.clone(),
47 })
48 }
49 }
50 Message::Assistant {
51 content,
52 stop_reason,
53 meta,
54 } => {
55 let filtered: Vec<Content> = content
56 .iter()
57 .filter(|c| {
58 !matches!(
59 c,
60 Content::ToolUse { .. }
61 | Content::ToolResult { .. }
62 | Content::Thinking { .. }
63 )
64 })
65 .cloned()
66 .collect();
67 if filtered.is_empty() {
68 None
69 } else {
70 Some(Message::Assistant {
71 content: filtered,
72 stop_reason: stop_reason.clone(),
73 meta: meta.clone(),
74 })
75 }
76 }
77 })
78 .collect()
79 }
80
81 pub fn token_estimate(&self) -> u32 {
84 let chars: usize = self
85 .messages
86 .iter()
87 .map(|m| match m {
88 Message::System { content, .. } => content.len(),
89 Message::User { content, .. } | Message::Assistant { content, .. } => content
90 .iter()
91 .map(|c| match c {
92 Content::Text { text } => text.len(),
93 Content::Thinking { thinking } => thinking.len(),
94 Content::ToolUse { input, .. } => input.to_string().len(),
95 Content::ToolResult { output, .. } => output
96 .content
97 .iter()
98 .map(|inner| match inner {
99 Content::Text { text } => text.len(),
100 _ => 0,
101 })
102 .sum(),
103 Content::Image(_)
104 | Content::Document(_)
105 | Content::Audio(_)
106 | Content::Citation(_) => 0,
107 })
108 .sum(),
109 })
110 .sum();
111 (chars / 4) as u32
112 }
113}