Skip to content

Commit f5988c3

Browse files
authored
Support templates in our string evaluators (#60)
2 parents 4a20abf + 77cdbf5 commit f5988c3

3 files changed

Lines changed: 45 additions & 12 deletions

File tree

cmd/eval/eval.go

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -356,7 +356,7 @@ func (h *evalCommandHandler) runEvaluators(ctx context.Context, testCase map[str
356356
func (h *evalCommandHandler) runSingleEvaluator(ctx context.Context, evaluator prompt.Evaluator, testCase map[string]interface{}, response string) (EvaluationResult, error) {
357357
switch {
358358
case evaluator.String != nil:
359-
return h.runStringEvaluator(evaluator.Name, *evaluator.String, response)
359+
return h.runStringEvaluator(evaluator.Name, *evaluator.String, testCase, response)
360360
case evaluator.LLM != nil:
361361
return h.runLLMEvaluator(ctx, evaluator.Name, *evaluator.LLM, testCase, response)
362362
case evaluator.Uses != "":
@@ -366,23 +366,39 @@ func (h *evalCommandHandler) runSingleEvaluator(ctx context.Context, evaluator p
366366
}
367367
}
368368

369-
func (h *evalCommandHandler) runStringEvaluator(name string, eval prompt.StringEvaluator, response string) (EvaluationResult, error) {
369+
func (h *evalCommandHandler) runStringEvaluator(name string, eval prompt.StringEvaluator, testCase map[string]interface{}, response string) (EvaluationResult, error) {
370370
var passed bool
371371
var details string
372372

373373
switch {
374374
case eval.Equals != "":
375-
passed = response == eval.Equals
376-
details = fmt.Sprintf("Expected exact match: '%s'", eval.Equals)
375+
equals, err := h.templateString(eval.Equals, testCase)
376+
if err != nil {
377+
return EvaluationResult{}, fmt.Errorf("failed to template message content: %w", err)
378+
}
379+
passed = response == equals
380+
details = fmt.Sprintf("Expected exact match: '%s'", equals)
377381
case eval.Contains != "":
378-
passed = strings.Contains(strings.ToLower(response), strings.ToLower(eval.Contains))
379-
details = fmt.Sprintf("Expected to contain: '%s'", eval.Contains)
382+
contains, err := h.templateString(eval.Contains, testCase)
383+
if err != nil {
384+
return EvaluationResult{}, fmt.Errorf("failed to template message content: %w", err)
385+
}
386+
passed = strings.Contains(strings.ToLower(response), strings.ToLower(contains))
387+
details = fmt.Sprintf("Expected to contain: '%s'", contains)
380388
case eval.StartsWith != "":
381-
passed = strings.HasPrefix(strings.ToLower(response), strings.ToLower(eval.StartsWith))
382-
details = fmt.Sprintf("Expected to start with: '%s'", eval.StartsWith)
389+
startsWith, err := h.templateString(eval.StartsWith, testCase)
390+
if err != nil {
391+
return EvaluationResult{}, fmt.Errorf("failed to template message content: %w", err)
392+
}
393+
passed = strings.HasPrefix(strings.ToLower(response), strings.ToLower(startsWith))
394+
details = fmt.Sprintf("Expected to start with: '%s'", startsWith)
383395
case eval.EndsWith != "":
384-
passed = strings.HasSuffix(strings.ToLower(response), strings.ToLower(eval.EndsWith))
385-
details = fmt.Sprintf("Expected to end with: '%s'", eval.EndsWith)
396+
endsWith, err := h.templateString(eval.EndsWith, testCase)
397+
if err != nil {
398+
return EvaluationResult{}, fmt.Errorf("failed to template message content: %w", err)
399+
}
400+
passed = strings.HasSuffix(strings.ToLower(response), strings.ToLower(endsWith))
401+
details = fmt.Sprintf("Expected to end with: '%s'", endsWith)
386402
default:
387403
return EvaluationResult{}, errors.New("no string evaluation criteria specified")
388404
}

cmd/eval/eval_test.go

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ evaluators:
8888
evaluator prompt.StringEvaluator
8989
response string
9090
expected bool
91+
variables map[string]interface{}
9192
}{
9293
{
9394
name: "contains match",
@@ -125,11 +126,25 @@ evaluators:
125126
response: "hello world",
126127
expected: true,
127128
},
129+
{
130+
name: "contains with variable",
131+
evaluator: prompt.StringEvaluator{Contains: "{{expected}}"},
132+
response: "hello world",
133+
expected: true,
134+
variables: map[string]interface{}{"expected": "world"},
135+
},
136+
{
137+
name: "fails with variable not match",
138+
evaluator: prompt.StringEvaluator{Contains: "{{expected}}"},
139+
response: "hello world",
140+
expected: false,
141+
variables: map[string]interface{}{"expected": "goodbye"},
142+
},
128143
}
129144

130145
for _, tt := range tests {
131146
t.Run(tt.name, func(t *testing.T) {
132-
result, err := handler.runStringEvaluator("test", tt.evaluator, tt.response)
147+
result, err := handler.runStringEvaluator("test", tt.evaluator, tt.variables, tt.response)
133148
require.NoError(t, err)
134149
require.Equal(t, tt.expected, result.Passed)
135150
if tt.expected {

examples/sample_prompt.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,10 @@ modelParameters:
66
maxTokens: 50
77
testData:
88
- input: 'hello world'
9+
string: hello
910
expected: 'greeting response'
1011
- input: 'goodbye world'
12+
string: goodbye
1113
expected: 'farewell response'
1214
messages:
1315
- role: system
@@ -17,6 +19,6 @@ messages:
1719
evaluators:
1820
- name: string evaluator
1921
string:
20-
contains: world
22+
contains: '{{string}}'
2123
- name: similarity check
2224
uses: github/similarity

0 commit comments

Comments
 (0)