pi-langfuse 1.0.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.trae/documents/optimize_langfuse_reporting.md +68 -0
- package/.trae/documents/pi-langfuse-refactor.md +78 -0
- package/AGENTS.md +33 -42
- package/README.md +270 -59
- package/README_CN.md +273 -61
- package/image.png +0 -0
- package/index.ts +109 -491
- package/package.json +18 -3
- package/src/config.ts +98 -0
- package/src/constants.ts +15 -0
- package/src/handlers/agent.ts +136 -0
- package/src/handlers/generation.ts +239 -0
- package/src/handlers/tool.ts +126 -0
- package/src/handlers/turn.ts +53 -0
- package/src/langfuse.ts +75 -0
- package/src/state.ts +38 -0
- package/src/types.ts +94 -0
- package/src/utils.ts +273 -0
- package/tsconfig.json +1 -0
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# Langfuse 追踪逻辑优化方案
|
|
2
|
+
|
|
3
|
+
## 1. 背景与目标
|
|
4
|
+
根据之前的建议,我们需要进一步优化 Pi-Langfuse 扩展的上报逻辑,使得在 Langfuse 控制台中观察 Agent 执行过程时更加细致、层级分明且数据完备。
|
|
5
|
+
本次优化包含 5 个核心点:引入 Turn 级别的 Span、精准追踪 TTFT(首字响应时间)、处理 Provider 请求异常、上报 System Prompt,以及完善 Session 的边缘生命周期和上下文压缩(Compact)处理。
|
|
6
|
+
|
|
7
|
+
## 2. 现状分析
|
|
8
|
+
- **层级结构**:目前 `llm-generation` 和 `tool` 是平级挂载在根 Trace 下,多轮对话时瀑布图非常扁平。
|
|
9
|
+
- **TTFT**:尚未记录 `completionStartTime`,无法在 Langfuse 中直观看到首字耗时。
|
|
10
|
+
- **异常闭环**:LLM Provider 发生 4xx/5xx HTTP 错误时,`message_end` 可能不触发,导致 Generation 无法正常闭合。
|
|
11
|
+
- **上下文完备性**:`before_agent_start` 仅提取了 user prompt,缺少 System Prompt,不利于后期 Debug。
|
|
12
|
+
- **Session 生命周期**:仅处理了 `session_start` 和 `session_shutdown`,忽略了 `/new`, `/resume`, `/fork`, `/compact` 等高级会话操作引发的状态混乱问题。
|
|
13
|
+
|
|
14
|
+
## 3. 具体修改方案
|
|
15
|
+
|
|
16
|
+
### 3.1 引入 Turn-Level Span (层级结构优化)
|
|
17
|
+
将一轮对话(Turn)包装为一个 Span,使其成为 Generation 和 Tool 的父节点。
|
|
18
|
+
- **`src/types.ts`**:
|
|
19
|
+
- 在 `AgentState` 接口中增加 `activeTurn?: LangfuseObservation` 字段。
|
|
20
|
+
- **`src/handlers/turn.ts` (新建)**:
|
|
21
|
+
- 实现 `startTurnObservation(event)`:调用 `state.agentState.root.startObservation("turn", {...}, { asType: "span" })` 并赋值给 `activeTurn`。
|
|
22
|
+
- 实现 `finishTurnObservation(event)`:结束 `activeTurn` 并将其置空。
|
|
23
|
+
- **`src/handlers/generation.ts` & `src/handlers/tool.ts`**:
|
|
24
|
+
- 在创建 `llm-generation` 和 `tool` 观察节点时,优先判断 `state.agentState.activeTurn` 是否存在,若存在则调用 `activeTurn.startObservation`,否则降级使用 `root.startObservation`。
|
|
25
|
+
- **`index.ts`**:
|
|
26
|
+
- 注册 `turn_start` 事件,调用 `startTurnObservation`。
|
|
27
|
+
- 在现有的 `turn_end` 监听器中,除了处理 Fallback Generation,还需要调用 `finishTurnObservation`。
|
|
28
|
+
|
|
29
|
+
### 3.2 精准追踪 TTFT (首字生成时间)
|
|
30
|
+
记录模型流式输出第一块 Chunk 的时间,用于计算 TTFT。
|
|
31
|
+
- **`src/types.ts`**:
|
|
32
|
+
- 在 `GenerationState` 中增加 `ttftRecorded?: boolean` 标志位。
|
|
33
|
+
- **`src/handlers/generation.ts`**:
|
|
34
|
+
- 新增 `recordTTFT(event)` 函数:获取当前 `activeGeneration`,若 `ttftRecorded` 为 false/undefined,则调用 `generation.observation.update({ completionStartTime: new Date() })`,并标记 `ttftRecorded = true`。
|
|
35
|
+
- **`index.ts`**:
|
|
36
|
+
- 在 `message_update` 事件监听器中,除了原有的提取逻辑外,新增调用 `recordTTFT(event)`。
|
|
37
|
+
|
|
38
|
+
### 3.3 异常流闭环:Provider 请求级错误处理
|
|
39
|
+
防止因 Provider HTTP 请求失败导致 Generation 永远处于开启状态。
|
|
40
|
+
- **`src/handlers/generation.ts`**:
|
|
41
|
+
- 修改 `updateGenerationMetadata(event)` 函数。在提取完 metadata 后,检查 `metadata.status`。
|
|
42
|
+
- 如果 `status >= 400` 或者存在明确的 error 信息,立刻将该 Generation 的状态设为 `level: "ERROR"`,附加 `statusMessage`,并调用 `.end()` 提前闭合它,同时设置 `ended = true`。
|
|
43
|
+
|
|
44
|
+
### 3.4 完善上下文信息:上报 System Prompt
|
|
45
|
+
将 System Prompt 纳入 Trace 的元数据或输入中,方便回溯。
|
|
46
|
+
- **`src/handlers/agent.ts`**:
|
|
47
|
+
- 修改 `startAgentRun(event, ctx)`。
|
|
48
|
+
- 增加 `const systemPrompt = await ctx.getSystemPrompt();`。
|
|
49
|
+
- 在 `root` Trace 的 `metadata` 中增加 `systemPrompt: truncate(systemPrompt, MAX_TOOL_PAYLOAD_LENGTH)`。
|
|
50
|
+
|
|
51
|
+
### 3.5 Session 边缘生命周期与 Compact 处理
|
|
52
|
+
完善 Pi 扩展 API 提供的各类会话切换与上下文管理事件。
|
|
53
|
+
- **`index.ts`**:
|
|
54
|
+
- 增加 `session_before_switch` 和 `session_before_fork` 的事件监听。当触发这些事件时,表明当前会话即将被替换,调用 `closeDanglingObservations("Session switched or forked")` 提前闭合所有挂起的节点,并调用 `resetRunState()`。
|
|
55
|
+
- 增加 `session_compact` 事件监听。如果当前 `state.agentState.root` 存在,调用 `root.startObservation("session_compact", { level: "DEFAULT", statusMessage: "Context was compacted" }, { asType: "span" }).end()`,在 Trace 中记录下压缩动作发生的时机。
|
|
56
|
+
|
|
57
|
+
## 4. 假设与决策
|
|
58
|
+
- **决策**: Turn Span 的命名直接使用 `"turn"`,类型使用 `span`,这样在 Langfuse 的甘特图中能清晰地包裹住内部的 Generation 和 Tool。
|
|
59
|
+
- **决策**: TTFT 使用系统当前时间 `new Date()`。尽管存在微小的事件传递延迟,但在 Node.js 环境下已足够精确。
|
|
60
|
+
- **假设**: `ctx.getSystemPrompt()` 是异步方法,在 `before_agent_start` 和 `agent_start` 中可以通过 `await` 正常获取。
|
|
61
|
+
- **决策**: 对于 Session Lifecycle 的处理,统一视作强行终止当前 Agent Run 的执行,因此复用 `closeDanglingObservations` 逻辑以保证不会出现悬空(Dangling)的 Trace/Span。
|
|
62
|
+
|
|
63
|
+
## 5. 验收标准
|
|
64
|
+
1. 在代码中实现上述 5 项修改,且不破坏现有 TypeScript 编译。
|
|
65
|
+
2. 运行 Pi Agent 进行一次多轮对话(调用工具),能够在 Langfuse 观察到 `Trace -> Turn -> Generation/Tool` 的层级结构。
|
|
66
|
+
3. Langfuse 的 Generation 详情中能够看到 `Time to First Token (TTFT)`。
|
|
67
|
+
4. Trace 的 Metadata 中包含 `systemPrompt` 字段。
|
|
68
|
+
5. 通过 `/new` 或 `/compact` 时,控制台不报挂起节点相关的警告。
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# Pi-Langfuse 代码重构与拆分计划
|
|
2
|
+
|
|
3
|
+
## 摘要 (Summary)
|
|
4
|
+
将当前长达 1105 行的单文件 `index.ts` 重构并按功能职责拆分至 `src/` 目录下的多个模块中。保留根目录的 `index.ts` 作为 Pi 扩展的纯入口文件。此举将极大提升代码的可读性和可维护性。
|
|
5
|
+
|
|
6
|
+
## 当前状态分析 (Current State Analysis)
|
|
7
|
+
目前 `index.ts` 是一个单文件巨石(Monolith),包含了以下所有逻辑:
|
|
8
|
+
1. 本地与环境变量配置读取
|
|
9
|
+
2. TypeScript 接口声明(`Config`, `AgentState`, `LangfuseObservation` 等)
|
|
10
|
+
3. 全局可变状态(如 `agentState`, `currentSessionId`, 各类计数器)
|
|
11
|
+
4. Langfuse SDK 的懒加载与生命周期管理
|
|
12
|
+
5. 数据格式化、截断与负载提取工具函数
|
|
13
|
+
6. Agent、Generation 和 Tool 各自的观测事件处理器
|
|
14
|
+
7. Pi 扩展命令注册与生命周期事件监听
|
|
15
|
+
|
|
16
|
+
所有逻辑混合在一起,导致状态变更难以追踪,且由于闭包共享了大量模块级状态,修改代码容易引入隐藏的副作用。
|
|
17
|
+
|
|
18
|
+
## 提议更改 (Proposed Changes)
|
|
19
|
+
|
|
20
|
+
### 1. 更新项目配置
|
|
21
|
+
- 修改 `tsconfig.json`:在 `include` 中增加 `"src/**/*.ts"`,以包含新的源码目录。
|
|
22
|
+
- **依赖引用规范**:由于 `tsconfig.json` 配置了 `"moduleResolution": "NodeNext"`,所有内部文件导入必须显式带有 `.js` 后缀(例如 `import { state } from "./state.js"`)。
|
|
23
|
+
|
|
24
|
+
### 2. 建立 `src/` 目录结构并拆分职责
|
|
25
|
+
计划新建如下文件与目录:
|
|
26
|
+
|
|
27
|
+
- **`src/types.ts`**
|
|
28
|
+
- **内容**:提取所有的 Interface 和 Type 定义。
|
|
29
|
+
- **包含**:`Config`, `LangfuseObservation`, `ObservationUpdate`, `LangfuseScoreClient`, `LangfuseRuntime`, `GenerationState`, `ToolState`, `AgentState`。
|
|
30
|
+
|
|
31
|
+
- **`src/constants.ts`**
|
|
32
|
+
- **内容**:提取所有魔法数字和常量配置。
|
|
33
|
+
- **包含**:`EXT_DIR`, `CONFIG_PATH`, `DEFAULT_LANGFUSE_HOST`, `MAX_STRING_LENGTH` 等。
|
|
34
|
+
|
|
35
|
+
- **`src/state.ts`**
|
|
36
|
+
- **内容**:统一管理所有全局可变状态。
|
|
37
|
+
- **实现方式**:将原来的 `let agentState`, `let currentSessionId` 等包裹在一个 `export const state = { ... }` 对象中,确保跨模块引用时状态一致。
|
|
38
|
+
- **包含函数**:`resetRunState`, `computeEvaluationScores`。
|
|
39
|
+
|
|
40
|
+
- **`src/utils.ts`**
|
|
41
|
+
- **内容**:集中存放与状态无关的纯工具函数。
|
|
42
|
+
- **包含**:`truncate`, `tryParseJson`, `shapePayload`, `safeSerialize`,以及一系列解析函数(`extractTextContent`, `extractToolCalls`, `getToolCallId` 等)。
|
|
43
|
+
|
|
44
|
+
- **`src/config.ts`**
|
|
45
|
+
- **内容**:负责配置读取与持久化,以及与 Pi UI 的交互提示。
|
|
46
|
+
- **包含**:`loadConfigFromFile`, `loadConfigFromEnv`, `saveConfig`, `ensureConfig`, `promptForConfig`。
|
|
47
|
+
|
|
48
|
+
- **`src/langfuse.ts`**
|
|
49
|
+
- **内容**:Langfuse SDK 客户端封装。
|
|
50
|
+
- **包含**:`getRuntime` (单例模式加载 SDK)、`shutdownRuntime`、`sendScore`。
|
|
51
|
+
|
|
52
|
+
- **`src/handlers/agent.ts`**
|
|
53
|
+
- **内容**:Agent 生命周期的事件逻辑。
|
|
54
|
+
- **包含**:`startAgentRun`, `finishAgentRun`, `updateTraceIO`。
|
|
55
|
+
|
|
56
|
+
- **`src/handlers/generation.ts`**
|
|
57
|
+
- **内容**:模型生成 (Generation) 相关的生命周期逻辑。
|
|
58
|
+
- **包含**:`getOpenGeneration`, `startGeneration`, `updateGenerationMetadata`, `finishGenerationFromMessage`, `createFallbackGenerationFromTurn`。
|
|
59
|
+
|
|
60
|
+
- **`src/handlers/tool.ts`**
|
|
61
|
+
- **内容**:工具调用 (Tool) 相关的逻辑。
|
|
62
|
+
- **包含**:`startToolObservation`, `finishToolObservation`, `closeDanglingObservations`。
|
|
63
|
+
|
|
64
|
+
### 3. 精简根目录 `index.ts`
|
|
65
|
+
- **内容**:将其转换为纯粹的事件路由中心。
|
|
66
|
+
- **改动**:删除原有的业务实现,改为从 `src/` 各个模块导入必要的 handler。保留默认导出的扩展注册函数 `export default async function (pi: ExtensionAPI)`,并在其中通过 `pi.on` 和 `pi.registerCommand` 将事件委派给对应的处理函数。
|
|
67
|
+
|
|
68
|
+
## 假设与决策 (Assumptions & Decisions)
|
|
69
|
+
- **零行为变更**:本次重构仅做结构上的梳理,不修改任何核心逻辑、状态变更时机或事件 payload 的生成规则,从而保证与当前 Langfuse 的数据对接一致。
|
|
70
|
+
- **状态管理**:采用共享的 `state` 单例对象替代原本的顶层变量,以最少的改动适配多文件架构。
|
|
71
|
+
- **兼容性**:保留根目录的 `index.ts` 以兼容现有的 `package.json` 中的 `pi.extensions` 配置,无需修改发布行为。
|
|
72
|
+
|
|
73
|
+
## 验证步骤 (Verification steps)
|
|
74
|
+
1. 运行 `npm run typecheck` 确认代码拆分后无 TypeScript 编译及引入报错。
|
|
75
|
+
2. 开启 Pi CLI (`pi "test prompt"`) 并挂载该本地扩展,验证:
|
|
76
|
+
- 配置初始化弹窗或环境变量读取是否正常工作。
|
|
77
|
+
- 工具调用、LLM 生成以及会话结束时的逻辑是否无异常抛出。
|
|
78
|
+
3. 登录 Langfuse 控制面板,核对新产生的 trace 数据结构(agent、generation、tool observations 及打分数据)是否完整,未出现状态泄漏或丢失。
|
package/AGENTS.md
CHANGED
|
@@ -1,57 +1,48 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Pi Langfuse Extension - Agents & Architecture
|
|
2
2
|
|
|
3
3
|
## Project Overview
|
|
4
4
|
|
|
5
|
-
This repository contains a Pi Coding Agent extension that
|
|
5
|
+
This repository contains a Pi Coding Agent extension that integrates with Langfuse to provide deep observability into agent sessions. By hooking into Pi's Extension API, it forwards telemetry data (traces, spans, and LLM generations) to Langfuse, enabling developers to monitor token usage, cost, tool execution success rates, and conversational context.
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
- `package.json`: package metadata for Pi/NPM
|
|
9
|
-
- `config.json`: local credentials file for manual development only, ignored by git
|
|
10
|
-
- `.agents/skills/langfuse/SKILL.md`: local skill instructions for Langfuse-related workflows
|
|
7
|
+
## Architecture & Event Mapping
|
|
11
8
|
|
|
12
|
-
|
|
9
|
+
The extension leverages the `@earendil-works/pi-coding-agent` Extension API to intercept lifecycle events and maps them to Langfuse's hierarchical observability model: **Trace** -> **Span** / **Generation**.
|
|
13
10
|
|
|
14
|
-
|
|
11
|
+
### 1. Trace Level (Agent Run)
|
|
12
|
+
The root of the observability tree is a **Trace**, representing a single user prompt and the agent's complete execution to fulfill it.
|
|
15
13
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
3. `tool_call` / `tool_result` create and close tool spans
|
|
19
|
-
4. `turn_end` records LLM usage/cost metadata as a generation
|
|
20
|
-
5. `agent_end` finalizes the trace and publishes aggregate evaluation scores
|
|
21
|
-
6. `session_shutdown` flushes any remaining state
|
|
14
|
+
- `before_agent_start` / `agent_start`: Initializes the `pi-agent` **Trace**. Captures the initial prompt, working directory (`cwd`), and session ID.
|
|
15
|
+
- `agent_end`: Finalizes the trace, captures the final assistant output, and submits evaluation scores (e.g., tool call count, success rate).
|
|
22
16
|
|
|
23
|
-
|
|
17
|
+
### 2. Generation Level (LLM Calls)
|
|
18
|
+
Every time the agent communicates with an LLM provider, a **Generation** is recorded to track token usage, costs, and latency.
|
|
24
19
|
|
|
25
|
-
|
|
20
|
+
- `before_provider_request`: Starts the `llm-generation` observation.
|
|
21
|
+
- `after_provider_response`: Updates the generation with HTTP status and provider metadata.
|
|
22
|
+
- `message_update`: Tracks streaming text (can be used to track Time-To-First-Token).
|
|
23
|
+
- `message_end`: Ends the generation, extracting `usageDetails` (input/output tokens, cache metrics) and `costDetails`.
|
|
24
|
+
- `turn_end`: Handles fallback generation logging if standard message events miss the completion.
|
|
26
25
|
|
|
27
|
-
|
|
26
|
+
### 3. Span Level (Tool Executions)
|
|
27
|
+
When the LLM decides to use a registered tool (e.g., bash, file read), a **Span** is created under the root trace.
|
|
28
28
|
|
|
29
|
-
-
|
|
30
|
-
-
|
|
29
|
+
- `tool_execution_start` / `tool_call`: Starts a `tool` span, logging the tool name and its input parameters (truncated for safety).
|
|
30
|
+
- `tool_result` / `tool_execution_end`: Finalizes the tool span. If the tool fails, the span's level is marked as `ERROR` and the error message is recorded.
|
|
31
31
|
|
|
32
|
-
|
|
32
|
+
### 4. Session & Lifecycle Management
|
|
33
|
+
- `session_start`: Captures the stable session ID from Pi (`ctx.sessionManager.getSessionFile()`).
|
|
34
|
+
- `session_shutdown`: Cleans up and flushes dangling observations (e.g., if the user abruptly exits via `Ctrl+C`).
|
|
33
35
|
|
|
34
|
-
|
|
35
|
-
npm install
|
|
36
|
-
pi "test prompt"
|
|
37
|
-
```
|
|
36
|
+
## Development & Testing
|
|
38
37
|
|
|
39
|
-
|
|
38
|
+
1. **Prerequisites**: Node.js `>=22` and a local `config.json` with Langfuse credentials (`LANGFUSE_PUBLIC_KEY`, `LANGFUSE_SECRET_KEY`, `LANGFUSE_HOST`).
|
|
39
|
+
2. **Run**:
|
|
40
|
+
```bash
|
|
41
|
+
npm install
|
|
42
|
+
pi -e ./index.ts "your test prompt"
|
|
43
|
+
```
|
|
44
|
+
3. **Validation**: Open your Langfuse dashboard to verify that Traces, Generations, and Tool Spans are accurately grouped, and that token usage/costs are populated.
|
|
40
45
|
|
|
41
|
-
##
|
|
42
|
-
|
|
43
|
-
-
|
|
44
|
-
- Keep credential material out of version control. `config.json` should stay local.
|
|
45
|
-
- When logging tool input/output, be mindful of payload size and serialization failures.
|
|
46
|
-
- If you add new Langfuse fields, verify them against current official docs because the SDK surface evolves.
|
|
47
|
-
- Avoid hard-coding install-layout assumptions when importing SDK files unless Pi requires it.
|
|
48
|
-
|
|
49
|
-
## Review Focus Areas
|
|
50
|
-
|
|
51
|
-
When reviewing or extending this project, pay extra attention to:
|
|
52
|
-
|
|
53
|
-
- Trace lifecycle consistency across `before_agent_start`, `agent_end`, and `session_shutdown`
|
|
54
|
-
- Correct score attribution at trace level vs observation level
|
|
55
|
-
- Defensive handling of unexpected event payload shapes
|
|
56
|
-
- Truncation/redaction strategy for large tool payloads
|
|
57
|
-
- Compatibility between the installed Langfuse SDK version and any manually declared local TypeScript interfaces
|
|
46
|
+
## Design Constraints
|
|
47
|
+
- **Statefulness**: The extension maintains module-level state (`state.ts`) because multiple Pi hooks share the same context across an agent run.
|
|
48
|
+
- **Data Safety**: Large tool payloads and circular references are aggressively truncated/shaped to prevent serialization crashes and excessive network overhead.
|
package/README.md
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
# pi-langfuse
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/pi-langfuse)
|
|
4
|
+
[](https://opensource.org/licenses/MIT)
|
|
5
|
+
|
|
6
|
+
[**English**](./README.md) | [**简体中文**](./README_CN.md)
|
|
7
|
+
|
|
8
|
+
Langfuse observability extension for [Pi Coding Agent](https://github.com/earendil-works/pi-coding-agent). Sends complete Pi agent runs to [Langfuse](https://langfuse.com) so you can inspect the user prompt, root agent workflow, every LLM generation, every tool call, final assistant response, usage, cost, and health scores in one trace.
|
|
4
9
|
|
|
5
10
|
## Why Langfuse?
|
|
6
11
|
|
|
@@ -8,113 +13,319 @@ Langfuse provides open-source observability for LLM applications. This extension
|
|
|
8
13
|
|
|
9
14
|
## Features
|
|
10
15
|
|
|
11
|
-
- **
|
|
12
|
-
- **
|
|
13
|
-
- **
|
|
14
|
-
- **
|
|
15
|
-
- **
|
|
16
|
-
- **
|
|
16
|
+
- **Complete Agent Traces**: Creates one trace per user prompt with a root `agent` observation containing the prompt input and final assistant output.
|
|
17
|
+
- **Per-Request Generations**: Records a separate `generation` observation for every provider request, including the actual provider payload instead of only the original prompt.
|
|
18
|
+
- **Final Message Capture**: Uses finalized assistant messages for generation and root outputs, so Langfuse shows what the user actually saw in Pi.
|
|
19
|
+
- **Tool Observability**: Creates Langfuse `tool` observations for every tool call, including arguments, results, and error states.
|
|
20
|
+
- **Parallel Tool Safety**: Correlates tool observations by `toolCallId`, avoiding result mix-ups when Pi runs tools concurrently.
|
|
21
|
+
- **Session Correlation**: Groups traces from the same Pi session under a shared Langfuse session ID.
|
|
22
|
+
- **Cost and Token Tracking**: Records usage and cost details on each generation when Pi/provider payloads expose them.
|
|
23
|
+
- **Evaluation Scores**: Automatically computes and sends tool success rates, error counts, and session health metrics.
|
|
24
|
+
- **Defensive Payload Shaping**: Parses JSON-like strings when possible, limits object depth, and truncates large payloads before upload.
|
|
25
|
+
|
|
26
|
+
## Highlights
|
|
27
|
+
|
|
28
|
+
`pi-langfuse` is designed to make a Pi run readable as an agent workflow, not just a bag of logs:
|
|
29
|
+
|
|
30
|
+
- The trace input/output mirrors the root `agent` observation, making the run understandable from the Langfuse trace list and detail view.
|
|
31
|
+
- The first generation in a tool-using run can show the assistant's tool-call message, the tool observation shows execution I/O, and the follow-up generation shows the final natural-language answer.
|
|
32
|
+
- Tool failures are marked on the tool observation and reflected in trace-level scores, while later generations still preserve the tool error result in their input history.
|
|
33
|
+
- Shutdown and interrupted runs flush pending telemetry and mark unfinished observations as cancelled/warning instead of silently losing the trace.
|
|
17
34
|
|
|
18
|
-
##
|
|
35
|
+
## Prerequisites
|
|
36
|
+
|
|
37
|
+
- **Node.js** >= 22
|
|
38
|
+
- **Pi Coding Agent** installed and configured
|
|
39
|
+
- A **Langfuse** account ([cloud](https://cloud.langfuse.com) or self-hosted)
|
|
40
|
+
|
|
41
|
+
## Installation
|
|
42
|
+
|
|
43
|
+
### Option 1: Install via npm (recommended for users)
|
|
19
44
|
|
|
20
|
-
### Via npm (recommended)
|
|
21
45
|
```bash
|
|
22
46
|
pi install npm:pi-langfuse
|
|
23
47
|
```
|
|
24
48
|
|
|
49
|
+
Pi will download the package and register it as an extension.
|
|
50
|
+
|
|
51
|
+
### Option 2: Install from local source (recommended for development)
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
git clone <your-repo-url>
|
|
55
|
+
cd pi-langfuse
|
|
56
|
+
npm install
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Then tell Pi to use it:
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
pi link /path/to/pi-langfuse
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Or run Pi from the project directory — Pi auto-discovers extensions in the current directory's `package.json`.
|
|
66
|
+
|
|
25
67
|
## Configuration
|
|
26
68
|
|
|
27
|
-
|
|
69
|
+
You need Langfuse API keys. Get them from **Langfuse Cloud** → **Settings** → **API Keys**.
|
|
28
70
|
|
|
29
|
-
|
|
71
|
+
There are three ways to configure the extension:
|
|
30
72
|
|
|
31
|
-
|
|
32
|
-
- `LANGFUSE_SECRET_KEY`
|
|
33
|
-
- `LANGFUSE_HOST` (optional, defaults to `https://cloud.langfuse.com`)
|
|
73
|
+
### Method 1: Interactive setup (easiest)
|
|
34
74
|
|
|
35
|
-
|
|
75
|
+
Run any `pi` command with the extension loaded. On first run without configuration, Pi will prompt you in the CLI or TUI for:
|
|
36
76
|
|
|
37
|
-
|
|
77
|
+
1. **Langfuse public key** — starts with `pk-lf-...`
|
|
78
|
+
2. **Langfuse secret key** — starts with `sk-lf-...`
|
|
79
|
+
3. **Langfuse host** — defaults to `https://cloud.langfuse.com`
|
|
38
80
|
|
|
39
|
-
|
|
81
|
+
The extension saves these to a local `config.json` (ignored by git).
|
|
40
82
|
|
|
41
|
-
|
|
83
|
+
To re-run setup at any time:
|
|
84
|
+
|
|
85
|
+
```
|
|
42
86
|
/langfuse-setup
|
|
43
87
|
```
|
|
44
88
|
|
|
89
|
+
### Method 2: Environment variables
|
|
90
|
+
|
|
91
|
+
Set these before starting Pi:
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
export LANGFUSE_PUBLIC_KEY="pk-lf-xxxx"
|
|
95
|
+
export LANGFUSE_SECRET_KEY="sk-lf-xxxx"
|
|
96
|
+
export LANGFUSE_BASE_URL="https://cloud.langfuse.com" # optional; LANGFUSE_HOST is also supported
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
The extension checks the local `config.json` first, then falls back to environment variables.
|
|
100
|
+
|
|
101
|
+
### Method 3: Local config.json (development only)
|
|
102
|
+
|
|
103
|
+
For local development, create a `config.json` in the project root:
|
|
104
|
+
|
|
105
|
+
```json
|
|
106
|
+
{
|
|
107
|
+
"publicKey": "pk-lf-xxxx",
|
|
108
|
+
"secretKey": "sk-lf-xxxx",
|
|
109
|
+
"host": "https://cloud.langfuse.com"
|
|
110
|
+
}
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
> **⚠️ Security**: `config.json` is not tracked by git. Never commit API keys to version control.
|
|
114
|
+
|
|
45
115
|
## Usage
|
|
46
116
|
|
|
47
|
-
###
|
|
117
|
+
### Basic usage
|
|
118
|
+
|
|
119
|
+
Run Pi as usual — the extension auto-loads and traces every agent run:
|
|
48
120
|
|
|
49
121
|
```bash
|
|
50
|
-
pi "
|
|
122
|
+
pi "Explain the architecture of Redis"
|
|
51
123
|
```
|
|
52
124
|
|
|
53
|
-
|
|
125
|
+
After the session ends, check your [Langfuse dashboard](https://cloud.langfuse.com) for the trace.
|
|
126
|
+
|
|
127
|
+
### Verify the extension is loaded
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
pi list
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
You should see `pi-langfuse` in the list of installed packages.
|
|
134
|
+
|
|
135
|
+
### Multiple sessions
|
|
136
|
+
|
|
137
|
+
Each Pi session gets its own Langfuse session ID. Each user prompt within that Pi session becomes a separate Langfuse trace grouped under the same session.
|
|
138
|
+
|
|
139
|
+
## Development Setup
|
|
140
|
+
|
|
141
|
+
If you're contributing to this extension:
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
# Clone and install dependencies
|
|
145
|
+
git clone <your-repo-url>
|
|
146
|
+
cd pi-langfuse
|
|
147
|
+
npm install
|
|
148
|
+
|
|
149
|
+
# Type-check your changes
|
|
150
|
+
npm run typecheck
|
|
151
|
+
|
|
152
|
+
# Test with Pi
|
|
153
|
+
pi "test prompt"
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### Project structure
|
|
157
|
+
|
|
158
|
+
```
|
|
159
|
+
pi-langfuse/
|
|
160
|
+
├── index.ts # Extension entrypoint and core logic
|
|
161
|
+
├── package.json # Package metadata
|
|
162
|
+
├── tsconfig.json # TypeScript configuration
|
|
163
|
+
├── config.json # Local credentials (git-ignored)
|
|
164
|
+
├── types/
|
|
165
|
+
│ ├── pi-coding-agent.d.ts # Pi extension API types
|
|
166
|
+
│ └── node-shims.d.ts # Node.js module shims
|
|
167
|
+
├── .agents/
|
|
168
|
+
│ └── skills/
|
|
169
|
+
│ └── langfuse/
|
|
170
|
+
│ └── SKILL.md # Langfuse CLI skill for data queries
|
|
171
|
+
├── AGENTS.md # Developer guide (extended)
|
|
172
|
+
├── README.md # This file
|
|
173
|
+
├── README_CN.md # Chinese translation
|
|
174
|
+
└── AGENTS_CN.md # Developer guide (Chinese)
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
### Validation
|
|
178
|
+
|
|
179
|
+
There is no dedicated test suite yet. To validate changes:
|
|
180
|
+
|
|
181
|
+
1. Run `npm run typecheck` for TypeScript errors
|
|
182
|
+
2. Start Pi with the extension enabled
|
|
183
|
+
3. Run a few prompts
|
|
184
|
+
4. Confirm traces, the root agent observation, tool observations, generations, and evaluation scores appear in your Langfuse project
|
|
54
185
|
|
|
55
186
|
## Trace Model
|
|
56
187
|
|
|
57
188
|
```
|
|
58
189
|
Trace (name: "pi-agent")
|
|
59
190
|
├── Session ID: <pi-session-id>
|
|
60
|
-
├──
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
├──
|
|
66
|
-
├──
|
|
67
|
-
|
|
191
|
+
├── input: user prompt, images/context summary when present
|
|
192
|
+
├── output: final assistant response
|
|
193
|
+
└── Agent observation (name: "pi-agent", type: agent)
|
|
194
|
+
├── input: current user prompt
|
|
195
|
+
├── output: final assistant response
|
|
196
|
+
├── Generation observation (name: "llm-generation", type: generation)
|
|
197
|
+
│ ├── input: provider request payload / message history
|
|
198
|
+
│ ├── output: finalized assistant message or tool-call message
|
|
199
|
+
│ ├── model, usageDetails, costDetails
|
|
200
|
+
│ └── metadata: provider/request details
|
|
201
|
+
└── Tool observation (name: "<tool-name>", type: tool)
|
|
202
|
+
├── input: tool parameters
|
|
203
|
+
├── output: tool result
|
|
204
|
+
└── metadata: toolCallId, isError
|
|
68
205
|
```
|
|
69
206
|
|
|
70
207
|
## What Gets Tracked
|
|
71
208
|
|
|
72
209
|
### Trace Level
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
210
|
+
| Field | Description |
|
|
211
|
+
|-------|-------------|
|
|
212
|
+
| `input` | User prompt, with images/context summary when available |
|
|
213
|
+
| `output` | Final assistant response shown in Pi |
|
|
214
|
+
| `sessionId` | Pi session identifier |
|
|
215
|
+
| `metadata.model` | Model identifier (e.g., "MiniMax-M2.7") |
|
|
216
|
+
| `metadata.provider` | LLM provider name |
|
|
217
|
+
| `metadata.cwd` | Working directory |
|
|
218
|
+
|
|
219
|
+
### Agent Observation (Root Workflow)
|
|
220
|
+
| Field | Description |
|
|
221
|
+
|-------|-------------|
|
|
222
|
+
| `type` | `agent` |
|
|
223
|
+
| `name` | `pi-agent` |
|
|
224
|
+
| `input` | Current user prompt payload |
|
|
225
|
+
| `output` | Final assistant response |
|
|
226
|
+
| `metadata.sessionId` | Pi session identifier |
|
|
227
|
+
| `metadata.cwd` | Working directory |
|
|
228
|
+
| `metadata.model` | Selected model when available |
|
|
229
|
+
| `metadata.provider` | Provider when available |
|
|
230
|
+
|
|
231
|
+
### Evaluation Scores (Trace Level)
|
|
232
|
+
|
|
233
|
+
| Score Name | Type | Description |
|
|
234
|
+
|------------|------|-------------|
|
|
235
|
+
| `tool_call_count` | number | Total tool calls in session |
|
|
236
|
+
| `turn_count` | number | Number of assistant turns |
|
|
237
|
+
| `total_tool_errors` | number | Tools that returned errors |
|
|
238
|
+
| `tool_success_rate` | float (0-1) | Ratio of successful tool calls |
|
|
239
|
+
| `session_had_errors` | 0 or 1 | Whether any tool errored |
|
|
77
240
|
|
|
78
241
|
### Generation Observations (LLM Calls)
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
242
|
+
| Field | Description |
|
|
243
|
+
|-------|-------------|
|
|
244
|
+
| `type` | `generation` |
|
|
245
|
+
| `name` | `llm-generation` |
|
|
246
|
+
| `input` | Actual provider request payload / message history |
|
|
247
|
+
| `output` | Finalized assistant message, including tool-call payloads for tool-calling turns |
|
|
248
|
+
| `model` | Model identifier (e.g., "MiniMax-M2.7") |
|
|
249
|
+
| `usageDetails.input` | Input token count |
|
|
250
|
+
| `usageDetails.output` | Output token count |
|
|
251
|
+
| `usageDetails.total` | Total token count |
|
|
252
|
+
| `costDetails.total` | Total cost in USD |
|
|
253
|
+
| `costDetails.input` | Input cost in USD |
|
|
254
|
+
| `costDetails.output` | Output cost in USD |
|
|
255
|
+
| `metadata.provider` | Provider name |
|
|
256
|
+
| `metadata.requestId` | Provider/Pi request identifier when available |
|
|
257
|
+
| `metadata.status` | HTTP/provider status when available |
|
|
258
|
+
|
|
259
|
+
### Tool Observations
|
|
260
|
+
| Field | Description |
|
|
261
|
+
|-------|-------------|
|
|
262
|
+
| `type` | `tool` |
|
|
263
|
+
| `name` | Tool name (e.g., "bash", "read") |
|
|
264
|
+
| `input` | Tool parameters |
|
|
265
|
+
| `output` | Tool result, shaped and truncated for readability |
|
|
266
|
+
| `metadata.toolCallId` | Stable Pi tool call identifier |
|
|
267
|
+
| `metadata.isError` | Whether the tool failed |
|
|
268
|
+
| `level` | `ERROR` for failed tool calls, otherwise `DEFAULT` |
|
|
269
|
+
|
|
270
|
+
### Observation-Level Scores
|
|
271
|
+
| Score Name | Description |
|
|
272
|
+
|------------|-------------|
|
|
273
|
+
| `tool_is_error` | Value 1 assigned to individual tool observations that errored |
|
|
88
274
|
|
|
89
275
|
## Langfuse Dashboard
|
|
90
276
|
|
|
91
277
|
After running, check your Langfuse project for:
|
|
92
278
|
|
|
93
|
-
1. **Traces**
|
|
94
|
-
2. **Sessions**
|
|
95
|
-
3. **Observations**
|
|
96
|
-
4. **Scores**
|
|
97
|
-
5. **Model Usage**
|
|
279
|
+
1. **Traces** — All pi agent runs with I/O
|
|
280
|
+
2. **Sessions** — Traces grouped by session ID
|
|
281
|
+
3. **Observations** — Tool calls and LLM generations
|
|
282
|
+
4. **Scores** — Evaluation metrics (tool errors, success rate, etc.)
|
|
283
|
+
5. **Model Usage** — Usage breakdown by model
|
|
284
|
+
|
|
285
|
+
You can also monitor your Langfuse data directly from the terminal using the built-in Langfuse skill:
|
|
286
|
+
|
|
287
|
+
```
|
|
288
|
+
/pi-langfuse-langfuse <your-query>
|
|
289
|
+
```
|
|
98
290
|
|
|
99
291
|
## Troubleshooting
|
|
100
292
|
|
|
101
|
-
|
|
102
|
-
- Verify API keys are correct
|
|
103
|
-
- Check Langfuse project is active
|
|
104
|
-
- Ensure API keys have write permissions
|
|
293
|
+
### No traces appearing?
|
|
294
|
+
- Verify API keys are correct — run `/langfuse-setup` to re-configure
|
|
295
|
+
- Check your Langfuse project is active and has write capacity
|
|
296
|
+
- Ensure API keys have write permissions (not read-only)
|
|
297
|
+
- Look for `📊 Langfuse:` log messages in the Pi output
|
|
298
|
+
|
|
299
|
+
### Extension not loading?
|
|
300
|
+
```bash
|
|
301
|
+
pi list # Verify pi-langfuse is installed
|
|
302
|
+
pi install npm:pi-langfuse # Reinstall if missing
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
### "Missing config" message on startup?
|
|
306
|
+
- The extension needs credentials. Use the interactive `/langfuse-setup` command
|
|
307
|
+
- Or set `LANGFUSE_PUBLIC_KEY` and `LANGFUSE_SECRET_KEY` environment variables
|
|
105
308
|
|
|
106
|
-
|
|
107
|
-
-
|
|
108
|
-
-
|
|
309
|
+
### Model/cost not showing?
|
|
310
|
+
- Not all providers expose cost information
|
|
311
|
+
- Check the Langfuse traces API for raw observation data
|
|
312
|
+
- The `model` field in generations comes from provider events, finalized assistant messages, `model_select`, or `ctx.model`
|
|
109
313
|
|
|
110
|
-
|
|
111
|
-
-
|
|
112
|
-
-
|
|
314
|
+
### API key errors?
|
|
315
|
+
- Langfuse public keys start with `pk-lf-`, secret keys with `sk-lf-`
|
|
316
|
+
- If self-hosting, verify your host URL is correct
|
|
113
317
|
|
|
114
318
|
## Dependencies
|
|
115
319
|
|
|
116
|
-
- [langfuse](https://www.npmjs.com/package/
|
|
117
|
-
- [@
|
|
320
|
+
- [@langfuse/tracing](https://www.npmjs.com/package/@langfuse/tracing) — Langfuse observation API for `agent`, `generation`, and `tool` traces
|
|
321
|
+
- [@langfuse/otel](https://www.npmjs.com/package/@langfuse/otel) — OpenTelemetry span processor for exporting traces to Langfuse
|
|
322
|
+
- [@langfuse/client](https://www.npmjs.com/package/@langfuse/client) — Langfuse API client used for scores
|
|
323
|
+
- [@opentelemetry/sdk-node](https://www.npmjs.com/package/@opentelemetry/sdk-node) — Node OpenTelemetry SDK
|
|
324
|
+
- [@earendil-works/pi-coding-agent](https://www.npmjs.com/package/@earendil-works/pi-coding-agent) — Pi extension API (peer dependency)
|
|
325
|
+
|
|
326
|
+
## About Langfuse Skill
|
|
327
|
+
|
|
328
|
+
This package includes a Langfuse CLI skill (at `.agents/skills/langfuse/`) that lets you query Langfuse data directly from Pi. Use it to look up traces, prompts, datasets, and scores without leaving the terminal. The skill is auto-registered when the extension is installed globally.
|
|
118
329
|
|
|
119
330
|
## License
|
|
120
331
|
|