pi-langfuse 1.4.2 → 1.4.4
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/README.md +104 -248
- package/README_CN.md +108 -248
- package/image.png +0 -0
- package/index.ts +5 -3
- package/package.json +2 -1
- package/src/capture-policy.ts +141 -0
- package/src/config.ts +21 -12
- package/src/constants.ts +1 -0
- package/src/handlers/agent.ts +61 -29
- package/src/handlers/generation.ts +58 -59
- package/src/handlers/tool.ts +40 -27
- package/src/handlers/turn.ts +23 -21
- package/src/langfuse.ts +78 -17
- package/src/observation.ts +21 -0
- package/src/redaction.ts +115 -0
- package/src/state.ts +25 -2
- package/src/types.ts +3 -0
- package/src/utils.ts +54 -5
- package/types/langfuse-runtime-shims.d.ts +49 -0
package/src/config.ts
CHANGED
|
@@ -2,18 +2,25 @@ import { mkdirSync, readFileSync, existsSync, writeFileSync } from "node:fs";
|
|
|
2
2
|
import type { Config } from "./types.js";
|
|
3
3
|
import { CONFIG_DIR, CONFIG_PATH, DEFAULT_LANGFUSE_HOST } from "./constants.js";
|
|
4
4
|
import { state } from "./state.js";
|
|
5
|
-
import {
|
|
5
|
+
import { forceShutdownRuntime } from "./langfuse.js";
|
|
6
|
+
import { createCapturePolicy, type EnvLike } from "./capture-policy.js";
|
|
6
7
|
|
|
7
|
-
export function loadConfigFromFile(): Config | null {
|
|
8
|
-
if (existsSync(
|
|
8
|
+
export function loadConfigFromFile(path = CONFIG_PATH, env: EnvLike = process.env as EnvLike): Config | null {
|
|
9
|
+
if (existsSync(path)) {
|
|
9
10
|
try {
|
|
10
|
-
const content = readFileSync(
|
|
11
|
-
const config = JSON.parse(content) as Config;
|
|
11
|
+
const content = readFileSync(path, "utf-8");
|
|
12
|
+
const config = JSON.parse(content) as Config & { capture?: EnvLike; privacyPreset?: string };
|
|
12
13
|
if (config.publicKey && config.secretKey) {
|
|
14
|
+
const captureSource: EnvLike = {
|
|
15
|
+
...(config.capture ?? {}),
|
|
16
|
+
...(config.privacyPreset ? { LANGFUSE_PRIVACY_PRESET: config.privacyPreset } : {}),
|
|
17
|
+
...env,
|
|
18
|
+
};
|
|
13
19
|
return {
|
|
14
20
|
publicKey: config.publicKey,
|
|
15
21
|
secretKey: config.secretKey,
|
|
16
22
|
host: config.host || DEFAULT_LANGFUSE_HOST,
|
|
23
|
+
capturePolicy: createCapturePolicy(captureSource),
|
|
17
24
|
};
|
|
18
25
|
}
|
|
19
26
|
} catch (e) {
|
|
@@ -24,9 +31,9 @@ export function loadConfigFromFile(): Config | null {
|
|
|
24
31
|
return null;
|
|
25
32
|
}
|
|
26
33
|
|
|
27
|
-
export function loadConfigFromEnv(): Config | null {
|
|
28
|
-
const publicKey =
|
|
29
|
-
const secretKey =
|
|
34
|
+
export function loadConfigFromEnv(env: EnvLike = process.env as EnvLike): Config | null {
|
|
35
|
+
const publicKey = env.LANGFUSE_PUBLIC_KEY || "";
|
|
36
|
+
const secretKey = env.LANGFUSE_SECRET_KEY || "";
|
|
30
37
|
if (!publicKey || !secretKey) {
|
|
31
38
|
return null;
|
|
32
39
|
}
|
|
@@ -34,12 +41,13 @@ export function loadConfigFromEnv(): Config | null {
|
|
|
34
41
|
return {
|
|
35
42
|
publicKey,
|
|
36
43
|
secretKey,
|
|
37
|
-
host:
|
|
44
|
+
host: env.LANGFUSE_BASE_URL || env.LANGFUSE_HOST || DEFAULT_LANGFUSE_HOST,
|
|
45
|
+
capturePolicy: createCapturePolicy(env),
|
|
38
46
|
};
|
|
39
47
|
}
|
|
40
48
|
|
|
41
|
-
export function loadConfig(): Config | null {
|
|
42
|
-
return loadConfigFromFile() || loadConfigFromEnv();
|
|
49
|
+
export function loadConfig(env: EnvLike = process.env as EnvLike, path = CONFIG_PATH): Config | null {
|
|
50
|
+
return loadConfigFromFile(path, env) || loadConfigFromEnv(env);
|
|
43
51
|
}
|
|
44
52
|
|
|
45
53
|
export function saveConfig(config: Config) {
|
|
@@ -72,6 +80,7 @@ async function collectConfigFromUI(ctx: any, reason: string): Promise<Config | n
|
|
|
72
80
|
publicKey,
|
|
73
81
|
secretKey,
|
|
74
82
|
host: hostInput || DEFAULT_LANGFUSE_HOST,
|
|
83
|
+
capturePolicy: createCapturePolicy(),
|
|
75
84
|
};
|
|
76
85
|
}
|
|
77
86
|
|
|
@@ -115,7 +124,7 @@ export async function ensureConfig(ctx: any): Promise<boolean> {
|
|
|
115
124
|
export async function promptForConfig(ctx: any): Promise<boolean> {
|
|
116
125
|
state.setupAttemptedThisSession = false;
|
|
117
126
|
state.config = null;
|
|
118
|
-
await
|
|
127
|
+
await forceShutdownRuntime();
|
|
119
128
|
|
|
120
129
|
const config = await collectConfigFromUI(ctx, "Manual setup requested");
|
|
121
130
|
if (!config) {
|
package/src/constants.ts
CHANGED
package/src/handlers/agent.ts
CHANGED
|
@@ -1,8 +1,25 @@
|
|
|
1
1
|
import { state, resetRunState, computeEvaluationScores } from "../state.js";
|
|
2
2
|
import { getRuntime, sendScore } from "../langfuse.js";
|
|
3
3
|
import { ensureConfig } from "../config.js";
|
|
4
|
-
import { shapePayload, truncate, extractFinalAssistant, extractAssistantOutput } from "../utils.js";
|
|
4
|
+
import { shapePayload, truncate, extractFinalAssistant, extractAssistantOutput, getCapturePolicy } from "../utils.js";
|
|
5
5
|
import { closeDanglingObservations } from "./tool.js";
|
|
6
|
+
import { applyCapturePolicy } from "../capture-policy.js";
|
|
7
|
+
|
|
8
|
+
function stringMetadata(metadata: Record<string, unknown> | undefined): Record<string, string> | undefined {
|
|
9
|
+
if (!metadata) {
|
|
10
|
+
return undefined;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const output: Record<string, string> = {};
|
|
14
|
+
for (const [key, value] of Object.entries(metadata)) {
|
|
15
|
+
if (typeof value === "string") {
|
|
16
|
+
output[key] = value;
|
|
17
|
+
} else if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") {
|
|
18
|
+
output[key] = String(value);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return Object.keys(output).length > 0 ? output : undefined;
|
|
22
|
+
}
|
|
6
23
|
|
|
7
24
|
export function updateTraceIO(input?: unknown, output?: unknown) {
|
|
8
25
|
const root = state.agentState?.root;
|
|
@@ -19,6 +36,7 @@ export function updateTraceIO(input?: unknown, output?: unknown) {
|
|
|
19
36
|
|
|
20
37
|
export async function startAgentRun(event: Record<string, unknown>, ctx: any) {
|
|
21
38
|
if (!(await ensureConfig(ctx))) {
|
|
39
|
+
state.isTracingDisabled = true;
|
|
22
40
|
return;
|
|
23
41
|
}
|
|
24
42
|
|
|
@@ -44,15 +62,28 @@ export async function startAgentRun(event: Record<string, unknown>, ctx: any) {
|
|
|
44
62
|
// Ignore if getSystemPrompt is not available or fails
|
|
45
63
|
}
|
|
46
64
|
|
|
47
|
-
const
|
|
65
|
+
const rawPromptInput = shapePayload({
|
|
48
66
|
prompt: event.prompt,
|
|
49
67
|
images: event.images,
|
|
50
68
|
context: event.context ?? event.attachments,
|
|
51
69
|
});
|
|
70
|
+
const captured = applyCapturePolicy(
|
|
71
|
+
{
|
|
72
|
+
input: rawPromptInput,
|
|
73
|
+
metadata: {
|
|
74
|
+
cwd,
|
|
75
|
+
...(state.currentModel ? { model: state.currentModel } : {}),
|
|
76
|
+
...(state.currentProvider ? { provider: state.currentProvider } : {}),
|
|
77
|
+
sessionId: state.currentSessionId || undefined,
|
|
78
|
+
},
|
|
79
|
+
systemPrompt: systemPrompt ? truncate(String(systemPrompt), 20000) : undefined,
|
|
80
|
+
},
|
|
81
|
+
getCapturePolicy(),
|
|
82
|
+
);
|
|
52
83
|
|
|
53
84
|
state.agentState = {
|
|
54
85
|
cwd,
|
|
55
|
-
promptInput,
|
|
86
|
+
promptInput: captured.input,
|
|
56
87
|
generationSeq: 0,
|
|
57
88
|
activeGenerations: new Map(),
|
|
58
89
|
generationOrder: [],
|
|
@@ -64,34 +95,28 @@ export async function startAgentRun(event: Record<string, unknown>, ctx: any) {
|
|
|
64
95
|
{
|
|
65
96
|
sessionId: state.currentSessionId ? truncate(state.currentSessionId, 200) : undefined,
|
|
66
97
|
traceName: "pi-agent",
|
|
67
|
-
metadata:
|
|
68
|
-
cwd: truncate(cwd, 200),
|
|
69
|
-
...(state.currentModel ? { model: truncate(state.currentModel, 200) } : {}),
|
|
70
|
-
...(state.currentProvider ? { provider: truncate(state.currentProvider, 200) } : {}),
|
|
71
|
-
},
|
|
98
|
+
metadata: stringMetadata(captured.metadata),
|
|
72
99
|
},
|
|
73
100
|
() =>
|
|
74
101
|
rt.startObservation(
|
|
75
102
|
"pi-agent",
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
sessionId: state.currentSessionId || undefined,
|
|
83
|
-
...(systemPrompt ? { systemPrompt: truncate(String(systemPrompt), 20000) } : {}),
|
|
103
|
+
{
|
|
104
|
+
input: captured.input,
|
|
105
|
+
metadata: {
|
|
106
|
+
...(captured.metadata ?? {}),
|
|
107
|
+
...(captured.systemPrompt ? { systemPrompt: captured.systemPrompt } : {}),
|
|
108
|
+
},
|
|
84
109
|
},
|
|
85
|
-
},
|
|
86
110
|
{ asType: "agent" },
|
|
87
111
|
),
|
|
88
112
|
);
|
|
89
113
|
|
|
90
114
|
state.agentState.root = root;
|
|
91
115
|
state.agentState.traceId = root.traceId;
|
|
92
|
-
updateTraceIO(
|
|
116
|
+
updateTraceIO(captured.input, undefined);
|
|
93
117
|
} catch (e) {
|
|
94
118
|
console.warn("📊 Langfuse: Failed to create agent observation", e);
|
|
119
|
+
state.isTracingDisabled = true;
|
|
95
120
|
}
|
|
96
121
|
}
|
|
97
122
|
|
|
@@ -102,7 +127,21 @@ export async function finishAgentRun(event: Record<string, unknown> = {}) {
|
|
|
102
127
|
}
|
|
103
128
|
|
|
104
129
|
const lastAssistant = extractFinalAssistant(event.messages);
|
|
105
|
-
const
|
|
130
|
+
const rawOutput = lastAssistant ? extractAssistantOutput(lastAssistant) : state.agentState.latestAssistantOutput;
|
|
131
|
+
const captured = applyCapturePolicy(
|
|
132
|
+
{
|
|
133
|
+
output: rawOutput,
|
|
134
|
+
metadata: {
|
|
135
|
+
cwd: state.agentState.cwd,
|
|
136
|
+
completed: true,
|
|
137
|
+
model: state.currentModel || undefined,
|
|
138
|
+
provider: state.currentProvider || undefined,
|
|
139
|
+
totalTools: state.toolCallCount,
|
|
140
|
+
...computeEvaluationScores(),
|
|
141
|
+
},
|
|
142
|
+
},
|
|
143
|
+
getCapturePolicy(),
|
|
144
|
+
);
|
|
106
145
|
const scores = computeEvaluationScores();
|
|
107
146
|
|
|
108
147
|
closeDanglingObservations("Agent run ended before observation finalized");
|
|
@@ -110,18 +149,11 @@ export async function finishAgentRun(event: Record<string, unknown> = {}) {
|
|
|
110
149
|
try {
|
|
111
150
|
state.agentState.root
|
|
112
151
|
.update({
|
|
113
|
-
output,
|
|
114
|
-
metadata:
|
|
115
|
-
cwd: state.agentState.cwd,
|
|
116
|
-
completed: true,
|
|
117
|
-
model: state.currentModel || undefined,
|
|
118
|
-
provider: state.currentProvider || undefined,
|
|
119
|
-
totalTools: state.toolCallCount,
|
|
120
|
-
...scores,
|
|
121
|
-
},
|
|
152
|
+
output: captured.output,
|
|
153
|
+
metadata: captured.metadata,
|
|
122
154
|
})
|
|
123
155
|
.end();
|
|
124
|
-
updateTraceIO(state.agentState.promptInput, output);
|
|
156
|
+
updateTraceIO(state.agentState.promptInput, captured.output);
|
|
125
157
|
|
|
126
158
|
await sendScore("tool_call_count", scores.tool_call_count, { traceId: state.agentState.traceId });
|
|
127
159
|
await sendScore("turn_count", scores.turn_count, { traceId: state.agentState.traceId });
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { state } from "../state.js";
|
|
2
2
|
import { getRuntime } from "../langfuse.js";
|
|
3
|
+
import { startChildObservation } from "../observation.js";
|
|
3
4
|
import {
|
|
4
5
|
getRequestKey,
|
|
5
6
|
getProviderPayload,
|
|
@@ -9,11 +10,13 @@ import {
|
|
|
9
10
|
extractAssistantOutput,
|
|
10
11
|
extractUsage,
|
|
11
12
|
extractCostDetails,
|
|
13
|
+
getCapturePolicy,
|
|
12
14
|
} from "../utils.js";
|
|
13
15
|
import type { GenerationState, ObservationUpdate } from "../types.js";
|
|
16
|
+
import { applyCapturePolicy } from "../capture-policy.js";
|
|
14
17
|
|
|
15
18
|
export function getOpenGeneration(): GenerationState | undefined {
|
|
16
|
-
if (!state.agentState) {
|
|
19
|
+
if (state.isTracingDisabled || !state.agentState) {
|
|
17
20
|
return undefined;
|
|
18
21
|
}
|
|
19
22
|
|
|
@@ -29,7 +32,7 @@ export function getOpenGeneration(): GenerationState | undefined {
|
|
|
29
32
|
}
|
|
30
33
|
|
|
31
34
|
export async function startGeneration(event: Record<string, unknown>) {
|
|
32
|
-
if (!state.agentState?.root) {
|
|
35
|
+
if (state.isTracingDisabled || !state.agentState?.root) {
|
|
33
36
|
return;
|
|
34
37
|
}
|
|
35
38
|
|
|
@@ -44,33 +47,32 @@ export async function startGeneration(event: Record<string, unknown>) {
|
|
|
44
47
|
url: event.url,
|
|
45
48
|
method: event.method,
|
|
46
49
|
}) as Record<string, unknown>;
|
|
50
|
+
const captured = applyCapturePolicy(
|
|
51
|
+
{
|
|
52
|
+
input: shapePayload(payload),
|
|
53
|
+
metadata,
|
|
54
|
+
},
|
|
55
|
+
getCapturePolicy(),
|
|
56
|
+
);
|
|
47
57
|
|
|
48
58
|
const parent = state.agentState.activeTurn ?? state.agentState.root;
|
|
49
|
-
const generation =
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
"llm-generation",
|
|
61
|
-
{
|
|
62
|
-
input: shapePayload(payload),
|
|
63
|
-
model: model || undefined,
|
|
64
|
-
metadata,
|
|
65
|
-
},
|
|
66
|
-
{ asType: "generation" },
|
|
67
|
-
);
|
|
59
|
+
const generation = await startChildObservation({
|
|
60
|
+
parent,
|
|
61
|
+
runtime: getRuntime,
|
|
62
|
+
name: "llm-generation",
|
|
63
|
+
body: {
|
|
64
|
+
input: captured.input,
|
|
65
|
+
model: model || undefined,
|
|
66
|
+
metadata: captured.metadata,
|
|
67
|
+
},
|
|
68
|
+
asType: "generation",
|
|
69
|
+
});
|
|
68
70
|
|
|
69
71
|
state.agentState.activeGenerations.set(key, {
|
|
70
72
|
observation: generation,
|
|
71
73
|
requestKey: key,
|
|
72
74
|
ended: false,
|
|
73
|
-
metadata,
|
|
75
|
+
metadata: captured.metadata ?? {},
|
|
74
76
|
});
|
|
75
77
|
state.agentState.generationOrder.push(key);
|
|
76
78
|
} catch (e) {
|
|
@@ -79,12 +81,12 @@ export async function startGeneration(event: Record<string, unknown>) {
|
|
|
79
81
|
}
|
|
80
82
|
|
|
81
83
|
export function updateGenerationMetadata(event: Record<string, unknown>) {
|
|
82
|
-
if (!state.agentState) {
|
|
84
|
+
if (state.isTracingDisabled || !state.agentState) {
|
|
83
85
|
return;
|
|
84
86
|
}
|
|
85
87
|
|
|
86
88
|
const key = getRequestKey(event, "");
|
|
87
|
-
const metadata = extractResponseMetadata(event);
|
|
89
|
+
const metadata = applyCapturePolicy({ metadata: extractResponseMetadata(event) }, getCapturePolicy()).metadata ?? {};
|
|
88
90
|
if (!key) {
|
|
89
91
|
const generation = getOpenGeneration();
|
|
90
92
|
if (generation) {
|
|
@@ -132,7 +134,7 @@ export function updateGenerationMetadata(event: Record<string, unknown>) {
|
|
|
132
134
|
}
|
|
133
135
|
|
|
134
136
|
export function recordTTFT(event: Record<string, unknown>) {
|
|
135
|
-
if (!state.agentState) {
|
|
137
|
+
if (state.isTracingDisabled || !state.agentState) {
|
|
136
138
|
return;
|
|
137
139
|
}
|
|
138
140
|
|
|
@@ -150,7 +152,7 @@ export function recordTTFT(event: Record<string, unknown>) {
|
|
|
150
152
|
}
|
|
151
153
|
|
|
152
154
|
export async function finishGenerationFromMessage(event: Record<string, unknown>) {
|
|
153
|
-
if (!state.agentState) {
|
|
155
|
+
if (state.isTracingDisabled || !state.agentState) {
|
|
154
156
|
return;
|
|
155
157
|
}
|
|
156
158
|
|
|
@@ -160,7 +162,9 @@ export async function finishGenerationFromMessage(event: Record<string, unknown>
|
|
|
160
162
|
}
|
|
161
163
|
|
|
162
164
|
const generation = getOpenGeneration();
|
|
163
|
-
const
|
|
165
|
+
const rawOutput = extractAssistantOutput(message);
|
|
166
|
+
const captured = applyCapturePolicy({ output: rawOutput }, getCapturePolicy());
|
|
167
|
+
const output = captured.output;
|
|
164
168
|
state.agentState.latestAssistantOutput = output;
|
|
165
169
|
|
|
166
170
|
if (!generation) {
|
|
@@ -180,6 +184,7 @@ export async function finishGenerationFromMessage(event: Record<string, unknown>
|
|
|
180
184
|
finishReason: message.finishReason ?? message.stopReason ?? event.finishReason,
|
|
181
185
|
},
|
|
182
186
|
};
|
|
187
|
+
update.metadata = applyCapturePolicy({ metadata: update.metadata }, getCapturePolicy()).metadata;
|
|
183
188
|
|
|
184
189
|
try {
|
|
185
190
|
generation.observation.update(update).end();
|
|
@@ -190,7 +195,7 @@ export async function finishGenerationFromMessage(event: Record<string, unknown>
|
|
|
190
195
|
}
|
|
191
196
|
|
|
192
197
|
export async function createFallbackGenerationFromTurn(event: Record<string, unknown>, message: Record<string, unknown>) {
|
|
193
|
-
if (!state.agentState?.root || state.agentState.generationOrder.length > 0) {
|
|
198
|
+
if (state.isTracingDisabled || !state.agentState?.root || state.agentState.generationOrder.length > 0) {
|
|
194
199
|
return;
|
|
195
200
|
}
|
|
196
201
|
|
|
@@ -198,38 +203,32 @@ export async function createFallbackGenerationFromTurn(event: Record<string, unk
|
|
|
198
203
|
const usageDetails = extractUsage({ ...event, message });
|
|
199
204
|
const costDetails = extractCostDetails({ ...event, message });
|
|
200
205
|
const model = String(message.model ?? event.model ?? state.currentModel ?? "");
|
|
206
|
+
const captured = applyCapturePolicy(
|
|
207
|
+
{
|
|
208
|
+
input: state.agentState.promptInput,
|
|
209
|
+
output: extractAssistantOutput(message),
|
|
210
|
+
metadata: {
|
|
211
|
+
provider: state.currentProvider || undefined,
|
|
212
|
+
sourceEvent: "turn_end",
|
|
213
|
+
},
|
|
214
|
+
},
|
|
215
|
+
getCapturePolicy(),
|
|
216
|
+
);
|
|
201
217
|
const parent = state.agentState.activeTurn ?? state.agentState.root;
|
|
202
|
-
const generation =
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
{ asType: "generation" },
|
|
217
|
-
)
|
|
218
|
-
: (await getRuntime()).startObservation(
|
|
219
|
-
"llm-generation",
|
|
220
|
-
{
|
|
221
|
-
input: state.agentState.promptInput,
|
|
222
|
-
output: extractAssistantOutput(message),
|
|
223
|
-
model: model || undefined,
|
|
224
|
-
usageDetails,
|
|
225
|
-
costDetails,
|
|
226
|
-
metadata: {
|
|
227
|
-
provider: state.currentProvider || undefined,
|
|
228
|
-
sourceEvent: "turn_end",
|
|
229
|
-
},
|
|
230
|
-
},
|
|
231
|
-
{ asType: "generation" },
|
|
232
|
-
);
|
|
218
|
+
const generation = await startChildObservation({
|
|
219
|
+
parent,
|
|
220
|
+
runtime: getRuntime,
|
|
221
|
+
name: "llm-generation",
|
|
222
|
+
body: {
|
|
223
|
+
input: captured.input,
|
|
224
|
+
output: captured.output,
|
|
225
|
+
model: model || undefined,
|
|
226
|
+
usageDetails,
|
|
227
|
+
costDetails,
|
|
228
|
+
metadata: captured.metadata,
|
|
229
|
+
},
|
|
230
|
+
asType: "generation",
|
|
231
|
+
});
|
|
233
232
|
|
|
234
233
|
generation.end();
|
|
235
234
|
state.agentState.generationOrder.push("turn-end-fallback");
|
package/src/handlers/tool.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { state } from "../state.js";
|
|
2
2
|
import { getRuntime, sendScore } from "../langfuse.js";
|
|
3
|
+
import { startChildObservation } from "../observation.js";
|
|
3
4
|
import {
|
|
4
5
|
getToolCallId,
|
|
5
6
|
getToolName,
|
|
@@ -8,11 +9,14 @@ import {
|
|
|
8
9
|
extractTextContent,
|
|
9
10
|
truncate,
|
|
10
11
|
estimatePayloadBytes,
|
|
12
|
+
getCapturePolicy,
|
|
11
13
|
} from "../utils.js";
|
|
12
14
|
import { MAX_TOOL_PAYLOAD_LENGTH } from "../constants.js";
|
|
15
|
+
import { applyCapturePolicy } from "../capture-policy.js";
|
|
16
|
+
import { redactString } from "../redaction.js";
|
|
13
17
|
|
|
14
18
|
export async function startToolObservation(event: Record<string, unknown>) {
|
|
15
|
-
if (!state.agentState?.root) {
|
|
19
|
+
if (state.isTracingDisabled || !state.agentState?.root) {
|
|
16
20
|
return;
|
|
17
21
|
}
|
|
18
22
|
|
|
@@ -25,25 +29,25 @@ export async function startToolObservation(event: Record<string, unknown>) {
|
|
|
25
29
|
const toolName = getToolName(event);
|
|
26
30
|
const toolInput = getToolInput(event);
|
|
27
31
|
const shapedInput = shapePayload(toolInput, { maxString: MAX_TOOL_PAYLOAD_LENGTH });
|
|
28
|
-
const
|
|
32
|
+
const captured = applyCapturePolicy(
|
|
33
|
+
{
|
|
34
|
+
toolInput: shapedInput,
|
|
35
|
+
metadata: { toolName, toolCallId },
|
|
36
|
+
},
|
|
37
|
+
getCapturePolicy(),
|
|
38
|
+
);
|
|
39
|
+
const inputBytes = estimatePayloadBytes(captured.toolInput, MAX_TOOL_PAYLOAD_LENGTH);
|
|
29
40
|
const parent = state.agentState.activeTurn ?? state.agentState.root;
|
|
30
|
-
const tool =
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
toolName,
|
|
41
|
-
{
|
|
42
|
-
input: shapedInput,
|
|
43
|
-
metadata: { toolName, toolCallId, inputBytes },
|
|
44
|
-
},
|
|
45
|
-
{ asType: "tool" },
|
|
46
|
-
);
|
|
41
|
+
const tool = await startChildObservation({
|
|
42
|
+
parent,
|
|
43
|
+
runtime: getRuntime,
|
|
44
|
+
name: toolName,
|
|
45
|
+
body: {
|
|
46
|
+
input: captured.toolInput,
|
|
47
|
+
metadata: { ...(captured.metadata ?? {}), inputBytes },
|
|
48
|
+
},
|
|
49
|
+
asType: "tool",
|
|
50
|
+
});
|
|
47
51
|
|
|
48
52
|
state.toolCallCount++;
|
|
49
53
|
state.agentState.activeTools.set(toolCallId, {
|
|
@@ -59,7 +63,7 @@ export async function startToolObservation(event: Record<string, unknown>) {
|
|
|
59
63
|
}
|
|
60
64
|
|
|
61
65
|
export async function finishToolObservation(event: Record<string, unknown>) {
|
|
62
|
-
if (!state.agentState) {
|
|
66
|
+
if (state.isTracingDisabled || !state.agentState) {
|
|
63
67
|
return;
|
|
64
68
|
}
|
|
65
69
|
|
|
@@ -84,18 +88,27 @@ export async function finishToolObservation(event: Record<string, unknown>) {
|
|
|
84
88
|
|
|
85
89
|
try {
|
|
86
90
|
const shapedOutput = shapePayload(output, { maxString: MAX_TOOL_PAYLOAD_LENGTH });
|
|
87
|
-
const
|
|
91
|
+
const captured = applyCapturePolicy(
|
|
92
|
+
{
|
|
93
|
+
toolOutput: shapedOutput,
|
|
94
|
+
metadata: {
|
|
95
|
+
toolName: activeTool.toolName,
|
|
96
|
+
toolCallId,
|
|
97
|
+
isError,
|
|
98
|
+
},
|
|
99
|
+
},
|
|
100
|
+
getCapturePolicy(),
|
|
101
|
+
);
|
|
102
|
+
const outputBytes = estimatePayloadBytes(captured.toolOutput, MAX_TOOL_PAYLOAD_LENGTH);
|
|
88
103
|
const durationMs = Math.max(0, Date.now() - activeTool.startedAt);
|
|
89
104
|
|
|
90
105
|
activeTool.observation
|
|
91
106
|
.update({
|
|
92
|
-
output:
|
|
107
|
+
output: captured.toolOutput,
|
|
93
108
|
level: isError ? "ERROR" : "DEFAULT",
|
|
94
|
-
statusMessage: isError ? truncate(String(event.error ?? output), 1_000) : undefined,
|
|
109
|
+
statusMessage: isError ? redactString(truncate(String(event.error ?? output), 1_000)) : undefined,
|
|
95
110
|
metadata: {
|
|
96
|
-
|
|
97
|
-
toolCallId,
|
|
98
|
-
isError,
|
|
111
|
+
...(captured.metadata ?? {}),
|
|
99
112
|
durationMs,
|
|
100
113
|
inputBytes: activeTool.inputBytes,
|
|
101
114
|
outputBytes,
|
|
@@ -119,7 +132,7 @@ export async function finishToolObservation(event: Record<string, unknown>) {
|
|
|
119
132
|
}
|
|
120
133
|
|
|
121
134
|
export function closeDanglingObservations(statusMessage: string) {
|
|
122
|
-
if (!state.agentState) {
|
|
135
|
+
if (state.isTracingDisabled || !state.agentState) {
|
|
123
136
|
return;
|
|
124
137
|
}
|
|
125
138
|
|
package/src/handlers/turn.ts
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { state } from "../state.js";
|
|
2
2
|
import { getRuntime } from "../langfuse.js";
|
|
3
|
-
import {
|
|
3
|
+
import { startChildObservation } from "../observation.js";
|
|
4
|
+
import { shapePayload, getCapturePolicy } from "../utils.js";
|
|
5
|
+
import { applyCapturePolicy } from "../capture-policy.js";
|
|
4
6
|
|
|
5
7
|
export async function startTurnObservation(event: Record<string, unknown>) {
|
|
6
|
-
if (!state.agentState?.root) {
|
|
8
|
+
if (state.isTracingDisabled || !state.agentState?.root) {
|
|
7
9
|
return;
|
|
8
10
|
}
|
|
9
11
|
|
|
@@ -15,23 +17,23 @@ export async function startTurnObservation(event: Record<string, unknown>) {
|
|
|
15
17
|
|
|
16
18
|
try {
|
|
17
19
|
const turnIndex = event.turnIndex ?? state.turnCount;
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
:
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
20
|
+
const captured = applyCapturePolicy(
|
|
21
|
+
{
|
|
22
|
+
input: shapePayload(event.context ?? event),
|
|
23
|
+
metadata: { turnIndex },
|
|
24
|
+
},
|
|
25
|
+
getCapturePolicy(),
|
|
26
|
+
);
|
|
27
|
+
const observation = await startChildObservation({
|
|
28
|
+
parent: state.agentState.root,
|
|
29
|
+
runtime: getRuntime,
|
|
30
|
+
name: "turn",
|
|
31
|
+
body: {
|
|
32
|
+
input: captured.input,
|
|
33
|
+
metadata: captured.metadata,
|
|
34
|
+
},
|
|
35
|
+
asType: "span",
|
|
36
|
+
});
|
|
35
37
|
|
|
36
38
|
state.agentState.activeTurn = observation;
|
|
37
39
|
} catch (e) {
|
|
@@ -39,8 +41,8 @@ export async function startTurnObservation(event: Record<string, unknown>) {
|
|
|
39
41
|
}
|
|
40
42
|
}
|
|
41
43
|
|
|
42
|
-
export function finishTurnObservation(
|
|
43
|
-
if (!state.agentState?.activeTurn) {
|
|
44
|
+
export function finishTurnObservation(_event?: Record<string, unknown>) {
|
|
45
|
+
if (state.isTracingDisabled || !state.agentState?.activeTurn) {
|
|
44
46
|
return;
|
|
45
47
|
}
|
|
46
48
|
|