Skip to content

Commit 5f0ee72

Browse files
tpfzCoda-bot
andcommitted
fix: [Coda] restore trace reporting in EvaluatorSourceCodeServiceImpl
(LogID: 20250919124622010091110134096510C) Co-Authored-By: Coda <[email protected]>
1 parent 54dbcb0 commit 5f0ee72

File tree

2 files changed

+224
-25
lines changed

2 files changed

+224
-25
lines changed
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
// Copyright (c) 2025 coze-dev Authors
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package service
5+
6+
import (
7+
"fmt"
8+
9+
"github.com/coze-dev/coze-loop/backend/modules/evaluation/domain/component"
10+
"github.com/coze-dev/coze-loop/backend/modules/evaluation/domain/entity"
11+
)
12+
13+
// UserCodeBuilder 用户代码构建器接口
14+
type UserCodeBuilder interface {
15+
// GetLanguageType 获取支持的语言类型
16+
GetLanguageType() entity.LanguageType
17+
// BuildCode 构建可执行的代码
18+
BuildCode(input *entity.EvaluatorInputData, codeVersion *entity.CodeEvaluatorVersion) (string, error)
19+
// BuildSyntaxCheckCode 构建语法检查代码
20+
BuildSyntaxCheckCode(userCode string) string
21+
// SetRuntime 设置运行时实例
22+
SetRuntime(runtime component.IRuntime)
23+
}
24+
25+
// CodeBuilderFactory 代码构建器工厂接口
26+
type CodeBuilderFactory interface {
27+
// CreateBuilder 根据语言类型创建代码构建器
28+
CreateBuilder(languageType entity.LanguageType) (UserCodeBuilder, error)
29+
// GetSupportedLanguages 获取支持的语言类型列表
30+
GetSupportedLanguages() []entity.LanguageType
31+
// SetRuntimeManager 设置运行时管理器(用于依赖注入runtime)
32+
SetRuntimeManager(manager component.IRuntimeManager)
33+
}
34+
35+
// CodeBuilderFactoryImpl 代码构建器工厂实现
36+
type CodeBuilderFactoryImpl struct {
37+
runtimeManager component.IRuntimeManager
38+
}
39+
40+
// NewCodeBuilderFactory 创建代码构建器工厂实例
41+
func NewCodeBuilderFactory() CodeBuilderFactory {
42+
return &CodeBuilderFactoryImpl{}
43+
}
44+
45+
// SetRuntimeManager 设置运行时管理器
46+
func (f *CodeBuilderFactoryImpl) SetRuntimeManager(manager component.IRuntimeManager) {
47+
f.runtimeManager = manager
48+
}
49+
50+
// CreateBuilder 根据语言类型创建代码构建器
51+
func (f *CodeBuilderFactoryImpl) CreateBuilder(languageType entity.LanguageType) (UserCodeBuilder, error) {
52+
var builder UserCodeBuilder
53+
54+
switch languageType {
55+
case entity.LanguageTypeJS:
56+
builder = NewJavaScriptCodeBuilder()
57+
case entity.LanguageTypePython:
58+
builder = NewPythonCodeBuilder()
59+
default:
60+
return nil, fmt.Errorf("unsupported language type: %s", languageType)
61+
}
62+
63+
// 如果有运行时管理器,为构建器注入相应的runtime
64+
if f.runtimeManager != nil {
65+
runtime, err := f.runtimeManager.GetRuntime(languageType)
66+
if err == nil {
67+
builder.SetRuntime(runtime)
68+
}
69+
}
70+
71+
return builder, nil
72+
}
73+
74+
// GetSupportedLanguages 获取支持的语言类型列表
75+
func (f *CodeBuilderFactoryImpl) GetSupportedLanguages() []entity.LanguageType {
76+
return []entity.LanguageType{
77+
entity.LanguageTypeJS,
78+
entity.LanguageTypePython,
79+
}
80+
}

backend/modules/evaluation/domain/service/evaluator_source_code_impl.go

Lines changed: 144 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,12 @@ import (
1212
"strings"
1313
"time"
1414

15+
"github.com/coze-dev/coze-loop/backend/infra/looptracer"
16+
"github.com/coze-dev/coze-loop/backend/infra/middleware/session"
1517
"github.com/coze-dev/coze-loop/backend/modules/evaluation/domain/component"
1618
"github.com/coze-dev/coze-loop/backend/modules/evaluation/domain/component/metrics"
1719
"github.com/coze-dev/coze-loop/backend/modules/evaluation/domain/entity"
20+
"github.com/coze-dev/coze-loop/backend/modules/evaluation/infra/tracer"
1821
"github.com/coze-dev/coze-loop/backend/modules/evaluation/pkg/errno"
1922
"github.com/coze-dev/coze-loop/backend/pkg/errorx"
2023
"github.com/coze-dev/coze-loop/backend/pkg/logs"
@@ -50,12 +53,16 @@ func (c *EvaluatorSourceCodeServiceImpl) Run(ctx context.Context, evaluator *ent
5053
var err error
5154
var code string
5255
startTime := time.Now()
53-
// 直接创建一个简单的span,避免依赖evaluator_source_prompt_impl.go中的函数
54-
rootSpan := &evaluatorSpan{}
55-
traceID = "code-evaluator-trace"
56+
// 创建trace span
57+
rootSpan, ctx := c.newEvaluatorSpan(ctx, evaluator.Name, "LoopEvaluation", strconv.FormatInt(evaluator.SpaceID, 10), false)
58+
traceID = rootSpan.GetTraceID()
5659

5760
defer func() {
58-
c.handleRunDefer(ctx, rootSpan, &output, &err, input, evaluator, code, runStatus)
61+
var errInfo error
62+
if err != nil {
63+
errInfo = err
64+
}
65+
c.handleRunDefer(ctx, rootSpan, &output, &errInfo, input, evaluator, code, runStatus)
5966
}()
6067

6168
// 1. 验证评估器
@@ -77,37 +84,41 @@ func (c *EvaluatorSourceCodeServiceImpl) Run(ctx context.Context, evaluator *ent
7784
}
7885

7986
// handleRunDefer 处理Run方法的defer逻辑
80-
func (c *EvaluatorSourceCodeServiceImpl) handleRunDefer(ctx context.Context, rootSpan *evaluatorSpan, output **entity.EvaluatorOutputData, err *error, input *entity.EvaluatorInputData, evaluator *entity.Evaluator, code string, runStatus entity.EvaluatorRunStatus) {
87+
func (c *EvaluatorSourceCodeServiceImpl) handleRunDefer(ctx context.Context, rootSpan *evaluatorSpan, output **entity.EvaluatorOutputData, errInfo *error, input *entity.EvaluatorInputData, evaluator *entity.Evaluator, code string, runStatus entity.EvaluatorRunStatus) {
8188
if *output == nil {
8289
*output = &entity.EvaluatorOutputData{
8390
EvaluatorRunError: &entity.EvaluatorRunError{},
8491
}
8592
}
8693

87-
if *err != nil {
94+
if *errInfo != nil {
8895
// 处理错误信息
8996
if (*output).EvaluatorRunError == nil {
9097
(*output).EvaluatorRunError = &entity.EvaluatorRunError{}
9198
}
92-
statusErr, ok := errorx.FromStatusError(*err)
99+
statusErr, ok := errorx.FromStatusError(*errInfo)
93100
if ok {
94101
(*output).EvaluatorRunError.Code = statusErr.Code()
95102
(*output).EvaluatorRunError.Message = statusErr.Error()
96103
} else {
97104
(*output).EvaluatorRunError.Code = errno.CodeExecutionFailedCode
98-
(*output).EvaluatorRunError.Message = (*err).Error()
105+
(*output).EvaluatorRunError.Message = (*errInfo).Error()
99106
}
100107
}
101108

102-
// 上报trace - 暂时跳过trace上报,避免复杂的依赖
103-
// rootSpan.reportCodeRootSpan(ctx, &ReportCodeRootSpanRequest{
104-
// input: input,
105-
// output: *output,
106-
// runStatus: runStatus,
107-
// evaluatorVersion: evaluator.CodeEvaluatorVersion,
108-
// errInfo: errInfo,
109-
// code: code, // 构建后的完整代码
110-
// })
109+
// 上报trace
110+
var finalErr error
111+
if errInfo != nil {
112+
finalErr = *errInfo
113+
}
114+
rootSpan.reportCodeRootSpan(ctx, &ReportCodeRootSpanRequest{
115+
input: input,
116+
output: *output,
117+
runStatus: runStatus,
118+
evaluatorVersion: evaluator.CodeEvaluatorVersion,
119+
errInfo: finalErr,
120+
code: code, // 构建后的完整代码
121+
})
111122
}
112123

113124
// validateEvaluator 验证评估器类型和版本
@@ -717,15 +728,25 @@ func (c *EvaluatorSourceCodeServiceImpl) validatePythonCode(ctx context.Context,
717728
// 构建Python语法检查代码,参考pyodide客户端的AST验证方式
718729
syntaxCheckCode := c.buildPythonSyntaxCheckCode(codeVersion.CodeContent)
719730

731+
var result *entity.ExecutionResult
732+
var valid bool
733+
var errorMsg string
734+
735+
// 创建语法检查的span
736+
syntaxCheckSpan, syntaxCtx := c.newEvaluatorSpan(ctx, "PythonSyntaxCheck", "LoopEvaluation", strconv.FormatInt(evaluator.SpaceID, 10), true)
737+
defer func() {
738+
c.reportSyntaxCheckSpan(syntaxCtx, syntaxCheckSpan, "python", codeVersion.CodeContent, syntaxCheckCode, result, valid, errorMsg, err)
739+
}()
740+
720741
// 使用runtime执行语法检查,设置较短的超时时间
721742
ext := c.buildExtParams(evaluator)
722-
result, err := runtime.RunCode(ctx, syntaxCheckCode, "python", 10000, ext) // 10秒超时用于语法验证
743+
result, err = runtime.RunCode(syntaxCtx, syntaxCheckCode, "python", 10000, ext) // 10秒超时用于语法验证
723744
if err != nil {
724745
return fmt.Errorf("python syntax validation failed: %w", err)
725746
}
726747

727748
// 处理执行结果并解析stdout中的JSON
728-
valid, errorMsg, err := c.processSyntaxValidationExecutionResult(result)
749+
valid, errorMsg, err = c.processSyntaxValidationExecutionResult(result)
729750
if err != nil {
730751
return fmt.Errorf("failed to process syntax validation result: %w", err)
731752
}
@@ -759,15 +780,25 @@ func (c *EvaluatorSourceCodeServiceImpl) validateJavaScriptCode(ctx context.Cont
759780
// 构建JavaScript语法检查代码 (使用Builder模式)
760781
syntaxCheckCode := c.buildJavaScriptSyntaxCheckCode(codeVersion.CodeContent)
761782

783+
var result *entity.ExecutionResult
784+
var valid bool
785+
var errorMsg string
786+
787+
// 创建语法检查的span
788+
syntaxCheckSpan, syntaxCtx := c.newEvaluatorSpan(ctx, "JavaScriptSyntaxCheck", "LoopEvaluation", strconv.FormatInt(evaluator.SpaceID, 10), true)
789+
defer func() {
790+
c.reportSyntaxCheckSpan(syntaxCtx, syntaxCheckSpan, "javascript", codeVersion.CodeContent, syntaxCheckCode, result, valid, errorMsg, err)
791+
}()
792+
762793
// 使用runtime执行语法检查,设置较短的超时时间
763794
ext := c.buildExtParams(evaluator)
764-
result, err := runtime.RunCode(ctx, syntaxCheckCode, "js", 10000, ext) // 与Python保持一致的10秒超时
795+
result, err = runtime.RunCode(syntaxCtx, syntaxCheckCode, "js", 10000, ext) // 与Python保持一致的10秒超时
765796
if err != nil {
766797
return fmt.Errorf("javascript syntax validation failed: %w", err)
767798
}
768799

769800
// 使用统一的结果处理方法 (与Python保持一致)
770-
valid, errorMsg, err := c.processSyntaxValidationExecutionResult(result)
801+
valid, errorMsg, err = c.processSyntaxValidationExecutionResult(result)
771802
if err != nil {
772803
return fmt.Errorf("failed to process syntax validation result: %w", err)
773804
}
@@ -1125,8 +1156,9 @@ func (c *EvaluatorSourceCodeServiceImpl) validateJavaScriptExecEvaluationFunctio
11251156

11261157

11271158

1128-
// evaluatorSpan 简化的span结构
1159+
// evaluatorSpan 评估器span包装器
11291160
type evaluatorSpan struct {
1161+
looptracer.Span
11301162
}
11311163

11321164
// ReportCodeRootSpanRequest Code评估器专用的上报请求结构
@@ -1139,8 +1171,95 @@ type ReportCodeRootSpanRequest struct {
11391171
code string // 评估器代码内容
11401172
}
11411173

1142-
// reportCodeRootSpan 上报Code评估器的根节点trace - 简化实现
1174+
// reportCodeRootSpan 上报Code评估器的根节点trace
11431175
func (e *evaluatorSpan) reportCodeRootSpan(ctx context.Context, request *ReportCodeRootSpanRequest) {
1144-
// 暂时跳过实际的trace上报,只做日志记录
1145-
logs.CtxInfo(ctx, "Code evaluator execution completed, status: %v", request.runStatus)
1176+
e.SetInput(ctx, tracer.Convert2TraceString(request.input))
1177+
if request.output != nil {
1178+
e.SetOutput(ctx, tracer.Convert2TraceString(request.output.EvaluatorResult))
1179+
}
1180+
switch request.runStatus {
1181+
case entity.EvaluatorRunStatusSuccess:
1182+
e.SetStatusCode(ctx, 0)
1183+
case entity.EvaluatorRunStatusFail:
1184+
e.SetStatusCode(ctx, int(entity.EvaluatorRunStatusFail))
1185+
e.SetError(ctx, request.errInfo)
1186+
default:
1187+
e.SetStatusCode(ctx, 0) // 默认为成功
1188+
}
1189+
tags := make(map[string]interface{}, 0)
1190+
tags["evaluator_id"] = request.evaluatorVersion.EvaluatorID
1191+
tags["evaluator_version"] = request.evaluatorVersion.Version
1192+
tags["code_content"] = request.code // 添加代码内容到trace
1193+
e.SetCallType("Evaluator")
1194+
userIDInContext := session.UserIDInCtxOrEmpty(ctx)
1195+
if userIDInContext != "" {
1196+
e.SetUserID(ctx, userIDInContext)
1197+
}
1198+
e.SetTags(ctx, tags)
1199+
e.Finish(ctx)
1200+
}
1201+
1202+
// newEvaluatorSpan 创建评估器span
1203+
func (c *EvaluatorSourceCodeServiceImpl) newEvaluatorSpan(ctx context.Context, spanName, spanType, spaceID string, asyncChild bool) (*evaluatorSpan, context.Context) {
1204+
var evalSpan looptracer.Span
1205+
var nctx context.Context
1206+
if asyncChild {
1207+
nctx, evalSpan = looptracer.GetTracer().StartSpan(ctx, spanName, spanType, looptracer.WithSpanWorkspaceID(spaceID))
1208+
} else {
1209+
nctx, evalSpan = looptracer.GetTracer().StartSpan(ctx, spanName, spanType, looptracer.WithStartNewTrace(), looptracer.WithSpanWorkspaceID(spaceID))
1210+
}
1211+
1212+
return &evaluatorSpan{
1213+
Span: evalSpan,
1214+
}, nctx
1215+
}
1216+
1217+
// reportSyntaxCheckSpan 上报语法检查的trace信息
1218+
func (c *EvaluatorSourceCodeServiceImpl) reportSyntaxCheckSpan(ctx context.Context, span *evaluatorSpan, language, userCode, syntaxCheckCode string, result *entity.ExecutionResult, valid bool, errorMsg string, err error) {
1219+
// 设置输入:用户代码
1220+
span.SetInput(ctx, tracer.Convert2TraceString(map[string]interface{}{
1221+
"user_code": userCode,
1222+
"language": language,
1223+
}))
1224+
1225+
// 设置输出:语法检查结果
1226+
output := map[string]interface{}{
1227+
"valid": valid,
1228+
}
1229+
if errorMsg != "" {
1230+
output["error"] = errorMsg
1231+
}
1232+
if result != nil && result.Output != nil {
1233+
output["stdout"] = result.Output.Stdout
1234+
output["stderr"] = result.Output.Stderr
1235+
output["ret_val"] = result.Output.RetVal
1236+
}
1237+
span.SetOutput(ctx, tracer.Convert2TraceString(output))
1238+
1239+
// 设置状态码
1240+
if err != nil {
1241+
span.SetStatusCode(ctx, errno.CodeExecutionFailedCode)
1242+
span.SetError(ctx, err)
1243+
} else if !valid {
1244+
span.SetStatusCode(ctx, errno.InvalidInputDataCode)
1245+
span.SetError(ctx, fmt.Errorf("syntax validation failed: %s", errorMsg))
1246+
} else {
1247+
span.SetStatusCode(ctx, 0)
1248+
}
1249+
1250+
// 设置标签
1251+
tags := map[string]interface{}{
1252+
"language": language,
1253+
"syntax_check_code": syntaxCheckCode,
1254+
"validation_result": valid,
1255+
}
1256+
span.SetTags(ctx, tags)
1257+
span.SetCallType("Evaluator")
1258+
1259+
userIDInContext := session.UserIDInCtxOrEmpty(ctx)
1260+
if userIDInContext != "" {
1261+
span.SetUserID(ctx, userIDInContext)
1262+
}
1263+
1264+
span.Finish(ctx)
11461265
}

0 commit comments

Comments
 (0)