Skip to content

Commit 9ae11b8

Browse files
favboxclaude
andcommitted
fix(langfuse): fix latency display showing incorrect huge hours value
## Problem Langfuse UI was displaying incorrect latency values like "17751749h" instead of the actual duration in milliseconds (e.g., "854ms"). ## Root Cause The callback handler was not preserving the original `startTime` when updating spans/generations in `OnEnd`. This caused the `startTime` to be reset to zero value ("0001-01-01T00:00:00Z"), which Langfuse backend uses to calculate latency as `endTime - startTime`, resulting in a massive time difference. ## Solution 1. Add `Duration` field (milliseconds) to `SpanEventBody` and `GenerationEventBody` in event data model 2. Store `startTime` in callback state during `OnStart` 3. Preserve original `startTime` and calculate `duration` in `OnEnd` ## Changes - `libs/acl/langfuse/event.go`: Add `Duration *int64` field - `callbacks/langfuse/langfuse.go`: Track startTime and compute duration - `callbacks/langfuse/go.mod`: Add local replace for development Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 1f91f54 commit 9ae11b8

File tree

3 files changed

+35
-9
lines changed

3 files changed

+35
-9
lines changed

callbacks/langfuse/go.mod

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ require (
1111
github.com/stretchr/testify v1.10.0
1212
)
1313

14+
replace github.com/cloudwego/eino-ext/libs/acl/langfuse => ../../libs/acl/langfuse
15+
1416
require (
1517
github.com/bahlo/generic-list-go v0.2.0 // indirect
1618
github.com/buger/jsonparser v1.1.1 // indirect

callbacks/langfuse/langfuse.go

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,7 @@ type langfuseStateKey struct{}
184184
type langfuseState struct {
185185
traceID string
186186
observationID string
187+
startTime time.Time // 用于计算 duration
187188
}
188189

189190
func (c *CallbackHandler) OnStart(ctx context.Context, info *callbacks.RunInfo, input callbacks.CallbackInput) context.Context {
@@ -197,6 +198,7 @@ func (c *CallbackHandler) OnStart(ctx context.Context, info *callbacks.RunInfo,
197198
}
198199
if info.Component == components.ComponentOfChatModel {
199200
mcbi := model.ConvCallbackInput(input)
201+
startTime := time.Now()
200202

201203
body := &langfuse.GenerationEventBody{
202204
BaseObservationEventBody: langfuse.BaseObservationEventBody{
@@ -206,7 +208,7 @@ func (c *CallbackHandler) OnStart(ctx context.Context, info *callbacks.RunInfo,
206208
},
207209
TraceID: state.traceID,
208210
ParentObservationID: state.observationID,
209-
StartTime: time.Now(),
211+
StartTime: startTime,
210212
},
211213
InMessages: mcbi.Messages,
212214
}
@@ -222,6 +224,7 @@ func (c *CallbackHandler) OnStart(ctx context.Context, info *callbacks.RunInfo,
222224
return context.WithValue(ctx, langfuseStateKey{}, &langfuseState{
223225
traceID: state.traceID,
224226
observationID: generationID,
227+
startTime: startTime,
225228
})
226229
}
227230

@@ -230,6 +233,7 @@ func (c *CallbackHandler) OnStart(ctx context.Context, info *callbacks.RunInfo,
230233
log.Printf("marshal input error: %v, runinfo: %+v", err, info)
231234
return ctx
232235
}
236+
startTime := time.Now()
233237
spanID, err := c.cli.CreateSpan(&langfuse.SpanEventBody{
234238
BaseObservationEventBody: langfuse.BaseObservationEventBody{
235239
BaseEventBody: langfuse.BaseEventBody{
@@ -238,7 +242,7 @@ func (c *CallbackHandler) OnStart(ctx context.Context, info *callbacks.RunInfo,
238242
Input: in,
239243
TraceID: state.traceID,
240244
ParentObservationID: state.observationID,
241-
StartTime: time.Now(),
245+
StartTime: startTime,
242246
},
243247
})
244248
if err != nil {
@@ -248,6 +252,7 @@ func (c *CallbackHandler) OnStart(ctx context.Context, info *callbacks.RunInfo,
248252
return context.WithValue(ctx, langfuseStateKey{}, &langfuseState{
249253
traceID: state.traceID,
250254
observationID: spanID,
255+
startTime: startTime,
251256
})
252257
}
253258

@@ -264,16 +269,18 @@ func (c *CallbackHandler) OnEnd(ctx context.Context, info *callbacks.RunInfo, ou
264269

265270
if info.Component == components.ComponentOfChatModel {
266271
mcbo := model.ConvCallbackOutput(output)
272+
endTime := time.Now()
267273

268274
body := &langfuse.GenerationEventBody{
269275
BaseObservationEventBody: langfuse.BaseObservationEventBody{
270276
BaseEventBody: langfuse.BaseEventBody{
271277
ID: state.observationID,
272278
},
279+
StartTime: state.startTime, // ← 必须保留原始 startTime
273280
},
274281
OutMessage: mcbo.Message,
275-
EndTime: time.Now(),
276-
CompletionStartTime: time.Now(),
282+
EndTime: endTime,
283+
CompletionStartTime: endTime,
277284
}
278285
if mcbo.TokenUsage != nil {
279286
body.Usage = &langfuse.Usage{
@@ -283,6 +290,12 @@ func (c *CallbackHandler) OnEnd(ctx context.Context, info *callbacks.RunInfo, ou
283290
}
284291
}
285292

293+
// 计算 duration(毫秒)
294+
if !state.startTime.IsZero() {
295+
duration := int64(endTime.Sub(state.startTime).Milliseconds())
296+
body.Duration = &duration
297+
}
298+
286299
err := c.cli.EndGeneration(body)
287300
if err != nil {
288301
log.Printf("end generation error: %v, runinfo: %+v", err, info)
@@ -295,15 +308,24 @@ func (c *CallbackHandler) OnEnd(ctx context.Context, info *callbacks.RunInfo, ou
295308
log.Printf("marshal output error: %v, runinfo: %+v", err, info)
296309
return ctx
297310
}
298-
err = c.cli.EndSpan(&langfuse.SpanEventBody{
311+
endTime := time.Now()
312+
spanBody := &langfuse.SpanEventBody{
299313
BaseObservationEventBody: langfuse.BaseObservationEventBody{
300314
BaseEventBody: langfuse.BaseEventBody{
301315
ID: state.observationID,
302316
},
303-
Output: out,
317+
Output: out,
318+
StartTime: state.startTime, // ← 必须保留原始 startTime
304319
},
305-
EndTime: time.Now(),
306-
})
320+
EndTime: endTime,
321+
}
322+
// 计算 duration(毫秒)
323+
if !state.startTime.IsZero() {
324+
duration := int64(endTime.Sub(state.startTime).Milliseconds())
325+
spanBody.Duration = &duration
326+
}
327+
328+
err = c.cli.EndSpan(spanBody)
307329
if err != nil {
308330
log.Printf("end span fail: %v, runinfo: %+v", err, info)
309331
}

libs/acl/langfuse/event.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,8 @@ type BaseObservationEventBody struct {
226226
type SpanEventBody struct {
227227
BaseObservationEventBody
228228

229-
EndTime time.Time `json:"endTime,omitempty"`
229+
EndTime time.Time `json:"endTime,omitempty"`
230+
Duration *int64 `json:"duration,omitempty"` // 毫秒
230231
}
231232

232233
type Usage struct {
@@ -247,6 +248,7 @@ type GenerationEventBody struct {
247248
PromptVersion int `json:"promptVersion,omitempty"`
248249
ModelParameters any `json:"modelParameters,omitempty"`
249250
Usage *Usage `json:"usage,omitempty"`
251+
Duration *int64 `json:"duration,omitempty"` // 毫秒
250252
}
251253

252254
type EventEventBody struct {

0 commit comments

Comments
 (0)