mirror of
https://github.com/farion1231/cc-switch.git
synced 2026-08-01 12:22:09 +08:00
fix: parse tool_use/tool_result messages and add OpenCode SQLite backend (#1401)
* fix: parse tool_use/tool_result messages and add OpenCode SQLite backend - Claude: reclassify user messages containing tool_result as "tool" role - Codex: handle function_call and function_call_output payload types - Gemini: support array content and toolCalls extraction, filter info/error types - OpenCode: add SQLite session scan, load and delete alongside legacy JSON - utils: extend parse_timestamp_to_ms for integer timestamps, extract tool_use/tool_result in shared extract_text * fix: address remaining issues from tool_use/tool_result parsing commit - Claude: fix role misclassification for mixed user+tool_result messages (any → all) - OpenCode: extract duplicate part text logic into extract_part_text() - OpenCode: add path validation for SQLite delete to prevent foreign DB access - OpenCode: wrap SQLite deletion in transaction for atomicity - openclaw_config: remove redundant as_deref() on Option<&str>
This commit is contained in:
@@ -46,6 +46,15 @@ pub fn read_head_tail_lines(
|
||||
}
|
||||
|
||||
pub fn parse_timestamp_to_ms(value: &Value) -> Option<i64> {
|
||||
// Integer: milliseconds (>1e12) or seconds
|
||||
if let Some(n) = value.as_i64() {
|
||||
return Some(if n > 1_000_000_000_000 { n } else { n * 1000 });
|
||||
}
|
||||
if let Some(n) = value.as_f64() {
|
||||
let n = n as i64;
|
||||
return Some(if n > 1_000_000_000_000 { n } else { n * 1000 });
|
||||
}
|
||||
// RFC3339 string
|
||||
let raw = value.as_str()?;
|
||||
DateTime::parse_from_rfc3339(raw)
|
||||
.ok()
|
||||
@@ -71,6 +80,28 @@ pub fn extract_text(content: &Value) -> String {
|
||||
}
|
||||
|
||||
fn extract_text_from_item(item: &Value) -> Option<String> {
|
||||
let item_type = item.get("type").and_then(Value::as_str).unwrap_or("");
|
||||
|
||||
// tool_use: show tool name
|
||||
if item_type == "tool_use" {
|
||||
let name = item
|
||||
.get("name")
|
||||
.and_then(Value::as_str)
|
||||
.unwrap_or("unknown");
|
||||
return Some(format!("[Tool: {name}]"));
|
||||
}
|
||||
|
||||
// tool_result: extract nested content
|
||||
if item_type == "tool_result" {
|
||||
if let Some(content) = item.get("content") {
|
||||
let text = extract_text(content);
|
||||
if !text.is_empty() {
|
||||
return Some(text);
|
||||
}
|
||||
}
|
||||
return None;
|
||||
}
|
||||
|
||||
if let Some(text) = item.get("text").and_then(|v| v.as_str()) {
|
||||
return Some(text.to_string());
|
||||
}
|
||||
@@ -119,3 +150,25 @@ pub fn path_basename(value: &str) -> Option<String> {
|
||||
.filter(|segment| !segment.is_empty())?;
|
||||
Some(last.to_string())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use serde_json::json;
|
||||
|
||||
#[test]
|
||||
fn parse_timestamp_to_ms_supports_integers_and_rfc3339() {
|
||||
assert_eq!(
|
||||
parse_timestamp_to_ms(&json!(1_771_061_953_033_i64)),
|
||||
Some(1_771_061_953_033)
|
||||
);
|
||||
assert_eq!(
|
||||
parse_timestamp_to_ms(&json!(1_771_061_953_i64)),
|
||||
Some(1_771_061_953_000)
|
||||
);
|
||||
assert_eq!(
|
||||
parse_timestamp_to_ms(&json!("1970-01-01T00:00:01Z")),
|
||||
Some(1_000)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user