SessionEvalHandler 返回一个 HTTP handler, 基于 Session 事件进行评估。 路径示例: POST /v1/evals/session 请求体: { "events": [...], // session.Event JSON 列表 "reference": "参考答案", "keywords": ["paris", "capital"], "scorers": ["keyword_coverage", "lexical_similarity"] }
()
| 777 | // "scorers": ["keyword_coverage", "lexical_similarity"] |
| 778 | // } |
| 779 | func (s *Server) SessionEvalHandler() http.Handler { |
| 780 | return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
| 781 | start := time.Now() |
| 782 | |
| 783 | if r.Method != http.MethodPost { |
| 784 | http.Error(w, "method not allowed", http.StatusMethodNotAllowed) |
| 785 | return |
| 786 | } |
| 787 | |
| 788 | var req SessionEvalRequest |
| 789 | if err := json.NewDecoder(r.Body).Decode(&req); err != nil { |
| 790 | http.Error(w, "invalid JSON body", http.StatusBadRequest) |
| 791 | return |
| 792 | } |
| 793 | |
| 794 | if len(req.Events) == 0 { |
| 795 | http.Error(w, "events is required", http.StatusBadRequest) |
| 796 | return |
| 797 | } |
| 798 | |
| 799 | scorerNames := req.Scorers |
| 800 | if len(scorerNames) == 0 { |
| 801 | scorerNames = []string{"keyword_coverage", "lexical_similarity"} |
| 802 | } |
| 803 | for _, name := range scorerNames { |
| 804 | if name != "keyword_coverage" && name != "lexical_similarity" { |
| 805 | http.Error(w, "unsupported scorer: "+name, http.StatusBadRequest) |
| 806 | return |
| 807 | } |
| 808 | } |
| 809 | |
| 810 | ctx := r.Context() |
| 811 | ctx, cancel := context.WithTimeout(ctx, 30*time.Second) |
| 812 | defer cancel() |
| 813 | |
| 814 | // 从事件构建 TextEvalInput |
| 815 | textInput := evals.BuildTextEvalInputFromEvents(req.Events) |
| 816 | textInput.Reference = req.Reference |
| 817 | |
| 818 | scores := make([]evals.ScoreResult, 0, len(scorerNames)) |
| 819 | |
| 820 | for _, name := range scorerNames { |
| 821 | switch name { |
| 822 | case "keyword_coverage": |
| 823 | scorer := evals.NewKeywordCoverageScorer(evals.KeywordCoverageConfig{ |
| 824 | Keywords: req.Keywords, |
| 825 | CaseInsensitive: true, |
| 826 | }) |
| 827 | res, err := scorer.Score(ctx, textInput) |
| 828 | if err != nil { |
| 829 | logging.Error(ctx, "http.evals.session.error", map[string]any{ |
| 830 | "scorer": "keyword_coverage", |
| 831 | "error": err.Error(), |
| 832 | "latency_ms": time.Since(start).Milliseconds(), |
| 833 | }) |
| 834 | |
| 835 | writeJSON(w, http.StatusInternalServerError, &TextEvalResponse{ |
| 836 | ErrorMessage: err.Error(), |
nothing calls this directly
no test coverage detected