From 4473c9fc47a68fc29d2e5add4bf81a33a7b27fa9 Mon Sep 17 00:00:00 2001 From: oudecheng <13802883547@139.com> Date: Tue, 12 May 2026 17:44:39 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=A2=9E=E5=BC=BA=E5=8F=82=E6=95=B0?= =?UTF-8?q?=E6=88=AA=E6=96=AD=E9=80=BB=E8=BE=91=EF=BC=8C=E7=A1=AE=E4=BF=9D?= =?UTF-8?q?=E5=9C=A8=E6=88=AA=E6=96=AD=E6=97=B6=E9=81=B5=E5=BE=AAUTF-8?= =?UTF-8?q?=E5=AD=97=E7=AC=A6=E8=BE=B9=E7=95=8C=EF=BC=8C=E5=B9=B6=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=E7=9B=B8=E5=85=B3=E5=8D=95=E5=85=83=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/agent/agent_loop.rs | 8 ++++---- src/observability/mod.rs | 20 +++++++++++++++++++- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/src/agent/agent_loop.rs b/src/agent/agent_loop.rs index 26fd7d8..ffbedec 100644 --- a/src/agent/agent_loop.rs +++ b/src/agent/agent_loop.rs @@ -1055,8 +1055,8 @@ impl AgentLoop { let error = result.error.unwrap_or_default(); tracing::error!( tool = %tool_call.name, - args = %truncate_args(&tool_call.arguments, 2_000), - normalized_args = %truncate_args(&normalized_arguments, 2_000), + args = %truncate_args(&tool_call.arguments, 4_000), + normalized_args = %truncate_args(&normalized_arguments, 4_000), error = %error, output = %result.output, "Tool returned an error result" @@ -1067,8 +1067,8 @@ impl AgentLoop { Err(e) => { tracing::error!( tool = %tool_call.name, - args = %truncate_args(&tool_call.arguments, 2_000), - normalized_args = %truncate_args(&normalized_arguments, 2_000), + args = %truncate_args(&tool_call.arguments, 4_000), + normalized_args = %truncate_args(&normalized_arguments, 4_000), error = %e, error_details = %format!("{:#}", e), "Tool execution failed" diff --git a/src/observability/mod.rs b/src/observability/mod.rs index 11521e9..4e06af5 100644 --- a/src/observability/mod.rs +++ b/src/observability/mod.rs @@ -187,7 +187,14 @@ pub fn truncate_args(args: &serde_json::Value, max_len: usize) -> String { if args_str.len() <= max_len { return args_str; } - format!("{}...truncated", &args_str[..max_len]) + // Find the last valid char boundary before max_len to avoid splitting UTF-8 chars + let truncate_at = args_str + .char_indices() + .take_while(|(idx, _)| *idx < max_len) + .last() + .map(|(idx, c)| idx + c.len_utf8()) + .unwrap_or(0); + format!("{}...truncated", &args_str[..truncate_at]) } #[cfg(test)] @@ -288,4 +295,15 @@ mod tests { assert!(truncated.ends_with("...truncated")); assert!(truncated.len() < long_args.to_string().len()); } + + #[test] + fn test_truncate_args_utf8_boundary() { + // Test that truncation respects UTF-8 character boundaries + // Each Chinese character is 3 bytes in UTF-8 + let long_args = serde_json::json!({"key": "测试测试测试测试测试测试测试测试测试测试测试测试测试测试"}); + let truncated = truncate_args(&long_args, 50); + assert!(truncated.ends_with("...truncated")); + // Verify the truncated string is valid UTF-8 (no panic occurred) + assert!(truncated.is_char_boundary(truncated.len())); + } }