diff --git a/.gitignore b/.gitignore index 0f5fa2d27..2213eedc1 100644 --- a/.gitignore +++ b/.gitignore @@ -51,6 +51,7 @@ release/deployment/helm-chart/umbrella/Chart.lock **/kitex_remote_config.json .coda/ +.coco backend/script/errorx/.env .cursor/ AGENTS.md \ No newline at end of file diff --git a/backend/infra/platestwrite/latest_write_tracker.go b/backend/infra/platestwrite/latest_write_tracker.go index dd4707b02..24e1df987 100644 --- a/backend/infra/platestwrite/latest_write_tracker.go +++ b/backend/infra/platestwrite/latest_write_tracker.go @@ -136,4 +136,7 @@ const ( ResourceTypeTarget ResourceType = "eval_target" ResourceTypeTargetVersion ResourceType = "eval_target_version" ResourceTypeEvaluator ResourceType = "evaluator" + + ResourceTypeExptInsightAnalysisRecord ResourceType = "expt_insight_analysis_record" + ResourceTypeExptInsightAnalysisFeedback ResourceType = "expt_insight_analysis_feedback" ) diff --git a/backend/kitex_gen/coze/loop/evaluation/domain/expt/expt.go b/backend/kitex_gen/coze/loop/evaluation/domain/expt/expt.go index df0f04ae3..339a7bec8 100644 --- a/backend/kitex_gen/coze/loop/evaluation/domain/expt/expt.go +++ b/backend/kitex_gen/coze/loop/evaluation/domain/expt/expt.go @@ -17394,6 +17394,7 @@ type ExptInsightAnalysisRecord struct { AnalysisReportContent *string `thrift:"analysis_report_content,6,optional" frugal:"6,optional,string" form:"analysis_report_content" json:"analysis_report_content,omitempty" query:"analysis_report_content"` ExptInsightAnalysisFeedback *ExptInsightAnalysisFeedback `thrift:"expt_insight_analysis_feedback,7,optional" frugal:"7,optional,ExptInsightAnalysisFeedback" form:"expt_insight_analysis_feedback" json:"expt_insight_analysis_feedback,omitempty" query:"expt_insight_analysis_feedback"` BaseInfo *common.BaseInfo `thrift:"base_info,8,optional" frugal:"8,optional,common.BaseInfo" form:"base_info" json:"base_info,omitempty" query:"base_info"` + AnalysisReportIndex []*ExptInsightAnalysisIndex `thrift:"analysis_report_index,21,optional" frugal:"21,optional,list" form:"analysis_report_index" json:"analysis_report_index,omitempty" query:"analysis_report_index"` } func NewExptInsightAnalysisRecord() *ExptInsightAnalysisRecord { @@ -17478,6 +17479,18 @@ func (p *ExptInsightAnalysisRecord) GetBaseInfo() (v *common.BaseInfo) { } return p.BaseInfo } + +var ExptInsightAnalysisRecord_AnalysisReportIndex_DEFAULT []*ExptInsightAnalysisIndex + +func (p *ExptInsightAnalysisRecord) GetAnalysisReportIndex() (v []*ExptInsightAnalysisIndex) { + if p == nil { + return + } + if !p.IsSetAnalysisReportIndex() { + return ExptInsightAnalysisRecord_AnalysisReportIndex_DEFAULT + } + return p.AnalysisReportIndex +} func (p *ExptInsightAnalysisRecord) SetRecordID(val int64) { p.RecordID = val } @@ -17502,16 +17515,20 @@ func (p *ExptInsightAnalysisRecord) SetExptInsightAnalysisFeedback(val *ExptInsi func (p *ExptInsightAnalysisRecord) SetBaseInfo(val *common.BaseInfo) { p.BaseInfo = val } +func (p *ExptInsightAnalysisRecord) SetAnalysisReportIndex(val []*ExptInsightAnalysisIndex) { + p.AnalysisReportIndex = val +} var fieldIDToName_ExptInsightAnalysisRecord = map[int16]string{ - 1: "record_id", - 2: "workspace_id", - 3: "expt_id", - 4: "analysis_status", - 5: "analysis_report_id", - 6: "analysis_report_content", - 7: "expt_insight_analysis_feedback", - 8: "base_info", + 1: "record_id", + 2: "workspace_id", + 3: "expt_id", + 4: "analysis_status", + 5: "analysis_report_id", + 6: "analysis_report_content", + 7: "expt_insight_analysis_feedback", + 8: "base_info", + 21: "analysis_report_index", } func (p *ExptInsightAnalysisRecord) IsSetAnalysisReportID() bool { @@ -17530,6 +17547,10 @@ func (p *ExptInsightAnalysisRecord) IsSetBaseInfo() bool { return p.BaseInfo != nil } +func (p *ExptInsightAnalysisRecord) IsSetAnalysisReportIndex() bool { + return p.AnalysisReportIndex != nil +} + func (p *ExptInsightAnalysisRecord) Read(iprot thrift.TProtocol) (err error) { var fieldTypeId thrift.TType var fieldId int16 @@ -17620,6 +17641,14 @@ func (p *ExptInsightAnalysisRecord) Read(iprot thrift.TProtocol) (err error) { } else if err = iprot.Skip(fieldTypeId); err != nil { goto SkipFieldError } + case 21: + if fieldTypeId == thrift.LIST { + if err = p.ReadField21(iprot); err != nil { + goto ReadFieldError + } + } else if err = iprot.Skip(fieldTypeId); err != nil { + goto SkipFieldError + } default: if err = iprot.Skip(fieldTypeId); err != nil { goto SkipFieldError @@ -17752,6 +17781,29 @@ func (p *ExptInsightAnalysisRecord) ReadField8(iprot thrift.TProtocol) error { p.BaseInfo = _field return nil } +func (p *ExptInsightAnalysisRecord) ReadField21(iprot thrift.TProtocol) error { + _, size, err := iprot.ReadListBegin() + if err != nil { + return err + } + _field := make([]*ExptInsightAnalysisIndex, 0, size) + values := make([]ExptInsightAnalysisIndex, size) + for i := 0; i < size; i++ { + _elem := &values[i] + _elem.InitDefault() + + if err := _elem.Read(iprot); err != nil { + return err + } + + _field = append(_field, _elem) + } + if err := iprot.ReadListEnd(); err != nil { + return err + } + p.AnalysisReportIndex = _field + return nil +} func (p *ExptInsightAnalysisRecord) Write(oprot thrift.TProtocol) (err error) { var fieldId int16 @@ -17791,6 +17843,10 @@ func (p *ExptInsightAnalysisRecord) Write(oprot thrift.TProtocol) (err error) { fieldId = 8 goto WriteFieldError } + if err = p.writeField21(oprot); err != nil { + fieldId = 21 + goto WriteFieldError + } } if err = oprot.WriteFieldStop(); err != nil { goto WriteFieldStopError @@ -17945,6 +18001,32 @@ WriteFieldBeginError: WriteFieldEndError: return thrift.PrependError(fmt.Sprintf("%T write field 8 end error: ", p), err) } +func (p *ExptInsightAnalysisRecord) writeField21(oprot thrift.TProtocol) (err error) { + if p.IsSetAnalysisReportIndex() { + if err = oprot.WriteFieldBegin("analysis_report_index", thrift.LIST, 21); err != nil { + goto WriteFieldBeginError + } + if err := oprot.WriteListBegin(thrift.STRUCT, len(p.AnalysisReportIndex)); err != nil { + return err + } + for _, v := range p.AnalysisReportIndex { + if err := v.Write(oprot); err != nil { + return err + } + } + if err := oprot.WriteListEnd(); err != nil { + return err + } + if err = oprot.WriteFieldEnd(); err != nil { + goto WriteFieldEndError + } + } + return nil +WriteFieldBeginError: + return thrift.PrependError(fmt.Sprintf("%T write field 21 begin error: ", p), err) +WriteFieldEndError: + return thrift.PrependError(fmt.Sprintf("%T write field 21 end error: ", p), err) +} func (p *ExptInsightAnalysisRecord) String() string { if p == nil { @@ -17984,6 +18066,9 @@ func (p *ExptInsightAnalysisRecord) DeepEqual(ano *ExptInsightAnalysisRecord) bo if !p.Field8DeepEqual(ano.BaseInfo) { return false } + if !p.Field21DeepEqual(ano.AnalysisReportIndex) { + return false + } return true } @@ -18053,6 +18138,277 @@ func (p *ExptInsightAnalysisRecord) Field8DeepEqual(src *common.BaseInfo) bool { } return true } +func (p *ExptInsightAnalysisRecord) Field21DeepEqual(src []*ExptInsightAnalysisIndex) bool { + + if len(p.AnalysisReportIndex) != len(src) { + return false + } + for i, v := range p.AnalysisReportIndex { + _src := src[i] + if !v.DeepEqual(_src) { + return false + } + } + return true +} + +type ExptInsightAnalysisIndex struct { + ID *string `thrift:"id,1,optional" frugal:"1,optional,string" form:"id" json:"id,omitempty" query:"id"` + Title *string `thrift:"title,2,optional" frugal:"2,optional,string" form:"title" json:"title,omitempty" query:"title"` +} + +func NewExptInsightAnalysisIndex() *ExptInsightAnalysisIndex { + return &ExptInsightAnalysisIndex{} +} + +func (p *ExptInsightAnalysisIndex) InitDefault() { +} + +var ExptInsightAnalysisIndex_ID_DEFAULT string + +func (p *ExptInsightAnalysisIndex) GetID() (v string) { + if p == nil { + return + } + if !p.IsSetID() { + return ExptInsightAnalysisIndex_ID_DEFAULT + } + return *p.ID +} + +var ExptInsightAnalysisIndex_Title_DEFAULT string + +func (p *ExptInsightAnalysisIndex) GetTitle() (v string) { + if p == nil { + return + } + if !p.IsSetTitle() { + return ExptInsightAnalysisIndex_Title_DEFAULT + } + return *p.Title +} +func (p *ExptInsightAnalysisIndex) SetID(val *string) { + p.ID = val +} +func (p *ExptInsightAnalysisIndex) SetTitle(val *string) { + p.Title = val +} + +var fieldIDToName_ExptInsightAnalysisIndex = map[int16]string{ + 1: "id", + 2: "title", +} + +func (p *ExptInsightAnalysisIndex) IsSetID() bool { + return p.ID != nil +} + +func (p *ExptInsightAnalysisIndex) IsSetTitle() bool { + return p.Title != nil +} + +func (p *ExptInsightAnalysisIndex) Read(iprot thrift.TProtocol) (err error) { + var fieldTypeId thrift.TType + var fieldId int16 + + if _, err = iprot.ReadStructBegin(); err != nil { + goto ReadStructBeginError + } + + for { + _, fieldTypeId, fieldId, err = iprot.ReadFieldBegin() + if err != nil { + goto ReadFieldBeginError + } + if fieldTypeId == thrift.STOP { + break + } + + switch fieldId { + case 1: + if fieldTypeId == thrift.STRING { + if err = p.ReadField1(iprot); err != nil { + goto ReadFieldError + } + } else if err = iprot.Skip(fieldTypeId); err != nil { + goto SkipFieldError + } + case 2: + if fieldTypeId == thrift.STRING { + if err = p.ReadField2(iprot); err != nil { + goto ReadFieldError + } + } else if err = iprot.Skip(fieldTypeId); err != nil { + goto SkipFieldError + } + default: + if err = iprot.Skip(fieldTypeId); err != nil { + goto SkipFieldError + } + } + if err = iprot.ReadFieldEnd(); err != nil { + goto ReadFieldEndError + } + } + if err = iprot.ReadStructEnd(); err != nil { + goto ReadStructEndError + } + + return nil +ReadStructBeginError: + return thrift.PrependError(fmt.Sprintf("%T read struct begin error: ", p), err) +ReadFieldBeginError: + return thrift.PrependError(fmt.Sprintf("%T read field %d begin error: ", p, fieldId), err) +ReadFieldError: + return thrift.PrependError(fmt.Sprintf("%T read field %d '%s' error: ", p, fieldId, fieldIDToName_ExptInsightAnalysisIndex[fieldId]), err) +SkipFieldError: + return thrift.PrependError(fmt.Sprintf("%T field %d skip type %d error: ", p, fieldId, fieldTypeId), err) + +ReadFieldEndError: + return thrift.PrependError(fmt.Sprintf("%T read field end error", p), err) +ReadStructEndError: + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) +} + +func (p *ExptInsightAnalysisIndex) ReadField1(iprot thrift.TProtocol) error { + + var _field *string + if v, err := iprot.ReadString(); err != nil { + return err + } else { + _field = &v + } + p.ID = _field + return nil +} +func (p *ExptInsightAnalysisIndex) ReadField2(iprot thrift.TProtocol) error { + + var _field *string + if v, err := iprot.ReadString(); err != nil { + return err + } else { + _field = &v + } + p.Title = _field + return nil +} + +func (p *ExptInsightAnalysisIndex) Write(oprot thrift.TProtocol) (err error) { + var fieldId int16 + if err = oprot.WriteStructBegin("ExptInsightAnalysisIndex"); err != nil { + goto WriteStructBeginError + } + if p != nil { + if err = p.writeField1(oprot); err != nil { + fieldId = 1 + goto WriteFieldError + } + if err = p.writeField2(oprot); err != nil { + fieldId = 2 + goto WriteFieldError + } + } + if err = oprot.WriteFieldStop(); err != nil { + goto WriteFieldStopError + } + if err = oprot.WriteStructEnd(); err != nil { + goto WriteStructEndError + } + return nil +WriteStructBeginError: + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) +WriteFieldError: + return thrift.PrependError(fmt.Sprintf("%T write field %d error: ", p, fieldId), err) +WriteFieldStopError: + return thrift.PrependError(fmt.Sprintf("%T write field stop error: ", p), err) +WriteStructEndError: + return thrift.PrependError(fmt.Sprintf("%T write struct end error: ", p), err) +} + +func (p *ExptInsightAnalysisIndex) writeField1(oprot thrift.TProtocol) (err error) { + if p.IsSetID() { + if err = oprot.WriteFieldBegin("id", thrift.STRING, 1); err != nil { + goto WriteFieldBeginError + } + if err := oprot.WriteString(*p.ID); err != nil { + return err + } + if err = oprot.WriteFieldEnd(); err != nil { + goto WriteFieldEndError + } + } + return nil +WriteFieldBeginError: + return thrift.PrependError(fmt.Sprintf("%T write field 1 begin error: ", p), err) +WriteFieldEndError: + return thrift.PrependError(fmt.Sprintf("%T write field 1 end error: ", p), err) +} +func (p *ExptInsightAnalysisIndex) writeField2(oprot thrift.TProtocol) (err error) { + if p.IsSetTitle() { + if err = oprot.WriteFieldBegin("title", thrift.STRING, 2); err != nil { + goto WriteFieldBeginError + } + if err := oprot.WriteString(*p.Title); err != nil { + return err + } + if err = oprot.WriteFieldEnd(); err != nil { + goto WriteFieldEndError + } + } + return nil +WriteFieldBeginError: + return thrift.PrependError(fmt.Sprintf("%T write field 2 begin error: ", p), err) +WriteFieldEndError: + return thrift.PrependError(fmt.Sprintf("%T write field 2 end error: ", p), err) +} + +func (p *ExptInsightAnalysisIndex) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("ExptInsightAnalysisIndex(%+v)", *p) + +} + +func (p *ExptInsightAnalysisIndex) DeepEqual(ano *ExptInsightAnalysisIndex) bool { + if p == ano { + return true + } else if p == nil || ano == nil { + return false + } + if !p.Field1DeepEqual(ano.ID) { + return false + } + if !p.Field2DeepEqual(ano.Title) { + return false + } + return true +} + +func (p *ExptInsightAnalysisIndex) Field1DeepEqual(src *string) bool { + + if p.ID == src { + return true + } else if p.ID == nil || src == nil { + return false + } + if strings.Compare(*p.ID, *src) != 0 { + return false + } + return true +} +func (p *ExptInsightAnalysisIndex) Field2DeepEqual(src *string) bool { + + if p.Title == src { + return true + } else if p.Title == nil || src == nil { + return false + } + if strings.Compare(*p.Title, *src) != 0 { + return false + } + return true +} // 洞察分析反馈统计 type ExptInsightAnalysisFeedback struct { diff --git a/backend/kitex_gen/coze/loop/evaluation/domain/expt/k-expt.go b/backend/kitex_gen/coze/loop/evaluation/domain/expt/k-expt.go index 0c8040458..902deae0d 100644 --- a/backend/kitex_gen/coze/loop/evaluation/domain/expt/k-expt.go +++ b/backend/kitex_gen/coze/loop/evaluation/domain/expt/k-expt.go @@ -12120,6 +12120,20 @@ func (p *ExptInsightAnalysisRecord) FastRead(buf []byte) (int, error) { goto SkipFieldError } } + case 21: + if fieldTypeId == thrift.LIST { + l, err = p.FastReadField21(buf[offset:]) + offset += l + if err != nil { + goto ReadFieldError + } + } else { + l, err = thrift.Binary.Skip(buf[offset:], fieldTypeId) + offset += l + if err != nil { + goto SkipFieldError + } + } default: l, err = thrift.Binary.Skip(buf[offset:], fieldTypeId) offset += l @@ -12267,6 +12281,31 @@ func (p *ExptInsightAnalysisRecord) FastReadField8(buf []byte) (int, error) { return offset, nil } +func (p *ExptInsightAnalysisRecord) FastReadField21(buf []byte) (int, error) { + offset := 0 + + _, size, l, err := thrift.Binary.ReadListBegin(buf[offset:]) + offset += l + if err != nil { + return offset, err + } + _field := make([]*ExptInsightAnalysisIndex, 0, size) + values := make([]ExptInsightAnalysisIndex, size) + for i := 0; i < size; i++ { + _elem := &values[i] + _elem.InitDefault() + if l, err := _elem.FastRead(buf[offset:]); err != nil { + return offset, err + } else { + offset += l + } + + _field = append(_field, _elem) + } + p.AnalysisReportIndex = _field + return offset, nil +} + func (p *ExptInsightAnalysisRecord) FastWrite(buf []byte) int { return p.FastWriteNocopy(buf, nil) } @@ -12282,6 +12321,7 @@ func (p *ExptInsightAnalysisRecord) FastWriteNocopy(buf []byte, w thrift.NocopyW offset += p.fastWriteField6(buf[offset:], w) offset += p.fastWriteField7(buf[offset:], w) offset += p.fastWriteField8(buf[offset:], w) + offset += p.fastWriteField21(buf[offset:], w) } offset += thrift.Binary.WriteFieldStop(buf[offset:]) return offset @@ -12298,6 +12338,7 @@ func (p *ExptInsightAnalysisRecord) BLength() int { l += p.field6Length() l += p.field7Length() l += p.field8Length() + l += p.field21Length() } l += thrift.Binary.FieldStopLength() return l @@ -12367,6 +12408,22 @@ func (p *ExptInsightAnalysisRecord) fastWriteField8(buf []byte, w thrift.NocopyW return offset } +func (p *ExptInsightAnalysisRecord) fastWriteField21(buf []byte, w thrift.NocopyWriter) int { + offset := 0 + if p.IsSetAnalysisReportIndex() { + offset += thrift.Binary.WriteFieldBegin(buf[offset:], thrift.LIST, 21) + listBeginOffset := offset + offset += thrift.Binary.ListBeginLength() + var length int + for _, v := range p.AnalysisReportIndex { + length++ + offset += v.FastWriteNocopy(buf[offset:], w) + } + thrift.Binary.WriteListBegin(buf[listBeginOffset:], thrift.STRUCT, length) + } + return offset +} + func (p *ExptInsightAnalysisRecord) field1Length() int { l := 0 l += thrift.Binary.FieldBeginLength() @@ -12431,6 +12488,19 @@ func (p *ExptInsightAnalysisRecord) field8Length() int { return l } +func (p *ExptInsightAnalysisRecord) field21Length() int { + l := 0 + if p.IsSetAnalysisReportIndex() { + l += thrift.Binary.FieldBeginLength() + l += thrift.Binary.ListBeginLength() + for _, v := range p.AnalysisReportIndex { + _ = v + l += v.BLength() + } + } + return l +} + func (p *ExptInsightAnalysisRecord) DeepCopy(s interface{}) error { src, ok := s.(*ExptInsightAnalysisRecord) if !ok { @@ -12476,6 +12546,197 @@ func (p *ExptInsightAnalysisRecord) DeepCopy(s interface{}) error { } p.BaseInfo = _baseInfo + if src.AnalysisReportIndex != nil { + p.AnalysisReportIndex = make([]*ExptInsightAnalysisIndex, 0, len(src.AnalysisReportIndex)) + for _, elem := range src.AnalysisReportIndex { + var _elem *ExptInsightAnalysisIndex + if elem != nil { + _elem = &ExptInsightAnalysisIndex{} + if err := _elem.DeepCopy(elem); err != nil { + return err + } + } + + p.AnalysisReportIndex = append(p.AnalysisReportIndex, _elem) + } + } + + return nil +} + +func (p *ExptInsightAnalysisIndex) FastRead(buf []byte) (int, error) { + + var err error + var offset int + var l int + var fieldTypeId thrift.TType + var fieldId int16 + for { + fieldTypeId, fieldId, l, err = thrift.Binary.ReadFieldBegin(buf[offset:]) + offset += l + if err != nil { + goto ReadFieldBeginError + } + if fieldTypeId == thrift.STOP { + break + } + switch fieldId { + case 1: + if fieldTypeId == thrift.STRING { + l, err = p.FastReadField1(buf[offset:]) + offset += l + if err != nil { + goto ReadFieldError + } + } else { + l, err = thrift.Binary.Skip(buf[offset:], fieldTypeId) + offset += l + if err != nil { + goto SkipFieldError + } + } + case 2: + if fieldTypeId == thrift.STRING { + l, err = p.FastReadField2(buf[offset:]) + offset += l + if err != nil { + goto ReadFieldError + } + } else { + l, err = thrift.Binary.Skip(buf[offset:], fieldTypeId) + offset += l + if err != nil { + goto SkipFieldError + } + } + default: + l, err = thrift.Binary.Skip(buf[offset:], fieldTypeId) + offset += l + if err != nil { + goto SkipFieldError + } + } + } + + return offset, nil +ReadFieldBeginError: + return offset, thrift.PrependError(fmt.Sprintf("%T read field %d begin error: ", p, fieldId), err) +ReadFieldError: + return offset, thrift.PrependError(fmt.Sprintf("%T read field %d '%s' error: ", p, fieldId, fieldIDToName_ExptInsightAnalysisIndex[fieldId]), err) +SkipFieldError: + return offset, thrift.PrependError(fmt.Sprintf("%T field %d skip type %d error: ", p, fieldId, fieldTypeId), err) +} + +func (p *ExptInsightAnalysisIndex) FastReadField1(buf []byte) (int, error) { + offset := 0 + + var _field *string + if v, l, err := thrift.Binary.ReadString(buf[offset:]); err != nil { + return offset, err + } else { + offset += l + _field = &v + } + p.ID = _field + return offset, nil +} + +func (p *ExptInsightAnalysisIndex) FastReadField2(buf []byte) (int, error) { + offset := 0 + + var _field *string + if v, l, err := thrift.Binary.ReadString(buf[offset:]); err != nil { + return offset, err + } else { + offset += l + _field = &v + } + p.Title = _field + return offset, nil +} + +func (p *ExptInsightAnalysisIndex) FastWrite(buf []byte) int { + return p.FastWriteNocopy(buf, nil) +} + +func (p *ExptInsightAnalysisIndex) FastWriteNocopy(buf []byte, w thrift.NocopyWriter) int { + offset := 0 + if p != nil { + offset += p.fastWriteField1(buf[offset:], w) + offset += p.fastWriteField2(buf[offset:], w) + } + offset += thrift.Binary.WriteFieldStop(buf[offset:]) + return offset +} + +func (p *ExptInsightAnalysisIndex) BLength() int { + l := 0 + if p != nil { + l += p.field1Length() + l += p.field2Length() + } + l += thrift.Binary.FieldStopLength() + return l +} + +func (p *ExptInsightAnalysisIndex) fastWriteField1(buf []byte, w thrift.NocopyWriter) int { + offset := 0 + if p.IsSetID() { + offset += thrift.Binary.WriteFieldBegin(buf[offset:], thrift.STRING, 1) + offset += thrift.Binary.WriteStringNocopy(buf[offset:], w, *p.ID) + } + return offset +} + +func (p *ExptInsightAnalysisIndex) fastWriteField2(buf []byte, w thrift.NocopyWriter) int { + offset := 0 + if p.IsSetTitle() { + offset += thrift.Binary.WriteFieldBegin(buf[offset:], thrift.STRING, 2) + offset += thrift.Binary.WriteStringNocopy(buf[offset:], w, *p.Title) + } + return offset +} + +func (p *ExptInsightAnalysisIndex) field1Length() int { + l := 0 + if p.IsSetID() { + l += thrift.Binary.FieldBeginLength() + l += thrift.Binary.StringLengthNocopy(*p.ID) + } + return l +} + +func (p *ExptInsightAnalysisIndex) field2Length() int { + l := 0 + if p.IsSetTitle() { + l += thrift.Binary.FieldBeginLength() + l += thrift.Binary.StringLengthNocopy(*p.Title) + } + return l +} + +func (p *ExptInsightAnalysisIndex) DeepCopy(s interface{}) error { + src, ok := s.(*ExptInsightAnalysisIndex) + if !ok { + return fmt.Errorf("%T's type not matched %T", s, p) + } + + if src.ID != nil { + var tmp string + if *src.ID != "" { + tmp = kutils.StringDeepCopy(*src.ID) + } + p.ID = &tmp + } + + if src.Title != nil { + var tmp string + if *src.Title != "" { + tmp = kutils.StringDeepCopy(*src.Title) + } + p.Title = &tmp + } + return nil } diff --git a/backend/modules/evaluation/application/convertor/experiment/expt_insight_analysis.go b/backend/modules/evaluation/application/convertor/experiment/expt_insight_analysis.go index 249311f85..c4d9da033 100644 --- a/backend/modules/evaluation/application/convertor/experiment/expt_insight_analysis.go +++ b/backend/modules/evaluation/application/convertor/experiment/expt_insight_analysis.go @@ -20,6 +20,7 @@ func ExptInsightAnalysisRecordDO2DTO(do *entity.ExptInsightAnalysisRecord) *doma AnalysisStatus: InsightAnalysisStatus2DTO(do.Status), AnalysisReportID: do.AnalysisReportID, AnalysisReportContent: ptr.Of(do.AnalysisReportContent), + AnalysisReportIndex: AnalysisReportIndex2DTO(do.AnalysisReportIndex), ExptInsightAnalysisFeedback: ExptInsightAnalysisFeedbackDO2DTO(do.ExptInsightAnalysisFeedback), BaseInfo: &domain_common.BaseInfo{ CreatedBy: &domain_common.UserInfo{ @@ -32,6 +33,20 @@ func ExptInsightAnalysisRecordDO2DTO(do *entity.ExptInsightAnalysisRecord) *doma return dto } +func AnalysisReportIndex2DTO(index []*entity.InsightAnalysisReportIndex) []*domain_expt.ExptInsightAnalysisIndex { + if len(index) == 0 { + return nil + } + dto := make([]*domain_expt.ExptInsightAnalysisIndex, 0, len(index)) + for _, item := range index { + dto = append(dto, &domain_expt.ExptInsightAnalysisIndex{ + ID: ptr.Of(item.ID), + Title: ptr.Of(item.Title), + }) + } + return dto +} + func ExptInsightAnalysisFeedbackDO2DTO(do entity.ExptInsightAnalysisFeedback) *domain_expt.ExptInsightAnalysisFeedback { dto := &domain_expt.ExptInsightAnalysisFeedback{ UpvoteCnt: ptr.Of(int32(do.UpvoteCount)), diff --git a/backend/modules/evaluation/application/experiment_app.go b/backend/modules/evaluation/application/experiment_app.go index 0084651ee..88778c254 100644 --- a/backend/modules/evaluation/application/experiment_app.go +++ b/backend/modules/evaluation/application/experiment_app.go @@ -1129,12 +1129,21 @@ func (e *experimentApplication) InsightAnalysisExperiment(ctx context.Context, r if err != nil { return nil, err } + + var startTime, endTime *int64 + if got.StartAt != nil { + startTime = gptr.Of(got.StartAt.UnixMilli()) + } + if got.EndAt != nil { + endTime = gptr.Of(got.EndAt.UnixMilli()) + } + recordID, err := e.CreateAnalysisRecord(ctx, &entity.ExptInsightAnalysisRecord{ SpaceID: req.GetWorkspaceID(), ExptID: req.GetExptID(), CreatedBy: session.UserID, Status: entity.InsightAnalysisStatus_Running, - }, session) + }, session, gptr.Indirect(startTime), gptr.Indirect(endTime)) if err != nil { return nil, err } @@ -1151,7 +1160,6 @@ func (e *experimentApplication) ListExptInsightAnalysisRecord(ctx context.Contex UserID: strconv.FormatInt(gptr.Indirect(req.Session.UserID), 10), } } - err = e.auth.Authorization(ctx, &rpc.AuthorizationParam{ ObjectID: strconv.FormatInt(req.WorkspaceID, 10), SpaceID: req.WorkspaceID, @@ -1160,6 +1168,9 @@ func (e *experimentApplication) ListExptInsightAnalysisRecord(ctx context.Contex if err != nil { return nil, err } + + // First record contains the upvote/downvote count info for display purpose, + // Other records' feedback is not necessary for this list api records, total, err := e.ListAnalysisRecord(ctx, req.GetWorkspaceID(), req.GetExptID(), entity.NewPage(int(req.GetPageNumber()), int(req.GetPageSize())), session) if err != nil { return nil, err diff --git a/backend/modules/evaluation/application/experiment_app_test.go b/backend/modules/evaluation/application/experiment_app_test.go index 7512843de..99416658f 100644 --- a/backend/modules/evaluation/application/experiment_app_test.go +++ b/backend/modules/evaluation/application/experiment_app_test.go @@ -10,6 +10,7 @@ import ( "reflect" "strconv" "testing" + "time" "github.com/bytedance/gg/gptr" "github.com/stretchr/testify/assert" @@ -3736,11 +3737,15 @@ func TestInsightAnalysisExperiment(t *testing.T) { t.Run("成功创建洞察分析", func(t *testing.T) { // Mock the manager.Get call - mockManager.EXPECT().Get(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(&entity.Experiment{CreatedBy: "test-user"}, nil) + mockManager.EXPECT().Get(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(&entity.Experiment{ + CreatedBy: "test-user", + StartAt: &[]time.Time{time.Now()}[0], + EndAt: &[]time.Time{time.Now()}[0], + }, nil) // Mock the auth.AuthorizationWithoutSPI call mockAuth.EXPECT().AuthorizationWithoutSPI(gomock.Any(), gomock.Any()).Return(nil) // Mock the CreateAnalysisRecord call - mockInsightService.EXPECT().CreateAnalysisRecord(gomock.Any(), gomock.Any(), gomock.Any()).Return(int64(123), nil) + mockInsightService.EXPECT().CreateAnalysisRecord(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(int64(123), nil) _, err := app.InsightAnalysisExperiment(ctx, req) assert.NoError(t, err) @@ -3764,9 +3769,13 @@ func TestInsightAnalysisExperiment(t *testing.T) { }) t.Run("创建分析记录失败", func(t *testing.T) { - mockManager.EXPECT().Get(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(&entity.Experiment{CreatedBy: "test-user"}, nil) + mockManager.EXPECT().Get(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(&entity.Experiment{ + CreatedBy: "test-user", + StartAt: &[]time.Time{time.Now()}[0], + EndAt: &[]time.Time{time.Now()}[0], + }, nil) mockAuth.EXPECT().AuthorizationWithoutSPI(gomock.Any(), gomock.Any()).Return(nil) - mockInsightService.EXPECT().CreateAnalysisRecord(gomock.Any(), gomock.Any(), gomock.Any()).Return(int64(0), errors.New("create analysis record error")) + mockInsightService.EXPECT().CreateAnalysisRecord(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(int64(0), errors.New("create analysis record error")) _, err := app.InsightAnalysisExperiment(ctx, req) assert.Error(t, err) diff --git a/backend/modules/evaluation/application/wire_gen.go b/backend/modules/evaluation/application/wire_gen.go index 701d4b2ae..7a90a96f2 100644 --- a/backend/modules/evaluation/application/wire_gen.go +++ b/backend/modules/evaluation/application/wire_gen.go @@ -156,7 +156,7 @@ func InitExperimentApplication(ctx context.Context, idgen2 idgen.IIDGenerator, d iExptInsightAnalysisRecordDAO := mysql.NewExptInsightAnalysisRecordDAO(db2) iExptInsightAnalysisFeedbackCommentDAO := mysql.NewExptInsightAnalysisFeedbackCommentDAO(db2) iExptInsightAnalysisFeedbackVoteDAO := mysql.NewExptInsightAnalysisFeedbackVoteDAO(db2) - iExptInsightAnalysisRecordRepo := experiment.NewExptInsightAnalysisRecordRepo(iExptInsightAnalysisRecordDAO, iExptInsightAnalysisFeedbackCommentDAO, iExptInsightAnalysisFeedbackVoteDAO, idgen2) + iExptInsightAnalysisRecordRepo := experiment.NewExptInsightAnalysisRecordRepo(iExptInsightAnalysisRecordDAO, iExptInsightAnalysisFeedbackCommentDAO, iExptInsightAnalysisFeedbackVoteDAO, idgen2, iLatestWriteTracker) iAgentAdapter := agent.NewAgentAdapter() iNotifyRPCAdapter := notify.NewNotifyRPCAdapter() iExptInsightAnalysisService := service.NewInsightAnalysisService(iExptInsightAnalysisRecordRepo, exptEventPublisher, objectStorage, iAgentAdapter, iExptResultExportService, iNotifyRPCAdapter, iUserProvider, iExperimentRepo) @@ -235,7 +235,7 @@ func InitEvalTargetApplication(ctx context.Context, idgen2 idgen.IIDGenerator, d return evalTargetService } -func InitEvalOpenAPIApplication(ctx context.Context, configFactory conf.IConfigLoaderFactory, rmqFactory mq.IFactory, cmdable redis.Cmdable, idgen2 idgen.IIDGenerator, db2 db.Provider, client promptmanageservice.Client, executeClient promptexecuteservice.Client, authClient authservice.Client, meter metrics.Meter, dataClient datasetservice.Client, userClient userservice.Client, llmClient llmruntimeservice.Client, tagClient tagservice.Client, limiterFactory limiter.IRateLimiterFactory, objectStorage fileserver.ObjectStorage, auditClient audit.IAuditService, benefitService benefit.IBenefitService, ckProvider ck.Provider) (IEvalOpenAPIApplication, error) { +func InitEvalOpenAPIApplication(ctx context.Context, configFactory conf.IConfigLoaderFactory, rmqFactory mq.IFactory, cmdable redis.Cmdable, idgen2 idgen.IIDGenerator, db2 db.Provider, client promptmanageservice.Client, executeClient promptexecuteservice.Client, authClient authservice.Client, meter metrics.Meter, dataClient datasetservice.Client, userClient userservice.Client, llmClient llmruntimeservice.Client, tagClient tagservice.Client, limiterFactory limiter.IRateLimiterFactory, objectStorage fileserver.ObjectStorage, auditClient audit.IAuditService, benefitService benefit.IBenefitService, ckProvider ck.Provider) (evaluation.EvalOpenAPIService, error) { iEvalAsyncDAO := dao.NewEvalAsyncDAO(cmdable) iEvalAsyncRepo := experiment.NewEvalAsyncRepo(iEvalAsyncDAO) exptEventPublisher, err := producer.NewExptEventPublisher(ctx, configFactory, rmqFactory) @@ -326,13 +326,13 @@ func InitEvalOpenAPIApplication(ctx context.Context, configFactory conf.IConfigL iExptInsightAnalysisRecordDAO := mysql.NewExptInsightAnalysisRecordDAO(db2) iExptInsightAnalysisFeedbackCommentDAO := mysql.NewExptInsightAnalysisFeedbackCommentDAO(db2) iExptInsightAnalysisFeedbackVoteDAO := mysql.NewExptInsightAnalysisFeedbackVoteDAO(db2) - iExptInsightAnalysisRecordRepo := experiment.NewExptInsightAnalysisRecordRepo(iExptInsightAnalysisRecordDAO, iExptInsightAnalysisFeedbackCommentDAO, iExptInsightAnalysisFeedbackVoteDAO, idgen2) + iExptInsightAnalysisRecordRepo := experiment.NewExptInsightAnalysisRecordRepo(iExptInsightAnalysisRecordDAO, iExptInsightAnalysisFeedbackCommentDAO, iExptInsightAnalysisFeedbackVoteDAO, idgen2, iLatestWriteTracker) iAgentAdapter := agent.NewAgentAdapter() iNotifyRPCAdapter := notify.NewNotifyRPCAdapter() iExptInsightAnalysisService := service.NewInsightAnalysisService(iExptInsightAnalysisRecordRepo, exptEventPublisher, objectStorage, iAgentAdapter, iExptResultExportService, iNotifyRPCAdapter, iUserProvider, iExperimentRepo) iExperimentApplication := NewExperimentApplication(exptAggrResultService, exptResultService, iExptManager, exptSchedulerEvent, exptItemEvalEvent, idgen2, componentIConfiger, iAuthProvider, userInfoService, iEvalTargetService, evaluationSetItemService, iExptAnnotateService, iTagRPCAdapter, iExptResultExportService, iExptInsightAnalysisService) - v3 := NewEvalOpenAPIApplication(iEvalAsyncRepo, exptEventPublisher, iEvalTargetService, iAuthProvider, iEvaluationSetService, evaluationSetVersionService, evaluationSetItemService, evaluationSetSchemaService, openAPIEvaluationMetrics, userInfoService, iExperimentApplication, iExptManager, exptResultService, exptAggrResultService, evaluatorService) - return v3, nil + evalOpenAPIService := NewEvalOpenAPIApplication(iEvalAsyncRepo, exptEventPublisher, iEvalTargetService, iAuthProvider, iEvaluationSetService, evaluationSetVersionService, evaluationSetItemService, evaluationSetSchemaService, openAPIEvaluationMetrics, userInfoService, iExperimentApplication, iExptManager, exptResultService, exptAggrResultService, evaluatorService) + return evalOpenAPIService, nil } // wire.go: diff --git a/backend/modules/evaluation/domain/component/rpc/mocks/trace_agent.go b/backend/modules/evaluation/domain/component/rpc/mocks/trace_agent.go index 509b8a761..896f24d82 100644 --- a/backend/modules/evaluation/domain/component/rpc/mocks/trace_agent.go +++ b/backend/modules/evaluation/domain/component/rpc/mocks/trace_agent.go @@ -42,26 +42,26 @@ func (m *MockIAgentAdapter) EXPECT() *MockIAgentAdapterMockRecorder { } // CallTraceAgent mocks base method. -func (m *MockIAgentAdapter) CallTraceAgent(ctx context.Context, spaceID int64, url string) (int64, error) { +func (m *MockIAgentAdapter) CallTraceAgent(ctx context.Context, spaceID int64, url string, exptId, startTime, endTime int64) (int64, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "CallTraceAgent", ctx, spaceID, url) + ret := m.ctrl.Call(m, "CallTraceAgent", ctx, spaceID, url, exptId, startTime, endTime) ret0, _ := ret[0].(int64) ret1, _ := ret[1].(error) return ret0, ret1 } // CallTraceAgent indicates an expected call of CallTraceAgent. -func (mr *MockIAgentAdapterMockRecorder) CallTraceAgent(ctx, spaceID, url any) *gomock.Call { +func (mr *MockIAgentAdapterMockRecorder) CallTraceAgent(ctx, spaceID, url, exptId, startTime, endTime any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CallTraceAgent", reflect.TypeOf((*MockIAgentAdapter)(nil).CallTraceAgent), ctx, spaceID, url) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CallTraceAgent", reflect.TypeOf((*MockIAgentAdapter)(nil).CallTraceAgent), ctx, spaceID, url, exptId, startTime, endTime) } // GetReport mocks base method. -func (m *MockIAgentAdapter) GetReport(ctx context.Context, spaceID, reportID int64) (string, entity.ReportStatus, error) { +func (m *MockIAgentAdapter) GetReport(ctx context.Context, spaceID, reportID int64) (string, []*entity.InsightAnalysisReportIndex, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetReport", ctx, spaceID, reportID) ret0, _ := ret[0].(string) - ret1, _ := ret[1].(entity.ReportStatus) + ret1, _ := ret[1].([]*entity.InsightAnalysisReportIndex) ret2, _ := ret[2].(error) return ret0, ret1, ret2 } diff --git a/backend/modules/evaluation/domain/component/rpc/trace_agent.go b/backend/modules/evaluation/domain/component/rpc/trace_agent.go index 658736cae..42c764c8d 100644 --- a/backend/modules/evaluation/domain/component/rpc/trace_agent.go +++ b/backend/modules/evaluation/domain/component/rpc/trace_agent.go @@ -11,6 +11,6 @@ import ( //go:generate mockgen -destination=mocks/trace_agent.go -package=mocks . IAgentAdapter type IAgentAdapter interface { - CallTraceAgent(ctx context.Context, spaceID int64, url string) (int64, error) - GetReport(ctx context.Context, spaceID, reportID int64) (report string, status entity.ReportStatus, err error) + CallTraceAgent(ctx context.Context, spaceID int64, url string, exptId int64, startTime, endTime int64) (int64, error) + GetReport(ctx context.Context, spaceID, reportID int64) (report string, list []*entity.InsightAnalysisReportIndex, status entity.ReportStatus, err error) } diff --git a/backend/modules/evaluation/domain/entity/event.go b/backend/modules/evaluation/domain/entity/event.go index 05d309766..3e8b6bc95 100644 --- a/backend/modules/evaluation/domain/entity/event.go +++ b/backend/modules/evaluation/domain/entity/event.go @@ -120,9 +120,11 @@ type ExportCSVEvent struct { ExperimentID int64 SpaceID int64 - Session *Session - ExportScene ExportScene - CreatedAt int64 + Session *Session + ExportScene ExportScene + CreatedAt int64 + ExptStartTime int64 // Unix Time + ExptEndTime int64 // Unix Time } type ExportScene int diff --git a/backend/modules/evaluation/domain/entity/expt_insight_analysis_record.go b/backend/modules/evaluation/domain/entity/expt_insight_analysis_record.go index ff35af7c8..0403dc27a 100644 --- a/backend/modules/evaluation/domain/entity/expt_insight_analysis_record.go +++ b/backend/modules/evaluation/domain/entity/expt_insight_analysis_record.go @@ -14,6 +14,10 @@ const ( InsightAnalysisStatus_Failed InsightAnalysisStatus = 3 ) +const ( + InsightAnalysisRunningTimeout = 2 * time.Hour +) + type ExptInsightAnalysisRecord struct { ID int64 SpaceID int64 @@ -22,6 +26,7 @@ type ExptInsightAnalysisRecord struct { ExptResultFilePath *string AnalysisReportID *int64 AnalysisReportContent string + AnalysisReportIndex []*InsightAnalysisReportIndex CreatedBy string CreatedAt time.Time UpdatedAt time.Time @@ -100,3 +105,8 @@ const ( // 生成失败 ReportStatus_Failed ReportStatus = 3 ) + +type InsightAnalysisReportIndex struct { + ID string + Title string +} diff --git a/backend/modules/evaluation/domain/service/insight_analysis.go b/backend/modules/evaluation/domain/service/insight_analysis.go index e345fd7be..623dca355 100644 --- a/backend/modules/evaluation/domain/service/insight_analysis.go +++ b/backend/modules/evaluation/domain/service/insight_analysis.go @@ -10,8 +10,8 @@ import ( ) type IExptInsightAnalysisService interface { - CreateAnalysisRecord(ctx context.Context, record *entity.ExptInsightAnalysisRecord, session *entity.Session) (int64, error) - GenAnalysisReport(ctx context.Context, spaceID, exptID, recordID, CreateAt int64) error + CreateAnalysisRecord(ctx context.Context, record *entity.ExptInsightAnalysisRecord, session *entity.Session, startTime, endTime int64) (int64, error) + GenAnalysisReport(ctx context.Context, spaceID, exptID, recordID, CreateAt, startTime, endTime int64) error GetAnalysisRecordByID(ctx context.Context, spaceID, exptID, recordID int64, session *entity.Session) (*entity.ExptInsightAnalysisRecord, error) ListAnalysisRecord(ctx context.Context, spaceID, exptID int64, page entity.Page, session *entity.Session) ([]*entity.ExptInsightAnalysisRecord, int64, error) DeleteAnalysisRecord(ctx context.Context, spaceID, exptID, recordID int64) error diff --git a/backend/modules/evaluation/domain/service/insight_analysis_impl.go b/backend/modules/evaluation/domain/service/insight_analysis_impl.go index 9f5869464..640df29a8 100644 --- a/backend/modules/evaluation/domain/service/insight_analysis_impl.go +++ b/backend/modules/evaluation/domain/service/insight_analysis_impl.go @@ -55,18 +55,20 @@ func NewInsightAnalysisService(repo repo.IExptInsightAnalysisRecordRepo, } } -func (e ExptInsightAnalysisServiceImpl) CreateAnalysisRecord(ctx context.Context, record *entity.ExptInsightAnalysisRecord, session *entity.Session) (int64, error) { +func (e ExptInsightAnalysisServiceImpl) CreateAnalysisRecord(ctx context.Context, record *entity.ExptInsightAnalysisRecord, session *entity.Session, startTime, endTime int64) (int64, error) { recordID, err := e.repo.CreateAnalysisRecord(ctx, record) if err != nil { return 0, err } exportEvent := &entity.ExportCSVEvent{ - ExportID: recordID, - ExperimentID: record.ExptID, - SpaceID: record.SpaceID, - ExportScene: entity.ExportSceneInsightAnalysis, - CreatedAt: time.Now().Unix(), + ExportID: recordID, + ExperimentID: record.ExptID, + SpaceID: record.SpaceID, + ExportScene: entity.ExportSceneInsightAnalysis, + CreatedAt: time.Now().Unix(), + ExptStartTime: startTime, + ExptEndTime: endTime, } err = e.exptPublisher.PublishExptExportCSVEvent(ctx, exportEvent, gptr.Of(time.Second*3)) if err != nil { @@ -76,7 +78,7 @@ func (e ExptInsightAnalysisServiceImpl) CreateAnalysisRecord(ctx context.Context return recordID, nil } -func (e ExptInsightAnalysisServiceImpl) GenAnalysisReport(ctx context.Context, spaceID, exptID, recordID, CreateAt int64) (err error) { +func (e ExptInsightAnalysisServiceImpl) GenAnalysisReport(ctx context.Context, spaceID, exptID, recordID, CreateAt, startTime, endTime int64) (err error) { analysisRecord, err := e.repo.GetAnalysisRecordByID(ctx, spaceID, exptID, recordID) if err != nil { return err @@ -125,7 +127,7 @@ func (e ExptInsightAnalysisServiceImpl) GenAnalysisReport(ctx context.Context, s return err } - reportID, err := e.agentAdapter.CallTraceAgent(ctx, spaceID, url) + reportID, err := e.agentAdapter.CallTraceAgent(ctx, spaceID, url, exptID, startTime, endTime) if err != nil { return err } @@ -135,11 +137,13 @@ func (e ExptInsightAnalysisServiceImpl) GenAnalysisReport(ctx context.Context, s // 发送时间检查分析报告生成状态 exportEvent := &entity.ExportCSVEvent{ - ExportID: recordID, - ExperimentID: exptID, - SpaceID: spaceID, - ExportScene: entity.ExportSceneInsightAnalysis, - CreatedAt: CreateAt, + ExportID: recordID, + ExperimentID: exptID, + SpaceID: spaceID, + ExportScene: entity.ExportSceneInsightAnalysis, + CreatedAt: CreateAt, + ExptStartTime: startTime, + ExptEndTime: endTime, } err = e.exptPublisher.PublishExptExportCSVEvent(ctx, exportEvent, gptr.Of(time.Minute*3)) if err != nil { @@ -150,10 +154,11 @@ func (e ExptInsightAnalysisServiceImpl) GenAnalysisReport(ctx context.Context, s } func (e ExptInsightAnalysisServiceImpl) checkAnalysisReportGenStatus(ctx context.Context, record *entity.ExptInsightAnalysisRecord, CreateAt int64) (err error) { - _, status, err := e.agentAdapter.GetReport(ctx, record.SpaceID, ptr.From(record.AnalysisReportID)) + _, _, status, err := e.agentAdapter.GetReport(ctx, record.SpaceID, ptr.From(record.AnalysisReportID)) if err != nil { return err } + if status == entity.ReportStatus_Failed { record.Status = entity.InsightAnalysisStatus_Failed return e.repo.UpdateAnalysisRecord(ctx, record) @@ -167,10 +172,10 @@ func (e ExptInsightAnalysisServiceImpl) checkAnalysisReportGenStatus(ctx context return e.repo.UpdateAnalysisRecord(ctx, record) } - defaultIntervalSecond := 60 * 60 * 1 - if time.Now().Unix()-CreateAt >= int64(defaultIntervalSecond) { - logs.CtxWarn(ctx, "checkAnalysisReportGenStatus found timeout event, expt_id: %v, record_id: %v", record.ExptID, record.ID) + // 超过2小时,未生成分析报告,认为是失败 + if status == entity.ReportStatus_Running && record.CreatedAt.Add(entity.InsightAnalysisRunningTimeout).Unix() <= time.Now().Unix() { record.Status = entity.InsightAnalysisStatus_Failed + logs.CtxWarn(ctx, "checkAnalysisReportGenStatus found timeout event, expt_id: %v, record_id: %v", record.ExptID, record.ID) return e.repo.UpdateAnalysisRecord(ctx, record) } @@ -195,17 +200,27 @@ func (e ExptInsightAnalysisServiceImpl) GetAnalysisRecordByID(ctx context.Contex return nil, err } + if analysisRecord.Status == entity.InsightAnalysisStatus_Running && analysisRecord.CreatedAt.Add(entity.InsightAnalysisRunningTimeout).Unix() < time.Now().Unix() { + analysisRecord.Status = entity.InsightAnalysisStatus_Failed + err = e.repo.UpdateAnalysisRecord(ctx, analysisRecord) + if err != nil { + logs.CtxError(ctx, "GetAnalysisRecordByID: UpdateAnalysisRecord failed: %v", err) + } + return analysisRecord, err + } + if analysisRecord.Status == entity.InsightAnalysisStatus_Running || analysisRecord.Status == entity.InsightAnalysisStatus_Failed { return analysisRecord, nil } - report, _, err := e.agentAdapter.GetReport(ctx, spaceID, ptr.From(analysisRecord.AnalysisReportID)) + report, reportIdx, _, err := e.agentAdapter.GetReport(ctx, spaceID, ptr.From(analysisRecord.AnalysisReportID)) if err != nil { return nil, err } analysisRecord.AnalysisReportContent = report + analysisRecord.AnalysisReportIndex = reportIdx upvoteCount, downvoteCount, err := e.repo.CountFeedbackVote(ctx, spaceID, exptID, recordID) if err != nil { @@ -254,7 +269,40 @@ func (e ExptInsightAnalysisServiceImpl) notifyAnalysisComplete(ctx context.Conte } func (e ExptInsightAnalysisServiceImpl) ListAnalysisRecord(ctx context.Context, spaceID, exptID int64, page entity.Page, session *entity.Session) ([]*entity.ExptInsightAnalysisRecord, int64, error) { - return e.repo.ListAnalysisRecord(ctx, spaceID, exptID, page) + analysisRecords, total, err := e.repo.ListAnalysisRecord(ctx, spaceID, exptID, page) + if err != nil { + return nil, 0, err + } + if total == 0 { + return analysisRecords, total, nil + } + + firstAnalysisRecord := analysisRecords[0] + + upvoteCount, downvoteCount, err := e.repo.CountFeedbackVote(ctx, spaceID, exptID, firstAnalysisRecord.ID) + if err != nil { + // side path, don't block the main flow + logs.CtxWarn(ctx, "CountFeedbackVote failed for space_id: %v, expt_id: %v, record_id: %v, err=%v", spaceID, exptID, firstAnalysisRecord.ID, err) + return analysisRecords, total, nil + } + + curUserFeedbackVote, err := e.repo.GetFeedbackVoteByUser(ctx, spaceID, exptID, firstAnalysisRecord.ID, session.UserID) + if err != nil { + // side path, don't block the main flow + logs.CtxWarn(ctx, "GetFeedbackVoteByUser failed for space_id: %v, expt_id: %v, record_id: %v, err=%v", spaceID, exptID, firstAnalysisRecord.ID, err) + return analysisRecords, total, nil + } + firstAnalysisRecord.ExptInsightAnalysisFeedback = entity.ExptInsightAnalysisFeedback{ + UpvoteCount: upvoteCount, + DownvoteCount: downvoteCount, + CurrentUserVoteType: entity.None, + } + firstAnalysisRecord.ExptInsightAnalysisFeedback.CurrentUserVoteType = entity.None + if curUserFeedbackVote != nil { + firstAnalysisRecord.ExptInsightAnalysisFeedback.CurrentUserVoteType = curUserFeedbackVote.VoteType + } + + return analysisRecords, total, nil } func (e ExptInsightAnalysisServiceImpl) DeleteAnalysisRecord(ctx context.Context, spaceID, exptID, recordID int64) error { diff --git a/backend/modules/evaluation/domain/service/insight_analysis_impl_test.go b/backend/modules/evaluation/domain/service/insight_analysis_impl_test.go index d4b434070..dbf918b6e 100644 --- a/backend/modules/evaluation/domain/service/insight_analysis_impl_test.go +++ b/backend/modules/evaluation/domain/service/insight_analysis_impl_test.go @@ -134,7 +134,7 @@ func TestExptInsightAnalysisServiceImpl_CreateAnalysisRecord(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { tt.setup() - result, err := service.CreateAnalysisRecord(ctx, tt.record, tt.session) + result, err := service.CreateAnalysisRecord(ctx, tt.record, tt.session, 0, 0) if tt.wantErr { assert.Error(t, err) } else { @@ -172,7 +172,7 @@ func TestExptInsightAnalysisServiceImpl_GenAnalysisReport(t *testing.T) { }, nil) mocks.exptResultExportService.EXPECT().DoExportCSV(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil) mocks.fileClient.EXPECT().SignDownloadReq(gomock.Any(), gomock.Any(), gomock.Any()).Return("http://test-url.com", make(map[string][]string), nil) - mocks.agentAdapter.EXPECT().CallTraceAgent(gomock.Any(), gomock.Any(), gomock.Any()).Return(int64(123), nil) + mocks.agentAdapter.EXPECT().CallTraceAgent(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(int64(123), nil) mocks.publisher.EXPECT().PublishExptExportCSVEvent(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil) mocks.repo.EXPECT().UpdateAnalysisRecord(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil) }, @@ -266,7 +266,7 @@ func TestExptInsightAnalysisServiceImpl_GenAnalysisReport(t *testing.T) { }, nil) mocks.exptResultExportService.EXPECT().DoExportCSV(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil) mocks.fileClient.EXPECT().SignDownloadReq(gomock.Any(), gomock.Any(), gomock.Any()).Return("http://test-url.com", make(map[string][]string), nil) - mocks.agentAdapter.EXPECT().CallTraceAgent(gomock.Any(), gomock.Any(), gomock.Any()).Return(int64(0), errors.New("agent error")) + mocks.agentAdapter.EXPECT().CallTraceAgent(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(int64(0), errors.New("agent error")) mocks.repo.EXPECT().UpdateAnalysisRecord(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(func(ctx context.Context, record *entity.ExptInsightAnalysisRecord, opts ...db.Option) error { assert.Equal(t, entity.InsightAnalysisStatus_Failed, record.Status) return nil @@ -289,7 +289,7 @@ func TestExptInsightAnalysisServiceImpl_GenAnalysisReport(t *testing.T) { }, nil) mocks.exptResultExportService.EXPECT().DoExportCSV(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil) mocks.fileClient.EXPECT().SignDownloadReq(gomock.Any(), gomock.Any(), gomock.Any()).Return("http://test-url.com", make(map[string][]string), nil) - mocks.agentAdapter.EXPECT().CallTraceAgent(gomock.Any(), gomock.Any(), gomock.Any()).Return(int64(123), nil) + mocks.agentAdapter.EXPECT().CallTraceAgent(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(int64(123), nil) mocks.publisher.EXPECT().PublishExptExportCSVEvent(gomock.Any(), gomock.Any(), gomock.Any()).Return(errors.New("publish error")) mocks.repo.EXPECT().UpdateAnalysisRecord(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(func(ctx context.Context, record *entity.ExptInsightAnalysisRecord, opts ...db.Option) error { assert.Equal(t, entity.InsightAnalysisStatus_Failed, record.Status) @@ -325,7 +325,7 @@ func TestExptInsightAnalysisServiceImpl_GenAnalysisReport(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { tt.setup() - err := service.GenAnalysisReport(ctx, tt.spaceID, tt.exptID, tt.recordID, tt.createAt) + err := service.GenAnalysisReport(ctx, tt.spaceID, tt.exptID, tt.recordID, tt.createAt, 0, 0) if tt.wantErr { assert.Error(t, err) } else { diff --git a/backend/modules/evaluation/domain/service/mocks/insight_analysis.go b/backend/modules/evaluation/domain/service/mocks/insight_analysis.go index 78d1223d8..7c8ad5c62 100644 --- a/backend/modules/evaluation/domain/service/mocks/insight_analysis.go +++ b/backend/modules/evaluation/domain/service/mocks/insight_analysis.go @@ -36,18 +36,18 @@ func (m *MockIExptInsightAnalysisService) EXPECT() *MockIExptInsightAnalysisServ } // CreateAnalysisRecord mocks base method. -func (m *MockIExptInsightAnalysisService) CreateAnalysisRecord(arg0 context.Context, arg1 *entity.ExptInsightAnalysisRecord, arg2 *entity.Session) (int64, error) { +func (m *MockIExptInsightAnalysisService) CreateAnalysisRecord(arg0 context.Context, arg1 *entity.ExptInsightAnalysisRecord, arg2 *entity.Session, arg3, arg4 int64) (int64, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "CreateAnalysisRecord", arg0, arg1, arg2) + ret := m.ctrl.Call(m, "CreateAnalysisRecord", arg0, arg1, arg2, arg3, arg4) ret0, _ := ret[0].(int64) ret1, _ := ret[1].(error) return ret0, ret1 } // CreateAnalysisRecord indicates an expected call of CreateAnalysisRecord. -func (mr *MockIExptInsightAnalysisServiceMockRecorder) CreateAnalysisRecord(arg0, arg1, arg2 interface{}) *gomock.Call { +func (mr *MockIExptInsightAnalysisServiceMockRecorder) CreateAnalysisRecord(arg0, arg1, arg2, arg3, arg4 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateAnalysisRecord", reflect.TypeOf((*MockIExptInsightAnalysisService)(nil).CreateAnalysisRecord), arg0, arg1, arg2) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateAnalysisRecord", reflect.TypeOf((*MockIExptInsightAnalysisService)(nil).CreateAnalysisRecord), arg0, arg1, arg2, arg3, arg4) } // DeleteAnalysisRecord mocks base method. @@ -79,17 +79,17 @@ func (mr *MockIExptInsightAnalysisServiceMockRecorder) FeedbackExptInsightAnalys } // GenAnalysisReport mocks base method. -func (m *MockIExptInsightAnalysisService) GenAnalysisReport(arg0 context.Context, arg1, arg2, arg3, arg4 int64) error { +func (m *MockIExptInsightAnalysisService) GenAnalysisReport(arg0 context.Context, arg1, arg2, arg3, arg4, arg5, arg6 int64) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GenAnalysisReport", arg0, arg1, arg2, arg3, arg4) + ret := m.ctrl.Call(m, "GenAnalysisReport", arg0, arg1, arg2, arg3, arg4, arg5, arg6) ret0, _ := ret[0].(error) return ret0 } // GenAnalysisReport indicates an expected call of GenAnalysisReport. -func (mr *MockIExptInsightAnalysisServiceMockRecorder) GenAnalysisReport(arg0, arg1, arg2, arg3, arg4 interface{}) *gomock.Call { +func (mr *MockIExptInsightAnalysisServiceMockRecorder) GenAnalysisReport(arg0, arg1, arg2, arg3, arg4, arg5, arg6 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GenAnalysisReport", reflect.TypeOf((*MockIExptInsightAnalysisService)(nil).GenAnalysisReport), arg0, arg1, arg2, arg3, arg4) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GenAnalysisReport", reflect.TypeOf((*MockIExptInsightAnalysisService)(nil).GenAnalysisReport), arg0, arg1, arg2, arg3, arg4, arg5, arg6) } // GetAnalysisRecordByID mocks base method. diff --git a/backend/modules/evaluation/infra/mq/rocket/consumer/expt_export.go b/backend/modules/evaluation/infra/mq/rocket/consumer/expt_export.go index 07e44f0fa..b3a3603b8 100644 --- a/backend/modules/evaluation/infra/mq/rocket/consumer/expt_export.go +++ b/backend/modules/evaluation/infra/mq/rocket/consumer/expt_export.go @@ -53,7 +53,7 @@ func (e *ExptExportConsumer) HandleMessage(ctx context.Context, ext *mq.MessageE func (e *ExptExportConsumer) handleEvent(ctx context.Context, event *entity.ExportCSVEvent) (err error) { switch event.ExportScene { case entity.ExportSceneInsightAnalysis: - err = e.exptInsightAnalysisService.GenAnalysisReport(ctx, event.SpaceID, event.ExperimentID, event.ExportID, event.CreatedAt) + err = e.exptInsightAnalysisService.GenAnalysisReport(ctx, event.SpaceID, event.ExperimentID, event.ExportID, event.CreatedAt, event.ExptStartTime, event.ExptEndTime) if err != nil { logs.CtxError(ctx, "ExptExportConsumer GenAnalysisReport fail, expt_id:%v, err: %v", event.ExperimentID, err) return nil diff --git a/backend/modules/evaluation/infra/repo/experiment/expt_insight_analysis_record.go b/backend/modules/evaluation/infra/repo/experiment/expt_insight_analysis_record.go index de1355145..ff0c48ea2 100644 --- a/backend/modules/evaluation/infra/repo/experiment/expt_insight_analysis_record.go +++ b/backend/modules/evaluation/infra/repo/experiment/expt_insight_analysis_record.go @@ -5,9 +5,11 @@ package experiment import ( "context" + "fmt" "github.com/coze-dev/coze-loop/backend/infra/db" "github.com/coze-dev/coze-loop/backend/infra/idgen" + "github.com/coze-dev/coze-loop/backend/infra/platestwrite" "github.com/coze-dev/coze-loop/backend/modules/evaluation/domain/entity" "github.com/coze-dev/coze-loop/backend/modules/evaluation/domain/repo" "github.com/coze-dev/coze-loop/backend/modules/evaluation/infra/repo/experiment/mysql" @@ -19,6 +21,7 @@ type ExptInsightAnalysisRecordRepo struct { exptInsightAnalysisFeedbackCommentDAO mysql.IExptInsightAnalysisFeedbackCommentDAO exptInsightAnalysisFeedbackVoteDAO mysql.IExptInsightAnalysisFeedbackVoteDAO idgenerator idgen.IIDGenerator + writeTracker platestwrite.ILatestWriteTracker } func NewExptInsightAnalysisRecordRepo( @@ -26,12 +29,14 @@ func NewExptInsightAnalysisRecordRepo( exptInsightAnalysisFeedbackCommentDAO mysql.IExptInsightAnalysisFeedbackCommentDAO, exptInsightAnalysisFeedbackVoteDAO mysql.IExptInsightAnalysisFeedbackVoteDAO, idgenerator idgen.IIDGenerator, + writeTracker platestwrite.ILatestWriteTracker, ) repo.IExptInsightAnalysisRecordRepo { return &ExptInsightAnalysisRecordRepo{ exptInsightAnalysisRecordDAO: exptInsightAnalysisRecordDAO, exptInsightAnalysisFeedbackCommentDAO: exptInsightAnalysisFeedbackCommentDAO, exptInsightAnalysisFeedbackVoteDAO: exptInsightAnalysisFeedbackVoteDAO, idgenerator: idgenerator, + writeTracker: writeTracker, } } @@ -47,15 +52,34 @@ func (e ExptInsightAnalysisRecordRepo) CreateAnalysisRecord(ctx context.Context, return 0, err } + if e.writeTracker != nil { + e.writeTracker.SetWriteFlag(ctx, platestwrite.ResourceTypeExptInsightAnalysisRecord, id, + platestwrite.SetWithSearchParam(buildRecordSearchParam(record.SpaceID, record.ExptID))) + } + return id, nil } func (e ExptInsightAnalysisRecordRepo) UpdateAnalysisRecord(ctx context.Context, record *entity.ExptInsightAnalysisRecord, opts ...db.Option) error { - return e.exptInsightAnalysisRecordDAO.Update(ctx, convert.ExptInsightAnalysisRecordDOToPO(record), opts...) + if err := e.exptInsightAnalysisRecordDAO.Update(ctx, convert.ExptInsightAnalysisRecordDOToPO(record), opts...); err != nil { + return err + } + + if e.writeTracker != nil { + e.writeTracker.SetWriteFlag(ctx, platestwrite.ResourceTypeExptInsightAnalysisRecord, record.ID, + platestwrite.SetWithSearchParam(buildRecordSearchParam(record.SpaceID, record.ExptID))) + } + + return nil } func (e ExptInsightAnalysisRecordRepo) GetAnalysisRecordByID(ctx context.Context, spaceID, exptID, recordID int64) (*entity.ExptInsightAnalysisRecord, error) { - po, err := e.exptInsightAnalysisRecordDAO.GetByID(ctx, spaceID, exptID, recordID) + opts := make([]db.Option, 0) + if e.needForceMasterByRecord(ctx, platestwrite.ResourceTypeExptInsightAnalysisRecord, recordID, buildRecordSearchParam(spaceID, exptID)) { + opts = append(opts, db.WithMaster()) + } + + po, err := e.exptInsightAnalysisRecordDAO.GetByID(ctx, spaceID, exptID, recordID, opts...) if err != nil { return nil, err } @@ -64,7 +88,11 @@ func (e ExptInsightAnalysisRecordRepo) GetAnalysisRecordByID(ctx context.Context } func (e ExptInsightAnalysisRecordRepo) ListAnalysisRecord(ctx context.Context, spaceID, exptID int64, page entity.Page) ([]*entity.ExptInsightAnalysisRecord, int64, error) { - pos, total, err := e.exptInsightAnalysisRecordDAO.List(ctx, spaceID, exptID, page) + opts := make([]db.Option, 0) + if e.needForceMasterByRecord(ctx, platestwrite.ResourceTypeExptInsightAnalysisRecord, 0, buildRecordSearchParam(spaceID, exptID)) { + opts = append(opts, db.WithMaster()) + } + pos, total, err := e.exptInsightAnalysisRecordDAO.List(ctx, spaceID, exptID, page, opts...) if err != nil { return nil, 0, err } @@ -77,7 +105,14 @@ func (e ExptInsightAnalysisRecordRepo) ListAnalysisRecord(ctx context.Context, s } func (e ExptInsightAnalysisRecordRepo) DeleteAnalysisRecord(ctx context.Context, spaceID, exptID, recordID int64) error { - return e.exptInsightAnalysisRecordDAO.Delete(ctx, spaceID, exptID, recordID) + if err := e.exptInsightAnalysisRecordDAO.Delete(ctx, spaceID, exptID, recordID); err != nil { + return err + } + if e.writeTracker != nil { + e.writeTracker.SetWriteFlag(ctx, platestwrite.ResourceTypeExptInsightAnalysisRecord, recordID, + platestwrite.SetWithSearchParam(buildRecordSearchParam(spaceID, exptID))) + } + return nil } func (e ExptInsightAnalysisRecordRepo) CreateFeedbackComment(ctx context.Context, feedbackComment *entity.ExptInsightAnalysisFeedbackComment, opts ...db.Option) error { @@ -86,15 +121,33 @@ func (e ExptInsightAnalysisRecordRepo) CreateFeedbackComment(ctx context.Context return err } feedbackComment.ID = id - return e.exptInsightAnalysisFeedbackCommentDAO.Create(ctx, convert.ExptInsightAnalysisFeedbackCommentDOToPO(feedbackComment), opts...) + if err := e.exptInsightAnalysisFeedbackCommentDAO.Create(ctx, convert.ExptInsightAnalysisFeedbackCommentDOToPO(feedbackComment), opts...); err != nil { + return err + } + if e.writeTracker != nil { + e.writeTracker.SetWriteFlag(ctx, platestwrite.ResourceTypeExptInsightAnalysisFeedback, feedbackComment.AnalysisRecordID, + platestwrite.SetWithSearchParam(buildFeedbackSearchParam(feedbackComment.SpaceID, feedbackComment.ExptID, feedbackComment.AnalysisRecordID))) + } + return nil } func (e ExptInsightAnalysisRecordRepo) UpdateFeedbackComment(ctx context.Context, feedbackComment *entity.ExptInsightAnalysisFeedbackComment, opts ...db.Option) error { - return e.exptInsightAnalysisFeedbackCommentDAO.Update(ctx, convert.ExptInsightAnalysisFeedbackCommentDOToPO(feedbackComment), opts...) + if err := e.exptInsightAnalysisFeedbackCommentDAO.Update(ctx, convert.ExptInsightAnalysisFeedbackCommentDOToPO(feedbackComment), opts...); err != nil { + return err + } + if e.writeTracker != nil { + e.writeTracker.SetWriteFlag(ctx, platestwrite.ResourceTypeExptInsightAnalysisFeedback, feedbackComment.AnalysisRecordID, + platestwrite.SetWithSearchParam(buildFeedbackSearchParam(feedbackComment.SpaceID, feedbackComment.ExptID, feedbackComment.AnalysisRecordID))) + } + return nil } func (e ExptInsightAnalysisRecordRepo) GetFeedbackCommentByRecordID(ctx context.Context, spaceID, exptID, recordID int64, opts ...db.Option) (*entity.ExptInsightAnalysisFeedbackComment, error) { - po, err := e.exptInsightAnalysisFeedbackCommentDAO.GetByRecordID(ctx, spaceID, exptID, recordID, opts...) + innerOpts := append([]db.Option{}, opts...) + if e.needForceMasterByRecord(ctx, platestwrite.ResourceTypeExptInsightAnalysisFeedback, recordID, buildFeedbackSearchParam(spaceID, exptID, recordID)) && !db.ContainWithMasterOpt(innerOpts) { + innerOpts = append(innerOpts, db.WithMaster()) + } + po, err := e.exptInsightAnalysisFeedbackCommentDAO.GetByRecordID(ctx, spaceID, exptID, recordID, innerOpts...) if err != nil { return nil, err } @@ -102,7 +155,22 @@ func (e ExptInsightAnalysisRecordRepo) GetFeedbackCommentByRecordID(ctx context. } func (e ExptInsightAnalysisRecordRepo) DeleteFeedbackComment(ctx context.Context, spaceID, exptID, commentID int64) error { - return e.exptInsightAnalysisFeedbackCommentDAO.Delete(ctx, spaceID, exptID, commentID) + po, err := e.exptInsightAnalysisFeedbackCommentDAO.GetByID(ctx, spaceID, exptID, commentID, db.WithMaster()) + if err != nil { + return err + } + if err := e.exptInsightAnalysisFeedbackCommentDAO.Delete(ctx, spaceID, exptID, commentID); err != nil { + return err + } + recordID := int64(0) + if po.AnalysisRecordID != nil { + recordID = *po.AnalysisRecordID + } + if e.writeTracker != nil && recordID > 0 { + e.writeTracker.SetWriteFlag(ctx, platestwrite.ResourceTypeExptInsightAnalysisFeedback, recordID, + platestwrite.SetWithSearchParam(buildFeedbackSearchParam(po.SpaceID, po.ExptID, recordID))) + } + return nil } func (e ExptInsightAnalysisRecordRepo) CreateFeedbackVote(ctx context.Context, feedbackVote *entity.ExptInsightAnalysisFeedbackVote, opts ...db.Option) error { @@ -111,15 +179,33 @@ func (e ExptInsightAnalysisRecordRepo) CreateFeedbackVote(ctx context.Context, f return err } feedbackVote.ID = id - return e.exptInsightAnalysisFeedbackVoteDAO.Create(ctx, convert.ExptInsightAnalysisFeedbackVoteDOToPO(feedbackVote), opts...) + if err := e.exptInsightAnalysisFeedbackVoteDAO.Create(ctx, convert.ExptInsightAnalysisFeedbackVoteDOToPO(feedbackVote), opts...); err != nil { + return err + } + if e.writeTracker != nil { + e.writeTracker.SetWriteFlag(ctx, platestwrite.ResourceTypeExptInsightAnalysisFeedback, feedbackVote.AnalysisRecordID, + platestwrite.SetWithSearchParam(buildFeedbackSearchParam(feedbackVote.SpaceID, feedbackVote.ExptID, feedbackVote.AnalysisRecordID))) + } + return nil } func (e ExptInsightAnalysisRecordRepo) UpdateFeedbackVote(ctx context.Context, feedbackVote *entity.ExptInsightAnalysisFeedbackVote, opts ...db.Option) error { - return e.exptInsightAnalysisFeedbackVoteDAO.Update(ctx, convert.ExptInsightAnalysisFeedbackVoteDOToPO(feedbackVote), opts...) + if err := e.exptInsightAnalysisFeedbackVoteDAO.Update(ctx, convert.ExptInsightAnalysisFeedbackVoteDOToPO(feedbackVote), opts...); err != nil { + return err + } + if e.writeTracker != nil { + e.writeTracker.SetWriteFlag(ctx, platestwrite.ResourceTypeExptInsightAnalysisFeedback, feedbackVote.AnalysisRecordID, + platestwrite.SetWithSearchParam(buildFeedbackSearchParam(feedbackVote.SpaceID, feedbackVote.ExptID, feedbackVote.AnalysisRecordID))) + } + return nil } func (e ExptInsightAnalysisRecordRepo) GetFeedbackVoteByUser(ctx context.Context, spaceID, exptID, recordID int64, userID string, opts ...db.Option) (*entity.ExptInsightAnalysisFeedbackVote, error) { - po, err := e.exptInsightAnalysisFeedbackVoteDAO.GetByUser(ctx, spaceID, exptID, recordID, userID, opts...) + innerOpts := append([]db.Option{}, opts...) + if e.needForceMasterByRecord(ctx, platestwrite.ResourceTypeExptInsightAnalysisFeedback, recordID, buildFeedbackSearchParam(spaceID, exptID, recordID)) && !db.ContainWithMasterOpt(innerOpts) { + innerOpts = append(innerOpts, db.WithMaster()) + } + po, err := e.exptInsightAnalysisFeedbackVoteDAO.GetByUser(ctx, spaceID, exptID, recordID, userID, innerOpts...) if err != nil { return nil, err } @@ -127,11 +213,19 @@ func (e ExptInsightAnalysisRecordRepo) GetFeedbackVoteByUser(ctx context.Context } func (e ExptInsightAnalysisRecordRepo) CountFeedbackVote(ctx context.Context, spaceID, exptID, recordID int64) (int64, int64, error) { - return e.exptInsightAnalysisFeedbackVoteDAO.Count(ctx, spaceID, exptID, recordID) + opts := make([]db.Option, 0) + if e.needForceMasterByRecord(ctx, platestwrite.ResourceTypeExptInsightAnalysisFeedback, recordID, buildFeedbackSearchParam(spaceID, exptID, recordID)) { + opts = append(opts, db.WithMaster()) + } + return e.exptInsightAnalysisFeedbackVoteDAO.Count(ctx, spaceID, exptID, recordID, opts...) } func (e ExptInsightAnalysisRecordRepo) List(ctx context.Context, spaceID, exptID, recordID int64, page entity.Page) ([]*entity.ExptInsightAnalysisFeedbackComment, int64, error) { - pos, total, err := e.exptInsightAnalysisFeedbackCommentDAO.List(ctx, spaceID, exptID, recordID, page) + opts := make([]db.Option, 0) + if e.needForceMasterByRecord(ctx, platestwrite.ResourceTypeExptInsightAnalysisFeedback, recordID, buildFeedbackSearchParam(spaceID, exptID, recordID)) { + opts = append(opts, db.WithMaster()) + } + pos, total, err := e.exptInsightAnalysisFeedbackCommentDAO.List(ctx, spaceID, exptID, recordID, page, opts...) if err != nil { return nil, 0, err } @@ -141,3 +235,24 @@ func (e ExptInsightAnalysisRecordRepo) List(ctx context.Context, spaceID, exptID } return dos, total, nil } + +func (e ExptInsightAnalysisRecordRepo) needForceMasterByRecord(ctx context.Context, resourceType platestwrite.ResourceType, resourceID int64, searchParam string) bool { + if e.writeTracker == nil { + return false + } + if resourceID > 0 && e.writeTracker.CheckWriteFlagByID(ctx, resourceType, resourceID) { + return true + } + if searchParam != "" && e.writeTracker.CheckWriteFlagBySearchParam(ctx, resourceType, searchParam) { + return true + } + return false +} + +func buildRecordSearchParam(spaceID, exptID int64) string { + return fmt.Sprintf("%d:%d", spaceID, exptID) +} + +func buildFeedbackSearchParam(spaceID, exptID, recordID int64) string { + return fmt.Sprintf("%d:%d:%d", spaceID, exptID, recordID) +} diff --git a/backend/modules/evaluation/infra/repo/experiment/expt_insight_analysis_record_test.go b/backend/modules/evaluation/infra/repo/experiment/expt_insight_analysis_record_test.go index a8070373b..30a829500 100644 --- a/backend/modules/evaluation/infra/repo/experiment/expt_insight_analysis_record_test.go +++ b/backend/modules/evaluation/infra/repo/experiment/expt_insight_analysis_record_test.go @@ -11,7 +11,9 @@ import ( "github.com/stretchr/testify/assert" "go.uber.org/mock/gomock" + "github.com/coze-dev/coze-loop/backend/infra/db" mockidgen "github.com/coze-dev/coze-loop/backend/infra/idgen/mocks" + platestwritemocks "github.com/coze-dev/coze-loop/backend/infra/platestwrite/mocks" "github.com/coze-dev/coze-loop/backend/modules/evaluation/domain/entity" "github.com/coze-dev/coze-loop/backend/modules/evaluation/infra/repo/experiment/mysql/gorm_gen/model" "github.com/coze-dev/coze-loop/backend/modules/evaluation/infra/repo/experiment/mysql/mocks" @@ -23,6 +25,7 @@ type testMocks struct { feedbackCommentDAO *mocks.MockIExptInsightAnalysisFeedbackCommentDAO feedbackVoteDAO *mocks.MockIExptInsightAnalysisFeedbackVoteDAO idGenerator *mockidgen.MockIIDGenerator + writeTracker *platestwritemocks.MockILatestWriteTracker } func newTestExptInsightAnalysisRecordRepo(ctrl *gomock.Controller) (*ExptInsightAnalysisRecordRepo, *testMocks) { @@ -31,6 +34,7 @@ func newTestExptInsightAnalysisRecordRepo(ctrl *gomock.Controller) (*ExptInsight feedbackCommentDAO: mocks.NewMockIExptInsightAnalysisFeedbackCommentDAO(ctrl), feedbackVoteDAO: mocks.NewMockIExptInsightAnalysisFeedbackVoteDAO(ctrl), idGenerator: mockidgen.NewMockIIDGenerator(ctrl), + writeTracker: platestwritemocks.NewMockILatestWriteTracker(ctrl), } repo := &ExptInsightAnalysisRecordRepo{ @@ -38,16 +42,29 @@ func newTestExptInsightAnalysisRecordRepo(ctrl *gomock.Controller) (*ExptInsight exptInsightAnalysisFeedbackCommentDAO: mocks.feedbackCommentDAO, exptInsightAnalysisFeedbackVoteDAO: mocks.feedbackVoteDAO, idgenerator: mocks.idGenerator, + writeTracker: mocks.writeTracker, } return repo, mocks } +func expectWriteFlagAny(m *testMocks) { + m.writeTracker.EXPECT().SetWriteFlag(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() +} + +func expectNoWriteFlagRead(m *testMocks) { + m.writeTracker.EXPECT().CheckWriteFlagByID(gomock.Any(), gomock.Any(), gomock.Any()).Return(false).AnyTimes() + m.writeTracker.EXPECT().CheckWriteFlagBySearchParam(gomock.Any(), gomock.Any(), gomock.Any()).Return(false).AnyTimes() +} + func TestExptInsightAnalysisRecordRepo_CreateAnalysisRecord(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() repo, mocks := newTestExptInsightAnalysisRecordRepo(ctrl) + expectWriteFlagAny(mocks) + expectWriteFlagAny(mocks) + expectWriteFlagAny(mocks) record := &entity.ExptInsightAnalysisRecord{ SpaceID: 1, @@ -72,6 +89,9 @@ func TestExptInsightAnalysisRecordRepo_UpdateAnalysisRecord(t *testing.T) { defer ctrl.Finish() repo, mocks := newTestExptInsightAnalysisRecordRepo(ctrl) + expectWriteFlagAny(mocks) + expectWriteFlagAny(mocks) + expectWriteFlagAny(mocks) record := &entity.ExptInsightAnalysisRecord{ ID: 1, @@ -93,8 +113,9 @@ func TestExptInsightAnalysisRecordRepo_GetAnalysisRecordByID(t *testing.T) { defer ctrl.Finish() repo, mocks := newTestExptInsightAnalysisRecordRepo(ctrl) + expectNoWriteFlagRead(mocks) - mocks.analysisRecordDAO.EXPECT().GetByID(gomock.Any(), int64(1), int64(1), int64(1), gomock.Any()).Return(&model.ExptInsightAnalysisRecord{ + mocks.analysisRecordDAO.EXPECT().GetByID(gomock.Any(), int64(1), int64(1), int64(1)).Return(&model.ExptInsightAnalysisRecord{ ID: 1, SpaceID: 1, ExptID: 1, @@ -111,11 +132,34 @@ func TestExptInsightAnalysisRecordRepo_GetAnalysisRecordByID(t *testing.T) { assert.Equal(t, int64(1), record.ID) } +func TestExptInsightAnalysisRecordRepo_GetAnalysisRecordByID_ForceMaster(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + repo, mocks := newTestExptInsightAnalysisRecordRepo(ctrl) + + mocks.writeTracker.EXPECT().CheckWriteFlagByID(gomock.Any(), gomock.Any(), int64(1)).Return(true) + mocks.writeTracker.EXPECT().CheckWriteFlagBySearchParam(gomock.Any(), gomock.Any(), gomock.Any()).Return(false).AnyTimes() + mocks.analysisRecordDAO.EXPECT().GetByID(gomock.Any(), int64(1), int64(1), int64(1), gomock.Any()).DoAndReturn( + func(_ context.Context, _ int64, _ int64, _ int64, opts ...db.Option) (*model.ExptInsightAnalysisRecord, error) { + assert.True(t, db.ContainWithMasterOpt(opts)) + return &model.ExptInsightAnalysisRecord{ID: 1, SpaceID: 1, ExptID: 1}, nil + }, + ) + + record, err := repo.GetAnalysisRecordByID(context.Background(), 1, 1, 1) + + assert.NoError(t, err) + assert.NotNil(t, record) + assert.Equal(t, int64(1), record.ID) +} + func TestExptInsightAnalysisRecordRepo_ListAnalysisRecord(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() repo, mocks := newTestExptInsightAnalysisRecordRepo(ctrl) + expectNoWriteFlagRead(mocks) mocks.analysisRecordDAO.EXPECT().List(gomock.Any(), int64(1), int64(1), entity.NewPage(1, 10)).Return([]*model.ExptInsightAnalysisRecord{ { @@ -140,6 +184,7 @@ func TestExptInsightAnalysisRecordRepo_DeleteAnalysisRecord(t *testing.T) { defer ctrl.Finish() repo, mocks := newTestExptInsightAnalysisRecordRepo(ctrl) + expectWriteFlagAny(mocks) mocks.analysisRecordDAO.EXPECT().Delete(gomock.Any(), int64(1), int64(1), int64(1)).Return(nil) @@ -153,6 +198,7 @@ func TestExptInsightAnalysisRecordRepo_CreateFeedbackComment(t *testing.T) { defer ctrl.Finish() repo, mocks := newTestExptInsightAnalysisRecordRepo(ctrl) + expectWriteFlagAny(mocks) comment := &entity.ExptInsightAnalysisFeedbackComment{ SpaceID: 1, @@ -176,6 +222,7 @@ func TestExptInsightAnalysisRecordRepo_UpdateFeedbackComment(t *testing.T) { defer ctrl.Finish() repo, mocks := newTestExptInsightAnalysisRecordRepo(ctrl) + expectWriteFlagAny(mocks) comment := &entity.ExptInsightAnalysisFeedbackComment{ ID: 1, @@ -197,7 +244,14 @@ func TestExptInsightAnalysisRecordRepo_DeleteFeedbackComment(t *testing.T) { defer ctrl.Finish() repo, mocks := newTestExptInsightAnalysisRecordRepo(ctrl) + expectWriteFlagAny(mocks) + mocks.feedbackCommentDAO.EXPECT().GetByID(gomock.Any(), int64(1), int64(1), int64(1), gomock.Any()).Return(&model.ExptInsightAnalysisFeedbackComment{ + ID: 1, + SpaceID: 1, + ExptID: 1, + AnalysisRecordID: ptr.Of(int64(1)), + }, nil) mocks.feedbackCommentDAO.EXPECT().Delete(gomock.Any(), int64(1), int64(1), int64(1)).Return(nil) err := repo.DeleteFeedbackComment(context.Background(), 1, 1, 1) @@ -205,11 +259,26 @@ func TestExptInsightAnalysisRecordRepo_DeleteFeedbackComment(t *testing.T) { assert.NoError(t, err) } +func TestExptInsightAnalysisRecordRepo_DeleteFeedbackComment_GetByIDError(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + repo, mocks := newTestExptInsightAnalysisRecordRepo(ctrl) + expectWriteFlagAny(mocks) + + mocks.feedbackCommentDAO.EXPECT().GetByID(gomock.Any(), int64(1), int64(1), int64(1), gomock.Any()).Return(nil, assert.AnError) + + err := repo.DeleteFeedbackComment(context.Background(), 1, 1, 1) + + assert.Error(t, err) +} + func TestExptInsightAnalysisRecordRepo_CreateFeedbackVote(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() repo, mocks := newTestExptInsightAnalysisRecordRepo(ctrl) + expectWriteFlagAny(mocks) vote := &entity.ExptInsightAnalysisFeedbackVote{ SpaceID: 1, @@ -233,6 +302,7 @@ func TestExptInsightAnalysisRecordRepo_UpdateFeedbackVote(t *testing.T) { defer ctrl.Finish() repo, mocks := newTestExptInsightAnalysisRecordRepo(ctrl) + expectWriteFlagAny(mocks) vote := &entity.ExptInsightAnalysisFeedbackVote{ ID: 1, @@ -254,8 +324,9 @@ func TestExptInsightAnalysisRecordRepo_GetFeedbackVoteByUser(t *testing.T) { defer ctrl.Finish() repo, mocks := newTestExptInsightAnalysisRecordRepo(ctrl) + expectNoWriteFlagRead(mocks) - mocks.feedbackVoteDAO.EXPECT().GetByUser(gomock.Any(), int64(1), int64(1), int64(1), "user123", gomock.Any()).Return(&model.ExptInsightAnalysisFeedbackVote{ + mocks.feedbackVoteDAO.EXPECT().GetByUser(gomock.Any(), int64(1), int64(1), int64(1), "user123").Return(&model.ExptInsightAnalysisFeedbackVote{ ID: 1, SpaceID: 1, ExptID: 1, @@ -278,6 +349,7 @@ func TestExptInsightAnalysisRecordRepo_CountFeedbackVote(t *testing.T) { defer ctrl.Finish() repo, mocks := newTestExptInsightAnalysisRecordRepo(ctrl) + expectNoWriteFlagRead(mocks) mocks.feedbackVoteDAO.EXPECT().Count(gomock.Any(), int64(1), int64(1), int64(1)).Return(int64(3), int64(2), nil) @@ -288,11 +360,34 @@ func TestExptInsightAnalysisRecordRepo_CountFeedbackVote(t *testing.T) { assert.Equal(t, int64(2), downVoteCount) } +func TestExptInsightAnalysisRecordRepo_CountFeedbackVote_ForceMaster(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + repo, mocks := newTestExptInsightAnalysisRecordRepo(ctrl) + + mocks.writeTracker.EXPECT().CheckWriteFlagByID(gomock.Any(), gomock.Any(), gomock.Any()).Return(false).AnyTimes() + mocks.writeTracker.EXPECT().CheckWriteFlagBySearchParam(gomock.Any(), gomock.Any(), gomock.Any()).Return(true) + mocks.feedbackVoteDAO.EXPECT().Count(gomock.Any(), int64(1), int64(1), int64(1), gomock.Any()).DoAndReturn( + func(_ context.Context, _ int64, _ int64, _ int64, opts ...db.Option) (int64, int64, error) { + assert.True(t, db.ContainWithMasterOpt(opts)) + return 3, 2, nil + }, + ) + + upVoteCount, downVoteCount, err := repo.CountFeedbackVote(context.Background(), 1, 1, 1) + + assert.NoError(t, err) + assert.Equal(t, int64(3), upVoteCount) + assert.Equal(t, int64(2), downVoteCount) +} + func TestExptInsightAnalysisRecordRepo_List(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() repo, mocks := newTestExptInsightAnalysisRecordRepo(ctrl) + expectNoWriteFlagRead(mocks) mocks.feedbackCommentDAO.EXPECT().List(gomock.Any(), int64(1), int64(1), int64(1), entity.NewPage(1, 10)).Return([]*model.ExptInsightAnalysisFeedbackComment{ { @@ -411,8 +506,9 @@ func TestExptInsightAnalysisRecordRepo_GetFeedbackCommentByRecordID(t *testing.T defer ctrl.Finish() repo, mocks := newTestExptInsightAnalysisRecordRepo(ctrl) + expectNoWriteFlagRead(mocks) - mocks.feedbackCommentDAO.EXPECT().GetByRecordID(gomock.Any(), int64(1), int64(1), int64(1), gomock.Any()).Return(&model.ExptInsightAnalysisFeedbackComment{ + mocks.feedbackCommentDAO.EXPECT().GetByRecordID(gomock.Any(), int64(1), int64(1), int64(1)).Return(&model.ExptInsightAnalysisFeedbackComment{ ID: 1, SpaceID: 1, ExptID: 1, @@ -430,13 +526,36 @@ func TestExptInsightAnalysisRecordRepo_GetFeedbackCommentByRecordID(t *testing.T assert.Equal(t, int64(1), comment.ID) } +func TestExptInsightAnalysisRecordRepo_GetFeedbackCommentByRecordID_ForceMaster(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + repo, mocks := newTestExptInsightAnalysisRecordRepo(ctrl) + + mocks.writeTracker.EXPECT().CheckWriteFlagByID(gomock.Any(), gomock.Any(), int64(1)).Return(true) + mocks.writeTracker.EXPECT().CheckWriteFlagBySearchParam(gomock.Any(), gomock.Any(), gomock.Any()).Return(false).AnyTimes() + mocks.feedbackCommentDAO.EXPECT().GetByRecordID(gomock.Any(), int64(1), int64(1), int64(1), gomock.Any()).DoAndReturn( + func(_ context.Context, _ int64, _ int64, _ int64, opts ...db.Option) (*model.ExptInsightAnalysisFeedbackComment, error) { + assert.True(t, db.ContainWithMasterOpt(opts)) + return &model.ExptInsightAnalysisFeedbackComment{ID: 1, SpaceID: 1, ExptID: 1, AnalysisRecordID: ptr.Of(int64(1))}, nil + }, + ) + + comment, err := repo.GetFeedbackCommentByRecordID(context.Background(), 1, 1, 1) + + assert.NoError(t, err) + assert.NotNil(t, comment) + assert.Equal(t, int64(1), comment.ID) +} + func TestExptInsightAnalysisRecordRepo_GetFeedbackCommentByRecordID_Error(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() repo, mocks := newTestExptInsightAnalysisRecordRepo(ctrl) + expectNoWriteFlagRead(mocks) - mocks.feedbackCommentDAO.EXPECT().GetByRecordID(gomock.Any(), int64(1), int64(1), int64(1), gomock.Any()).Return(nil, assert.AnError) + mocks.feedbackCommentDAO.EXPECT().GetByRecordID(gomock.Any(), int64(1), int64(1), int64(1)).Return(nil, assert.AnError) comment, err := repo.GetFeedbackCommentByRecordID(context.Background(), 1, 1, 1) @@ -449,8 +568,9 @@ func TestExptInsightAnalysisRecordRepo_GetFeedbackVoteByUser_Error(t *testing.T) defer ctrl.Finish() repo, mocks := newTestExptInsightAnalysisRecordRepo(ctrl) + expectNoWriteFlagRead(mocks) - mocks.feedbackVoteDAO.EXPECT().GetByUser(gomock.Any(), int64(1), int64(1), int64(1), "user123", gomock.Any()).Return(nil, assert.AnError) + mocks.feedbackVoteDAO.EXPECT().GetByUser(gomock.Any(), int64(1), int64(1), int64(1), "user123").Return(nil, assert.AnError) vote, err := repo.GetFeedbackVoteByUser(context.Background(), 1, 1, 1, "user123") @@ -463,6 +583,7 @@ func TestExptInsightAnalysisRecordRepo_List_Error(t *testing.T) { defer ctrl.Finish() repo, mocks := newTestExptInsightAnalysisRecordRepo(ctrl) + expectNoWriteFlagRead(mocks) mocks.feedbackCommentDAO.EXPECT().List(gomock.Any(), int64(1), int64(1), int64(1), entity.NewPage(1, 10)).Return(nil, int64(0), assert.AnError) diff --git a/backend/modules/evaluation/infra/repo/experiment/mysql/expt_insight_analysis_feedback_comment.go b/backend/modules/evaluation/infra/repo/experiment/mysql/expt_insight_analysis_feedback_comment.go index 33944ba43..b495b1e7a 100644 --- a/backend/modules/evaluation/infra/repo/experiment/mysql/expt_insight_analysis_feedback_comment.go +++ b/backend/modules/evaluation/infra/repo/experiment/mysql/expt_insight_analysis_feedback_comment.go @@ -19,8 +19,9 @@ type IExptInsightAnalysisFeedbackCommentDAO interface { Create(ctx context.Context, feedbackComment *model.ExptInsightAnalysisFeedbackComment, opts ...db.Option) error Update(ctx context.Context, feedbackComment *model.ExptInsightAnalysisFeedbackComment, opts ...db.Option) error GetByRecordID(ctx context.Context, spaceID, exptID, recordID int64, opts ...db.Option) (*model.ExptInsightAnalysisFeedbackComment, error) + GetByID(ctx context.Context, spaceID, exptID, commentID int64, opts ...db.Option) (*model.ExptInsightAnalysisFeedbackComment, error) Delete(ctx context.Context, spaceID, exptID, commentID int64) error - List(ctx context.Context, spaceID, exptID, recordID int64, page entity.Page) ([]*model.ExptInsightAnalysisFeedbackComment, int64, error) + List(ctx context.Context, spaceID, exptID, recordID int64, page entity.Page, opts ...db.Option) ([]*model.ExptInsightAnalysisFeedbackComment, int64, error) } func NewExptInsightAnalysisFeedbackCommentDAO(db db.Provider) IExptInsightAnalysisFeedbackCommentDAO { @@ -50,7 +51,7 @@ func (e exptInsightAnalysisFeedbackCommentDAO) Update(ctx context.Context, feedb } func (e exptInsightAnalysisFeedbackCommentDAO) GetByRecordID(ctx context.Context, spaceID, exptID, recordID int64, opts ...db.Option) (*model.ExptInsightAnalysisFeedbackComment, error) { - db := e.db.NewSession(ctx) + db := e.db.NewSession(ctx, opts...) q := query.Use(db).ExptInsightAnalysisFeedbackComment feedbackVote, err := q.WithContext(ctx).Where( @@ -65,6 +66,22 @@ func (e exptInsightAnalysisFeedbackCommentDAO) GetByRecordID(ctx context.Context return feedbackVote, nil } +func (e exptInsightAnalysisFeedbackCommentDAO) GetByID(ctx context.Context, spaceID, exptID, commentID int64, opts ...db.Option) (*model.ExptInsightAnalysisFeedbackComment, error) { + db := e.db.NewSession(ctx, opts...) + q := query.Use(db).ExptInsightAnalysisFeedbackComment + + comment, err := q.WithContext(ctx).Where( + q.SpaceID.Eq(spaceID), + q.ExptID.Eq(exptID), + q.ID.Eq(commentID), + ).First() + if err != nil { + return nil, errorx.Wrapf(err, "exptInsightAnalysisFeedbackCommentDAO GetByID fail, commentID: %v", commentID) + } + + return comment, nil +} + func (e exptInsightAnalysisFeedbackCommentDAO) Delete(ctx context.Context, spaceID, exptID, commentID int64) error { po := &model.ExptInsightAnalysisFeedbackComment{} db := e.db.NewSession(ctx) @@ -77,12 +94,12 @@ func (e exptInsightAnalysisFeedbackCommentDAO) Delete(ctx context.Context, space return nil } -func (e exptInsightAnalysisFeedbackCommentDAO) List(ctx context.Context, spaceID, exptID, recordID int64, page entity.Page) ([]*model.ExptInsightAnalysisFeedbackComment, int64, error) { +func (e exptInsightAnalysisFeedbackCommentDAO) List(ctx context.Context, spaceID, exptID, recordID int64, page entity.Page, opts ...db.Option) ([]*model.ExptInsightAnalysisFeedbackComment, int64, error) { var ( finds []*model.ExptInsightAnalysisFeedbackComment total int64 ) - db := e.db.NewSession(ctx).Model(&model.ExptInsightAnalysisFeedbackComment{}). + db := e.db.NewSession(ctx, opts...).Model(&model.ExptInsightAnalysisFeedbackComment{}). Where("space_id =?", spaceID). Where("expt_id =?", exptID). Where("analysis_record_id =?", recordID).Order("created_at DESC") diff --git a/backend/modules/evaluation/infra/repo/experiment/mysql/expt_insight_analysis_feedback_vote.go b/backend/modules/evaluation/infra/repo/experiment/mysql/expt_insight_analysis_feedback_vote.go index a22fcd40e..799b3073b 100644 --- a/backend/modules/evaluation/infra/repo/experiment/mysql/expt_insight_analysis_feedback_vote.go +++ b/backend/modules/evaluation/infra/repo/experiment/mysql/expt_insight_analysis_feedback_vote.go @@ -23,7 +23,7 @@ type IExptInsightAnalysisFeedbackVoteDAO interface { Create(ctx context.Context, feedbackVote *model.ExptInsightAnalysisFeedbackVote, opts ...db.Option) error Update(ctx context.Context, feedbackVote *model.ExptInsightAnalysisFeedbackVote, opts ...db.Option) error GetByUser(ctx context.Context, spaceID, exptID, recordID int64, userID string, opts ...db.Option) (*model.ExptInsightAnalysisFeedbackVote, error) - Count(ctx context.Context, spaceID, exptID, recordID int64) (int64, int64, error) + Count(ctx context.Context, spaceID, exptID, recordID int64, opts ...db.Option) (int64, int64, error) } func NewExptInsightAnalysisFeedbackVoteDAO(db db.Provider) IExptInsightAnalysisFeedbackVoteDAO { @@ -59,7 +59,7 @@ func (e exptInsightAnalysisFeedbackVoteDAO) Update(ctx context.Context, feedback } func (e exptInsightAnalysisFeedbackVoteDAO) GetByUser(ctx context.Context, spaceID, exptID, recordID int64, userID string, opts ...db.Option) (*model.ExptInsightAnalysisFeedbackVote, error) { - db := e.db.NewSession(ctx) + db := e.db.NewSession(ctx, opts...) q := query.Use(db).ExptInsightAnalysisFeedbackVote feedbackVote, err := q.WithContext(ctx).Where( @@ -78,8 +78,8 @@ func (e exptInsightAnalysisFeedbackVoteDAO) GetByUser(ctx context.Context, space return feedbackVote, nil } -func (e exptInsightAnalysisFeedbackVoteDAO) Count(ctx context.Context, spaceID, exptID, recordID int64) (int64, int64, error) { - db := e.db.NewSession(ctx) +func (e exptInsightAnalysisFeedbackVoteDAO) Count(ctx context.Context, spaceID, exptID, recordID int64, opts ...db.Option) (int64, int64, error) { + db := e.db.NewSession(ctx, opts...) type VoteStatistic struct { UpvoteCount int64 `json:"upvote_count"` DownvoteCount int64 `json:"downvote_count"` diff --git a/backend/modules/evaluation/infra/repo/experiment/mysql/expt_insight_analysis_record.go b/backend/modules/evaluation/infra/repo/experiment/mysql/expt_insight_analysis_record.go index a19161106..7cf1e4714 100644 --- a/backend/modules/evaluation/infra/repo/experiment/mysql/expt_insight_analysis_record.go +++ b/backend/modules/evaluation/infra/repo/experiment/mysql/expt_insight_analysis_record.go @@ -19,7 +19,7 @@ type IExptInsightAnalysisRecordDAO interface { Create(ctx context.Context, record *model.ExptInsightAnalysisRecord, opts ...db.Option) error Update(ctx context.Context, record *model.ExptInsightAnalysisRecord, opts ...db.Option) error GetByID(ctx context.Context, spaceID, exptID, recordID int64, opts ...db.Option) (*model.ExptInsightAnalysisRecord, error) - List(ctx context.Context, spaceID, exptID int64, page entity.Page) ([]*model.ExptInsightAnalysisRecord, int64, error) + List(ctx context.Context, spaceID, exptID int64, page entity.Page, opts ...db.Option) ([]*model.ExptInsightAnalysisRecord, int64, error) Delete(ctx context.Context, spaceID, exptID, recordID int64) error } @@ -50,7 +50,7 @@ func (e exptInsightAnalysisRecordDAO) Update(ctx context.Context, record *model. } func (e exptInsightAnalysisRecordDAO) GetByID(ctx context.Context, spaceID, exptID, recordID int64, opts ...db.Option) (*model.ExptInsightAnalysisRecord, error) { - db := e.db.NewSession(ctx) + db := e.db.NewSession(ctx, opts...) q := query.Use(db).ExptInsightAnalysisRecord record, err := q.WithContext(ctx).Where( @@ -65,13 +65,13 @@ func (e exptInsightAnalysisRecordDAO) GetByID(ctx context.Context, spaceID, expt return record, nil } -func (e exptInsightAnalysisRecordDAO) List(ctx context.Context, spaceID, exptID int64, page entity.Page) ([]*model.ExptInsightAnalysisRecord, int64, error) { +func (e exptInsightAnalysisRecordDAO) List(ctx context.Context, spaceID, exptID int64, page entity.Page, opts ...db.Option) ([]*model.ExptInsightAnalysisRecord, int64, error) { var ( finds []*model.ExptInsightAnalysisRecord total int64 ) - db := e.db.NewSession(ctx).Model(&model.ExptInsightAnalysisRecord{}).Where("space_id = ?", spaceID).Where("expt_id = ?", exptID) + db := e.db.NewSession(ctx, opts...).Model(&model.ExptInsightAnalysisRecord{}).Where("space_id = ?", spaceID).Where("expt_id = ?", exptID) db = db.Order("created_at desc") // 总记录数 diff --git a/backend/modules/evaluation/infra/repo/experiment/mysql/mocks/expt_insight_analysis_feedback_comment.go b/backend/modules/evaluation/infra/repo/experiment/mysql/mocks/expt_insight_analysis_feedback_comment.go index faee0a332..187b29441 100644 --- a/backend/modules/evaluation/infra/repo/experiment/mysql/mocks/expt_insight_analysis_feedback_comment.go +++ b/backend/modules/evaluation/infra/repo/experiment/mysql/mocks/expt_insight_analysis_feedback_comment.go @@ -90,10 +90,34 @@ func (mr *MockIExptInsightAnalysisFeedbackCommentDAOMockRecorder) GetByRecordID( return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetByRecordID", reflect.TypeOf((*MockIExptInsightAnalysisFeedbackCommentDAO)(nil).GetByRecordID), varargs...) } +// GetByID mocks base method. +func (m *MockIExptInsightAnalysisFeedbackCommentDAO) GetByID(arg0 context.Context, arg1, arg2, arg3 int64, arg4 ...db.Option) (*model.ExptInsightAnalysisFeedbackComment, error) { + m.ctrl.T.Helper() + varargs := []interface{}{arg0, arg1, arg2, arg3} + for _, a := range arg4 { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "GetByID", varargs...) + ret0, _ := ret[0].(*model.ExptInsightAnalysisFeedbackComment) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetByID indicates an expected call of GetByID. +func (mr *MockIExptInsightAnalysisFeedbackCommentDAOMockRecorder) GetByID(arg0, arg1, arg2, arg3 interface{}, arg4 ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{arg0, arg1, arg2, arg3}, arg4...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetByID", reflect.TypeOf((*MockIExptInsightAnalysisFeedbackCommentDAO)(nil).GetByID), varargs...) +} + // List mocks base method. -func (m *MockIExptInsightAnalysisFeedbackCommentDAO) List(arg0 context.Context, arg1, arg2, arg3 int64, arg4 entity.Page) ([]*model.ExptInsightAnalysisFeedbackComment, int64, error) { +func (m *MockIExptInsightAnalysisFeedbackCommentDAO) List(arg0 context.Context, arg1, arg2, arg3 int64, arg4 entity.Page, arg5 ...db.Option) ([]*model.ExptInsightAnalysisFeedbackComment, int64, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "List", arg0, arg1, arg2, arg3, arg4) + varargs := []interface{}{arg0, arg1, arg2, arg3, arg4} + for _, a := range arg5 { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "List", varargs...) ret0, _ := ret[0].([]*model.ExptInsightAnalysisFeedbackComment) ret1, _ := ret[1].(int64) ret2, _ := ret[2].(error) @@ -101,9 +125,10 @@ func (m *MockIExptInsightAnalysisFeedbackCommentDAO) List(arg0 context.Context, } // List indicates an expected call of List. -func (mr *MockIExptInsightAnalysisFeedbackCommentDAOMockRecorder) List(arg0, arg1, arg2, arg3, arg4 interface{}) *gomock.Call { +func (mr *MockIExptInsightAnalysisFeedbackCommentDAOMockRecorder) List(arg0, arg1, arg2, arg3, arg4 interface{}, arg5 ...interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "List", reflect.TypeOf((*MockIExptInsightAnalysisFeedbackCommentDAO)(nil).List), arg0, arg1, arg2, arg3, arg4) + varargs := append([]interface{}{arg0, arg1, arg2, arg3, arg4}, arg5...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "List", reflect.TypeOf((*MockIExptInsightAnalysisFeedbackCommentDAO)(nil).List), varargs...) } // Update mocks base method. diff --git a/backend/modules/evaluation/infra/repo/experiment/mysql/mocks/expt_insight_analysis_feedback_vote.go b/backend/modules/evaluation/infra/repo/experiment/mysql/mocks/expt_insight_analysis_feedback_vote.go index addd54589..2c12c6390 100644 --- a/backend/modules/evaluation/infra/repo/experiment/mysql/mocks/expt_insight_analysis_feedback_vote.go +++ b/backend/modules/evaluation/infra/repo/experiment/mysql/mocks/expt_insight_analysis_feedback_vote.go @@ -37,9 +37,13 @@ func (m *MockIExptInsightAnalysisFeedbackVoteDAO) EXPECT() *MockIExptInsightAnal } // Count mocks base method. -func (m *MockIExptInsightAnalysisFeedbackVoteDAO) Count(arg0 context.Context, arg1, arg2, arg3 int64) (int64, int64, error) { +func (m *MockIExptInsightAnalysisFeedbackVoteDAO) Count(arg0 context.Context, arg1, arg2, arg3 int64, arg4 ...db.Option) (int64, int64, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Count", arg0, arg1, arg2, arg3) + varargs := []interface{}{arg0, arg1, arg2, arg3} + for _, a := range arg4 { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "Count", varargs...) ret0, _ := ret[0].(int64) ret1, _ := ret[1].(int64) ret2, _ := ret[2].(error) @@ -47,9 +51,10 @@ func (m *MockIExptInsightAnalysisFeedbackVoteDAO) Count(arg0 context.Context, ar } // Count indicates an expected call of Count. -func (mr *MockIExptInsightAnalysisFeedbackVoteDAOMockRecorder) Count(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { +func (mr *MockIExptInsightAnalysisFeedbackVoteDAOMockRecorder) Count(arg0, arg1, arg2, arg3 interface{}, arg4 ...interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Count", reflect.TypeOf((*MockIExptInsightAnalysisFeedbackVoteDAO)(nil).Count), arg0, arg1, arg2, arg3) + varargs := append([]interface{}{arg0, arg1, arg2, arg3}, arg4...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Count", reflect.TypeOf((*MockIExptInsightAnalysisFeedbackVoteDAO)(nil).Count), varargs...) } // Create mocks base method. diff --git a/backend/modules/evaluation/infra/repo/experiment/mysql/mocks/expt_insight_analysis_record.go b/backend/modules/evaluation/infra/repo/experiment/mysql/mocks/expt_insight_analysis_record.go index ccffbe7fc..d27da1fab 100644 --- a/backend/modules/evaluation/infra/repo/experiment/mysql/mocks/expt_insight_analysis_record.go +++ b/backend/modules/evaluation/infra/repo/experiment/mysql/mocks/expt_insight_analysis_record.go @@ -91,9 +91,13 @@ func (mr *MockIExptInsightAnalysisRecordDAOMockRecorder) GetByID(arg0, arg1, arg } // List mocks base method. -func (m *MockIExptInsightAnalysisRecordDAO) List(arg0 context.Context, arg1, arg2 int64, arg3 entity.Page) ([]*model.ExptInsightAnalysisRecord, int64, error) { +func (m *MockIExptInsightAnalysisRecordDAO) List(arg0 context.Context, arg1, arg2 int64, arg3 entity.Page, arg4 ...db.Option) ([]*model.ExptInsightAnalysisRecord, int64, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "List", arg0, arg1, arg2, arg3) + varargs := []interface{}{arg0, arg1, arg2, arg3} + for _, a := range arg4 { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "List", varargs...) ret0, _ := ret[0].([]*model.ExptInsightAnalysisRecord) ret1, _ := ret[1].(int64) ret2, _ := ret[2].(error) @@ -101,9 +105,10 @@ func (m *MockIExptInsightAnalysisRecordDAO) List(arg0 context.Context, arg1, arg } // List indicates an expected call of List. -func (mr *MockIExptInsightAnalysisRecordDAOMockRecorder) List(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { +func (mr *MockIExptInsightAnalysisRecordDAOMockRecorder) List(arg0, arg1, arg2, arg3 interface{}, arg4 ...interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "List", reflect.TypeOf((*MockIExptInsightAnalysisRecordDAO)(nil).List), arg0, arg1, arg2, arg3) + varargs := append([]interface{}{arg0, arg1, arg2, arg3}, arg4...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "List", reflect.TypeOf((*MockIExptInsightAnalysisRecordDAO)(nil).List), varargs...) } // Update mocks base method. diff --git a/backend/modules/evaluation/infra/rpc/agent/agent.go b/backend/modules/evaluation/infra/rpc/agent/agent.go index c5b9e529f..f58c2a9d4 100644 --- a/backend/modules/evaluation/infra/rpc/agent/agent.go +++ b/backend/modules/evaluation/infra/rpc/agent/agent.go @@ -18,10 +18,10 @@ func NewAgentAdapter() rpc.IAgentAdapter { return &AgentAdapter{} } -func (a AgentAdapter) CallTraceAgent(ctx context.Context, spaceID int64, url string) (int64, error) { +func (a AgentAdapter) CallTraceAgent(ctx context.Context, spaceID int64, url string, exptId int64, startTime, endTime int64) (int64, error) { return 0, errorx.NewByCode(errno.CommonInternalErrorCode, errorx.WithExtraMsg("CallTraceAgent not implement")) } -func (a AgentAdapter) GetReport(ctx context.Context, spaceID, reportID int64) (report string, status entity.ReportStatus, err error) { - return "", 0, errorx.NewByCode(errno.CommonInternalErrorCode, errorx.WithExtraMsg("GetReport not implement")) +func (a AgentAdapter) GetReport(ctx context.Context, spaceID, reportID int64) (report string, list []*entity.InsightAnalysisReportIndex, status entity.ReportStatus, err error) { + return "", nil, entity.ReportStatus_Failed, errorx.NewByCode(errno.CommonInternalErrorCode, errorx.WithExtraMsg("GetReport not implement")) } diff --git a/backend/modules/evaluation/infra/rpc/agent/agent_test.go b/backend/modules/evaluation/infra/rpc/agent/agent_test.go index 295820750..eaa88f63c 100644 --- a/backend/modules/evaluation/infra/rpc/agent/agent_test.go +++ b/backend/modules/evaluation/infra/rpc/agent/agent_test.go @@ -34,7 +34,7 @@ func TestAgentAdapter_CallTraceAgent(t *testing.T) { ctx := context.Background() adapter, ctx := tt.setup(ctx) - result, err := adapter.CallTraceAgent(ctx, 123, "http://example.com") + result, err := adapter.CallTraceAgent(ctx, 123, "http://example.com", 0, 0) if tt.wantErr { assert.Error(t, err) diff --git a/idl/thrift/coze/loop/evaluation/domain/expt.thrift b/idl/thrift/coze/loop/evaluation/domain/expt.thrift index ed1fac97d..e906e93eb 100644 --- a/idl/thrift/coze/loop/evaluation/domain/expt.thrift +++ b/idl/thrift/coze/loop/evaluation/domain/expt.thrift @@ -479,6 +479,13 @@ struct ExptInsightAnalysisRecord { 6: optional string analysis_report_content 7: optional ExptInsightAnalysisFeedback expt_insight_analysis_feedback 8: optional common.BaseInfo base_info + + 21: optional list analysis_report_index +} + +struct ExptInsightAnalysisIndex { + 1: optional string id + 2: optional string title } // 洞察分析反馈统计