local grader = require("evalframe.model.grader")
local std = require("evalframe.std")
local M = {}
M.exact_match = grader "exact_match" {
check = function(resp, case)
if not case.expected then return false end
local text = resp.text or ""
for _, exp in ipairs(case.expected) do
if text == exp then return true end
end
return false
end,
}
M.contains = grader "contains" {
check = function(resp, case)
if not case.expected then return false end
local text = resp.text or ""
for _, exp in ipairs(case.expected) do
if text:find(exp, 1, true) then return true end
end
return false
end,
}
M.starts_with = grader "starts_with" {
check = function(resp, case)
if not case.expected then return false end
local text = resp.text or ""
for _, exp in ipairs(case.expected) do
if text:sub(1, #exp) == exp then return true end
end
return false
end,
}
M.regex = grader "regex" {
check = function(resp, case)
local text = resp.text or ""
local pattern = (case.context and case.context.pattern)
or (case.expected and case.expected[1])
if not pattern then return false end
return text:match(pattern) ~= nil
end,
}
M.json_valid = grader "json_valid" {
check = function(resp, _case)
local text = resp.text or ""
local ok = pcall(std.json.decode, text)
return ok
end,
}
M.length = grader "length" {
check = function(resp, _case)
return #(resp.text or "")
end,
}
M.latency = grader "latency" {
check = function(resp, _case)
return resp.latency_ms end,
}
M.not_empty = grader "not_empty" {
check = function(resp, _case)
local text = resp.text or ""
return #text > 0
end,
}
M.valid_json_response = grader.all(M.not_empty, M.json_valid)
M.flexible_match = grader.any(M.exact_match, M.contains)
return M