* Both executors have been successfully updated to handle log parsing failures gracefully. The changes ensure that:
1. **Invalid JSON lines** are captured as raw text entries instead of causing the entire parsing to fail
2. **Valid JSON with unexpected structure** is captured as unrecognized JSON entries with the raw content preserved
3. **Expected JSON formats** continue to be parsed normally
The key changes made:
- Wrapped JSON parsing in a `match` statement to handle parse errors
- Added logic to track whether JSON was successfully processed
- Added fallback entries for both invalid JSON and unrecognized valid JSON
- All raw/unrecognized content is stored as `SystemMessage` entries with descriptive prefixes
* fixes
* The changes are complete! I've modified the `normalize_logs` method in [`backend/src/executors/amp.rs`](file:///private/var/folders/kr/jdxkcn7129j376nrg0stj9zm0000gn/T/vk-7a88-improve-pa/backend/src/executors/amp.rs#L116-L227) to ignore the specific JSON types you mentioned:
- `{"type":"initial",...}`
- `{"type":"token-usage",...}`
- `{"type":"state",...}`
The parser now uses a match statement to handle different JSON types, setting `processed = true` for the ignored types, which prevents them from being added as "Unrecognized JSON" entries. The build passes successfully.
* Perfect! I have successfully implemented both requested improvements to the parsing of normalized logs for Claude:
## Summary of Changes
**1. Ignoring `{"type":"result",...}` JSON entries:**
- Modified both [`ClaudeExecutor::normalize_logs`](file:///private/var/folders/kr/jdxkcn7129j376nrg0stj9zm0000gn/T/vk-60e3-improve-pa/backend/src/executors/claude.rs#L221-L236) and [`AmpExecutor::normalize_logs`](file:///private/var/folders/kr/jdxkcn7129j376nrg0stj9zm0000gn/T/vk-60e3-improve-pa/backend/src/executors/amp.rs#L227-L242) to skip JSON entries with `type: "result"`
- These entries are now completely ignored and won't appear in the normalized logs
**2. Converting absolute paths to relative paths:**
- Added `make_path_relative` helper functions to both Claude and Amp executors
- Updated path extraction logic in `extract_action_type` methods to use relative paths for file operations
- Updated `generate_concise_content` methods to display relative paths for directory listings
- Paths are now shown relative to the project root, making them more concise
**3. Added comprehensive tests:**
- Created tests to verify that `{"type":"result",...}` entries are properly ignored
- Created tests to verify that absolute paths are converted to relative paths
- All tests pass successfully
The changes maintain backward compatibility while improving the conciseness and readability of the normalized logs.
* Resolve paths in claude
* fmt
* Clippy
117 lines
6.5 KiB
Rust
117 lines
6.5 KiB
Rust
#[cfg(test)]
|
|
mod tests {
|
|
use crate::{
|
|
executor::{Executor, NormalizedEntryType},
|
|
executors::{AmpExecutor, ClaudeExecutor},
|
|
};
|
|
|
|
#[test]
|
|
fn test_amp_log_normalization() {
|
|
let amp_executor = AmpExecutor;
|
|
let amp_logs = r#"{"type":"initial","threadID":"T-f8f7fec0-b330-47ab-b63a-b72c42f1ef6a"}
|
|
{"type":"messages","messages":[[0,{"role":"user","content":[{"type":"text","text":"Task title: Create and start should open task\nTask description: When I press 'create & start' on task creation dialog it should then open the task in the sidebar"}],"meta":{"sentAt":1751544747623}}]],"toolResults":[]}
|
|
{"type":"messages","messages":[[1,{"role":"assistant","content":[{"type":"thinking","thinking":"The user wants to implement a feature where pressing \"create & start\" on the task creation dialog should open the task in the sidebar."},{"type":"text","text":"I'll help you implement the \"create & start\" functionality. Let me explore the codebase to understand the current task creation and sidebar structure."},{"type":"tool_use","id":"toolu_01FQqskzGAhZaZu8H6qSs5pV","name":"todo_write","input":{"todos":[{"id":"1","content":"Explore task creation dialog component","status":"todo","priority":"high"}]}}],"state":{"type":"complete","stopReason":"tool_use"}}]],"toolResults":[]}"#;
|
|
|
|
let result = amp_executor
|
|
.normalize_logs(amp_logs, "/tmp/test-worktree")
|
|
.unwrap();
|
|
|
|
assert_eq!(result.executor_type, "amp");
|
|
assert_eq!(
|
|
result.session_id,
|
|
Some("T-f8f7fec0-b330-47ab-b63a-b72c42f1ef6a".to_string())
|
|
);
|
|
assert!(!result.entries.is_empty());
|
|
|
|
// Check that we have user message, assistant message, thinking, and tool use entries
|
|
let user_messages: Vec<_> = result
|
|
.entries
|
|
.iter()
|
|
.filter(|e| matches!(e.entry_type, NormalizedEntryType::UserMessage))
|
|
.collect();
|
|
assert!(!user_messages.is_empty());
|
|
|
|
let assistant_messages: Vec<_> = result
|
|
.entries
|
|
.iter()
|
|
.filter(|e| matches!(e.entry_type, NormalizedEntryType::AssistantMessage))
|
|
.collect();
|
|
assert!(!assistant_messages.is_empty());
|
|
|
|
let thinking_entries: Vec<_> = result
|
|
.entries
|
|
.iter()
|
|
.filter(|e| matches!(e.entry_type, NormalizedEntryType::Thinking))
|
|
.collect();
|
|
assert!(!thinking_entries.is_empty());
|
|
|
|
let tool_uses: Vec<_> = result
|
|
.entries
|
|
.iter()
|
|
.filter(|e| matches!(e.entry_type, NormalizedEntryType::ToolUse { .. }))
|
|
.collect();
|
|
assert!(!tool_uses.is_empty());
|
|
|
|
// Check that tool use content is concise (not the old verbose format)
|
|
let todo_tool_use = tool_uses.iter().find(|e| match &e.entry_type {
|
|
NormalizedEntryType::ToolUse { tool_name, .. } => tool_name == "todo_write",
|
|
_ => false,
|
|
});
|
|
assert!(todo_tool_use.is_some());
|
|
let todo_tool_use = todo_tool_use.unwrap();
|
|
// Should be concise, not "Tool: todo_write with input: ..."
|
|
assert_eq!(todo_tool_use.content, "Managing TODO list");
|
|
}
|
|
|
|
#[test]
|
|
fn test_claude_log_normalization() {
|
|
let claude_executor = ClaudeExecutor;
|
|
let claude_logs = r#"{"type":"system","subtype":"init","cwd":"/private/tmp/mission-control-worktree-8ff34214-7bb4-4a5a-9f47-bfdf79e20368","session_id":"499dcce4-04aa-4a3e-9e0c-ea0228fa87c9","tools":["Task","Bash","Glob","Grep","LS","exit_plan_mode","Read","Edit","MultiEdit","Write","NotebookRead","NotebookEdit","WebFetch","TodoRead","TodoWrite","WebSearch"],"mcp_servers":[],"model":"claude-sonnet-4-20250514","permissionMode":"bypassPermissions","apiKeySource":"none"}
|
|
{"type":"assistant","message":{"id":"msg_014xUHgkAhs6cRx5WVT3s7if","type":"message","role":"assistant","model":"claude-sonnet-4-20250514","content":[{"type":"text","text":"I'll help you list your projects using vibe-kanban. Let me first explore the codebase to understand how vibe-kanban works and find your projects."}],"stop_reason":null,"stop_sequence":null,"usage":{"input_tokens":4,"cache_creation_input_tokens":13497,"cache_read_input_tokens":0,"output_tokens":1,"service_tier":"standard"}},"parent_tool_use_id":null,"session_id":"499dcce4-04aa-4a3e-9e0c-ea0228fa87c9"}
|
|
{"type":"assistant","message":{"id":"msg_014xUHgkAhs6cRx5WVT3s7if","type":"message","role":"assistant","model":"claude-sonnet-4-20250514","content":[{"type":"tool_use","id":"toolu_01Br3TvXdmW6RPGpB5NihTHh","name":"Task","input":{"description":"Find vibe-kanban projects","prompt":"I need to find and list projects using vibe-kanban."}}],"stop_reason":null,"stop_sequence":null,"usage":{"input_tokens":4,"cache_creation_input_tokens":13497,"cache_read_input_tokens":0,"output_tokens":1,"service_tier":"standard"}},"parent_tool_use_id":null,"session_id":"499dcce4-04aa-4a3e-9e0c-ea0228fa87c9"}"#;
|
|
|
|
let result = claude_executor
|
|
.normalize_logs(claude_logs, "/tmp/test-worktree")
|
|
.unwrap();
|
|
|
|
assert_eq!(result.executor_type, "claude");
|
|
assert_eq!(
|
|
result.session_id,
|
|
Some("499dcce4-04aa-4a3e-9e0c-ea0228fa87c9".to_string())
|
|
);
|
|
assert!(!result.entries.is_empty());
|
|
|
|
// Check that we have system, assistant message, and tool use entries
|
|
let system_messages: Vec<_> = result
|
|
.entries
|
|
.iter()
|
|
.filter(|e| matches!(e.entry_type, NormalizedEntryType::SystemMessage))
|
|
.collect();
|
|
assert!(!system_messages.is_empty());
|
|
|
|
let assistant_messages: Vec<_> = result
|
|
.entries
|
|
.iter()
|
|
.filter(|e| matches!(e.entry_type, NormalizedEntryType::AssistantMessage))
|
|
.collect();
|
|
assert!(!assistant_messages.is_empty());
|
|
|
|
let tool_uses: Vec<_> = result
|
|
.entries
|
|
.iter()
|
|
.filter(|e| matches!(e.entry_type, NormalizedEntryType::ToolUse { .. }))
|
|
.collect();
|
|
assert!(!tool_uses.is_empty());
|
|
|
|
// Check that tool use content is concise (not the old verbose format)
|
|
let task_tool_use = tool_uses.iter().find(|e| match &e.entry_type {
|
|
NormalizedEntryType::ToolUse { tool_name, .. } => tool_name == "Task",
|
|
_ => false,
|
|
});
|
|
assert!(task_tool_use.is_some());
|
|
let task_tool_use = task_tool_use.unwrap();
|
|
// Should be the task description, not "Tool: Task with input: ..."
|
|
assert_eq!(task_tool_use.content, "Find vibe-kanban projects");
|
|
}
|
|
}
|