@mastra/evals 1.10.2-alpha.0 → 1.10.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -323,8 +323,11 @@ function usedNoTools() {
323
323
  });
324
324
  }
325
325
  /**
326
- * Scores 1 if none of the tool invocations resulted in an error state.
327
- * Checks for tool invocations with state other than 'result' (i.e., missing results).
326
+ * Scores 1 if none of the tool invocations resulted in an error.
327
+ *
328
+ * Treats every failure representation a native run can persist as an error: a
329
+ * thrown call (`state: 'output-error'`), an incomplete call (`state: 'call'`), a
330
+ * result the executor flagged with `isError`, and a result carrying an `error` field.
328
331
  *
329
332
  * @example
330
333
  * ```ts
@@ -340,7 +343,7 @@ function noToolErrors() {
340
343
  type: "agent"
341
344
  }).preprocess(async ({ run }) => {
342
345
  const invocations = extractRawInvocations(run.output);
343
- const errorCount = invocations.filter((inv) => inv.state === "call" || inv.result && inv.result.error).length;
346
+ const errorCount = invocations.filter(isErrorInvocation).length;
344
347
  return {
345
348
  errorCount,
346
349
  totalCalls: invocations.length,
@@ -350,13 +353,21 @@ function noToolErrors() {
350
353
  return results.preprocessStepResult?.passed ? 1 : 0;
351
354
  });
352
355
  }
356
+ /**
357
+ * True when an invocation represents a tool failure rather than a success.
358
+ *
359
+ * A natively thrown tool call is persisted as `state: 'output-error'` with
360
+ * `errorText`, so keying only on `state: 'call'` or `result.error` lets it pass.
361
+ * `isError` is set alongside `state: 'result'` when the executor reported a failure
362
+ * even though the result carries no `error` field; core's `extractTrajectory`
363
+ * likewise defines success as `state === 'result' && isError !== true`.
364
+ */
365
+ function isErrorInvocation(inv) {
366
+ return inv.state === "call" || inv.state === "output-error" || inv.isError === true || Boolean(inv.result?.error);
367
+ }
353
368
  function extractRawInvocations(output) {
354
369
  const invocations = [];
355
- for (const message of output) {
356
- const legacy = message?.content?.toolInvocations;
357
- const fromParts = legacy ? void 0 : (message?.content)?.parts?.filter((p) => p.type === "tool-invocation").map((p) => p.toolInvocation);
358
- for (const inv of legacy ?? fromParts ?? []) if (inv) invocations.push(inv);
359
- }
370
+ for (const message of output) for (const inv of require_scorers_utils.mergeToolInvocations(message)) if (inv) invocations.push(inv);
360
371
  return invocations;
361
372
  }
362
373
  /**
@@ -476,4 +487,4 @@ Object.defineProperty(exports, "usedNoTools", {
476
487
  }
477
488
  });
478
489
 
479
- //# sourceMappingURL=checks-DGTgg-nW.cjs.map
490
+ //# sourceMappingURL=checks-CGRgwpDb.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"checks-CGRgwpDb.cjs","names":["getTextContentFromMastraDBMessage","stringSimilarity","extractToolCalls","mergeToolInvocations"],"sources":["../src/scorers/code/checks/index.ts"],"sourcesContent":["import { createScorer } from '@mastra/core/evals';\nimport stringSimilarity from 'string-similarity';\nimport { extractToolCalls, getTextContentFromMastraDBMessage, mergeToolInvocations } from '../../utils';\n\n// ─── Output Text Checks ───────────────────────────────────────────────────────\n\nexport interface IncludesOptions {\n /** Case-insensitive match (default: true) */\n ignoreCase?: boolean;\n}\n\n/**\n * Scores 1 if the agent's output text contains the expected substring, 0 otherwise.\n *\n * @example\n * ```ts\n * import { checks } from '@mastra/evals';\n * const scorer = checks.includes('sunny');\n * ```\n */\nexport function includes(expected: string, options: IncludesOptions = {}) {\n const { ignoreCase = true } = options;\n return createScorer({\n id: 'check-includes',\n name: 'Includes Check',\n description: `Checks if output includes \"${expected}\"`,\n type: 'agent',\n })\n .preprocess(async ({ run }) => {\n let output = run.output\n .filter(m => m.role === 'assistant')\n .map(m => getTextContentFromMastraDBMessage(m))\n .join(' ');\n let target = expected;\n if (ignoreCase) {\n output = output.toLowerCase();\n target = target.toLowerCase();\n }\n return { output, target, found: output.includes(target) };\n })\n .generateScore(({ results }) => {\n return results.preprocessStepResult?.found ? 1 : 0;\n });\n}\n\n/**\n * Scores 1 if the agent's output text does NOT contain the substring, 0 otherwise.\n *\n * @example\n * ```ts\n * import { checks } from '@mastra/evals';\n * const scorer = checks.excludes('error');\n * ```\n */\nexport function excludes(unwanted: string, options: IncludesOptions = {}) {\n const { ignoreCase = true } = options;\n return createScorer({\n id: 'check-excludes',\n name: 'Excludes Check',\n description: `Checks that output does not include \"${unwanted}\"`,\n type: 'agent',\n })\n .preprocess(async ({ run }) => {\n let output = run.output\n .filter(m => m.role === 'assistant')\n .map(m => getTextContentFromMastraDBMessage(m))\n .join(' ');\n let target = unwanted;\n if (ignoreCase) {\n output = output.toLowerCase();\n target = target.toLowerCase();\n }\n return { output, target, excluded: !output.includes(target) };\n })\n .generateScore(({ results }) => {\n return results.preprocessStepResult?.excluded ? 1 : 0;\n });\n}\n\n/**\n * Scores 1 if the output text exactly equals the expected string (after optional normalization).\n *\n * @example\n * ```ts\n * import { checks } from '@mastra/evals';\n * const scorer = checks.equals('Hello, world!');\n * ```\n */\nexport function equals(expected: string, options: IncludesOptions = {}) {\n const { ignoreCase = true } = options;\n return createScorer({\n id: 'check-equals',\n name: 'Equals Check',\n description: `Checks if output equals \"${expected}\"`,\n type: 'agent',\n })\n .preprocess(async ({ run }) => {\n let output = run.output\n .filter(m => m.role === 'assistant')\n .map(m => getTextContentFromMastraDBMessage(m))\n .join('');\n let target = expected;\n if (ignoreCase) {\n output = output.toLowerCase();\n target = target.toLowerCase();\n }\n return { output, target, isEqual: output === target };\n })\n .generateScore(({ results }) => {\n return results.preprocessStepResult?.isEqual ? 1 : 0;\n });\n}\n\nexport interface MatchesOptions {\n /** If true, the output must match the pattern exactly (anchored). Default: false (substring match). */\n exact?: boolean;\n}\n\n/**\n * Scores 1 if the output matches the given regular expression, 0 otherwise.\n *\n * @example\n * ```ts\n * import { checks } from '@mastra/evals';\n * const scorer = checks.matches(/\\d{1,3}°[FC]/);\n * ```\n */\nexport function matches(pattern: RegExp, options: MatchesOptions = {}) {\n const { exact = false } = options;\n return createScorer({\n id: 'check-matches',\n name: 'Matches Check',\n description: `Checks if output matches pattern ${pattern}`,\n type: 'agent',\n })\n .preprocess(async ({ run }) => {\n const output = run.output\n .filter(m => m.role === 'assistant')\n .map(m => getTextContentFromMastraDBMessage(m))\n .join('');\n const regex = exact ? new RegExp(`^${pattern.source}$`, pattern.flags) : pattern;\n const matched = regex.test(output);\n return { output, pattern: pattern.toString(), matched };\n })\n .generateScore(({ results }) => {\n return results.preprocessStepResult?.matched ? 1 : 0;\n });\n}\n\nexport interface SimilarityOptions {\n /** Minimum similarity threshold (0-1) to score 1. Default: 0.7 */\n threshold?: number;\n /** Case-insensitive comparison (default: true) */\n ignoreCase?: boolean;\n}\n\n/**\n * Returns the string similarity score (0-1) between the output and an expected string.\n * Useful for fuzzy matching when exact equality is too strict.\n *\n * @example\n * ```ts\n * import { checks } from '@mastra/evals';\n * const scorer = checks.similarity('Sunny, 72°F');\n * ```\n */\nexport function similarity(expected: string, options: SimilarityOptions = {}) {\n const { ignoreCase = true, threshold } = options;\n return createScorer({\n id: 'check-similarity',\n name: 'Similarity Check',\n description: `Checks string similarity to \"${expected}\"`,\n type: 'agent',\n })\n .preprocess(async ({ run }) => {\n let output = run.output\n .filter(m => m.role === 'assistant')\n .map(m => getTextContentFromMastraDBMessage(m))\n .join(' ');\n let target = expected;\n if (ignoreCase) {\n output = output.toLowerCase();\n target = target.toLowerCase();\n }\n const score = stringSimilarity.compareTwoStrings(output, target);\n return { output, target, score, threshold };\n })\n .generateScore(({ results }) => {\n const score = results.preprocessStepResult?.score ?? 0;\n const t = results.preprocessStepResult?.threshold;\n return t !== undefined ? (score >= t ? 1 : 0) : score;\n });\n}\n\n// ─── Tool Call Checks ─────────────────────────────────────────────────────────\n\nexport interface CalledToolOptions {\n /** Minimum number of times the tool must be called. Default: 1 */\n times?: number;\n}\n\n/**\n * Scores 1 if the agent called the specified tool (at least `times` times).\n *\n * @example\n * ```ts\n * import { checks } from '@mastra/evals';\n * const scorer = checks.calledTool('get_weather');\n * const twice = checks.calledTool('search', { times: 2 });\n * ```\n */\nexport function calledTool(toolName: string, options: CalledToolOptions = {}) {\n const { times = 1 } = options;\n return createScorer({\n id: 'check-called-tool',\n name: 'Called Tool Check',\n description: `Checks that \"${toolName}\" was called${times > 1 ? ` at least ${times} times` : ''}`,\n type: 'agent',\n })\n .preprocess(async ({ run }) => {\n const { tools } = extractToolCalls(run.output);\n const count = tools.filter(t => t === toolName).length;\n return { toolName, expectedTimes: times, actualCount: count, passed: count >= times };\n })\n .generateScore(({ results }) => {\n return results.preprocessStepResult?.passed ? 1 : 0;\n });\n}\n\n/**\n * Scores 1 if the agent did NOT call the specified tool.\n *\n * @example\n * ```ts\n * import { checks } from '@mastra/evals';\n * const scorer = checks.didNotCall('delete_user');\n * ```\n */\nexport function didNotCall(toolName: string) {\n return createScorer({\n id: 'check-did-not-call',\n name: 'Did Not Call Check',\n description: `Checks that \"${toolName}\" was NOT called`,\n type: 'agent',\n })\n .preprocess(async ({ run }) => {\n const { tools } = extractToolCalls(run.output);\n const count = tools.filter(t => t === toolName).length;\n return { toolName, count, passed: count === 0 };\n })\n .generateScore(({ results }) => {\n return results.preprocessStepResult?.passed ? 1 : 0;\n });\n}\n\n/**\n * Scores 1 if the tools were called in the specified order (relaxed: allows other calls in between).\n *\n * @example\n * ```ts\n * import { checks } from '@mastra/evals';\n * const scorer = checks.toolOrder(['search', 'summarize', 'respond']);\n * ```\n */\nexport function toolOrder(expectedOrder: string[]) {\n return createScorer({\n id: 'check-tool-order',\n name: 'Tool Order Check',\n description: `Checks tool call order: [${expectedOrder.join(' → ')}]`,\n type: 'agent',\n })\n .preprocess(async ({ run }) => {\n const { tools } = extractToolCalls(run.output);\n // Check that expectedOrder appears as a subsequence of tools\n let orderIndex = 0;\n for (const tool of tools) {\n if (orderIndex < expectedOrder.length && tool === expectedOrder[orderIndex]) {\n orderIndex++;\n }\n }\n const passed = orderIndex === expectedOrder.length;\n return { actualTools: tools, expectedOrder, passed };\n })\n .generateScore(({ results }) => {\n return results.preprocessStepResult?.passed ? 1 : 0;\n });\n}\n\n/**\n * Scores 1 if the agent used no more than `max` tool calls.\n *\n * @example\n * ```ts\n * import { checks } from '@mastra/evals';\n * const scorer = checks.maxToolCalls(5);\n * ```\n */\nexport function maxToolCalls(max: number) {\n return createScorer({\n id: 'check-max-tool-calls',\n name: 'Max Tool Calls Check',\n description: `Checks that no more than ${max} tool calls were made`,\n type: 'agent',\n })\n .preprocess(async ({ run }) => {\n const { tools } = extractToolCalls(run.output);\n return { count: tools.length, max, passed: tools.length <= max };\n })\n .generateScore(({ results }) => {\n return results.preprocessStepResult?.passed ? 1 : 0;\n });\n}\n\n/**\n * Scores 1 if the agent made no tool calls at all.\n *\n * @example\n * ```ts\n * import { checks } from '@mastra/evals';\n * const scorer = checks.usedNoTools();\n * ```\n */\nexport function usedNoTools() {\n return createScorer({\n id: 'check-used-no-tools',\n name: 'Used No Tools Check',\n description: 'Checks that no tools were called',\n type: 'agent',\n })\n .preprocess(async ({ run }) => {\n const { tools } = extractToolCalls(run.output);\n return { count: tools.length, passed: tools.length === 0 };\n })\n .generateScore(({ results }) => {\n return results.preprocessStepResult?.passed ? 1 : 0;\n });\n}\n\n/**\n * Scores 1 if none of the tool invocations resulted in an error.\n *\n * Treats every failure representation a native run can persist as an error: a\n * thrown call (`state: 'output-error'`), an incomplete call (`state: 'call'`), a\n * result the executor flagged with `isError`, and a result carrying an `error` field.\n *\n * @example\n * ```ts\n * import { checks } from '@mastra/evals';\n * const scorer = checks.noToolErrors();\n * ```\n */\nexport function noToolErrors() {\n return createScorer({\n id: 'check-no-tool-errors',\n name: 'No Tool Errors Check',\n description: 'Checks that no tool calls resulted in errors',\n type: 'agent',\n })\n .preprocess(async ({ run }) => {\n const invocations = extractRawInvocations(run.output);\n const errorCount = invocations.filter(isErrorInvocation).length;\n return { errorCount, totalCalls: invocations.length, passed: errorCount === 0 };\n })\n .generateScore(({ results }) => {\n return results.preprocessStepResult?.passed ? 1 : 0;\n });\n}\n\n// ─── Internal helpers ──────────────────────────────────────────────────────────\n\n/**\n * True when an invocation represents a tool failure rather than a success.\n *\n * A natively thrown tool call is persisted as `state: 'output-error'` with\n * `errorText`, so keying only on `state: 'call'` or `result.error` lets it pass.\n * `isError` is set alongside `state: 'result'` when the executor reported a failure\n * even though the result carries no `error` field; core's `extractTrajectory`\n * likewise defines success as `state === 'result' && isError !== true`.\n */\nfunction isErrorInvocation(inv: any): boolean {\n return inv.state === 'call' || inv.state === 'output-error' || inv.isError === true || Boolean(inv.result?.error);\n}\n\nfunction extractRawInvocations(output: Parameters<typeof extractToolCalls>[0]) {\n const invocations: any[] = [];\n for (const message of output) {\n // Merge both storage forms so a call present only in `content.parts` — which is\n // where thrown calls live — is never hidden by a legacy `toolInvocations` array.\n for (const inv of mergeToolInvocations(message)) {\n if (inv) invocations.push(inv);\n }\n }\n return invocations;\n}\n\n// ─── Convenience namespace ────────────────────────────────────────────────────\n\n/**\n * Quick Checks — composable micro-scorers for common assertions.\n *\n * These are zero-LLM, zero-ceremony scorers that plug into the existing\n * `scorers: [...]` array anywhere scorers are used. Internally they are\n * standard `createScorer()` instances with the same observability, storage,\n * and pipeline integration as any other scorer.\n *\n * @example\n * ```ts\n * import { checks } from '@mastra/evals';\n *\n * await runEvals({\n * data: [...],\n * target: myAgent,\n * scorers: [\n * checks.includes('sunny'),\n * checks.calledTool('get_weather'),\n * checks.toolOrder(['search', 'summarize']),\n * checks.noToolErrors(),\n * ],\n * });\n * ```\n */\nexport const checks = {\n includes,\n excludes,\n equals,\n matches,\n similarity,\n calledTool,\n didNotCall,\n toolOrder,\n maxToolCalls,\n usedNoTools,\n noToolErrors,\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAoBA,SAAgB,SAAS,UAAkB,UAA2B,CAAC,GAAG;CACxE,MAAM,EAAE,aAAa,SAAS;CAC9B,QAAA,GAAA,mBAAA,aAAA,CAAoB;EAClB,IAAI;EACJ,MAAM;EACN,aAAa,8BAA8B,SAAS;EACpD,MAAM;CACR,CAAC,CAAC,CACC,WAAW,OAAO,EAAE,UAAU;EAC7B,IAAI,SAAS,IAAI,OACd,QAAO,MAAK,EAAE,SAAS,WAAW,CAAC,CACnC,KAAI,MAAKA,sBAAAA,kCAAkC,CAAC,CAAC,CAAC,CAC9C,KAAK,GAAG;EACX,IAAI,SAAS;EACb,IAAI,YAAY;GACd,SAAS,OAAO,YAAY;GAC5B,SAAS,OAAO,YAAY;EAC9B;EACA,OAAO;GAAE;GAAQ;GAAQ,OAAO,OAAO,SAAS,MAAM;EAAE;CAC1D,CAAC,CAAC,CACD,eAAe,EAAE,cAAc;EAC9B,OAAO,QAAQ,sBAAsB,QAAQ,IAAI;CACnD,CAAC;AACL;;;;;;;;;;AAWA,SAAgB,SAAS,UAAkB,UAA2B,CAAC,GAAG;CACxE,MAAM,EAAE,aAAa,SAAS;CAC9B,QAAA,GAAA,mBAAA,aAAA,CAAoB;EAClB,IAAI;EACJ,MAAM;EACN,aAAa,wCAAwC,SAAS;EAC9D,MAAM;CACR,CAAC,CAAC,CACC,WAAW,OAAO,EAAE,UAAU;EAC7B,IAAI,SAAS,IAAI,OACd,QAAO,MAAK,EAAE,SAAS,WAAW,CAAC,CACnC,KAAI,MAAKA,sBAAAA,kCAAkC,CAAC,CAAC,CAAC,CAC9C,KAAK,GAAG;EACX,IAAI,SAAS;EACb,IAAI,YAAY;GACd,SAAS,OAAO,YAAY;GAC5B,SAAS,OAAO,YAAY;EAC9B;EACA,OAAO;GAAE;GAAQ;GAAQ,UAAU,CAAC,OAAO,SAAS,MAAM;EAAE;CAC9D,CAAC,CAAC,CACD,eAAe,EAAE,cAAc;EAC9B,OAAO,QAAQ,sBAAsB,WAAW,IAAI;CACtD,CAAC;AACL;;;;;;;;;;AAWA,SAAgB,OAAO,UAAkB,UAA2B,CAAC,GAAG;CACtE,MAAM,EAAE,aAAa,SAAS;CAC9B,QAAA,GAAA,mBAAA,aAAA,CAAoB;EAClB,IAAI;EACJ,MAAM;EACN,aAAa,4BAA4B,SAAS;EAClD,MAAM;CACR,CAAC,CAAC,CACC,WAAW,OAAO,EAAE,UAAU;EAC7B,IAAI,SAAS,IAAI,OACd,QAAO,MAAK,EAAE,SAAS,WAAW,CAAC,CACnC,KAAI,MAAKA,sBAAAA,kCAAkC,CAAC,CAAC,CAAC,CAC9C,KAAK,EAAE;EACV,IAAI,SAAS;EACb,IAAI,YAAY;GACd,SAAS,OAAO,YAAY;GAC5B,SAAS,OAAO,YAAY;EAC9B;EACA,OAAO;GAAE;GAAQ;GAAQ,SAAS,WAAW;EAAO;CACtD,CAAC,CAAC,CACD,eAAe,EAAE,cAAc;EAC9B,OAAO,QAAQ,sBAAsB,UAAU,IAAI;CACrD,CAAC;AACL;;;;;;;;;;AAgBA,SAAgB,QAAQ,SAAiB,UAA0B,CAAC,GAAG;CACrE,MAAM,EAAE,QAAQ,UAAU;CAC1B,QAAA,GAAA,mBAAA,aAAA,CAAoB;EAClB,IAAI;EACJ,MAAM;EACN,aAAa,oCAAoC;EACjD,MAAM;CACR,CAAC,CAAC,CACC,WAAW,OAAO,EAAE,UAAU;EAC7B,MAAM,SAAS,IAAI,OAChB,QAAO,MAAK,EAAE,SAAS,WAAW,CAAC,CACnC,KAAI,MAAKA,sBAAAA,kCAAkC,CAAC,CAAC,CAAC,CAC9C,KAAK,EAAE;EAEV,MAAM,WADQ,QAAQ,IAAI,OAAO,IAAI,QAAQ,OAAO,IAAI,QAAQ,KAAK,IAAI,QAAA,CACnD,KAAK,MAAM;EACjC,OAAO;GAAE;GAAQ,SAAS,QAAQ,SAAS;GAAG;EAAQ;CACxD,CAAC,CAAC,CACD,eAAe,EAAE,cAAc;EAC9B,OAAO,QAAQ,sBAAsB,UAAU,IAAI;CACrD,CAAC;AACL;;;;;;;;;;;AAmBA,SAAgB,WAAW,UAAkB,UAA6B,CAAC,GAAG;CAC5E,MAAM,EAAE,aAAa,MAAM,cAAc;CACzC,QAAA,GAAA,mBAAA,aAAA,CAAoB;EAClB,IAAI;EACJ,MAAM;EACN,aAAa,gCAAgC,SAAS;EACtD,MAAM;CACR,CAAC,CAAC,CACC,WAAW,OAAO,EAAE,UAAU;EAC7B,IAAI,SAAS,IAAI,OACd,QAAO,MAAK,EAAE,SAAS,WAAW,CAAC,CACnC,KAAI,MAAKA,sBAAAA,kCAAkC,CAAC,CAAC,CAAC,CAC9C,KAAK,GAAG;EACX,IAAI,SAAS;EACb,IAAI,YAAY;GACd,SAAS,OAAO,YAAY;GAC5B,SAAS,OAAO,YAAY;EAC9B;EACA,MAAM,QAAQC,kBAAAA,QAAiB,kBAAkB,QAAQ,MAAM;EAC/D,OAAO;GAAE;GAAQ;GAAQ;GAAO;EAAU;CAC5C,CAAC,CAAC,CACD,eAAe,EAAE,cAAc;EAC9B,MAAM,QAAQ,QAAQ,sBAAsB,SAAS;EACrD,MAAM,IAAI,QAAQ,sBAAsB;EACxC,OAAO,MAAM,KAAA,IAAa,SAAS,IAAI,IAAI,IAAK;CAClD,CAAC;AACL;;;;;;;;;;;AAmBA,SAAgB,WAAW,UAAkB,UAA6B,CAAC,GAAG;CAC5E,MAAM,EAAE,QAAQ,MAAM;CACtB,QAAA,GAAA,mBAAA,aAAA,CAAoB;EAClB,IAAI;EACJ,MAAM;EACN,aAAa,gBAAgB,SAAS,cAAc,QAAQ,IAAI,aAAa,MAAM,UAAU;EAC7F,MAAM;CACR,CAAC,CAAC,CACC,WAAW,OAAO,EAAE,UAAU;EAC7B,MAAM,EAAE,UAAUC,sBAAAA,iBAAiB,IAAI,MAAM;EAC7C,MAAM,QAAQ,MAAM,QAAO,MAAK,MAAM,QAAQ,CAAC,CAAC;EAChD,OAAO;GAAE;GAAU,eAAe;GAAO,aAAa;GAAO,QAAQ,SAAS;EAAM;CACtF,CAAC,CAAC,CACD,eAAe,EAAE,cAAc;EAC9B,OAAO,QAAQ,sBAAsB,SAAS,IAAI;CACpD,CAAC;AACL;;;;;;;;;;AAWA,SAAgB,WAAW,UAAkB;CAC3C,QAAA,GAAA,mBAAA,aAAA,CAAoB;EAClB,IAAI;EACJ,MAAM;EACN,aAAa,gBAAgB,SAAS;EACtC,MAAM;CACR,CAAC,CAAC,CACC,WAAW,OAAO,EAAE,UAAU;EAC7B,MAAM,EAAE,UAAUA,sBAAAA,iBAAiB,IAAI,MAAM;EAC7C,MAAM,QAAQ,MAAM,QAAO,MAAK,MAAM,QAAQ,CAAC,CAAC;EAChD,OAAO;GAAE;GAAU;GAAO,QAAQ,UAAU;EAAE;CAChD,CAAC,CAAC,CACD,eAAe,EAAE,cAAc;EAC9B,OAAO,QAAQ,sBAAsB,SAAS,IAAI;CACpD,CAAC;AACL;;;;;;;;;;AAWA,SAAgB,UAAU,eAAyB;CACjD,QAAA,GAAA,mBAAA,aAAA,CAAoB;EAClB,IAAI;EACJ,MAAM;EACN,aAAa,4BAA4B,cAAc,KAAK,KAAK,EAAE;EACnE,MAAM;CACR,CAAC,CAAC,CACC,WAAW,OAAO,EAAE,UAAU;EAC7B,MAAM,EAAE,UAAUA,sBAAAA,iBAAiB,IAAI,MAAM;EAE7C,IAAI,aAAa;EACjB,KAAK,MAAM,QAAQ,OACjB,IAAI,aAAa,cAAc,UAAU,SAAS,cAAc,aAC9D;EAIJ,OAAO;GAAE,aAAa;GAAO;GAAe,QAD7B,eAAe,cAAc;EACO;CACrD,CAAC,CAAC,CACD,eAAe,EAAE,cAAc;EAC9B,OAAO,QAAQ,sBAAsB,SAAS,IAAI;CACpD,CAAC;AACL;;;;;;;;;;AAWA,SAAgB,aAAa,KAAa;CACxC,QAAA,GAAA,mBAAA,aAAA,CAAoB;EAClB,IAAI;EACJ,MAAM;EACN,aAAa,4BAA4B,IAAI;EAC7C,MAAM;CACR,CAAC,CAAC,CACC,WAAW,OAAO,EAAE,UAAU;EAC7B,MAAM,EAAE,UAAUA,sBAAAA,iBAAiB,IAAI,MAAM;EAC7C,OAAO;GAAE,OAAO,MAAM;GAAQ;GAAK,QAAQ,MAAM,UAAU;EAAI;CACjE,CAAC,CAAC,CACD,eAAe,EAAE,cAAc;EAC9B,OAAO,QAAQ,sBAAsB,SAAS,IAAI;CACpD,CAAC;AACL;;;;;;;;;;AAWA,SAAgB,cAAc;CAC5B,QAAA,GAAA,mBAAA,aAAA,CAAoB;EAClB,IAAI;EACJ,MAAM;EACN,aAAa;EACb,MAAM;CACR,CAAC,CAAC,CACC,WAAW,OAAO,EAAE,UAAU;EAC7B,MAAM,EAAE,UAAUA,sBAAAA,iBAAiB,IAAI,MAAM;EAC7C,OAAO;GAAE,OAAO,MAAM;GAAQ,QAAQ,MAAM,WAAW;EAAE;CAC3D,CAAC,CAAC,CACD,eAAe,EAAE,cAAc;EAC9B,OAAO,QAAQ,sBAAsB,SAAS,IAAI;CACpD,CAAC;AACL;;;;;;;;;;;;;;AAeA,SAAgB,eAAe;CAC7B,QAAA,GAAA,mBAAA,aAAA,CAAoB;EAClB,IAAI;EACJ,MAAM;EACN,aAAa;EACb,MAAM;CACR,CAAC,CAAC,CACC,WAAW,OAAO,EAAE,UAAU;EAC7B,MAAM,cAAc,sBAAsB,IAAI,MAAM;EACpD,MAAM,aAAa,YAAY,OAAO,iBAAiB,CAAC,CAAC;EACzD,OAAO;GAAE;GAAY,YAAY,YAAY;GAAQ,QAAQ,eAAe;EAAE;CAChF,CAAC,CAAC,CACD,eAAe,EAAE,cAAc;EAC9B,OAAO,QAAQ,sBAAsB,SAAS,IAAI;CACpD,CAAC;AACL;;;;;;;;;;AAaA,SAAS,kBAAkB,KAAmB;CAC5C,OAAO,IAAI,UAAU,UAAU,IAAI,UAAU,kBAAkB,IAAI,YAAY,QAAQ,QAAQ,IAAI,QAAQ,KAAK;AAClH;AAEA,SAAS,sBAAsB,QAAgD;CAC7E,MAAM,cAAqB,CAAC;CAC5B,KAAK,MAAM,WAAW,QAGpB,KAAK,MAAM,OAAOC,sBAAAA,qBAAqB,OAAO,GAC5C,IAAI,KAAK,YAAY,KAAK,GAAG;CAGjC,OAAO;AACT;;;;;;;;;;;;;;;;;;;;;;;;;AA4BA,MAAa,SAAS;CACpB;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF"}
@@ -1,4 +1,4 @@
1
- import { extractToolCalls, getTextContentFromMastraDBMessage } from "./scorers/utils.js";
1
+ import { extractToolCalls, getTextContentFromMastraDBMessage, mergeToolInvocations } from "./scorers/utils.js";
2
2
  import { createScorer } from "@mastra/core/evals";
3
3
  import stringSimilarity from "string-similarity";
4
4
  //#region src/scorers/code/checks/index.ts
@@ -300,8 +300,11 @@ function usedNoTools() {
300
300
  });
301
301
  }
302
302
  /**
303
- * Scores 1 if none of the tool invocations resulted in an error state.
304
- * Checks for tool invocations with state other than 'result' (i.e., missing results).
303
+ * Scores 1 if none of the tool invocations resulted in an error.
304
+ *
305
+ * Treats every failure representation a native run can persist as an error: a
306
+ * thrown call (`state: 'output-error'`), an incomplete call (`state: 'call'`), a
307
+ * result the executor flagged with `isError`, and a result carrying an `error` field.
305
308
  *
306
309
  * @example
307
310
  * ```ts
@@ -317,7 +320,7 @@ function noToolErrors() {
317
320
  type: "agent"
318
321
  }).preprocess(async ({ run }) => {
319
322
  const invocations = extractRawInvocations(run.output);
320
- const errorCount = invocations.filter((inv) => inv.state === "call" || inv.result && inv.result.error).length;
323
+ const errorCount = invocations.filter(isErrorInvocation).length;
321
324
  return {
322
325
  errorCount,
323
326
  totalCalls: invocations.length,
@@ -327,13 +330,21 @@ function noToolErrors() {
327
330
  return results.preprocessStepResult?.passed ? 1 : 0;
328
331
  });
329
332
  }
333
+ /**
334
+ * True when an invocation represents a tool failure rather than a success.
335
+ *
336
+ * A natively thrown tool call is persisted as `state: 'output-error'` with
337
+ * `errorText`, so keying only on `state: 'call'` or `result.error` lets it pass.
338
+ * `isError` is set alongside `state: 'result'` when the executor reported a failure
339
+ * even though the result carries no `error` field; core's `extractTrajectory`
340
+ * likewise defines success as `state === 'result' && isError !== true`.
341
+ */
342
+ function isErrorInvocation(inv) {
343
+ return inv.state === "call" || inv.state === "output-error" || inv.isError === true || Boolean(inv.result?.error);
344
+ }
330
345
  function extractRawInvocations(output) {
331
346
  const invocations = [];
332
- for (const message of output) {
333
- const legacy = message?.content?.toolInvocations;
334
- const fromParts = legacy ? void 0 : (message?.content)?.parts?.filter((p) => p.type === "tool-invocation").map((p) => p.toolInvocation);
335
- for (const inv of legacy ?? fromParts ?? []) if (inv) invocations.push(inv);
336
- }
347
+ for (const message of output) for (const inv of mergeToolInvocations(message)) if (inv) invocations.push(inv);
337
348
  return invocations;
338
349
  }
339
350
  /**
@@ -376,4 +387,4 @@ const checks = {
376
387
  //#endregion
377
388
  export { excludes as a, maxToolCalls as c, toolOrder as d, usedNoTools as f, equals as i, noToolErrors as l, checks as n, includes as o, didNotCall as r, matches as s, calledTool as t, similarity as u };
378
389
 
379
- //# sourceMappingURL=checks-64AonnEK.js.map
390
+ //# sourceMappingURL=checks-DX9-p5w0.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"checks-DX9-p5w0.js","names":[],"sources":["../src/scorers/code/checks/index.ts"],"sourcesContent":["import { createScorer } from '@mastra/core/evals';\nimport stringSimilarity from 'string-similarity';\nimport { extractToolCalls, getTextContentFromMastraDBMessage, mergeToolInvocations } from '../../utils';\n\n// ─── Output Text Checks ───────────────────────────────────────────────────────\n\nexport interface IncludesOptions {\n /** Case-insensitive match (default: true) */\n ignoreCase?: boolean;\n}\n\n/**\n * Scores 1 if the agent's output text contains the expected substring, 0 otherwise.\n *\n * @example\n * ```ts\n * import { checks } from '@mastra/evals';\n * const scorer = checks.includes('sunny');\n * ```\n */\nexport function includes(expected: string, options: IncludesOptions = {}) {\n const { ignoreCase = true } = options;\n return createScorer({\n id: 'check-includes',\n name: 'Includes Check',\n description: `Checks if output includes \"${expected}\"`,\n type: 'agent',\n })\n .preprocess(async ({ run }) => {\n let output = run.output\n .filter(m => m.role === 'assistant')\n .map(m => getTextContentFromMastraDBMessage(m))\n .join(' ');\n let target = expected;\n if (ignoreCase) {\n output = output.toLowerCase();\n target = target.toLowerCase();\n }\n return { output, target, found: output.includes(target) };\n })\n .generateScore(({ results }) => {\n return results.preprocessStepResult?.found ? 1 : 0;\n });\n}\n\n/**\n * Scores 1 if the agent's output text does NOT contain the substring, 0 otherwise.\n *\n * @example\n * ```ts\n * import { checks } from '@mastra/evals';\n * const scorer = checks.excludes('error');\n * ```\n */\nexport function excludes(unwanted: string, options: IncludesOptions = {}) {\n const { ignoreCase = true } = options;\n return createScorer({\n id: 'check-excludes',\n name: 'Excludes Check',\n description: `Checks that output does not include \"${unwanted}\"`,\n type: 'agent',\n })\n .preprocess(async ({ run }) => {\n let output = run.output\n .filter(m => m.role === 'assistant')\n .map(m => getTextContentFromMastraDBMessage(m))\n .join(' ');\n let target = unwanted;\n if (ignoreCase) {\n output = output.toLowerCase();\n target = target.toLowerCase();\n }\n return { output, target, excluded: !output.includes(target) };\n })\n .generateScore(({ results }) => {\n return results.preprocessStepResult?.excluded ? 1 : 0;\n });\n}\n\n/**\n * Scores 1 if the output text exactly equals the expected string (after optional normalization).\n *\n * @example\n * ```ts\n * import { checks } from '@mastra/evals';\n * const scorer = checks.equals('Hello, world!');\n * ```\n */\nexport function equals(expected: string, options: IncludesOptions = {}) {\n const { ignoreCase = true } = options;\n return createScorer({\n id: 'check-equals',\n name: 'Equals Check',\n description: `Checks if output equals \"${expected}\"`,\n type: 'agent',\n })\n .preprocess(async ({ run }) => {\n let output = run.output\n .filter(m => m.role === 'assistant')\n .map(m => getTextContentFromMastraDBMessage(m))\n .join('');\n let target = expected;\n if (ignoreCase) {\n output = output.toLowerCase();\n target = target.toLowerCase();\n }\n return { output, target, isEqual: output === target };\n })\n .generateScore(({ results }) => {\n return results.preprocessStepResult?.isEqual ? 1 : 0;\n });\n}\n\nexport interface MatchesOptions {\n /** If true, the output must match the pattern exactly (anchored). Default: false (substring match). */\n exact?: boolean;\n}\n\n/**\n * Scores 1 if the output matches the given regular expression, 0 otherwise.\n *\n * @example\n * ```ts\n * import { checks } from '@mastra/evals';\n * const scorer = checks.matches(/\\d{1,3}°[FC]/);\n * ```\n */\nexport function matches(pattern: RegExp, options: MatchesOptions = {}) {\n const { exact = false } = options;\n return createScorer({\n id: 'check-matches',\n name: 'Matches Check',\n description: `Checks if output matches pattern ${pattern}`,\n type: 'agent',\n })\n .preprocess(async ({ run }) => {\n const output = run.output\n .filter(m => m.role === 'assistant')\n .map(m => getTextContentFromMastraDBMessage(m))\n .join('');\n const regex = exact ? new RegExp(`^${pattern.source}$`, pattern.flags) : pattern;\n const matched = regex.test(output);\n return { output, pattern: pattern.toString(), matched };\n })\n .generateScore(({ results }) => {\n return results.preprocessStepResult?.matched ? 1 : 0;\n });\n}\n\nexport interface SimilarityOptions {\n /** Minimum similarity threshold (0-1) to score 1. Default: 0.7 */\n threshold?: number;\n /** Case-insensitive comparison (default: true) */\n ignoreCase?: boolean;\n}\n\n/**\n * Returns the string similarity score (0-1) between the output and an expected string.\n * Useful for fuzzy matching when exact equality is too strict.\n *\n * @example\n * ```ts\n * import { checks } from '@mastra/evals';\n * const scorer = checks.similarity('Sunny, 72°F');\n * ```\n */\nexport function similarity(expected: string, options: SimilarityOptions = {}) {\n const { ignoreCase = true, threshold } = options;\n return createScorer({\n id: 'check-similarity',\n name: 'Similarity Check',\n description: `Checks string similarity to \"${expected}\"`,\n type: 'agent',\n })\n .preprocess(async ({ run }) => {\n let output = run.output\n .filter(m => m.role === 'assistant')\n .map(m => getTextContentFromMastraDBMessage(m))\n .join(' ');\n let target = expected;\n if (ignoreCase) {\n output = output.toLowerCase();\n target = target.toLowerCase();\n }\n const score = stringSimilarity.compareTwoStrings(output, target);\n return { output, target, score, threshold };\n })\n .generateScore(({ results }) => {\n const score = results.preprocessStepResult?.score ?? 0;\n const t = results.preprocessStepResult?.threshold;\n return t !== undefined ? (score >= t ? 1 : 0) : score;\n });\n}\n\n// ─── Tool Call Checks ─────────────────────────────────────────────────────────\n\nexport interface CalledToolOptions {\n /** Minimum number of times the tool must be called. Default: 1 */\n times?: number;\n}\n\n/**\n * Scores 1 if the agent called the specified tool (at least `times` times).\n *\n * @example\n * ```ts\n * import { checks } from '@mastra/evals';\n * const scorer = checks.calledTool('get_weather');\n * const twice = checks.calledTool('search', { times: 2 });\n * ```\n */\nexport function calledTool(toolName: string, options: CalledToolOptions = {}) {\n const { times = 1 } = options;\n return createScorer({\n id: 'check-called-tool',\n name: 'Called Tool Check',\n description: `Checks that \"${toolName}\" was called${times > 1 ? ` at least ${times} times` : ''}`,\n type: 'agent',\n })\n .preprocess(async ({ run }) => {\n const { tools } = extractToolCalls(run.output);\n const count = tools.filter(t => t === toolName).length;\n return { toolName, expectedTimes: times, actualCount: count, passed: count >= times };\n })\n .generateScore(({ results }) => {\n return results.preprocessStepResult?.passed ? 1 : 0;\n });\n}\n\n/**\n * Scores 1 if the agent did NOT call the specified tool.\n *\n * @example\n * ```ts\n * import { checks } from '@mastra/evals';\n * const scorer = checks.didNotCall('delete_user');\n * ```\n */\nexport function didNotCall(toolName: string) {\n return createScorer({\n id: 'check-did-not-call',\n name: 'Did Not Call Check',\n description: `Checks that \"${toolName}\" was NOT called`,\n type: 'agent',\n })\n .preprocess(async ({ run }) => {\n const { tools } = extractToolCalls(run.output);\n const count = tools.filter(t => t === toolName).length;\n return { toolName, count, passed: count === 0 };\n })\n .generateScore(({ results }) => {\n return results.preprocessStepResult?.passed ? 1 : 0;\n });\n}\n\n/**\n * Scores 1 if the tools were called in the specified order (relaxed: allows other calls in between).\n *\n * @example\n * ```ts\n * import { checks } from '@mastra/evals';\n * const scorer = checks.toolOrder(['search', 'summarize', 'respond']);\n * ```\n */\nexport function toolOrder(expectedOrder: string[]) {\n return createScorer({\n id: 'check-tool-order',\n name: 'Tool Order Check',\n description: `Checks tool call order: [${expectedOrder.join(' → ')}]`,\n type: 'agent',\n })\n .preprocess(async ({ run }) => {\n const { tools } = extractToolCalls(run.output);\n // Check that expectedOrder appears as a subsequence of tools\n let orderIndex = 0;\n for (const tool of tools) {\n if (orderIndex < expectedOrder.length && tool === expectedOrder[orderIndex]) {\n orderIndex++;\n }\n }\n const passed = orderIndex === expectedOrder.length;\n return { actualTools: tools, expectedOrder, passed };\n })\n .generateScore(({ results }) => {\n return results.preprocessStepResult?.passed ? 1 : 0;\n });\n}\n\n/**\n * Scores 1 if the agent used no more than `max` tool calls.\n *\n * @example\n * ```ts\n * import { checks } from '@mastra/evals';\n * const scorer = checks.maxToolCalls(5);\n * ```\n */\nexport function maxToolCalls(max: number) {\n return createScorer({\n id: 'check-max-tool-calls',\n name: 'Max Tool Calls Check',\n description: `Checks that no more than ${max} tool calls were made`,\n type: 'agent',\n })\n .preprocess(async ({ run }) => {\n const { tools } = extractToolCalls(run.output);\n return { count: tools.length, max, passed: tools.length <= max };\n })\n .generateScore(({ results }) => {\n return results.preprocessStepResult?.passed ? 1 : 0;\n });\n}\n\n/**\n * Scores 1 if the agent made no tool calls at all.\n *\n * @example\n * ```ts\n * import { checks } from '@mastra/evals';\n * const scorer = checks.usedNoTools();\n * ```\n */\nexport function usedNoTools() {\n return createScorer({\n id: 'check-used-no-tools',\n name: 'Used No Tools Check',\n description: 'Checks that no tools were called',\n type: 'agent',\n })\n .preprocess(async ({ run }) => {\n const { tools } = extractToolCalls(run.output);\n return { count: tools.length, passed: tools.length === 0 };\n })\n .generateScore(({ results }) => {\n return results.preprocessStepResult?.passed ? 1 : 0;\n });\n}\n\n/**\n * Scores 1 if none of the tool invocations resulted in an error.\n *\n * Treats every failure representation a native run can persist as an error: a\n * thrown call (`state: 'output-error'`), an incomplete call (`state: 'call'`), a\n * result the executor flagged with `isError`, and a result carrying an `error` field.\n *\n * @example\n * ```ts\n * import { checks } from '@mastra/evals';\n * const scorer = checks.noToolErrors();\n * ```\n */\nexport function noToolErrors() {\n return createScorer({\n id: 'check-no-tool-errors',\n name: 'No Tool Errors Check',\n description: 'Checks that no tool calls resulted in errors',\n type: 'agent',\n })\n .preprocess(async ({ run }) => {\n const invocations = extractRawInvocations(run.output);\n const errorCount = invocations.filter(isErrorInvocation).length;\n return { errorCount, totalCalls: invocations.length, passed: errorCount === 0 };\n })\n .generateScore(({ results }) => {\n return results.preprocessStepResult?.passed ? 1 : 0;\n });\n}\n\n// ─── Internal helpers ──────────────────────────────────────────────────────────\n\n/**\n * True when an invocation represents a tool failure rather than a success.\n *\n * A natively thrown tool call is persisted as `state: 'output-error'` with\n * `errorText`, so keying only on `state: 'call'` or `result.error` lets it pass.\n * `isError` is set alongside `state: 'result'` when the executor reported a failure\n * even though the result carries no `error` field; core's `extractTrajectory`\n * likewise defines success as `state === 'result' && isError !== true`.\n */\nfunction isErrorInvocation(inv: any): boolean {\n return inv.state === 'call' || inv.state === 'output-error' || inv.isError === true || Boolean(inv.result?.error);\n}\n\nfunction extractRawInvocations(output: Parameters<typeof extractToolCalls>[0]) {\n const invocations: any[] = [];\n for (const message of output) {\n // Merge both storage forms so a call present only in `content.parts` — which is\n // where thrown calls live — is never hidden by a legacy `toolInvocations` array.\n for (const inv of mergeToolInvocations(message)) {\n if (inv) invocations.push(inv);\n }\n }\n return invocations;\n}\n\n// ─── Convenience namespace ────────────────────────────────────────────────────\n\n/**\n * Quick Checks — composable micro-scorers for common assertions.\n *\n * These are zero-LLM, zero-ceremony scorers that plug into the existing\n * `scorers: [...]` array anywhere scorers are used. Internally they are\n * standard `createScorer()` instances with the same observability, storage,\n * and pipeline integration as any other scorer.\n *\n * @example\n * ```ts\n * import { checks } from '@mastra/evals';\n *\n * await runEvals({\n * data: [...],\n * target: myAgent,\n * scorers: [\n * checks.includes('sunny'),\n * checks.calledTool('get_weather'),\n * checks.toolOrder(['search', 'summarize']),\n * checks.noToolErrors(),\n * ],\n * });\n * ```\n */\nexport const checks = {\n includes,\n excludes,\n equals,\n matches,\n similarity,\n calledTool,\n didNotCall,\n toolOrder,\n maxToolCalls,\n usedNoTools,\n noToolErrors,\n};\n"],"mappings":"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"}
package/dist/checks.cjs CHANGED
@@ -1,5 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_checks = require("./checks-DGTgg-nW.cjs");
2
+ const require_checks = require("./checks-CGRgwpDb.cjs");
3
3
  exports.calledTool = require_checks.calledTool;
4
4
  exports.checks = require_checks.checks;
5
5
  exports.didNotCall = require_checks.didNotCall;
package/dist/checks.js CHANGED
@@ -1,2 +1,2 @@
1
- import { a as excludes, c as maxToolCalls, d as toolOrder, f as usedNoTools, i as equals, l as noToolErrors, n as checks, o as includes, r as didNotCall, s as matches, t as calledTool, u as similarity } from "./checks-64AonnEK.js";
1
+ import { a as excludes, c as maxToolCalls, d as toolOrder, f as usedNoTools, i as equals, l as noToolErrors, n as checks, o as includes, r as didNotCall, s as matches, t as calledTool, u as similarity } from "./checks-DX9-p5w0.js";
2
2
  export { calledTool, checks, didNotCall, equals, excludes, includes, matches, maxToolCalls, noToolErrors, similarity, toolOrder, usedNoTools };
@@ -3,7 +3,7 @@ name: mastra-evals
3
3
  description: Documentation for @mastra/evals. Use when working with @mastra/evals APIs, configuration, or implementation.
4
4
  metadata:
5
5
  package: "@mastra/evals"
6
- version: "1.10.2-alpha.0"
6
+ version: "1.10.2"
7
7
  ---
8
8
 
9
9
  ## When to use
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.10.2-alpha.0",
2
+ "version": "1.10.2",
3
3
  "package": "@mastra/evals",
4
4
  "exports": {},
5
5
  "modules": {}
@@ -160,8 +160,11 @@ export declare function usedNoTools(): import("@mastra/core/evals").MastraScorer
160
160
  passed: boolean;
161
161
  }> & Record<"generateScoreStepResult", number>>;
162
162
  /**
163
- * Scores 1 if none of the tool invocations resulted in an error state.
164
- * Checks for tool invocations with state other than 'result' (i.e., missing results).
163
+ * Scores 1 if none of the tool invocations resulted in an error.
164
+ *
165
+ * Treats every failure representation a native run can persist as an error: a
166
+ * thrown call (`state: 'output-error'`), an incomplete call (`state: 'call'`), a
167
+ * result the executor flagged with `isError`, and a result carrying an `error` field.
165
168
  *
166
169
  * @example
167
170
  * ```ts
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/scorers/code/checks/index.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,eAAe;IAC9B,6CAA6C;IAC7C,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED;;;;;;;;GAQG;AACH,wBAAgB,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,GAAE,eAAoB;;;;gDAuBvE;AAED;;;;;;;;GAQG;AACH,wBAAgB,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,GAAE,eAAoB;;;;gDAuBvE;AAED;;;;;;;;GAQG;AACH,wBAAgB,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,GAAE,eAAoB;;;;gDAuBrE;AAED,MAAM,WAAW,cAAc;IAC7B,uGAAuG;IACvG,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED;;;;;;;;GAQG;AACH,wBAAgB,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,GAAE,cAAmB;;;;gDAoBpE;AAED,MAAM,WAAW,iBAAiB;IAChC,kEAAkE;IAClE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,kDAAkD;IAClD,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED;;;;;;;;;GASG;AACH,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,GAAE,iBAAsB;;;;;gDA0B3E;AAID,MAAM,WAAW,iBAAiB;IAChC,kEAAkE;IAClE,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;;;GASG;AACH,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,GAAE,iBAAsB;;;;;gDAgB3E;AAED;;;;;;;;GAQG;AACH,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM;;;;gDAe1C;AAED;;;;;;;;GAQG;AACH,wBAAgB,SAAS,CAAC,aAAa,EAAE,MAAM,EAAE;;;;gDAsBhD;AAED;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM;;;;gDAcvC;AAED;;;;;;;;GAQG;AACH,wBAAgB,WAAW;;;gDAc1B;AAED;;;;;;;;;GASG;AACH,wBAAgB,YAAY;;;;gDAe3B;AAsBD;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,eAAO,MAAM,MAAM;;;;;;;;;;;;CAYlB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/scorers/code/checks/index.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,eAAe;IAC9B,6CAA6C;IAC7C,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED;;;;;;;;GAQG;AACH,wBAAgB,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,GAAE,eAAoB;;;;gDAuBvE;AAED;;;;;;;;GAQG;AACH,wBAAgB,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,GAAE,eAAoB;;;;gDAuBvE;AAED;;;;;;;;GAQG;AACH,wBAAgB,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,GAAE,eAAoB;;;;gDAuBrE;AAED,MAAM,WAAW,cAAc;IAC7B,uGAAuG;IACvG,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED;;;;;;;;GAQG;AACH,wBAAgB,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,GAAE,cAAmB;;;;gDAoBpE;AAED,MAAM,WAAW,iBAAiB;IAChC,kEAAkE;IAClE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,kDAAkD;IAClD,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED;;;;;;;;;GASG;AACH,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,GAAE,iBAAsB;;;;;gDA0B3E;AAID,MAAM,WAAW,iBAAiB;IAChC,kEAAkE;IAClE,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;;;GASG;AACH,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,GAAE,iBAAsB;;;;;gDAgB3E;AAED;;;;;;;;GAQG;AACH,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM;;;;gDAe1C;AAED;;;;;;;;GAQG;AACH,wBAAgB,SAAS,CAAC,aAAa,EAAE,MAAM,EAAE;;;;gDAsBhD;AAED;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM;;;;gDAcvC;AAED;;;;;;;;GAQG;AACH,wBAAgB,WAAW;;;gDAc1B;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,YAAY;;;;gDAe3B;AA+BD;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,eAAO,MAAM,MAAM;;;;;;;;;;;;CAYlB,CAAC"}
@@ -1,5 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_checks = require("../../checks-DGTgg-nW.cjs");
2
+ const require_checks = require("../../checks-CGRgwpDb.cjs");
3
3
  const require_scorers_utils = require("../utils.cjs");
4
4
  let _mastra_core_evals = require("@mastra/core/evals");
5
5
  let compromise = require("compromise");
@@ -1,5 +1,5 @@
1
1
  import { analyzeToolFailures, checkTrajectoryBlacklist, checkTrajectoryEfficiency, compareTrajectories, extractAgentResponseMessages, extractToolCalls, getAssistantMessageFromRunOutput, getCombinedSystemPrompt, getConversationHistoryFromRunInput, getTextContentFromMastraDBMessage, getUserMessageFromRunInput, isScorerRunInputForAgent, isScorerRunOutputForAgent, roundToTwoDecimals } from "../utils.js";
2
- import { a as excludes, c as maxToolCalls, d as toolOrder, f as usedNoTools, i as equals, l as noToolErrors, n as checks, o as includes, r as didNotCall, s as matches, t as calledTool, u as similarity } from "../../checks-64AonnEK.js";
2
+ import { a as excludes, c as maxToolCalls, d as toolOrder, f as usedNoTools, i as equals, l as noToolErrors, n as checks, o as includes, r as didNotCall, s as matches, t as calledTool, u as similarity } from "../../checks-DX9-p5w0.js";
3
3
  import { createScorer } from "@mastra/core/evals";
4
4
  import nlp from "compromise";
5
5
  import keyword_extractor from "keyword-extractor";
@@ -352,8 +352,10 @@ const getReasoningFromRunOutput = (output) => {
352
352
  * @param options.toolCallId - Unique identifier for the tool call
353
353
  * @param options.toolName - Name of the tool being called
354
354
  * @param options.args - Arguments passed to the tool
355
- * @param options.result - Result returned by the tool
355
+ * @param options.result - Result returned by the tool (absent for a thrown call)
356
356
  * @param options.state - State of the invocation (default: 'result')
357
+ * @param options.errorText - Error text recorded when a tool threw (`state: 'output-error'`)
358
+ * @param options.isError - Set alongside `state: 'result'` when the executor reported a failure
357
359
  * @returns A tool invocation object
358
360
  *
359
361
  * @example
@@ -364,15 +366,26 @@ const getReasoningFromRunOutput = (output) => {
364
366
  * args: { location: 'London' },
365
367
  * result: { temperature: 20, condition: 'sunny' },
366
368
  * });
369
+ *
370
+ * // A tool that threw
371
+ * const thrown = createToolInvocation({
372
+ * toolCallId: 'call-456',
373
+ * toolName: 'saveTool',
374
+ * args: { value: 'x' },
375
+ * state: 'output-error',
376
+ * errorText: 'Save failed',
377
+ * });
367
378
  * ```
368
379
  */
369
- const createToolInvocation = ({ toolCallId, toolName, args, result, state = "result" }) => {
380
+ const createToolInvocation = ({ toolCallId, toolName, args, result, state = "result", errorText, isError }) => {
370
381
  return {
371
382
  toolCallId,
372
383
  toolName,
373
384
  args,
374
385
  result,
375
- state
386
+ state,
387
+ ...errorText !== void 0 && { errorText },
388
+ ...isError !== void 0 && { isError }
376
389
  };
377
390
  };
378
391
  /**
@@ -406,15 +419,34 @@ const createToolInvocation = ({ toolCallId, toolName, args, result, state = "res
406
419
  * state: 'result',
407
420
  * }],
408
421
  * });
422
+ *
423
+ * // With native V2 parts — the only form that can carry a thrown call
424
+ * const messageWithThrownTool = createTestMessage({
425
+ * content: 'That failed.',
426
+ * role: 'assistant',
427
+ * parts: [
428
+ * { type: 'text', text: 'That failed.' },
429
+ * {
430
+ * type: 'tool-invocation',
431
+ * toolInvocation: createToolInvocation({
432
+ * toolCallId: 'call-2',
433
+ * toolName: 'saveTool',
434
+ * args: { value: 'x' },
435
+ * state: 'output-error',
436
+ * errorText: 'Save failed',
437
+ * }),
438
+ * },
439
+ * ],
440
+ * });
409
441
  * ```
410
442
  */
411
- function createTestMessage({ content, role, id = "test-message", toolInvocations = [] }) {
443
+ function createTestMessage({ content, role, id = "test-message", toolInvocations = [], parts }) {
412
444
  return {
413
445
  id,
414
446
  role,
415
447
  content: {
416
448
  format: 2,
417
- parts: [{
449
+ parts: parts ?? [{
418
450
  type: "text",
419
451
  text: content
420
452
  }],
@@ -503,10 +535,56 @@ const createTrajectoryTestRun = ({ inputMessages = [], trajectory, rememberedMes
503
535
  };
504
536
  };
505
537
  /**
538
+ * Merges the two places a message can store tool invocations into one list.
539
+ *
540
+ * A native message may carry `content.parts` tool-invocation entries, the legacy
541
+ * `content.toolInvocations` array, or both. Neither form is a superset of the
542
+ * other: thrown calls are recorded in parts as `state: 'output-error'` and can be
543
+ * missing from the legacy array, while older persisted messages may only carry the
544
+ * legacy array. Reading one form exclusively therefore hides real calls.
545
+ *
546
+ * Parts win when the same `toolCallId` appears in both, since they hold the real
547
+ * terminal state. Legacy-only calls are retained before their next shared anchor so
548
+ * compatible call order survives instead of being appended.
549
+ *
550
+ * Mirrors `extractTrajectory` in `@mastra/core` (`packages/core/src/evals/types.ts`)
551
+ * so tool-use checks and trajectory scorers agree on what a run actually called.
552
+ * Keep the two in step.
553
+ *
554
+ * @param message - A single message from the scorer run output
555
+ * @returns The merged tool invocations for that message
556
+ */
557
+ function mergeToolInvocations(message) {
558
+ const legacy = message?.content?.toolInvocations;
559
+ const fromParts = message?.content?.parts?.filter((p) => p.type === "tool-invocation").map((p) => p.toolInvocation).filter(Boolean) ?? [];
560
+ const partCallIds = new Set(fromParts.map((invocation) => invocation.toolCallId).filter(Boolean));
561
+ const legacyInvocations = legacy ?? [];
562
+ const legacyPositions = new Map(legacyInvocations.map((invocation, index) => [invocation?.toolCallId, index]));
563
+ const toolInvocations = [];
564
+ let legacyIndex = 0;
565
+ for (const invocation of fromParts) {
566
+ const sharedIndex = invocation.toolCallId ? legacyPositions.get(invocation.toolCallId) : void 0;
567
+ if (sharedIndex !== void 0 && sharedIndex >= legacyIndex) {
568
+ for (; legacyIndex < sharedIndex; legacyIndex++) {
569
+ const previous = legacyInvocations[legacyIndex];
570
+ if (previous && !partCallIds.has(previous.toolCallId)) toolInvocations.push(previous);
571
+ }
572
+ legacyIndex++;
573
+ }
574
+ toolInvocations.push(invocation);
575
+ }
576
+ for (; legacyIndex < legacyInvocations.length; legacyIndex++) {
577
+ const invocation = legacyInvocations[legacyIndex];
578
+ if (invocation && !partCallIds.has(invocation.toolCallId)) toolInvocations.push(invocation);
579
+ }
580
+ return toolInvocations;
581
+ }
582
+ /**
506
583
  * Extracts all tool calls from a scorer run output.
507
584
  *
508
- * Iterates through all messages and their tool invocations to collect
509
- * information about tools that were called (with state 'result' or 'call').
585
+ * Iterates through all messages and their tool invocations to collect information
586
+ * about tools that were called. A call counts regardless of outcome, so the states
587
+ * `'result'`, `'call'`, and `'output-error'` (a tool that threw) are all included.
510
588
  *
511
589
  * @param output - The scorer run output (array of MastraDBMessage)
512
590
  * @returns An object containing tool names and detailed tool call info
@@ -528,13 +606,11 @@ function extractToolCalls(output) {
528
606
  const toolCallInfos = [];
529
607
  for (let messageIndex = 0; messageIndex < output.length; messageIndex++) {
530
608
  const message = output[messageIndex];
531
- const legacy = message?.content?.toolInvocations;
532
- const fromParts = legacy ? void 0 : message?.content?.parts?.filter((p) => p.type === "tool-invocation").map((p) => p.toolInvocation);
533
- const toolInvocations = legacy ?? fromParts;
534
- if (!toolInvocations?.length) continue;
609
+ const toolInvocations = mergeToolInvocations(message);
610
+ if (!toolInvocations.length) continue;
535
611
  for (let invocationIndex = 0; invocationIndex < toolInvocations.length; invocationIndex++) {
536
612
  const invocation = toolInvocations[invocationIndex];
537
- if (invocation && invocation.toolName && (invocation.state === "result" || invocation.state === "call")) {
613
+ if (invocation && invocation.toolName && (invocation.state === "result" || invocation.state === "call" || invocation.state === "output-error")) {
538
614
  toolCalls.push(invocation.toolName);
539
615
  toolCallInfos.push({
540
616
  toolName: invocation.toolName,
@@ -616,10 +692,8 @@ const extractAgentResponseMessages = (runOutput) => {
616
692
  function extractToolResults(output) {
617
693
  const results = [];
618
694
  for (const message of output) {
619
- const legacy = message?.content?.toolInvocations;
620
- const fromParts = legacy ? void 0 : message?.content?.parts?.filter((p) => p.type === "tool-invocation").map((p) => p.toolInvocation);
621
- const toolInvocations = legacy ?? fromParts;
622
- if (!toolInvocations?.length) continue;
695
+ const toolInvocations = mergeToolInvocations(message);
696
+ if (!toolInvocations.length) continue;
623
697
  for (const invocation of toolInvocations) if (invocation.state === "result" && invocation.result !== void 0) results.push({
624
698
  toolName: invocation.toolName,
625
699
  toolCallId: invocation.toolCallId || "",
@@ -1030,6 +1104,7 @@ exports.getUserMessageFromRunInput = getUserMessageFromRunInput;
1030
1104
  exports.isCloserTo = isCloserTo;
1031
1105
  exports.isScorerRunInputForAgent = isScorerRunInputForAgent;
1032
1106
  exports.isScorerRunOutputForAgent = isScorerRunOutputForAgent;
1107
+ exports.mergeToolInvocations = mergeToolInvocations;
1033
1108
  exports.roundToTwoDecimals = roundToTwoDecimals;
1034
1109
 
1035
1110
  //# sourceMappingURL=utils.cjs.map