Skip to content

Commit 7462589

Browse files
committed
few updates to output format
1 parent 5901c7e commit 7462589

File tree

4 files changed

+111
-69
lines changed

4 files changed

+111
-69
lines changed

crates/pattern_cli/src/chat.rs

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -515,7 +515,7 @@ pub async fn setup_group(
515515
let constellation_tracker = Arc::new(
516516
pattern_core::constellation_memory::ConstellationActivityTracker::with_memory_id(
517517
tracker_memory_id,
518-
50,
518+
25,
519519
),
520520
);
521521

@@ -1176,7 +1176,7 @@ pub async fn print_group_response_event(
11761176
.map(|a| a.agent.name())
11771177
.unwrap_or("Unknown Agent".to_string());
11781178

1179-
output.info(&format!("{} reasoning:", agent_name), &text);
1179+
output.agent_reasoning(&agent_name, &text);
11801180
}
11811181
}
11821182
GroupResponseEvent::ToolCallStarted {
@@ -1228,17 +1228,26 @@ pub async fn print_group_response_event(
12281228
output.tool_call(&fn_name, &args_display);
12291229
}
12301230
GroupResponseEvent::ToolCallCompleted {
1231-
agent_id: _,
1231+
agent_id,
12321232
call_id,
12331233
result,
1234-
} => match result {
1235-
Ok(result) => {
1236-
output.tool_result(&result);
1237-
}
1238-
Err(error) => {
1239-
output.error(&format!("Tool error (call {}): {}", call_id, error));
1234+
} => {
1235+
// Find agent name
1236+
let agent_name = agents_with_membership
1237+
.iter()
1238+
.find(|a| a.agent.id() == agent_id)
1239+
.map(|a| a.agent.name())
1240+
.unwrap_or("Unknown Agent".to_string());
1241+
output.status(&format!("Tool result {} for {}", call_id, agent_name));
1242+
match result {
1243+
Ok(result) => {
1244+
output.tool_result(&result);
1245+
}
1246+
Err(error) => {
1247+
output.error(&format!("Tool error (call {}): {}", call_id, error));
1248+
}
12401249
}
1241-
},
1250+
}
12421251
GroupResponseEvent::AgentCompleted { agent_name, .. } => {
12431252
output.status(&format!("{} completed", agent_name));
12441253
}

crates/pattern_cli/src/output.rs

Lines changed: 85 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use termimad::MadSkin;
77
#[derive(Clone)]
88
pub struct Output {
99
skin: MadSkin,
10+
reasoning_skin: MadSkin,
1011
writer: Option<SharedWriter>,
1112
}
1213

@@ -28,19 +29,43 @@ impl Output {
2829
skin.code_block.set_fg(termimad::ansi(15)); // bright white text
2930
// If a global SharedWriter is available (when chat has initialized
3031
// the readline UI), use it to avoid cursor glitches
32+
let mut reasoning_skin = MadSkin::default();
33+
// Keep it simple for copy-paste friendliness
34+
reasoning_skin.set_headers_fg(termimad::ansi(5)); // purple
35+
reasoning_skin.bold.set_fg(termimad::ansi(7)); // light grey
36+
37+
// Make inline code stand out with color but no background
38+
reasoning_skin.inline_code.set_fg(termimad::ansi(11)); // bright yellow
39+
reasoning_skin
40+
.inline_code
41+
.set_bg(termimad::crossterm::style::Color::Black);
42+
43+
// Fix code blocks to not have background
44+
reasoning_skin
45+
.code_block
46+
.set_bg(termimad::crossterm::style::Color::Black);
47+
reasoning_skin.code_block.set_fg(termimad::ansi(7)); // bright white
48+
// If a global SharedWriter is available (when chat has initialized
49+
// the readline UI), use it to avoid cursor glitches
3150
if let Some(shared) = crate::tracing_writer::get_shared_writer() {
3251
Self {
3352
skin,
53+
reasoning_skin,
3454
writer: Some(shared),
3555
}
3656
} else {
37-
Self { skin, writer: None }
57+
Self {
58+
skin,
59+
reasoning_skin,
60+
writer: None,
61+
}
3862
}
3963
}
4064

4165
pub fn with_writer(self, writer: SharedWriter) -> Self {
4266
Self {
4367
skin: self.skin,
68+
reasoning_skin: self.reasoning_skin,
4469
writer: Some(writer),
4570
}
4671
}
@@ -63,16 +88,40 @@ impl Output {
6388
}
6489
}
6590

91+
/// flush the buffer
92+
fn flush(&self) {
93+
if let Some(ref writer) = self.writer {
94+
// Clone the writer to get a mutable version
95+
let mut writer = writer.clone();
96+
let _ = writer.flush();
97+
} else {
98+
use std::io::{self, Write};
99+
let _ = io::stdout().flush();
100+
}
101+
}
102+
103+
/// Helper method to write output either to SharedWriter or stdout
104+
fn write_line_noclear(&self, content: &str) {
105+
if let Some(ref writer) = self.writer {
106+
// Clone the writer to get a mutable version
107+
let mut writer = writer.clone();
108+
// When using SharedWriter, it handles the synchronization
109+
let _ = writeln!(writer, "{}", content);
110+
} else {
111+
// Fallback to regular println
112+
println!("{}", content);
113+
}
114+
}
115+
66116
/// Print an agent message with markdown formatting
67117
pub fn agent_message(&self, agent_name: &str, content: &str) {
68118
// Clear visual separation without box drawing chars
69-
self.write_line("");
70-
self.write_line(&format!(
119+
self.write_line_noclear("");
120+
self.write_line_noclear(&format!(
71121
"{} {}",
72122
agent_name.bright_cyan().bold(),
73123
"says:".dimmed()
74124
));
75-
self.write_line("");
76125

77126
// Use termimad to format the markdown content
78127
use termimad::FmtText;
@@ -81,7 +130,27 @@ impl Output {
81130

82131
// Write each line through our write_line method
83132
for line in formatted_string.lines() {
84-
self.write_line(line);
133+
self.write_line_noclear(line);
134+
}
135+
136+
self.write_line("");
137+
}
138+
139+
/// Print an agent message with markdown formatting
140+
pub fn agent_reasoning(&self, agent_name: &str, content: &str) {
141+
self.write_line_noclear(&format!(
142+
"{} reasoning:",
143+
agent_name.bright_magenta().bold()
144+
));
145+
146+
// Use termimad to format the markdown content
147+
use termimad::FmtText;
148+
let formatted = FmtText::from(&self.reasoning_skin, content, Some(80));
149+
let formatted_string = formatted.to_string();
150+
151+
// Write each line through our write_line method
152+
for line in formatted_string.lines() {
153+
self.write_line_noclear(line);
85154
}
86155

87156
self.write_line("");
@@ -126,7 +195,7 @@ impl Output {
126195

127196
/// Print a tool call
128197
pub fn tool_call(&self, tool_name: &str, args: &str) {
129-
self.write_line(&format!(
198+
self.write_line_noclear(&format!(
130199
" {} Using tool: {}",
131200
">>".bright_blue(),
132201
tool_name.bright_yellow()
@@ -135,30 +204,32 @@ impl Output {
135204
// Indent each line of the args for proper alignment
136205
for (i, line) in args.lines().enumerate() {
137206
if i == 0 {
138-
self.write_line(&format!(" Args: {}", line).dimmed().to_string());
207+
self.write_line_noclear(&format!(" Args: {}", line).dimmed().to_string());
139208
} else {
140-
self.write_line(&format!(" {}", line).dimmed().to_string());
209+
self.write_line_noclear(&format!(" {}", line).dimmed().to_string());
141210
}
142211
}
143212
}
213+
self.flush();
144214
}
145215

146216
/// Print a tool result
147217
pub fn tool_result(&self, result: &str) {
148218
// Handle multi-line results with proper indentation
149219
let lines: Vec<&str> = result.lines().collect();
150220
if lines.len() == 1 {
151-
self.write_line(&format!(
221+
self.write_line_noclear(&format!(
152222
" {} Tool result: {}",
153223
"=>".bright_green(),
154224
result.dimmed()
155225
));
156226
} else {
157-
self.write_line(&format!(" {} Tool result:", "=>".bright_green()));
227+
self.write_line_noclear(&format!(" {} Tool result:", "=>".bright_green()));
158228
for line in lines {
159-
self.write_line(&format!(" {}", line.dimmed()));
229+
self.write_line_noclear(&format!(" {}", line.dimmed()));
160230
}
161231
}
232+
self.flush();
162233
}
163234

164235
/// Print a "working on it" status message
@@ -196,15 +267,16 @@ impl Output {
196267

197268
// Write each line through our write_line method
198269
for line in formatted_string.lines() {
199-
self.write_line(line);
270+
self.write_line_noclear(line);
200271
}
272+
self.flush();
201273
}
202274

203275
/// Print a table-like header
204276
#[allow(dead_code)]
205277
pub fn table_header(&self, columns: &[&str]) {
206278
let header = columns.join(" | ");
207-
self.write_line(&format!(" {}", header.bright_white().bold()));
279+
self.write_line_noclear(&format!(" {}", header.bright_white().bold()));
208280
self.write_line(&format!(" {}", "─".repeat(header.len()).dimmed()));
209281
}
210282

crates/pattern_core/src/message.rs

Lines changed: 7 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -976,11 +976,7 @@ impl Message {
976976
}
977977
ChatRole::User => self.content.clone(),
978978
},
979-
MessageContent::Blocks(_) => {
980-
// Blocks are preserved as-is for providers that support them
981-
tracing::trace!("Preserving Blocks message with role {:?}", role);
982-
self.content.clone()
983-
}
979+
MessageContent::Blocks(_) => self.content.clone(),
984980
};
985981

986982
genai::chat::ChatMessage {
@@ -1017,16 +1013,8 @@ pub struct Request {
10171013
}
10181014

10191015
impl Request {
1020-
/// Validate that the request has no orphaned tool calls and proper ordering
1021-
pub fn validate(&mut self) -> crate::Result<()> {
1022-
Ok(())
1023-
}
1024-
10251016
/// Convert this request to a genai ChatRequest
10261017
pub fn as_chat_request(&mut self) -> crate::Result<genai::chat::ChatRequest> {
1027-
// Validate before converting
1028-
self.validate()?;
1029-
10301018
// Fix assistant messages that end with thinking blocks
10311019
for msg in &mut self.messages {
10321020
if msg.role == ChatRole::User || msg.role == ChatRole::System {
@@ -1864,30 +1852,6 @@ impl Message {
18641852
});
18651853
}
18661854

1867-
// // If response was empty but had reasoning, create a text message
1868-
// if messages.is_empty() && response.reasoning.is_some() {
1869-
// messages.push(Self {
1870-
// id: MessageId::generate(),
1871-
// role: ChatRole::Assistant,
1872-
// content: MessageContent::Text(response.reasoning.clone().unwrap_or_default()),
1873-
// metadata: MessageMetadata {
1874-
// user_id: Some(agent_id.to_string()),
1875-
// ..Default::default()
1876-
// },
1877-
// options: MessageOptions::default(),
1878-
// created_at: Utc::now(),
1879-
// owner_id: None,
1880-
// has_tool_calls: false,
1881-
// word_count: response
1882-
// .reasoning
1883-
// .as_ref()
1884-
// .map(|r| r.split_whitespace().count() as u32)
1885-
// .unwrap_or(0),
1886-
// embedding: None,
1887-
// embedding_model: None,
1888-
// });
1889-
// }
1890-
18911855
messages
18921856
}
18931857

@@ -1943,7 +1907,7 @@ impl Message {
19431907
}
19441908
})
19451909
.collect::<Vec<_>>()
1946-
.join(" ")
1910+
.join("\n")
19471911
}
19481912
MessageContent::ToolCalls(calls) => {
19491913
// Just dump the JSON for tool calls
@@ -1958,15 +1922,15 @@ impl Message {
19581922
)
19591923
})
19601924
.collect::<Vec<_>>()
1961-
.join(" ")
1925+
.join("\n")
19621926
}
19631927
MessageContent::ToolResponses(responses) => {
19641928
// Include tool response content
19651929
responses
19661930
.iter()
19671931
.map(|resp| format!("[Tool Response] {}", resp.content))
19681932
.collect::<Vec<_>>()
1969-
.join(" ")
1933+
.join("\n")
19701934
}
19711935
MessageContent::Blocks(blocks) => {
19721936
// Extract text from all block types including reasoning
@@ -1996,7 +1960,7 @@ impl Message {
19961960
}
19971961
})
19981962
.collect::<Vec<_>>()
1999-
.join(" ")
1963+
.join("\n")
20001964
}
20011965
}
20021966
}
@@ -2039,7 +2003,7 @@ impl Message {
20392003
/// Rough estimation of token count for this message
20402004
///
20412005
/// Uses the approximation of ~4 characters per token
2042-
/// Images are estimated at 1600 tokens each
2006+
/// Images are estimated at 1200 tokens each
20432007
pub fn estimate_tokens(&self) -> usize {
20442008
let text_tokens = self.display_content().len() / 4;
20452009

@@ -2052,7 +2016,7 @@ impl Message {
20522016
_ => 0,
20532017
};
20542018

2055-
text_tokens + (image_count * 1600)
2019+
text_tokens + (image_count * 1200)
20562020
}
20572021
}
20582022

crates/pattern_core/src/oauth/resolver.rs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,15 @@
33
//! Provides AuthResolver and ServiceTargetResolver implementations that
44
//! integrate with Pattern's OAuth token storage.
55
6-
use crate::db::DbEntity;
76
use crate::error::CoreError;
87
use crate::id::UserId;
9-
use crate::oauth::OAuthToken;
108
use genai::ModelIden;
119
use genai::ServiceTarget;
1210
use genai::adapter::AdapterKind;
1311
use genai::resolver::{AuthData, AuthResolver, Result as ResolverResult, ServiceTargetResolver};
1412
use std::future::Future;
1513
use std::pin::Pin;
1614
use std::sync::Arc;
17-
use surrealdb::RecordId;
1815
use surrealdb::{Connection, Surreal};
1916

2017
/// Create an OAuth-aware auth resolver for Pattern

0 commit comments

Comments
 (0)