Skip to content

Commit 367ece8

Browse files
test(evals): Add search agent evals for regex log search
Covers log searches that need key://pattern// regex, plus wildcard searches where regex isn't the better choice. The regex cases fail until the search agent's prompt teaches the syntax. Refs LOGS-1013
1 parent dc98e07 commit 367ece8

1 file changed

Lines changed: 120 additions & 0 deletions

File tree

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
import { SentryApiService } from "@sentry/mcp-core/api-client";
2+
import { searchEventsAgent } from "@sentry/mcp-core/tools/search-events/agent";
3+
import { describeEval, ToolCallScorer } from "vitest-evals";
4+
import { StructuredOutputScorer } from "./utils/structuredOutputScorer";
5+
import "../setup-env";
6+
7+
function messageRegexPattern(query: unknown): string | undefined {
8+
if (typeof query !== "string") {
9+
return undefined;
10+
}
11+
return query.match(/(?:^|[\s(])!?message:\/\/(.+?)\/\/(?=[\s)]|$)/)?.[1];
12+
}
13+
14+
describeEval("search-events-agent-regex", {
15+
data: async () => [
16+
{
17+
input:
18+
"Show logs from the last day whose message matches the regex `timeout after \\d+ms`",
19+
expectedTools: [],
20+
expected: {
21+
dataset: "logs",
22+
query: (value: unknown) => {
23+
const pattern = messageRegexPattern(value);
24+
return (
25+
pattern !== undefined &&
26+
pattern.includes("timeout after") &&
27+
pattern.includes("\\d")
28+
);
29+
},
30+
timeRange: { statsPeriod: "24h" },
31+
},
32+
},
33+
{
34+
input: "Find logs whose message contains an IPv4 address",
35+
expectedTools: [],
36+
expected: {
37+
dataset: "logs",
38+
query: (value: unknown) => {
39+
const pattern = messageRegexPattern(value);
40+
return (
41+
pattern !== undefined &&
42+
/\\d|\[0-9\]/.test(pattern) &&
43+
pattern.includes("\\.")
44+
);
45+
},
46+
},
47+
},
48+
{
49+
input:
50+
"Find error logs whose message ends with a 5xx status code, like 'upstream returned status=503'",
51+
expectedTools: [],
52+
expected: {
53+
dataset: "logs",
54+
query: (value: unknown) => {
55+
const pattern = messageRegexPattern(value);
56+
return (
57+
/\b(severity|level):error\b/.test(String(value)) &&
58+
pattern !== undefined &&
59+
pattern.includes("5") &&
60+
pattern.endsWith("$")
61+
);
62+
},
63+
},
64+
},
65+
{
66+
input:
67+
"Show logs whose message does not look like 'job <number> completed'",
68+
expectedTools: [],
69+
expected: {
70+
dataset: "logs",
71+
query: (value: unknown) =>
72+
typeof value === "string" &&
73+
value.includes("!message://") &&
74+
messageRegexPattern(value)?.includes("completed") === true,
75+
},
76+
},
77+
{
78+
input: "Show me error logs about database",
79+
expectedTools: [],
80+
expected: {
81+
dataset: "logs",
82+
query: (value: unknown) =>
83+
typeof value === "string" &&
84+
value.includes("*database*") &&
85+
!value.includes("://"),
86+
},
87+
},
88+
{
89+
input: "Find warning logs that mention memory",
90+
expectedTools: [],
91+
expected: {
92+
dataset: "logs",
93+
query: (value: unknown) =>
94+
typeof value === "string" &&
95+
value.includes("*memory*") &&
96+
!value.includes("://"),
97+
},
98+
},
99+
],
100+
task: async (input) => {
101+
const apiService = new SentryApiService({ accessToken: "test-token" });
102+
const agentResult = await searchEventsAgent({
103+
query: input,
104+
organizationSlug: "sentry-mcp-evals",
105+
apiService,
106+
});
107+
108+
return {
109+
result: JSON.stringify(agentResult.result),
110+
toolCalls: agentResult.toolCalls.map((call) => ({
111+
name: call.toolName,
112+
arguments:
113+
typeof call.args === "object" && call.args !== null
114+
? { ...call.args }
115+
: {},
116+
})),
117+
};
118+
},
119+
scorers: [ToolCallScorer(), StructuredOutputScorer({ match: "fuzzy" })],
120+
});

0 commit comments

Comments
 (0)