Files
dohooo__helmor/src-tauri/examples/debug_historical.rs
Caspian Zhao 3408700ce5 feat(cloud): thread author_id end-to-end for cloud team rooms (Phase 3 P3.1)
Carry a team member's SERVER-DERIVED author id from the companion's trusted
X-Helmor-Member-Id header onto their persisted user-prompt row, surfacing it
as ThreadMessageLike.author. The companion overwrites request.author_id from
the header after deserialization (never client-asserted; resolve_request helper
+ direct tests). Sandbox SQLite stores only author_id; display fields are
hydrated frontend-side. Desktop/local IPC sets no author_id, so author stays
None and the single-user wire shape (and all pipeline snapshots) is unchanged.
Adds non-null author E2E, wire camelCase, and trusted-header-over-body tests.
2026-06-13 21:13:10 -07:00

93 lines
3.1 KiB
Rust

//! Debug: run convert_historical on a pipeline fixture and dump part lists.
//! Usage: cargo run --example debug_historical -- <fixture-dir-or-input.json>
use helmor_lib::pipeline::types::HistoricalRecord;
use helmor_lib::pipeline::MessagePipeline;
use serde_json::Value;
fn part_brief(p: &Value) -> String {
let t = p.get("type").and_then(Value::as_str).unwrap_or("?");
match t {
"reasoning" => format!(
"reasoning(id={} len={} dur={:?})",
p.get("id").and_then(Value::as_str).unwrap_or("-"),
p.get("text")
.and_then(Value::as_str)
.map(str::len)
.unwrap_or(0),
p.get("durationMs").and_then(Value::as_u64),
),
"tool-call" => format!(
"tool:{}(id={} hasResult={} status={:?})",
p.get("toolName").and_then(Value::as_str).unwrap_or("?"),
p.get("toolCallId").and_then(Value::as_str).unwrap_or("-"),
p.get("result").is_some(),
p.get("status").and_then(Value::as_str),
),
"text" => format!(
"text(len={})",
p.get("text")
.and_then(Value::as_str)
.map(str::len)
.unwrap_or(0)
),
"collapsed-group" => format!(
"collapsed-group(n={})",
p.get("tools")
.and_then(Value::as_array)
.map(Vec::len)
.unwrap_or(0)
),
other => other.to_string(),
}
}
fn main() {
let arg = std::env::args().nth(1).unwrap();
let path = if arg.ends_with(".json") {
arg.clone()
} else {
format!("{arg}/input.json")
};
let data = std::fs::read_to_string(&path).unwrap();
let raw: Vec<Value> = serde_json::from_str(&data).unwrap();
let records: Vec<HistoricalRecord> = raw
.iter()
.map(|r| HistoricalRecord {
id: r
.get("id")
.and_then(Value::as_str)
.unwrap_or("")
.to_string(),
role: serde_json::from_value(r.get("role").cloned().unwrap()).unwrap(),
content: r
.get("content")
.and_then(Value::as_str)
.unwrap_or("")
.to_string(),
parsed_content: r.get("parsed_content").cloned(),
created_at: r
.get("created_at")
.and_then(Value::as_str)
.unwrap_or("")
.to_string(),
author_id: r
.get("author_id")
.and_then(Value::as_str)
.map(str::to_string),
})
.collect();
let msgs = MessagePipeline::convert_historical(&records);
println!("messages: {}", msgs.len());
for m in &msgs {
let v = serde_json::to_value(m).unwrap();
let role = v.get("role").and_then(Value::as_str).unwrap_or("?");
println!("== [{role}] streaming={:?}", v.get("streaming"));
if let Some(parts) = v.get("content").and_then(Value::as_array) {
for p in parts {
println!(" {}", part_brief(p));
}
}
}
}