ollie/format/block.go

76 lines
2.3 KiB
Go

// Package format provides chat log serialization.
//
// The chat log uses JSON Lines format: one JSON object per line.
// Each line is a complete, self-contained block.
//
// For streaming, blocks can be partial (Partial=true). The GUI shows
// partial blocks immediately; when the final block arrives (same ID,
// Partial=false), it replaces the partial version.
package format
import (
"encoding/json"
)
// Block represents a single chat log entry.
type Block struct {
Role string `json:"role"` // user, assistant, context, reasoning, call, tool, error, info, retry, stalled
ID string `json:"id"` // 8-char hex block ID
Content string `json:"content,omitempty"` // Block content (text, args for call, output for tool)
Name string `json:"name,omitempty"` // Tool/function name (for call blocks)
Format string `json:"format,omitempty"` // Output format hint (for tool blocks)
Partial bool `json:"partial,omitempty"` // True if streaming (not final)
}
// MarshalBlock returns a JSONL line for the block (with trailing newline).
func MarshalBlock(b Block) []byte {
data, _ := json.Marshal(b)
return append(data, '\n')
}
// UnmarshalBlock parses a JSONL line into a Block.
func UnmarshalBlock(line []byte) (Block, error) {
var b Block
err := json.Unmarshal(line, &b)
return b, err
}
// RenderBlock returns human-readable text for a block (for log file).
// Partial blocks are skipped (only final blocks render).
func RenderBlock(b Block) string {
if b.Partial {
return ""
}
switch b.Role {
case "user":
return "You: " + b.Content + "\n\n"
case "assistant":
return b.Content + "\n\n"
case "context":
return "" // Hidden from rendered log
case "reasoning":
return "" // Hidden from rendered log
case "call":
if b.Content != "" {
return "→ " + b.Name + " " + b.Content + "\n\n"
}
return "→ " + b.Name + "\n\n"
case "tool":
return b.Content + "\n\n"
case "bypass":
return "⚠ bypass requested (needs approval): " + b.Content + "\n\n"
case "bypass-resolved":
return "bypass " + b.Content + "\n\n"
case "error":
return "Error: " + b.Content + "\n\n"
case "info":
return "Info: " + b.Content + "\n\n"
case "retry":
return "Retry: " + b.Content + "\n\n"
case "stalled":
return "[Stalled]\n\n"
default:
return b.Content + "\n\n"
}
}