Target: npm:@hdkiller/pi-langfuse
Core Concept: Production-grade Langfuse tracing integration for Pi Coding Agent mapping conversational AI lifecycles (Prompt -> Turn -> LLM/Tools) into hierarchical, observable Langfuse structures.
🧬 Architectural Patterns
- Hierarchical Agent Observation Tree:
Trace(pi-agent): Root object encapsulating the entire conversational loop, storing global config (cwd,model,provider,release,environment).Span(agent.prompt): Groups multi-turn execution flows mapped to a single user instruction.Span(agent.turn): Tracks reasoning loops consisting of tool calls and LLM evaluation steps.Generation(llm-response): Captures complete prompt arrays, streaming outputs, latency, token usage, and cost matrices.Span(tool:<name>): Captures detailed execution of discrete tools (e.g.,bash,read,write), nesting dynamically underneath turns.
- Stateful Event Reconciliation: Uses decoupled
Mapstructures (activeTurns,activeTools) to correctly correlate asynchronous parallel tool executions and non-linear Pi hooks back to their parent traces. - Deep-Copy Context Retention: Hooks into Pi’s
contextevent to clone raw message arrays before the generation phase. This ensures Langfuse receives the exact structured payload (system prompts + history) natively. - Dynamic Tag Injection: Automatically injects structural metadata tags:
project:<cwd_basename>,provider:<name>,model:<name>, andsession:<reason>, mapping unstructured workspaces into filterable dashboards.
⚠️ Anti-Patterns (What to Avoid)
- Raw Output Dumping (The Payload Crash): Piping raw
read_file,web_search, or massivebashexecution results directly into traces.- Correction: Implement localized truncation mechanisms (
trace-input-max-chars,tool-output-max-charsdefaults: ~1200) before emitting trace updates.
- Correction: Implement localized truncation mechanisms (
- Dangling Spans on Abort: Failing to close active generations and tool spans when the core loop crashes, truncates, or triggers early.
- Correction: Enforce a
finalizePrompt()sweep algorithm that iterates through unresolved Maps, closing abandoned spans/generations with anabandoned: truemetadata flag and marking them errored.
- Correction: Enforce a
- Flattening Chat Arrays: Passing the prompt history as a single concatenated string into the
inputof aGeneration.- Correction: Pass the raw message array so the Langfuse UI can natively render chat roles.
- Ignoring Cache Metrics: Discarding
cacheReadandcacheWritestats provided by the LLM provider, leading to inaccurate cost analysis.- Correction: Explicitly integrate caching tokens into total/input sums and push discrete
usageDetails.
- Correction: Explicitly integrate caching tokens into total/input sums and push discrete
💡 Best Practices
- Isolate “Thinking” from Output: Explicitly intercept
thinking_deltaevents natively, segregating them fromtext_delta. Store thinking logic inmetadata.thinkingwhile logging finalized text as primary output. - Real-time Streaming Overlays: If tracking real-time streams (
captureMessageUpdates), use Langfuse’s.update()with{ partial: true }to observe thinking blocks dynamically before the turn resolves. - Session Lifecycle Alignment: Derive Langfuse
sessionIddynamically from the underlying Pi session file name (.jsonl), creating a unified 1:1 mapping between local agent sessions and remote traces. - Scored Token Tracking: Utilize Langfuse
.score()events for critical LLM telemetry (input_tokens,output_tokens,total_cost) mapping directly back to the activeGenerationID, enabling immediate dashboard aggregation. - Aggressive Config Reloading: Tie telemetry configuration directly to the agent’s live settings view. Use event listeners (
pi-extension-settings:changed) to hot-reload Langfuse clients seamlessly without restarting the core agent.