added streaming support

This commit is contained in:
leach
2025-08-19 00:20:29 -04:00
parent 6d0592bda5
commit 0faecbf657
7 changed files with 969 additions and 30 deletions

View File

@@ -6,6 +6,8 @@ use crate::core::{
ChatClient, Session,
};
use crate::utils::{Display, InputHandler, SessionAction};
use std::future::Future;
use std::pin::Pin;
pub struct ChatCLI {
session: Session,
@@ -82,8 +84,6 @@ impl ChatCLI {
self.session.add_user_message(message.to_string());
self.session.save()?;
let spinner = self.display.show_spinner("Thinking");
// Clone data needed for the API call before getting mutable client reference
let model = self.session.model.clone();
let messages = self.session.messages.clone();
@@ -91,27 +91,75 @@ impl ChatCLI {
let enable_reasoning_summary = self.session.enable_reasoning_summary;
let reasoning_effort = self.session.reasoning_effort.clone();
let client = self.get_client()?;
// Check if we should use streaming before getting client
let should_use_streaming = {
let client = self.get_client()?;
client.supports_streaming()
};
match client
.chat_completion(
&model,
&messages,
enable_web_search,
enable_reasoning_summary,
&reasoning_effort,
)
.await
{
Ok(response) => {
spinner.finish("Done");
self.display.print_assistant_response(&response);
self.session.add_assistant_message(response);
self.session.save()?;
if should_use_streaming {
print!("{} ", console::style("🤖").magenta());
use std::io::{self, Write};
io::stdout().flush().ok();
let stream_callback = {
use crate::core::StreamCallback;
Box::new(move |chunk: &str| {
print!("{}", chunk);
use std::io::{self, Write};
io::stdout().flush().ok();
Box::pin(async move {}) as Pin<Box<dyn Future<Output = ()> + Send>>
}) as StreamCallback
};
let client = self.get_client()?;
match client
.chat_completion_stream(
&model,
&messages,
enable_web_search,
enable_reasoning_summary,
&reasoning_effort,
stream_callback,
)
.await
{
Ok(response) => {
println!(); // Add newline after streaming
self.session.add_assistant_message(response);
self.session.save()?;
}
Err(e) => {
println!(); // Add newline after failed streaming
self.display.print_error(&format!("Streaming failed: {}", e));
return Err(e);
}
}
Err(e) => {
spinner.finish_with_error("Failed");
return Err(e);
} else {
// Fallback to non-streaming
let spinner = self.display.show_spinner("Thinking");
let client = self.get_client()?;
match client
.chat_completion(
&model,
&messages,
enable_web_search,
enable_reasoning_summary,
&reasoning_effort,
)
.await
{
Ok(response) => {
spinner.finish("Done");
self.display.print_assistant_response(&response);
self.session.add_assistant_message(response);
self.session.save()?;
}
Err(e) => {
spinner.finish_with_error("Failed");
return Err(e);
}
}
}
@@ -150,6 +198,9 @@ impl ChatCLI {
"/tools" => {
self.tools_manager().await?;
}
"/history" => {
self.handle_history_command(&parts)?;
}
_ => {
self.display.print_error(&format!("Unknown command: {} (see /help)", parts[0]));
}
@@ -391,4 +442,52 @@ impl ChatCLI {
Ok(())
}
fn handle_history_command(&mut self, parts: &[&str]) -> Result<()> {
let mut filter_role: Option<&str> = None;
let mut limit: Option<usize> = None;
// Parse parameters
for &part in parts.iter().skip(1) {
match part {
"user" | "assistant" => filter_role = Some(part),
_ => {
if let Ok(num) = part.parse::<usize>() {
limit = Some(num);
} else {
self.display.print_error(&format!("Invalid parameter: {}", part));
self.display.print_info("Usage: /history [user|assistant] [number]");
return Ok(());
}
}
}
}
// Filter messages (skip system prompt at index 0)
let mut messages: Vec<(usize, &crate::core::Message)> = self.session.messages
.iter()
.enumerate()
.skip(1) // Skip system prompt
.collect();
// Apply role filter
if let Some(role) = filter_role {
messages.retain(|(_, msg)| msg.role == role);
}
// Apply limit
if let Some(limit_count) = limit {
let start_index = messages.len().saturating_sub(limit_count);
messages = messages[start_index..].to_vec();
}
if messages.is_empty() {
self.display.print_info("No messages to display");
return Ok(());
}
// Format and display
self.display.print_conversation_history(&messages);
Ok(())
}
}