package eino import ( "context" "fmt" "github.com/cloudwego/eino/callbacks" "github.com/cloudwego/eino/components/model" "github.com/cloudwego/eino/components/tool" "github.com/cloudwego/eino/schema" ucallbacks "github.com/cloudwego/eino/utils/callbacks" ) // composeTracer 把 Eino compose 运行时的回调(ChatModel / Tool 的 start/end/error) // 翻译成我们现有的 ExecEvent 轨迹——可观测"归一":用框架原生回调,而非各处手写 emit。 // node 为该次运行在"运行·观测"里的归属节点 id(如 agent:xxx)。 func composeTracer(tr *execTracer, node string) callbacks.Handler { return ucallbacks.NewHandlerHelper(). ChatModel(&ucallbacks.ModelCallbackHandler{ OnStart: func(ctx context.Context, _ *callbacks.RunInfo, in *model.CallbackInput) context.Context { tr.emit(node, "model", "start", "compose·ChatModel", inputMsgsPreview(in), 0) return ctx }, OnEnd: func(ctx context.Context, _ *callbacks.RunInfo, out *model.CallbackOutput) context.Context { detail := "" if out != nil && out.Message != nil { detail = truncate(out.Message.Content, 120) } tr.emit(node, "model", "end", "compose·ChatModel", detail, 0) return ctx }, OnEndWithStreamOutput: func(ctx context.Context, _ *callbacks.RunInfo, out *schema.StreamReader[*model.CallbackOutput]) context.Context { out.Close() // 仅观测:正文经主输出流消费,这里只标记结束 tr.emit(node, "model", "end", "compose·ChatModel", "流式完成", 0) return ctx }, OnError: func(ctx context.Context, _ *callbacks.RunInfo, err error) context.Context { tr.emit(node, "model", "error", "compose·ChatModel", err.Error(), 0) return ctx }, }). Tool(&ucallbacks.ToolCallbackHandler{ OnStart: func(ctx context.Context, _ *callbacks.RunInfo, in *tool.CallbackInput) context.Context { if in != nil { tr.emit(node, "tool", "start", "compose·工具", truncate(in.ArgumentsInJSON, 120), 0) } return ctx }, OnEnd: func(ctx context.Context, _ *callbacks.RunInfo, out *tool.CallbackOutput) context.Context { if out != nil { tr.emit(node, "tool", "end", "compose·工具", truncate(out.Response, 160), 0) } return ctx }, OnError: func(ctx context.Context, _ *callbacks.RunInfo, err error) context.Context { tr.emit(node, "tool", "error", "compose·工具", err.Error(), 0) return ctx }, }). Handler() } func inputMsgsPreview(in *model.CallbackInput) string { if in == nil { return "" } return fmt.Sprintf("%d 条消息", len(in.Messages)) }