refactor: remove legacy plural log entries
This commit is contained in:
@@ -429,39 +429,43 @@ fn extract_assistant_text(entries: &[serde_json::Value]) -> String {
|
||||
let mut out = String::new();
|
||||
for value in entries {
|
||||
// The wire payload is the JSON form of `session_store::LogEntry`.
|
||||
// Walk Assistant items inside each entry that can carry them:
|
||||
// post-compaction `SegmentStart.history` (seed) and per-LLM-call
|
||||
// `AssistantItems` deltas.
|
||||
// Walk current singular assistant items and the seeded history in
|
||||
// post-compaction `SegmentStart` entries.
|
||||
let Ok(entry) = serde_json::from_value::<LogEntry>(value.clone()) else {
|
||||
continue;
|
||||
};
|
||||
let logged_items = match entry {
|
||||
LogEntry::SegmentStart { history, .. } => history,
|
||||
LogEntry::AssistantItems { items, .. } => items,
|
||||
_ => continue,
|
||||
};
|
||||
for logged in logged_items {
|
||||
let item: Item = logged.into();
|
||||
if let Item::Message {
|
||||
role: Role::Assistant,
|
||||
content,
|
||||
..
|
||||
} = item
|
||||
{
|
||||
for part in content {
|
||||
if let ContentPart::Text { text } = part {
|
||||
if !out.is_empty() {
|
||||
out.push_str("\n\n");
|
||||
}
|
||||
out.push_str(&text);
|
||||
}
|
||||
match entry {
|
||||
LogEntry::SegmentStart { history, .. } => {
|
||||
for logged in history {
|
||||
push_assistant_text(&mut out, logged);
|
||||
}
|
||||
}
|
||||
LogEntry::AssistantItem { item, .. } => push_assistant_text(&mut out, item),
|
||||
_ => continue,
|
||||
}
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
fn push_assistant_text(out: &mut String, logged: session_store::LoggedItem) {
|
||||
let item: Item = logged.into();
|
||||
if let Item::Message {
|
||||
role: Role::Assistant,
|
||||
content,
|
||||
..
|
||||
} = item
|
||||
{
|
||||
for part in content {
|
||||
if let ContentPart::Text { text } = part {
|
||||
if !out.is_empty() {
|
||||
out.push_str("\n\n");
|
||||
}
|
||||
out.push_str(&text);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn summarize_scope(record: &SpawnedPodRecord) -> String {
|
||||
if record.scope_delegated.is_empty() {
|
||||
return "(none)".into();
|
||||
|
||||
@@ -35,14 +35,6 @@ fn history_from_sink(handle: &PodHandle) -> Vec<Item> {
|
||||
LogEntry::SystemItem { item, .. } => {
|
||||
items.push(item.to_history_item());
|
||||
}
|
||||
LogEntry::AssistantItems { items: i, .. }
|
||||
| LogEntry::ToolResults { items: i, .. }
|
||||
| LogEntry::HookInjectedItems { items: i, .. } => {
|
||||
items.extend(i.into_iter().map(Item::from));
|
||||
}
|
||||
LogEntry::SystemItems { items: si, .. } => {
|
||||
items.extend(si.iter().map(|s| s.to_history_item()));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -158,18 +158,18 @@ fn serve_history(listener: UnixListener, items: Vec<Item>) -> JoinHandle<()> {
|
||||
};
|
||||
let (_r, w) = stream.into_split();
|
||||
let mut writer = JsonLineWriter::new(w);
|
||||
// Wrap the assistant items in a single
|
||||
// `LogEntry::AssistantItems` entry — that's the only kind
|
||||
// that contributes assistant text via `extract_assistant_text`.
|
||||
let logged: Vec<session_store::LoggedItem> =
|
||||
items.iter().map(session_store::LoggedItem::from).collect();
|
||||
let entry = session_store::LogEntry::AssistantItems {
|
||||
ts: 0,
|
||||
items: logged,
|
||||
};
|
||||
let entry_value = serde_json::to_value(&entry).unwrap();
|
||||
let entries: Vec<serde_json::Value> = items
|
||||
.iter()
|
||||
.map(|item| {
|
||||
let entry = session_store::LogEntry::AssistantItem {
|
||||
ts: 0,
|
||||
item: session_store::LoggedItem::from(item),
|
||||
};
|
||||
serde_json::to_value(&entry).unwrap()
|
||||
})
|
||||
.collect();
|
||||
let event = Event::Snapshot {
|
||||
entries: vec![entry_value],
|
||||
entries,
|
||||
greeting: Greeting {
|
||||
pod_name: "child".into(),
|
||||
cwd: "/tmp".into(),
|
||||
|
||||
@@ -226,9 +226,7 @@ pub enum Event {
|
||||
/// `[File: …]`.
|
||||
///
|
||||
/// One event per `LogEntry::SystemItem` commit. Disk-side and
|
||||
/// wire-side are 1:1 (singular variant); legacy `SystemItems`
|
||||
/// entries from older sessions are read-only and never emitted on
|
||||
/// this lane.
|
||||
/// wire-side are 1:1.
|
||||
SystemItem {
|
||||
item: serde_json::Value,
|
||||
},
|
||||
@@ -363,9 +361,8 @@ pub enum Event {
|
||||
///
|
||||
/// Live updates after the snapshot arrive through the streaming
|
||||
/// events (`TextDelta` / `ToolCall*` / `ToolResult` / etc.) plus
|
||||
/// the two role-specific entry events
|
||||
/// (`SegmentRotated` / `HookInjectedItems`) — there is no generic
|
||||
/// "every committed entry" broadcast.
|
||||
/// role-specific entry events (`SegmentRotated` / `SystemItem`) —
|
||||
/// there is no generic "every committed entry" broadcast.
|
||||
Snapshot {
|
||||
entries: Vec<serde_json::Value>,
|
||||
greeting: Greeting,
|
||||
|
||||
@@ -20,7 +20,7 @@ Worker のセッション永続化を提供するクレート。追記専用の
|
||||
|
||||
### ログ
|
||||
|
||||
- `LogEntry` — セッションログのエントリ型(`SessionStart`, `UserInput`, `AssistantItems`, `TurnEnd` など)
|
||||
- `LogEntry` — セッションログのエントリ型(`SegmentStart`, `UserInput`, `AssistantItem`, `ToolResult`, `SystemItem`, `TurnEnd` など)
|
||||
- `RestoredState` — ログ再生で復元された状態
|
||||
- `collect_state()` — ログエントリ列から状態を復元する関数
|
||||
|
||||
|
||||
@@ -197,8 +197,8 @@ pub fn save_user_input(
|
||||
|
||||
/// Log the history delta — new items added since the previous snapshot.
|
||||
///
|
||||
/// Classifies items into AssistantItem / ToolResult / HookInjectedItems
|
||||
/// entries automatically (one entry per item). User messages are skipped
|
||||
/// Classifies items into AssistantItem / ToolResult entries automatically
|
||||
/// (one entry per item). User messages are skipped
|
||||
/// because they are persisted upfront via [`save_user_input`] at submit
|
||||
/// time; the worker pushes a flattened copy into its history that
|
||||
/// arrives here in `new_items` and would otherwise produce a duplicate
|
||||
|
||||
@@ -22,7 +22,7 @@ use crate::system_item::SystemItem;
|
||||
/// Variants correspond to specific mutation points in `Worker`:
|
||||
/// - `SegmentStart` — always the first entry; captures initial state
|
||||
/// - `Invoke` — IDLE → active marker (start of a new self-driving cycle)
|
||||
/// - `UserInput` / `AssistantItems` / `ToolResults` / `HookInjectedItems` — history appends
|
||||
/// - `UserInput` / `AssistantItem` / `ToolResult` / `SystemItem` — history appends
|
||||
/// - `TurnEnd` — AgentTurn boundary marker; carries the post-increment
|
||||
/// `turn_count`. With retry unimplemented today this fires once per
|
||||
/// `run()`/`resume()` (current callers persist a single TurnEnd at
|
||||
@@ -94,23 +94,6 @@ pub enum LogEntry {
|
||||
/// dispatch on `kind` for typed rendering.
|
||||
SystemItem { ts: u64, item: SystemItem },
|
||||
|
||||
/// Legacy plural form: kept **read-only** so old segment logs still
|
||||
/// open. New writes always use the singular `AssistantItem`. Items
|
||||
/// are flattened on replay.
|
||||
AssistantItems { ts: u64, items: Vec<LoggedItem> },
|
||||
|
||||
/// Legacy plural form: kept **read-only**. New writes use the
|
||||
/// singular `ToolResult`.
|
||||
ToolResults { ts: u64, items: Vec<LoggedItem> },
|
||||
|
||||
/// Legacy plural form: kept **read-only**. New writes use the
|
||||
/// singular `SystemItem`.
|
||||
SystemItems { ts: u64, items: Vec<SystemItem> },
|
||||
|
||||
/// Legacy pre-`SystemItem*` form. Deserialize-only. Items are
|
||||
/// flattened to `Item::system_message` on replay.
|
||||
HookInjectedItems { ts: u64, items: Vec<LoggedItem> },
|
||||
|
||||
/// Turn boundary. Records the turn count after increment.
|
||||
TurnEnd { ts: u64, turn_count: usize },
|
||||
|
||||
@@ -279,20 +262,6 @@ pub fn collect_state(entries: &[LogEntry]) -> RestoredState {
|
||||
LogEntry::SystemItem { item, .. } => {
|
||||
state.history.push(item.to_history_item());
|
||||
}
|
||||
LogEntry::AssistantItems { items, .. } => {
|
||||
state.history.extend(items.iter().cloned().map(Item::from));
|
||||
}
|
||||
LogEntry::ToolResults { items, .. } => {
|
||||
state.history.extend(items.iter().cloned().map(Item::from));
|
||||
}
|
||||
LogEntry::SystemItems { items, .. } => {
|
||||
state
|
||||
.history
|
||||
.extend(items.iter().map(|si| si.to_history_item()));
|
||||
}
|
||||
LogEntry::HookInjectedItems { items, .. } => {
|
||||
state.history.extend(items.iter().cloned().map(Item::from));
|
||||
}
|
||||
LogEntry::TurnEnd { turn_count, .. } => {
|
||||
state.turn_count = *turn_count;
|
||||
}
|
||||
@@ -396,9 +365,9 @@ mod tests {
|
||||
ts: 2000,
|
||||
segments: vec![Segment::text("Hello")],
|
||||
},
|
||||
LogEntry::AssistantItems {
|
||||
LogEntry::AssistantItem {
|
||||
ts: 3000,
|
||||
items: vec![Item::assistant_message("Hi!").into()],
|
||||
item: Item::assistant_message("Hi!").into(),
|
||||
},
|
||||
LogEntry::TurnEnd {
|
||||
ts: 3100,
|
||||
@@ -431,17 +400,17 @@ mod tests {
|
||||
ts: 2000,
|
||||
segments: vec![Segment::text("Check weather")],
|
||||
},
|
||||
LogEntry::AssistantItems {
|
||||
LogEntry::AssistantItem {
|
||||
ts: 3000,
|
||||
items: vec![Item::tool_call("call_1", "get_weather", r#"{"city":"Tokyo"}"#).into()],
|
||||
item: Item::tool_call("call_1", "get_weather", r#"{"city":"Tokyo"}"#).into(),
|
||||
},
|
||||
LogEntry::ToolResults {
|
||||
LogEntry::ToolResult {
|
||||
ts: 3500,
|
||||
items: vec![Item::tool_result("call_1", "Sunny, 25C").into()],
|
||||
item: Item::tool_result("call_1", "Sunny, 25C").into(),
|
||||
},
|
||||
LogEntry::AssistantItems {
|
||||
LogEntry::AssistantItem {
|
||||
ts: 4000,
|
||||
items: vec![Item::assistant_message("It's sunny in Tokyo!").into()],
|
||||
item: Item::assistant_message("It's sunny in Tokyo!").into(),
|
||||
},
|
||||
LogEntry::TurnEnd {
|
||||
ts: 4100,
|
||||
@@ -497,9 +466,9 @@ mod tests {
|
||||
cache_write_tokens: 0,
|
||||
output_tokens: 10,
|
||||
},
|
||||
LogEntry::AssistantItems {
|
||||
LogEntry::AssistantItem {
|
||||
ts: 2200,
|
||||
items: vec![Item::assistant_message("yo").into()],
|
||||
item: Item::assistant_message("yo").into(),
|
||||
},
|
||||
LogEntry::LlmUsage {
|
||||
ts: 3100,
|
||||
|
||||
@@ -172,7 +172,7 @@ async fn session_run_logs_entries() {
|
||||
|
||||
let entries = store.read_all(sid, segid).unwrap();
|
||||
|
||||
// SegmentStart, UserInput, AssistantItems, TurnEnd, RunCompleted (at minimum)
|
||||
// SegmentStart, UserInput, AssistantItem, TurnEnd, RunCompleted (at minimum)
|
||||
assert!(
|
||||
entries.len() >= 4,
|
||||
"expected at least 4 entries, got {}",
|
||||
|
||||
+18
-26
@@ -982,22 +982,6 @@ impl App {
|
||||
let value = serde_json::to_value(&item).expect("SystemItem is Serialize");
|
||||
self.apply_system_item(&value);
|
||||
}
|
||||
session_store::LogEntry::AssistantItems { items, .. }
|
||||
| session_store::LogEntry::ToolResults { items, .. }
|
||||
| session_store::LogEntry::HookInjectedItems { items, .. } => {
|
||||
for logged in items {
|
||||
let item: llm_worker::Item = logged.into();
|
||||
let item_value = serde_json::to_value(&item).expect("Item is Serialize");
|
||||
self.push_history_item(&item_value);
|
||||
}
|
||||
}
|
||||
session_store::LogEntry::SystemItems { items, .. } => {
|
||||
for system_item in items {
|
||||
let value =
|
||||
serde_json::to_value(&system_item).expect("SystemItem is Serialize");
|
||||
self.apply_system_item(&value);
|
||||
}
|
||||
}
|
||||
// Non-history-bearing variants don't affect the block view.
|
||||
_ => {}
|
||||
}
|
||||
@@ -1685,33 +1669,41 @@ mod completion_flow_tests {
|
||||
arguments: r#"{"subject":"live","description":""}"#.into(),
|
||||
});
|
||||
|
||||
let assistant_items_entry = serde_json::json!({
|
||||
"kind": "assistant_items",
|
||||
"ts": 1,
|
||||
"items": [
|
||||
{
|
||||
let assistant_item_entries = vec![
|
||||
serde_json::json!({
|
||||
"kind": "assistant_item",
|
||||
"ts": 1,
|
||||
"item": {
|
||||
"kind": "tool_call",
|
||||
"call_id": "c1",
|
||||
"name": "TaskCreate",
|
||||
"arguments": r#"{"subject":"a","description":"A"}"#,
|
||||
},
|
||||
{
|
||||
}),
|
||||
serde_json::json!({
|
||||
"kind": "assistant_item",
|
||||
"ts": 2,
|
||||
"item": {
|
||||
"kind": "tool_call",
|
||||
"call_id": "c2",
|
||||
"name": "TaskCreate",
|
||||
"arguments": r#"{"subject":"b","description":"B"}"#,
|
||||
},
|
||||
{
|
||||
}),
|
||||
serde_json::json!({
|
||||
"kind": "assistant_item",
|
||||
"ts": 3,
|
||||
"item": {
|
||||
"kind": "tool_call",
|
||||
"call_id": "u1",
|
||||
"name": "TaskUpdate",
|
||||
"arguments": r#"{"taskid":2,"status":"inprogress"}"#,
|
||||
},
|
||||
],
|
||||
});
|
||||
}),
|
||||
];
|
||||
app.handle_pod_event(Event::Snapshot {
|
||||
greeting: test_greeting(),
|
||||
entries: vec![assistant_items_entry],
|
||||
entries: assistant_item_entries,
|
||||
status: PodStatus::Running,
|
||||
});
|
||||
|
||||
|
||||
@@ -200,11 +200,6 @@ fn last_message_preview(entries: &[LogEntry]) -> Option<String> {
|
||||
return Some(format!("assistant: {}", trim_one_line(&text, 60)));
|
||||
}
|
||||
}
|
||||
LogEntry::AssistantItems { items, .. } => {
|
||||
if let Some(text) = items.iter().find_map(first_text_logged) {
|
||||
return Some(format!("assistant: {}", trim_one_line(&text, 60)));
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user