vulcan_luaskills/runtime/result.rs
1use serde::{Deserialize, Serialize};
2
3/// Unified English error message returned when a tool emits a non-string result.
4/// 当工具返回非字符串结果时,统一返回的英文错误提示。
5pub const NON_STRING_TOOL_RESULT_ERROR: &str = "Tool results must be returned as plain strings. Structured JSON or table results are not supported.";
6
7/// Stable overflow-mode enum returned from the Lua runtime to the host.
8/// Lua runtime 返回给宿主的稳定超限模式枚举。
9#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
10pub enum ToolOverflowMode {
11 /// Suggest that the host handles overflow in truncate mode.
12 /// 超限时建议宿主按截断模式处理。
13 Truncate,
14 /// Suggest that the host handles overflow in page mode.
15 /// 超限时建议宿主按分页模式处理。
16 Page,
17}
18
19impl ToolOverflowMode {
20 /// Parse an overflow mode string returned from Lua.
21 /// 解析来自 Lua 的超限模式字符串。
22 pub fn parse(value: &str) -> Option<Self> {
23 match value.trim() {
24 "truncate" => Some(Self::Truncate),
25 "page" => Some(Self::Page),
26 _ => None,
27 }
28 }
29}
30
31/// Unified intermediate result object returned from the Lua runtime to the host.
32/// Lua runtime 返回给宿主的统一中间结果对象。
33#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
34pub struct RuntimeInvocationResult {
35 /// Tool body content, which must always be a string.
36 /// 工具正文内容,必须始终为字符串。
37 pub content: String,
38 /// Optional overflow mode; when absent the host applies its own default policy.
39 /// 可选超限模式;为空时由宿主按自身默认策略处理。
40 pub overflow_mode: Option<ToolOverflowMode>,
41 /// Optional template hint used only as a host-side suggestion, never rendered directly by the runtime.
42 /// 可选模板建议名,仅作为宿主层提示,不在 runtime 中直接渲染。
43 pub template_hint: Option<String>,
44 /// Normalized body byte count used by the host to decide pagination, truncation, or compression.
45 /// 正文规范化后的字节数,供宿主判断是否需要分页、截断或压缩。
46 pub content_bytes: usize,
47 /// Normalized body line count used by the host to decide whether the line budget is exceeded.
48 /// 正文规范化后的行数,供宿主判断是否命中行预算。
49 pub content_lines: usize,
50}
51
52impl RuntimeInvocationResult {
53 /// Build the unified runtime result from content and optional overflow hints while computing byte and line metrics at creation time.
54 /// 根据正文和可选超限提示构造统一运行时结果,并在创建时计算字节与行数。
55 pub fn from_content_parts(
56 content: String,
57 overflow_mode: Option<ToolOverflowMode>,
58 template_hint: Option<String>,
59 ) -> Self {
60 let normalized = normalize_text(&content);
61 let content_bytes = normalized.len();
62 let content_lines = split_lines(&normalized).len();
63 Self {
64 content,
65 overflow_mode,
66 template_hint,
67 content_bytes,
68 content_lines,
69 }
70 }
71
72 /// Build a content-only string result.
73 /// 构造只包含正文的字符串返回值。
74 pub fn plain(content: String) -> Self {
75 Self::from_content_parts(content, None, None)
76 }
77}
78
79/// Normalize line endings so byte and line metrics are computed consistently.
80/// 规范化文本中的换行,统一统计字节与行数。
81fn normalize_text(text: &str) -> String {
82 text.replace("\r\n", "\n").replace('\r', "\n")
83}
84
85/// Split text into lines after newline normalization.
86/// 按规范化后的换行拆分文本行。
87fn split_lines(text: &str) -> Vec<&str> {
88 if text.is_empty() {
89 Vec::new()
90 } else {
91 text.split('\n').collect()
92 }
93}