Signed-off-by: toyamagu2021@gmail.com <toyamagu2021@gmail.com>
This commit is contained in:
@@ -4,7 +4,7 @@ use cliclack::{self, intro, outro};
|
||||
use std::path::Path;
|
||||
|
||||
use crate::project_tracker::ProjectTracker;
|
||||
use crate::utils::safe_truncate;
|
||||
use goose::utils::safe_truncate;
|
||||
|
||||
/// Format a DateTime for display
|
||||
fn format_date(date: DateTime<chrono::Utc>) -> String {
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
use crate::session::message_to_markdown;
|
||||
use crate::utils::safe_truncate;
|
||||
use anyhow::{Context, Result};
|
||||
use cliclack::{confirm, multiselect, select};
|
||||
use goose::session::info::{get_valid_sorted_sessions, SessionInfo, SortOrder};
|
||||
use goose::session::{self, Identifier};
|
||||
use goose::utils::safe_truncate;
|
||||
use regex::Regex;
|
||||
use std::fs;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
@@ -7,7 +7,6 @@ pub mod project_tracker;
|
||||
pub mod recipes;
|
||||
pub mod session;
|
||||
pub mod signal;
|
||||
pub mod utils;
|
||||
// Re-export commonly used types
|
||||
pub use session::Session;
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use goose::message::{Message, MessageContent, ToolRequest, ToolResponse};
|
||||
use goose::utils::safe_truncate;
|
||||
use mcp_core::content::Content as McpContent;
|
||||
use mcp_core::resource::ResourceContents;
|
||||
use mcp_core::role::Role;
|
||||
@@ -10,9 +11,9 @@ const REDACTED_PREFIX_LENGTH: usize = 100; // Show first 100 chars before trimmi
|
||||
fn value_to_simple_markdown_string(value: &Value, export_full_strings: bool) -> String {
|
||||
match value {
|
||||
Value::String(s) => {
|
||||
if !export_full_strings && s.len() > MAX_STRING_LENGTH_MD_EXPORT {
|
||||
let prefix = &s[..REDACTED_PREFIX_LENGTH.min(s.len())];
|
||||
let trimmed_chars = s.len() - prefix.len();
|
||||
if !export_full_strings && s.chars().count() > MAX_STRING_LENGTH_MD_EXPORT {
|
||||
let prefix = safe_truncate(s, REDACTED_PREFIX_LENGTH);
|
||||
let trimmed_chars = s.chars().count() - prefix.chars().count();
|
||||
format!("`{}[ ... trimmed : {} chars ... ]`", prefix, trimmed_chars)
|
||||
} else {
|
||||
// Escape backticks and newlines for inline code.
|
||||
@@ -40,7 +41,7 @@ fn value_to_markdown(value: &Value, depth: usize, export_full_strings: bool) ->
|
||||
md_string.push_str(&format!("{}* **{}**: ", base_indent_str, key));
|
||||
match val {
|
||||
Value::String(s) => {
|
||||
if s.contains('\n') || s.len() > 80 {
|
||||
if s.contains('\n') || s.chars().count() > 80 {
|
||||
// Heuristic for block
|
||||
md_string.push_str(&format!(
|
||||
"\n{} ```\n{}{}\n{} ```\n",
|
||||
@@ -74,7 +75,7 @@ fn value_to_markdown(value: &Value, depth: usize, export_full_strings: bool) ->
|
||||
md_string.push_str(&format!("{}* - ", base_indent_str));
|
||||
match item {
|
||||
Value::String(s) => {
|
||||
if s.contains('\n') || s.len() > 80 {
|
||||
if s.contains('\n') || s.chars().count() > 80 {
|
||||
// Heuristic for block
|
||||
md_string.push_str(&format!(
|
||||
"\n{} ```\n{}{}\n{} ```\n",
|
||||
@@ -397,7 +398,7 @@ mod tests {
|
||||
assert!(result.starts_with("`"));
|
||||
assert!(result.contains("[ ... trimmed : "));
|
||||
assert!(result.contains("4900 chars ... ]`"));
|
||||
assert!(result.contains(&"a".repeat(100))); // Should contain the prefix
|
||||
assert!(result.contains(&"a".repeat(97))); // Should contain the prefix (100 - 3 for "...")
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -16,6 +16,7 @@ use goose::permission::Permission;
|
||||
use goose::permission::PermissionConfirmation;
|
||||
use goose::providers::base::Provider;
|
||||
pub use goose::session::Identifier;
|
||||
use goose::utils::safe_truncate;
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
use completion::GooseCompleter;
|
||||
@@ -1037,11 +1038,7 @@ impl Session {
|
||||
if min_priority > 0.1 && !self.debug {
|
||||
// High/Medium verbosity: show truncated response
|
||||
if let Some(response_content) = msg.strip_prefix("Responded: ") {
|
||||
if response_content.len() > 100 {
|
||||
format!("🤖 Responded: {}...", &response_content[..100])
|
||||
} else {
|
||||
format!("🤖 {}", msg)
|
||||
}
|
||||
format!("🤖 Responded: {}", safe_truncate(response_content, 100))
|
||||
} else {
|
||||
format!("🤖 {}", msg)
|
||||
}
|
||||
|
||||
@@ -1,50 +0,0 @@
|
||||
/// Utility functions for safe string handling and other common operations
|
||||
/// Safely truncate a string at character boundaries, not byte boundaries
|
||||
///
|
||||
/// This function ensures that multi-byte UTF-8 characters (like Japanese, emoji, etc.)
|
||||
/// are not split in the middle, which would cause a panic.
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `s` - The string to truncate
|
||||
/// * `max_chars` - Maximum number of characters to keep
|
||||
///
|
||||
/// # Returns
|
||||
/// A truncated string with "..." appended if truncation occurred
|
||||
pub fn safe_truncate(s: &str, max_chars: usize) -> String {
|
||||
if s.chars().count() <= max_chars {
|
||||
s.to_string()
|
||||
} else {
|
||||
let truncated: String = s.chars().take(max_chars.saturating_sub(3)).collect();
|
||||
format!("{}...", truncated)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_safe_truncate_ascii() {
|
||||
assert_eq!(safe_truncate("hello world", 20), "hello world");
|
||||
assert_eq!(safe_truncate("hello world", 8), "hello...");
|
||||
assert_eq!(safe_truncate("hello", 5), "hello");
|
||||
assert_eq!(safe_truncate("hello", 3), "...");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_safe_truncate_japanese() {
|
||||
// Japanese characters: "こんにちは世界" (Hello World)
|
||||
let japanese = "こんにちは世界";
|
||||
assert_eq!(safe_truncate(japanese, 10), japanese);
|
||||
assert_eq!(safe_truncate(japanese, 5), "こん...");
|
||||
assert_eq!(safe_truncate(japanese, 7), japanese);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_safe_truncate_mixed() {
|
||||
// Mixed ASCII and Japanese
|
||||
let mixed = "Hello こんにちは";
|
||||
assert_eq!(safe_truncate(mixed, 20), mixed);
|
||||
assert_eq!(safe_truncate(mixed, 8), "Hello...");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user