@mastra/evals 1.10.3 → 1.10.4-alpha.0

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.
@@ -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.3"
6
+ version: "1.10.4-alpha.0"
7
7
  ---
8
8
 
9
9
  ## When to use
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.10.3",
2
+ "version": "1.10.4-alpha.0",
3
3
  "package": "@mastra/evals",
4
4
  "exports": {},
5
5
  "modules": {}
@@ -960,18 +960,23 @@ function checkTrajectoryEfficiency(trajectory, options = {}) {
960
960
  if (!(trajectory.totalDurationMs !== void 0 ? isMeasurement(trajectory.totalDurationMs) : trajectory.steps.length > 0 && trajectory.steps.every((step) => isMeasurement(step.durationMs))) || !isMeasurement(totalDurationMs)) throw new Error("Cannot evaluate duration budget: missing or invalid duration measurements");
961
961
  }
962
962
  const redundantCalls = [];
963
- if (noRedundantCalls) for (let i = 1; i < trajectory.steps.length; i++) {
964
- const prev = trajectory.steps[i - 1];
965
- const curr = trajectory.steps[i];
966
- if (prev.name === curr.name && prev.stepType === curr.stepType && (prev.stepType === "tool_call" || prev.stepType === "mcp_tool_call")) {
967
- const prevArgs = prev.toolArgs;
968
- const currArgs = curr.toolArgs;
969
- try {
970
- if (JSON.stringify(prevArgs) === JSON.stringify(currArgs)) redundantCalls.push({
971
- name: curr.name,
972
- index: i
973
- });
974
- } catch {}
963
+ const siblingLists = noRedundantCalls ? [trajectory.steps] : [];
964
+ while (siblingLists.length > 0) {
965
+ const steps = siblingLists.pop();
966
+ for (const step of steps) if (step.children?.length) siblingLists.push(step.children);
967
+ for (let i = 1; i < steps.length; i++) {
968
+ const prev = steps[i - 1];
969
+ const curr = steps[i];
970
+ if (prev.name === curr.name && prev.stepType === curr.stepType && (prev.stepType === "tool_call" || prev.stepType === "mcp_tool_call")) {
971
+ const prevArgs = prev.toolArgs;
972
+ const currArgs = curr.toolArgs;
973
+ try {
974
+ if (JSON.stringify(prevArgs) === JSON.stringify(currArgs)) redundantCalls.push({
975
+ name: curr.name,
976
+ index: i
977
+ });
978
+ } catch {}
979
+ }
975
980
  }
976
981
  }
977
982
  const overStepBudget = maxSteps !== void 0 && totalSteps > maxSteps;
@@ -1002,21 +1007,32 @@ function checkTrajectoryBlacklist(trajectory, options = {}) {
1002
1007
  const { blacklistedTools = [], blacklistedSequences = [] } = options;
1003
1008
  const violatedTools = [];
1004
1009
  const violatedSequences = [];
1005
- const stepNames = trajectory.steps.map((s) => s.name);
1006
- for (const forbidden of blacklistedTools) if (stepNames.includes(forbidden)) violatedTools.push(forbidden);
1007
- for (const sequence of blacklistedSequences) {
1008
- if (sequence.length === 0) continue;
1009
- for (let i = 0; i <= stepNames.length - sequence.length; i++) {
1010
+ const siblingLists = [];
1011
+ const allNames = /* @__PURE__ */ new Set();
1012
+ const pending = [trajectory.steps];
1013
+ while (pending.length > 0) {
1014
+ const steps = pending.pop();
1015
+ siblingLists.push(steps.map((s) => s.name));
1016
+ for (const step of steps) {
1017
+ allNames.add(step.name);
1018
+ if (step.children?.length) pending.push(step.children);
1019
+ }
1020
+ }
1021
+ for (const forbidden of blacklistedTools) if (allNames.has(forbidden)) violatedTools.push(forbidden);
1022
+ const containsSequence = (names, sequence) => {
1023
+ for (let i = 0; i <= names.length - sequence.length; i++) {
1010
1024
  let match = true;
1011
- for (let j = 0; j < sequence.length; j++) if (stepNames[i + j] !== sequence[j]) {
1025
+ for (let j = 0; j < sequence.length; j++) if (names[i + j] !== sequence[j]) {
1012
1026
  match = false;
1013
1027
  break;
1014
1028
  }
1015
- if (match) {
1016
- violatedSequences.push(sequence);
1017
- break;
1018
- }
1029
+ if (match) return true;
1019
1030
  }
1031
+ return false;
1032
+ };
1033
+ for (const sequence of blacklistedSequences) {
1034
+ if (sequence.length === 0) continue;
1035
+ if (siblingLists.some((names) => containsSequence(names, sequence))) violatedSequences.push(sequence);
1020
1036
  }
1021
1037
  return {
1022
1038
  score: violatedTools.length > 0 || violatedSequences.length > 0 ? 0 : 1,
@@ -1 +1 @@
1
- {"version":3,"file":"utils.cjs","names":["RequestContext"],"sources":["../../src/scorers/utils.ts"],"sourcesContent":["import type { MastraDBMessage } from '@mastra/core/agent';\nimport type {\n ExpectedStep,\n ScorerRunInputForAgent,\n ScorerRunOutputForAgent,\n ScoringInput,\n TrajectoryExpectation,\n TrajectoryStep,\n Trajectory,\n} from '@mastra/core/evals';\nimport { RequestContext } from '@mastra/core/request-context';\n\nexport type ScorerRunInputForLLMJudge =\n | ScorerRunInputForAgent\n | string\n | {\n inputMessages?: unknown[];\n messages?: unknown[];\n prompt?: string;\n text?: string;\n content?: unknown;\n input?: unknown;\n user?: unknown;\n [key: string]: unknown;\n };\n\nexport type ScorerRunOutputForLLMJudge =\n | ScorerRunOutputForAgent\n | string\n | unknown[]\n | {\n text?: string;\n content?: unknown;\n role?: string;\n [key: string]: unknown;\n };\n\n/**\n * Extracts text content from a MastraDBMessage or ModelMessage-like object.\n *\n * @param message - The message to extract text from\n * @returns The extracted text content, or an empty string if no text is found\n *\n * @example\n * ```ts\n * const message: MastraDBMessage = {\n * id: 'msg-1',\n * role: 'assistant',\n * content: { format: 2, parts: [{ type: 'text', text: 'Hello!' }] },\n * createdAt: new Date(),\n * };\n * const text = getTextContentFromMastraDBMessage(message); // 'Hello!'\n * ```\n */\nexport function getTextContentFromMastraDBMessage(message: MastraDBMessage): string {\n const content = message.content as any;\n\n if (typeof content === 'string') {\n return content;\n }\n if (Array.isArray(content)) {\n const textParts = content.filter(p => p.type === 'text');\n return textParts.length > 0 ? textParts[textParts.length - 1]?.text || '' : '';\n }\n if (typeof content?.content === 'string' && content.content !== '') {\n return content.content;\n }\n if (typeof content?.text === 'string' && content.text !== '') {\n return content.text;\n }\n if (content?.parts && Array.isArray(content.parts)) {\n // Return only the last text part like AI SDK does\n const textParts = content.parts.filter((p: any) => p.type === 'text');\n return textParts.length > 0 ? textParts[textParts.length - 1]?.text || '' : '';\n }\n return '';\n}\n\nconst isRecord = (value: unknown): value is Record<string, any> => {\n return typeof value === 'object' && value !== null;\n};\n\nconst getTextFromValue = (value: unknown): string | undefined => {\n if (typeof value === 'string') return value === '' ? undefined : value;\n if (Array.isArray(value)) {\n const textParts = value\n .filter(part => isRecord(part) && part.type === 'text' && typeof part.text === 'string')\n .map(part => part.text);\n return textParts.length > 0 ? textParts[textParts.length - 1] : undefined;\n }\n if (!isRecord(value)) return undefined;\n\n const fromParts = Array.isArray(value.parts) ? getTextFromValue(value.parts) : undefined;\n\n return (\n getTextFromValue(value.content) ??\n (typeof value.text === 'string' && value.text !== '' ? value.text : undefined) ??\n (typeof value.body === 'string' && value.body !== '' ? value.body : undefined) ??\n fromParts\n );\n};\n\nexport const isScorerRunInputForAgent = (input: unknown): input is ScorerRunInputForAgent => {\n return (\n isRecord(input) &&\n Array.isArray(input.inputMessages) &&\n Array.isArray(input.rememberedMessages) &&\n Array.isArray(input.systemMessages) &&\n isRecord(input.taggedSystemMessages)\n );\n};\n\nconst isMastraDBMessageLike = (message: unknown): message is MastraDBMessage => {\n return (\n isRecord(message) &&\n typeof message.id === 'string' &&\n typeof message.role === 'string' &&\n 'content' in message &&\n 'createdAt' in message\n );\n};\n\nexport const isScorerRunOutputForAgent = (output: unknown): output is ScorerRunOutputForAgent => {\n return Array.isArray(output) && output.every(isMastraDBMessageLike);\n};\n\n/**\n * Resolves the effective role of a message, accounting for agent signal messages.\n *\n * Messages delivered through the agent subscription / signal API are persisted with\n * `role: 'signal'` and carry their semantic role (e.g. `user`) on `type` and on\n * `content.metadata.signal.{type,tagName}`. Treat those as their underlying role so\n * helpers like `getUserMessageFromRunInput` can find them.\n */\nconst getEffectiveMessageRole = (message: Record<string, any>): string | undefined => {\n if (message.role !== 'signal') return typeof message.role === 'string' ? message.role : undefined;\n\n const signalMeta =\n isRecord(message.content) && isRecord(message.content.metadata) ? message.content.metadata.signal : undefined;\n\n const tagName = isRecord(signalMeta) && typeof signalMeta.tagName === 'string' ? signalMeta.tagName : undefined;\n const signalType = isRecord(signalMeta) && typeof signalMeta.type === 'string' ? signalMeta.type : undefined;\n const topLevelType = typeof message.type === 'string' ? message.type : undefined;\n\n return tagName ?? signalType ?? topLevelType;\n};\n\nconst getTextFromMessages = (messages: unknown, role: string): string | undefined => {\n if (!Array.isArray(messages)) return undefined;\n\n const message = messages.find(message => isRecord(message) && getEffectiveMessageRole(message) === role);\n return message ? getTextFromValue(message) : undefined;\n};\n\n/**\n * Rounds a number to two decimal places.\n *\n * Uses `Number.EPSILON` to handle floating-point precision issues.\n *\n * @param num - The number to round\n * @returns The number rounded to two decimal places\n *\n * @example\n * ```ts\n * roundToTwoDecimals(0.1 + 0.2); // 0.3\n * roundToTwoDecimals(1.005); // 1.01\n * ```\n */\nexport const roundToTwoDecimals = (num: number) => {\n return Math.round((num + Number.EPSILON) * 100) / 100;\n};\n\n/**\n * Determines if a value is closer to the first target than the second.\n *\n * @param value - The value to compare\n * @param target1 - The first target value\n * @param target2 - The second target value\n * @returns `true` if `value` is closer to `target1` than `target2`\n *\n * @example\n * ```ts\n * isCloserTo(0.6, 1, 0); // true (0.6 is closer to 1)\n * isCloserTo(0.3, 1, 0); // false (0.3 is closer to 0)\n * ```\n */\nexport function isCloserTo(value: number, target1: number, target2: number): boolean {\n return Math.abs(value - target1) < Math.abs(value - target2);\n}\n\n/**\n * Represents a test case for scorer evaluation.\n */\nexport type TestCase = {\n /** The input text to evaluate */\n input: string;\n /** The output text to evaluate */\n output: string;\n /** The expected result of the evaluation */\n expectedResult: {\n /** The expected score */\n score: number;\n /** The optional expected reason */\n reason?: string;\n };\n};\n\n/**\n * Represents a test case with additional context for scorer evaluation.\n */\nexport type TestCaseWithContext = TestCase & {\n /** Additional context strings for the evaluation */\n context: string[];\n};\n\n/**\n * Creates a scoring input object for testing purposes.\n *\n * @param input - The user input text\n * @param output - The assistant output text\n * @param additionalContext - Optional additional context data\n * @param requestContext - Optional request context data\n * @returns A ScoringInput object ready for use in scorer tests\n *\n * @example\n * ```ts\n * const run = createTestRun(\n * 'What is 2+2?',\n * 'The answer is 4.',\n * { topic: 'math' }\n * );\n * ```\n */\nexport const createTestRun = (\n input: string,\n output: string,\n additionalContext?: Record<string, any>,\n requestContext?: Record<string, any>,\n): ScoringInput => {\n return {\n input: [{ role: 'user', content: input }],\n output: { role: 'assistant', text: output },\n additionalContext: additionalContext ?? {},\n requestContext: requestContext ?? {},\n };\n};\n\n/**\n * Extracts the user message text from a scorer run input.\n *\n * Accepts the agent shape (`{ inputMessages }`), `ModelMessage[]`\n * (`{ messages }`), workflow input (`{ prompt }`), and a bare string.\n *\n * @param input - The scorer run input\n * @returns The user message text, or `undefined` if none can be extracted\n *\n * @example\n * ```ts\n * const scorer = createScorer({ ... })\n * .preprocess(({ run }) => {\n * const userText = getUserMessageFromRunInput(run.input);\n * return { userText };\n * });\n * ```\n */\nexport const getUserMessageFromRunInput = (input?: unknown): string | undefined => {\n if (typeof input === 'string') return input;\n if (!isRecord(input)) return undefined;\n\n return (\n getTextFromMessages(input.inputMessages, 'user') ??\n getTextFromMessages(input.messages, 'user') ??\n (typeof input.prompt === 'string' ? input.prompt : undefined) ??\n (typeof input.text === 'string' ? input.text : undefined) ??\n getTextFromValue(input.content) ??\n getTextFromValue(input.input) ??\n getTextFromValue(input.user)\n );\n};\n\nconst DEFAULT_CONVERSATION_HISTORY_MESSAGES = 10;\n\n/**\n * Renders the remembered conversation history from an agent scorer run input as a transcript.\n *\n * Only agent run inputs carry `rememberedMessages`; any other input shape (a bare string,\n * a plain `{ prompt }` object, `ModelMessage[]`) returns `undefined` so callers can safely\n * fall back to single-turn behaviour.\n *\n * @param input - The scorer run input\n * @param options.maxMessages - How many of the most recent remembered messages to keep (default 10)\n * @returns A newline separated `role: text` transcript, or `undefined` if there is no history\n *\n * @example\n * ```ts\n * const scorer = createScorer({ ... })\n * .preprocess(({ run }) => {\n * const history = getConversationHistoryFromRunInput(run.input, { maxMessages: 4 });\n * return { history };\n * });\n * ```\n */\nexport const getConversationHistoryFromRunInput = (\n input?: unknown,\n options?: { maxMessages?: number },\n): string | undefined => {\n if (!isScorerRunInputForAgent(input)) return undefined;\n\n const maxMessages = options?.maxMessages ?? DEFAULT_CONVERSATION_HISTORY_MESSAGES;\n if (maxMessages <= 0) return undefined;\n\n const lines = input.rememberedMessages\n .slice(-maxMessages)\n .map(message => {\n if (!isRecord(message)) return undefined;\n\n const role = getEffectiveMessageRole(message);\n const text = getTextContentFromMastraDBMessage(message as MastraDBMessage).trim();\n if (!role || !text) return undefined;\n\n return `${role}: ${text}`;\n })\n .filter((line): line is string => Boolean(line));\n\n return lines.length > 0 ? lines.join('\\n') : undefined;\n};\n\n/**\n * Extracts all system messages from a scorer run input.\n *\n * Collects text from both standard system messages and tagged system messages\n * (specialized system prompts like memory instructions).\n *\n * @param input - The scorer run input containing system messages\n * @returns An array of system message strings\n *\n * @example\n * ```ts\n * const scorer = createScorer({ ... })\n * .preprocess(({ run }) => {\n * const systemMessages = getSystemMessagesFromRunInput(run.input);\n * return { systemPrompt: systemMessages.join('\\n') };\n * });\n * ```\n */\nexport const getSystemMessagesFromRunInput = (input?: unknown): string[] => {\n const systemMessages: string[] = [];\n if (!isRecord(input)) return systemMessages;\n\n // Add standard system messages\n if (Array.isArray(input.systemMessages)) {\n systemMessages.push(\n ...input.systemMessages\n .map(msg => {\n // Handle different content types - extract text if it's an array of parts\n if (typeof msg.content === 'string') {\n return msg.content;\n } else if (Array.isArray(msg.content)) {\n // Extract text from parts array\n return msg.content\n .filter((part: any) => part.type === 'text')\n .map((part: any) => part.text || '')\n .join(' ');\n }\n return '';\n })\n .filter(content => content),\n );\n }\n\n const addSystemMessages = (messages: unknown) => {\n if (!Array.isArray(messages)) return;\n\n systemMessages.push(\n ...messages\n .filter(message => isRecord(message) && message.role === 'system')\n .map(message => getTextFromValue(message))\n .filter((content): content is string => Boolean(content)),\n );\n };\n\n addSystemMessages(input.inputMessages);\n addSystemMessages(input.messages);\n\n // Add tagged system messages (these are specialized system prompts)\n if (isRecord(input.taggedSystemMessages)) {\n Object.values(input.taggedSystemMessages).forEach(messages => {\n if (!Array.isArray(messages)) return;\n messages.forEach(msg => {\n const content = getTextFromValue(msg);\n if (content) {\n systemMessages.push(content);\n }\n });\n });\n }\n\n return systemMessages;\n};\n\n/**\n * Combines all system messages into a single prompt string.\n *\n * Joins all system messages (standard and tagged) with double newlines.\n *\n * @param input - The scorer run input containing system messages\n * @returns A combined system prompt string\n *\n * @example\n * ```ts\n * const scorer = createScorer({ ... })\n * .preprocess(({ run }) => {\n * const systemPrompt = getCombinedSystemPrompt(run.input);\n * return { systemPrompt };\n * });\n * ```\n */\nexport const getCombinedSystemPrompt = (input?: unknown): string => {\n const systemMessages = getSystemMessagesFromRunInput(input);\n return systemMessages.join('\\n\\n');\n};\n\n/**\n * Extracts the assistant message text from a scorer run output.\n *\n * Accepts the agent shape (`MastraDBMessage[]` / `ModelMessage[]`), workflow\n * output (`{ text }`), task output (`{ content }`), a single assistant message\n * object, and a bare string.\n *\n * For array outputs the final assistant message is used, so multi-step agent\n * responses are scored on the last response rather than an intermediate one.\n *\n * @param output - The scorer run output\n * @returns The assistant message text, or `undefined` if none can be extracted\n *\n * @example\n * ```ts\n * const scorer = createScorer({ ... })\n * .preprocess(({ run }) => {\n * const response = getAssistantMessageFromRunOutput(run.output);\n * return { response };\n * });\n * ```\n */\nexport const getAssistantMessageFromRunOutput = (output?: unknown) => {\n if (typeof output === 'string') return output;\n if (Array.isArray(output)) {\n const assistantMessages = output.filter(\n message => isRecord(message) && getEffectiveMessageRole(message) === 'assistant',\n );\n // Prefer the last assistant message that carries text; a trailing assistant\n // message may hold only tool-call or data parts.\n for (let i = assistantMessages.length - 1; i >= 0; i--) {\n const text = getTextFromValue(assistantMessages[i]);\n if (text) return text;\n }\n return undefined;\n }\n if (!isRecord(output)) return undefined;\n\n const isAssistantOutput = output.role === undefined || output.role === 'assistant';\n\n if (isAssistantOutput && typeof output.text === 'string') return output.text;\n if (isAssistantOutput && typeof output.content === 'string') return output.content;\n if (isAssistantOutput && (isRecord(output.content) || Array.isArray(output.content))) {\n return (\n getTextContentFromMastraDBMessage(output as MastraDBMessage) ||\n getTextContentFromMastraDBMessage(output.content as MastraDBMessage) ||\n undefined\n );\n }\n if (output.role === 'assistant') return getTextContentFromMastraDBMessage(output as MastraDBMessage) || undefined;\n\n return undefined;\n};\n\n/**\n * Extracts reasoning text from a scorer run output.\n *\n * This function extracts reasoning content from assistant messages, which is\n * produced by reasoning models like `deepseek-reasoner`. The reasoning can be\n * stored in two places:\n * 1. `content.reasoning` - a string field on the message content\n * 2. `content.parts` - as parts with `type: 'reasoning'` containing `details`\n *\n * @param output - The scorer run output (array of MastraDBMessage)\n * @returns The reasoning text, or `undefined` if no reasoning is present\n *\n * @example\n * ```ts\n * const reasoningScorer = createScorer({\n * id: 'reasoning-scorer',\n * name: 'Reasoning Quality',\n * description: 'Evaluates the quality of model reasoning',\n * type: 'agent',\n * })\n * .preprocess(({ run }) => {\n * const reasoning = getReasoningFromRunOutput(run.output);\n * const response = getAssistantMessageFromRunOutput(run.output);\n * return { reasoning, response };\n * })\n * .generateScore(({ results }) => {\n * // Score based on reasoning quality\n * return results.preprocessStepResult?.reasoning ? 1 : 0;\n * });\n * ```\n */\nexport const getReasoningFromRunOutput = (output?: ScorerRunOutputForAgent): string | undefined => {\n if (!output) return undefined;\n\n const message = output.find(({ role }) => role === 'assistant');\n if (!message) return undefined;\n\n // Check for reasoning in content.reasoning (string format)\n if (message.content.reasoning) {\n return message.content.reasoning;\n }\n\n // Check for reasoning in parts with type 'reasoning'\n // Reasoning models store reasoning in parts as { type: 'reasoning', details: [{ type: 'text', text: '...' }] }\n const reasoningParts = message.content.parts?.filter((p: any) => p.type === 'reasoning');\n if (reasoningParts && reasoningParts.length > 0) {\n const reasoningTexts = reasoningParts\n .map((p: any) => {\n // The reasoning text can be in p.reasoning or in p.details[].text\n if (p.details && Array.isArray(p.details)) {\n return p.details\n .filter((d: any) => d.type === 'text')\n .map((d: any) => d.text)\n .join('');\n }\n return p.reasoning || '';\n })\n .filter(Boolean);\n\n return reasoningTexts.length > 0 ? reasoningTexts.join('\\n') : undefined;\n }\n\n return undefined;\n};\n\n/**\n * Creates a tool invocation object for testing purposes.\n *\n * @param options - The tool invocation configuration\n * @param options.toolCallId - Unique identifier for the tool call\n * @param options.toolName - Name of the tool being called\n * @param options.args - Arguments passed to the tool\n * @param options.result - Result returned by the tool (absent for a thrown call)\n * @param options.state - State of the invocation (default: 'result')\n * @param options.errorText - Error text recorded when a tool threw (`state: 'output-error'`)\n * @param options.isError - Set alongside `state: 'result'` when the executor reported a failure\n * @returns A tool invocation object\n *\n * @example\n * ```ts\n * const invocation = createToolInvocation({\n * toolCallId: 'call-123',\n * toolName: 'weatherTool',\n * args: { location: 'London' },\n * result: { temperature: 20, condition: 'sunny' },\n * });\n *\n * // A tool that threw\n * const thrown = createToolInvocation({\n * toolCallId: 'call-456',\n * toolName: 'saveTool',\n * args: { value: 'x' },\n * state: 'output-error',\n * errorText: 'Save failed',\n * });\n * ```\n */\nexport const createToolInvocation = ({\n toolCallId,\n toolName,\n args,\n result,\n state = 'result',\n errorText,\n isError,\n}: {\n toolCallId: string;\n toolName: string;\n args: Record<string, any>;\n result?: Record<string, any>;\n state?: 'call' | 'partial-call' | 'result' | 'output-error';\n errorText?: string;\n isError?: boolean;\n}): {\n toolCallId: string;\n toolName: string;\n args: Record<string, any>;\n result: Record<string, any> | undefined;\n state: 'call' | 'partial-call' | 'result' | 'output-error';\n errorText?: string;\n isError?: boolean;\n} => {\n return {\n toolCallId,\n toolName,\n args,\n result,\n state,\n ...(errorText !== undefined && { errorText }),\n ...(isError !== undefined && { isError }),\n };\n};\n\n/**\n * Creates a MastraDBMessage object for testing purposes.\n *\n * Supports optional tool invocations for testing tool call scenarios.\n *\n * @param options - The message configuration\n * @param options.content - The text content of the message\n * @param options.role - The role of the message sender ('user', 'assistant', or 'system')\n * @param options.id - Optional message ID (default: 'test-message')\n * @param options.toolInvocations - Optional array of tool invocations\n * @returns A MastraDBMessage object\n *\n * @example\n * ```ts\n * const message = createTestMessage({\n * content: 'Hello, how can I help?',\n * role: 'assistant',\n * });\n *\n * // With tool invocations\n * const messageWithTools = createTestMessage({\n * content: 'Let me check the weather.',\n * role: 'assistant',\n * toolInvocations: [{\n * toolCallId: 'call-1',\n * toolName: 'weatherTool',\n * args: { location: 'Paris' },\n * result: { temp: 22 },\n * state: 'result',\n * }],\n * });\n *\n * // With native V2 parts — the only form that can carry a thrown call\n * const messageWithThrownTool = createTestMessage({\n * content: 'That failed.',\n * role: 'assistant',\n * parts: [\n * { type: 'text', text: 'That failed.' },\n * {\n * type: 'tool-invocation',\n * toolInvocation: createToolInvocation({\n * toolCallId: 'call-2',\n * toolName: 'saveTool',\n * args: { value: 'x' },\n * state: 'output-error',\n * errorText: 'Save failed',\n * }),\n * },\n * ],\n * });\n * ```\n */\nexport function createTestMessage({\n content,\n role,\n id = 'test-message',\n toolInvocations = [],\n parts,\n}: {\n content: string;\n role: 'user' | 'assistant' | 'system';\n id?: string;\n toolInvocations?: Array<{\n toolCallId: string;\n toolName: string;\n args: Record<string, any>;\n result?: Record<string, any>;\n state: any;\n }>;\n /**\n * Native V2 message parts. Replaces the default single text part when provided.\n * Needed to express invocations the legacy array cannot carry, such as a thrown\n * call (`state: 'output-error'` with `errorText`).\n */\n parts?: MastraDBMessage['content']['parts'];\n}): MastraDBMessage {\n return {\n id,\n role,\n content: {\n format: 2,\n parts: parts ?? [{ type: 'text', text: content }],\n content,\n ...(toolInvocations.length > 0 && {\n toolInvocations: toolInvocations.map(ti => ({\n toolCallId: ti.toolCallId,\n toolName: ti.toolName,\n args: ti.args,\n result: ti.result,\n state: ti.state,\n })),\n }),\n },\n createdAt: new Date(),\n };\n}\n\n/**\n * Creates a complete agent test run object for testing scorers.\n *\n * Provides a convenient way to construct the full run object that scorers receive,\n * including input messages, output, system messages, and request context.\n *\n * @param options - The test run configuration\n * @param options.inputMessages - Array of input messages (default: [])\n * @param options.output - The output messages (required)\n * @param options.rememberedMessages - Array of remembered messages from memory (default: [])\n * @param options.systemMessages - Array of system messages (default: [])\n * @param options.taggedSystemMessages - Tagged system messages map (default: {})\n * @param options.requestContext - Request context (default: new RequestContext())\n * @param options.runId - Unique run ID (default: random UUID)\n * @returns A complete test run object\n *\n * @example\n * ```ts\n * const testRun = createAgentTestRun({\n * inputMessages: [createTestMessage({ content: 'Hello', role: 'user' })],\n * output: [createTestMessage({ content: 'Hi there!', role: 'assistant' })],\n * });\n *\n * const result = await scorer.run({\n * input: testRun.input,\n * output: testRun.output,\n * });\n * ```\n */\nexport const createAgentTestRun = ({\n inputMessages = [],\n output,\n rememberedMessages = [],\n systemMessages = [],\n taggedSystemMessages = {},\n requestContext = new RequestContext(),\n runId = crypto.randomUUID(),\n}: {\n inputMessages?: ScorerRunInputForAgent['inputMessages'];\n output: ScorerRunOutputForAgent;\n rememberedMessages?: ScorerRunInputForAgent['rememberedMessages'];\n systemMessages?: ScorerRunInputForAgent['systemMessages'];\n taggedSystemMessages?: ScorerRunInputForAgent['taggedSystemMessages'];\n requestContext?: RequestContext;\n runId?: string;\n}): {\n input: ScorerRunInputForAgent;\n output: ScorerRunOutputForAgent;\n requestContext: RequestContext;\n runId: string;\n} => {\n return {\n input: {\n inputMessages,\n rememberedMessages,\n systemMessages,\n taggedSystemMessages,\n },\n output,\n requestContext,\n runId,\n };\n};\n\n/**\n * Creates a test run for trajectory scorers where `output` is a `Trajectory`\n * (pre-extracted by the `runEvals` pipeline).\n *\n * @example\n * ```ts\n * const testRun = createTrajectoryTestRun({\n * inputMessages: [createTestMessage({ content: 'Do X', role: 'user', id: 'u1' })],\n * trajectory: {\n * steps: [\n * { stepType: 'tool_call', name: 'search', toolArgs: { q: 'test' } },\n * ],\n * },\n * });\n * ```\n */\nexport const createTrajectoryTestRun = ({\n inputMessages = [],\n trajectory,\n rememberedMessages = [],\n systemMessages = [],\n taggedSystemMessages = {},\n requestContext = new RequestContext(),\n runId = crypto.randomUUID(),\n expectedTrajectory,\n}: {\n inputMessages?: ScorerRunInputForAgent['inputMessages'];\n trajectory: Trajectory;\n rememberedMessages?: ScorerRunInputForAgent['rememberedMessages'];\n systemMessages?: ScorerRunInputForAgent['systemMessages'];\n taggedSystemMessages?: ScorerRunInputForAgent['taggedSystemMessages'];\n requestContext?: RequestContext;\n runId?: string;\n expectedTrajectory?: TrajectoryExpectation;\n}): {\n input: ScorerRunInputForAgent;\n output: Trajectory;\n requestContext: RequestContext;\n runId: string;\n expectedTrajectory?: TrajectoryExpectation;\n} => {\n return {\n input: {\n inputMessages,\n rememberedMessages,\n systemMessages,\n taggedSystemMessages,\n },\n output: trajectory,\n expectedTrajectory,\n requestContext,\n runId,\n };\n};\n\n/**\n * Information about a tool call extracted from scorer output.\n */\nexport type ToolCallInfo = {\n /** Name of the tool that was called */\n toolName: string;\n /** Unique identifier for the tool call */\n toolCallId: string;\n /** Index of the message containing this tool call */\n messageIndex: number;\n /** Index of the invocation within the message's tool invocations */\n invocationIndex: number;\n};\n\n/**\n * Merges the two places a message can store tool invocations into one list.\n *\n * A native message may carry `content.parts` tool-invocation entries, the legacy\n * `content.toolInvocations` array, or both. Neither form is a superset of the\n * other: thrown calls are recorded in parts as `state: 'output-error'` and can be\n * missing from the legacy array, while older persisted messages may only carry the\n * legacy array. Reading one form exclusively therefore hides real calls.\n *\n * Parts win when the same `toolCallId` appears in both, since they hold the real\n * terminal state. Legacy-only calls are retained before their next shared anchor so\n * compatible call order survives instead of being appended.\n *\n * Mirrors `extractTrajectory` in `@mastra/core` (`packages/core/src/evals/types.ts`)\n * so tool-use checks and trajectory scorers agree on what a run actually called.\n * Keep the two in step.\n *\n * @param message - A single message from the scorer run output\n * @returns The merged tool invocations for that message\n */\nexport function mergeToolInvocations(message: MastraDBMessage | undefined) {\n const legacy = message?.content?.toolInvocations;\n const fromParts =\n message?.content?.parts\n ?.filter((p): p is Extract<typeof p, { type: 'tool-invocation' }> => p.type === 'tool-invocation')\n .map(p => p.toolInvocation)\n .filter(Boolean) ?? [];\n const partCallIds = new Set(fromParts.map(invocation => invocation.toolCallId).filter(Boolean));\n const legacyInvocations = legacy ?? [];\n const legacyPositions = new Map(legacyInvocations.map((invocation, index) => [invocation?.toolCallId, index]));\n const toolInvocations: typeof fromParts = [];\n let legacyIndex = 0;\n for (const invocation of fromParts) {\n const sharedIndex = invocation.toolCallId ? legacyPositions.get(invocation.toolCallId) : undefined;\n if (sharedIndex !== undefined && sharedIndex >= legacyIndex) {\n // Retain legacy-only calls before their next shared anchor.\n for (; legacyIndex < sharedIndex; legacyIndex++) {\n const previous = legacyInvocations[legacyIndex];\n if (previous && !partCallIds.has(previous.toolCallId)) toolInvocations.push(previous);\n }\n legacyIndex++;\n }\n toolInvocations.push(invocation);\n }\n for (; legacyIndex < legacyInvocations.length; legacyIndex++) {\n const invocation = legacyInvocations[legacyIndex];\n if (invocation && !partCallIds.has(invocation.toolCallId)) toolInvocations.push(invocation);\n }\n return toolInvocations;\n}\n\n/**\n * Extracts all tool calls from a scorer run output.\n *\n * Iterates through all messages and their tool invocations to collect information\n * about tools that were called. A call counts regardless of outcome, so the states\n * `'result'`, `'call'`, and `'output-error'` (a tool that threw) are all included.\n *\n * @param output - The scorer run output (array of MastraDBMessage)\n * @returns An object containing tool names and detailed tool call info\n *\n * @example\n * ```ts\n * const scorer = createScorer({ ... })\n * .preprocess(({ run }) => {\n * const { tools, toolCallInfos } = extractToolCalls(run.output);\n * return {\n * toolsUsed: tools,\n * toolCount: tools.length,\n * };\n * });\n * ```\n */\nexport function extractToolCalls(output: ScorerRunOutputForAgent): { tools: string[]; toolCallInfos: ToolCallInfo[] } {\n const toolCalls: string[] = [];\n const toolCallInfos: ToolCallInfo[] = [];\n\n for (let messageIndex = 0; messageIndex < output.length; messageIndex++) {\n const message = output[messageIndex];\n const toolInvocations = mergeToolInvocations(message);\n\n if (!toolInvocations.length) continue;\n\n for (let invocationIndex = 0; invocationIndex < toolInvocations.length; invocationIndex++) {\n const invocation = toolInvocations[invocationIndex];\n // A thrown call (`output-error`) still counts: the tool ran. Partial calls do not.\n // Same state set core's `extractTrajectory` accepts.\n if (\n invocation &&\n invocation.toolName &&\n (invocation.state === 'result' || invocation.state === 'call' || invocation.state === 'output-error')\n ) {\n toolCalls.push(invocation.toolName);\n toolCallInfos.push({\n toolName: invocation.toolName,\n toolCallId: invocation.toolCallId || `${messageIndex}-${invocationIndex}`,\n messageIndex,\n invocationIndex,\n });\n }\n }\n }\n\n return { tools: toolCalls, toolCallInfos };\n}\n\n/**\n * Extracts text content from all input messages.\n *\n * @param runInput - The scorer run input\n * @returns An array of text strings from each input message\n *\n * @example\n * ```ts\n * const scorer = createScorer({ ... })\n * .preprocess(({ run }) => {\n * const messages = extractInputMessages(run.input);\n * return { allUserMessages: messages.join('\\n') };\n * });\n * ```\n */\nexport const extractInputMessages = (runInput: ScorerRunInputForAgent | undefined): string[] => {\n return runInput?.inputMessages?.map(msg => getTextContentFromMastraDBMessage(msg)) || [];\n};\n\n/**\n * Extracts text content from all assistant response messages.\n *\n * Filters for messages with role 'assistant' and extracts their text content.\n *\n * @param runOutput - The scorer run output (array of MastraDBMessage)\n * @returns An array of text strings from each assistant message\n *\n * @example\n * ```ts\n * const scorer = createScorer({ ... })\n * .preprocess(({ run }) => {\n * const responses = extractAgentResponseMessages(run.output);\n * return { allResponses: responses.join('\\n') };\n * });\n * ```\n */\nexport const extractAgentResponseMessages = (runOutput: ScorerRunOutputForAgent): string[] => {\n return runOutput.filter(msg => msg.role === 'assistant').map(msg => getTextContentFromMastraDBMessage(msg));\n};\n\n/**\n * Information about a tool result extracted from scorer output.\n */\nexport type ToolResultInfo = {\n /** Name of the tool that was called */\n toolName: string;\n /** Unique identifier for the tool call */\n toolCallId: string;\n /** Arguments passed to the tool */\n args: Record<string, any>;\n /** Result returned by the tool */\n result: any;\n};\n\n/**\n * Extracts tool results from a scorer run output.\n *\n * Returns structured objects that can be used with the hallucination scorer's\n * `getContext` hook or for other scorer logic.\n *\n * @param output - The scorer run output (array of MastraDBMessage)\n * @returns An array of ToolResultInfo objects\n *\n * @example\n * ```ts\n * import { extractToolResults } from '@mastra/evals/scorers';\n * import { createHallucinationScorer } from '@mastra/evals/scorers/prebuilt';\n *\n * const scorer = createHallucinationScorer({\n * model: openai('gpt-4o'),\n * options: {\n * getContext: (run) => {\n * const toolResults = extractToolResults(run.output);\n * return toolResults.map(t => JSON.stringify({ tool: t.toolName, result: t.result }));\n * },\n * },\n * });\n * ```\n */\nexport function extractToolResults(output: ScorerRunOutputForAgent): ToolResultInfo[] {\n const results: ToolResultInfo[] = [];\n\n for (const message of output) {\n const toolInvocations = mergeToolInvocations(message);\n\n if (!toolInvocations.length) continue;\n\n for (const invocation of toolInvocations) {\n // A thrown call carries `errorText` rather than a result, so it yields no entry here.\n if (invocation.state === 'result' && invocation.result !== undefined) {\n results.push({\n toolName: invocation.toolName,\n toolCallId: invocation.toolCallId || '',\n args: invocation.args || {},\n result: invocation.result,\n });\n }\n }\n }\n\n return results;\n}\n\n// Re-export extractTrajectory from core — it's called automatically by runEvals\n// for trajectory scorers, but users may still want it for custom use cases.\nexport { extractTrajectory } from '@mastra/core/evals';\n\n/**\n * Compares two trajectories and returns detailed comparison results.\n *\n * This is the core comparison logic used by trajectory scorers. It supports\n * strict and non-strict ordering, optional step data comparison, and loop detection.\n *\n * @param actual - The trajectory the agent actually took\n * @param expected - The expected trajectory to compare against\n * @param options - Comparison configuration options\n * @returns Detailed comparison results including match scores and diagnostics\n *\n * @example\n * ```ts\n * const result = compareTrajectories(\n * { steps: [{ stepType: 'tool_call', name: 'search' }, { stepType: 'tool_call', name: 'summarize' }] },\n * { steps: [{ stepType: 'tool_call', name: 'search' }, { stepType: 'tool_call', name: 'summarize' }] },\n * { ordering: 'strict' }\n * );\n * // result.score = 1.0\n * ```\n */\nexport function compareTrajectories(\n actual: Trajectory,\n expected: Trajectory | { steps: ExpectedStep[] },\n options: {\n ordering?: 'strict' | 'relaxed' | 'unordered';\n allowRepeatedSteps?: boolean;\n } = {},\n): TrajectoryComparisonResult {\n const { allowRepeatedSteps = true, ordering = 'relaxed' } = options;\n\n // Normalize expected to ExpectedStep[]. TrajectoryStep and ExpectedStep share\n // the same field names, so TrajectoryStep[] can be used directly as ExpectedStep[].\n // The only structural difference is `children` (TrajectoryStep[] vs TrajectoryExpectation),\n // but compareTrajectories doesn't recurse into children.\n const normalizedExpected: { steps: ExpectedStep[] } = {\n steps: expected.steps as ExpectedStep[],\n };\n\n if (normalizedExpected.steps.length === 0) {\n return {\n score: actual.steps.length === 0 ? 1 : 0,\n matchedSteps: 0,\n totalExpectedSteps: 0,\n totalActualSteps: actual.steps.length,\n missingSteps: [],\n extraSteps: actual.steps.map((s: TrajectoryStep) => s.name),\n outOfOrderSteps: [],\n repeatedSteps: [],\n };\n }\n\n const actualNames = actual.steps.map((s: TrajectoryStep) => s.name);\n\n // Detect repeated steps\n const nameCounts = new Map<string, number>();\n for (const name of actualNames) {\n nameCounts.set(name, (nameCounts.get(name) || 0) + 1);\n }\n const repeatedSteps = [...nameCounts.entries()]\n .filter(([_, count]: [string, number]) => count > 1)\n .map(([name]: [string, number]) => name);\n\n if (ordering === 'strict') {\n return compareStrictOrder(actual, normalizedExpected, { allowRepeatedSteps, repeatedSteps });\n }\n\n if (ordering === 'unordered') {\n return compareUnorderedPresence(actual, normalizedExpected, { allowRepeatedSteps, repeatedSteps });\n }\n\n return compareRelaxedOrder(actual, normalizedExpected, { allowRepeatedSteps, repeatedSteps });\n}\n\n/**\n * Result of comparing two trajectories.\n */\nexport type TrajectoryComparisonResult = {\n /** Overall match score from 0 to 1 */\n score: number;\n /** Number of expected steps that were matched */\n matchedSteps: number;\n /** Total number of expected steps */\n totalExpectedSteps: number;\n /** Total number of actual steps taken */\n totalActualSteps: number;\n /** Expected steps that were not found in the actual trajectory */\n missingSteps: string[];\n /** Actual steps that were not in the expected trajectory */\n extraSteps: string[];\n /** Steps that appear but not in the expected position */\n outOfOrderSteps: string[];\n /** Steps that were repeated (appeared more than once) */\n repeatedSteps: string[];\n};\n\nfunction compareStrictOrder(\n actual: Trajectory,\n expected: { steps: ExpectedStep[] },\n opts: { allowRepeatedSteps: boolean; repeatedSteps: string[] },\n): TrajectoryComparisonResult {\n const actualNames: string[] = actual.steps.map((s: TrajectoryStep) => s.name);\n const expectedNames: string[] = expected.steps.map((s: ExpectedStep) => s.name);\n\n // Strict: exact same sequence\n let matchedSteps = 0;\n const outOfOrderSteps: string[] = [];\n const matchedExpectedIndices = new Set<number>();\n const maxLen = Math.max(actualNames.length, expectedNames.length);\n\n for (let i = 0; i < maxLen; i++) {\n const actualName = actualNames[i];\n const expectedName = expectedNames[i];\n if (actualName === expectedName) {\n if (actual.steps[i] && expected.steps[i]) {\n if (expectedStepMatches(actual.steps[i]!, expected.steps[i]!)) {\n matchedSteps++;\n matchedExpectedIndices.add(i);\n }\n } else {\n matchedSteps++;\n matchedExpectedIndices.add(i);\n }\n } else if (actualName && expectedNames.includes(actualName)) {\n outOfOrderSteps.push(actualName);\n }\n }\n\n // Missing steps = expected steps that were not matched (accounts for stepType/data mismatches)\n const missingSteps: string[] = expectedNames.filter((_: string, i: number) => !matchedExpectedIndices.has(i));\n const extraSteps: string[] = actualNames.filter((name: string) => !expectedNames.includes(name));\n\n let score = matchedSteps / expected.steps.length;\n\n // Penalize extra steps in strict mode\n if (actualNames.length > expectedNames.length) {\n const extraPenalty = (actualNames.length - expectedNames.length) / expectedNames.length;\n score = Math.max(0, score - extraPenalty * 0.5);\n }\n\n // Penalize repeated steps if not allowed\n if (!opts.allowRepeatedSteps && opts.repeatedSteps.length > 0) {\n score = Math.max(0, score - opts.repeatedSteps.length * 0.1);\n }\n\n return {\n score: roundToTwoDecimals(Math.max(0, Math.min(1, score))),\n matchedSteps,\n totalExpectedSteps: expected.steps.length,\n totalActualSteps: actual.steps.length,\n missingSteps,\n extraSteps,\n outOfOrderSteps,\n repeatedSteps: opts.repeatedSteps,\n };\n}\n\nfunction compareRelaxedOrder(\n actual: Trajectory,\n expected: { steps: ExpectedStep[] },\n opts: { allowRepeatedSteps: boolean; repeatedSteps: string[] },\n): TrajectoryComparisonResult {\n const actualNames: string[] = actual.steps.map((s: TrajectoryStep) => s.name);\n const expectedNames: string[] = expected.steps.map((s: ExpectedStep) => s.name);\n\n // Relaxed: expected steps must appear in order but extra steps are allowed\n let matchedSteps = 0;\n let lastMatchedIndex = -1;\n const outOfOrderSteps: string[] = [];\n const matchedExpectedIndices = new Set<number>();\n\n for (let i = 0; i < expectedNames.length; i++) {\n const expectedName = expectedNames[i];\n let found = false;\n\n for (let j = lastMatchedIndex + 1; j < actualNames.length; j++) {\n if (actualNames[j] === expectedName) {\n if (actual.steps[j] && expected.steps[i]) {\n if (expectedStepMatches(actual.steps[j]!, expected.steps[i]!)) {\n matchedSteps++;\n lastMatchedIndex = j;\n matchedExpectedIndices.add(i);\n found = true;\n break;\n }\n } else {\n matchedSteps++;\n lastMatchedIndex = j;\n matchedExpectedIndices.add(i);\n found = true;\n break;\n }\n }\n }\n\n if (!found) {\n // Check if the step exists but is out of order\n if (actualNames.includes(expectedName!)) {\n outOfOrderSteps.push(expectedName!);\n }\n }\n }\n\n // Missing steps = expected steps that were not matched (by name + stepType + data, not just name)\n const missingSteps = expectedNames.filter((_, i) => !matchedExpectedIndices.has(i));\n const expectedSet = new Set(expectedNames);\n const extraSteps = actualNames.filter(name => !expectedSet.has(name));\n\n let score = matchedSteps / expected.steps.length;\n\n // Penalize repeated steps if not allowed\n if (!opts.allowRepeatedSteps && opts.repeatedSteps.length > 0) {\n score = Math.max(0, score - opts.repeatedSteps.length * 0.1);\n }\n\n return {\n score: roundToTwoDecimals(Math.max(0, Math.min(1, score))),\n matchedSteps,\n totalExpectedSteps: expected.steps.length,\n totalActualSteps: actual.steps.length,\n missingSteps,\n extraSteps,\n outOfOrderSteps,\n repeatedSteps: opts.repeatedSteps,\n };\n}\n\n/**\n * Fields on each ExpectedStep variant that are comparable data (not structural).\n * Used by `expectedStepMatches` to know which fields to compare when `compareData` is true.\n */\nconst COMPARABLE_FIELDS_BY_TYPE: Record<string, string[]> = {\n tool_call: ['toolArgs', 'toolResult', 'success'],\n mcp_tool_call: ['toolArgs', 'toolResult', 'mcpServer', 'success'],\n model_generation: ['modelId', 'promptTokens', 'completionTokens', 'finishReason'],\n agent_run: ['agentId'],\n workflow_step: ['stepId', 'status', 'output'],\n workflow_run: ['workflowId', 'status'],\n workflow_conditional: ['conditionCount', 'selectedSteps'],\n workflow_parallel: ['branchCount', 'parallelSteps'],\n workflow_loop: ['loopType', 'totalIterations'],\n workflow_sleep: ['sleepDurationMs', 'sleepType'],\n workflow_wait_event: ['eventName', 'eventReceived'],\n processor_run: ['processorId'],\n};\n\n/**\n * Check if an actual TrajectoryStep matches an ExpectedStep.\n * Matches by name, optionally by stepType, and auto-compares any variant-specific\n * fields that are present on the expected step.\n */\nfunction expectedStepMatches(actual: TrajectoryStep, expected: ExpectedStep): boolean {\n if (actual.name !== expected.name) return false;\n if (expected.stepType && actual.stepType !== expected.stepType) return false;\n\n if (expected.stepType) {\n const fields = COMPARABLE_FIELDS_BY_TYPE[expected.stepType] ?? [];\n for (const field of fields) {\n const expectedVal = (expected as any)[field];\n if (expectedVal === undefined) continue; // field not specified in expectation, skip\n const actualVal = (actual as any)[field];\n if (actualVal === undefined) return false;\n try {\n if (JSON.stringify(actualVal) !== JSON.stringify(expectedVal)) return false;\n } catch {\n return false;\n }\n }\n }\n\n return true;\n}\n\nfunction compareUnorderedPresence(\n actual: Trajectory,\n expected: { steps: ExpectedStep[] },\n opts: { allowRepeatedSteps: boolean; repeatedSteps: string[] },\n): TrajectoryComparisonResult {\n const actualNames: string[] = actual.steps.map((s: TrajectoryStep) => s.name);\n const expectedNames: string[] = expected.steps.map((s: ExpectedStep) => s.name);\n\n let matchedSteps = 0;\n const matchedExpectedIndices = new Set<number>();\n const usedIndices = new Set<number>();\n for (let i = 0; i < expected.steps.length; i++) {\n const expectedStep = expected.steps[i]!;\n for (let j = 0; j < actual.steps.length; j++) {\n if (!usedIndices.has(j) && expectedStepMatches(actual.steps[j]!, expectedStep)) {\n matchedSteps++;\n matchedExpectedIndices.add(i);\n usedIndices.add(j);\n break;\n }\n }\n }\n\n // Missing steps = expected steps that were not matched (accounts for stepType/data mismatches)\n const missingSteps = expectedNames.filter((_, i) => !matchedExpectedIndices.has(i));\n const expectedSet = new Set(expectedNames);\n const extraSteps = actualNames.filter(name => !expectedSet.has(name));\n\n let score = matchedSteps / expected.steps.length;\n\n // Penalize repeated steps if not allowed\n if (!opts.allowRepeatedSteps && opts.repeatedSteps.length > 0) {\n score = Math.max(0, score - opts.repeatedSteps.length * 0.1);\n }\n\n return {\n score: roundToTwoDecimals(Math.max(0, Math.min(1, score))),\n matchedSteps,\n totalExpectedSteps: expected.steps.length,\n totalActualSteps: actual.steps.length,\n missingSteps,\n extraSteps,\n outOfOrderSteps: [], // ordering not checked in unordered mode\n repeatedSteps: opts.repeatedSteps,\n };\n}\n\n// ─── Efficiency evaluation ───\n\n/**\n * Result of checking trajectory efficiency.\n */\nexport type TrajectoryEfficiencyResult = {\n /** Overall efficiency score from 0 to 1 */\n score: number;\n /** Total number of steps taken */\n totalSteps: number;\n /** Whether the step budget was exceeded */\n overStepBudget: boolean;\n /** Total tokens used across model_generation steps */\n totalTokens: number;\n /** Whether the token budget was exceeded */\n overTokenBudget: boolean;\n /** Total duration in milliseconds */\n totalDurationMs: number;\n /** Whether the duration budget was exceeded */\n overDurationBudget: boolean;\n /** Redundant calls detected (same tool + same args consecutively) */\n redundantCalls: Array<{ name: string; index: number }>;\n};\n\n/**\n * Evaluate trajectory efficiency against budgets and redundancy checks.\n * Throws when a configured budget is invalid or its required measurements are incomplete or invalid.\n */\nexport function checkTrajectoryEfficiency(\n trajectory: Trajectory,\n options: {\n maxSteps?: number;\n maxTotalTokens?: number;\n maxTotalDurationMs?: number;\n noRedundantCalls?: boolean;\n } = {},\n): TrajectoryEfficiencyResult {\n const { maxSteps, maxTotalTokens, maxTotalDurationMs, noRedundantCalls = true } = options;\n\n const isMeasurement = (value: number | undefined): value is number =>\n typeof value === 'number' && Number.isFinite(value) && value >= 0;\n for (const [name, limit] of Object.entries({ maxSteps, maxTotalTokens, maxTotalDurationMs })) {\n if (limit !== undefined && !isMeasurement(limit)) {\n throw new Error(`Invalid ${name} budget: expected a finite nonnegative number`);\n }\n }\n\n const totalSteps = trajectory.steps.length;\n\n // Count each model generation, including nested agents, without counting container aggregates.\n let totalTokens = 0;\n let modelGenerations = 0;\n const remaining = [...trajectory.steps];\n while (remaining.length > 0) {\n const step = remaining.pop()!;\n if (step.stepType === 'model_generation') {\n modelGenerations++;\n if (\n maxTotalTokens !== undefined &&\n (!isMeasurement(step.promptTokens) || !isMeasurement(step.completionTokens))\n ) {\n throw new Error(\n `Cannot evaluate token budget: model generation \"${step.name}\" has missing or invalid token counts`,\n );\n }\n totalTokens += (step.promptTokens ?? 0) + (step.completionTokens ?? 0);\n }\n if (step.children) {\n for (const child of step.children) remaining.push(child);\n }\n }\n if (maxTotalTokens !== undefined && (modelGenerations === 0 || !isMeasurement(totalTokens))) {\n throw new Error('Cannot evaluate token budget: missing model-generation measurements or invalid total tokens');\n }\n\n // A parent duration already covers its children; never add both.\n const totalDurationMs =\n trajectory.totalDurationMs ?? trajectory.steps.reduce((sum, s) => sum + (s.durationMs ?? 0), 0);\n if (maxTotalDurationMs !== undefined) {\n const hasDuration =\n trajectory.totalDurationMs !== undefined\n ? isMeasurement(trajectory.totalDurationMs)\n : trajectory.steps.length > 0 && trajectory.steps.every(step => isMeasurement(step.durationMs));\n if (!hasDuration || !isMeasurement(totalDurationMs)) {\n throw new Error('Cannot evaluate duration budget: missing or invalid duration measurements');\n }\n }\n\n // Detect redundant calls (same tool name + same args in consecutive calls)\n const redundantCalls: Array<{ name: string; index: number }> = [];\n if (noRedundantCalls) {\n for (let i = 1; i < trajectory.steps.length; i++) {\n const prev = trajectory.steps[i - 1]!;\n const curr = trajectory.steps[i]!;\n if (\n prev.name === curr.name &&\n prev.stepType === curr.stepType &&\n (prev.stepType === 'tool_call' || prev.stepType === 'mcp_tool_call')\n ) {\n const prevArgs = (prev as TrajectoryStep & { toolArgs?: Record<string, unknown> }).toolArgs;\n const currArgs = (curr as TrajectoryStep & { toolArgs?: Record<string, unknown> }).toolArgs;\n try {\n if (JSON.stringify(prevArgs) === JSON.stringify(currArgs)) {\n redundantCalls.push({ name: curr.name, index: i });\n }\n } catch {\n // If serialization fails, don't flag as redundant\n }\n }\n }\n }\n\n const overStepBudget = maxSteps !== undefined && totalSteps > maxSteps;\n const overTokenBudget = maxTotalTokens !== undefined && totalTokens > maxTotalTokens;\n const overDurationBudget = maxTotalDurationMs !== undefined && totalDurationMs > maxTotalDurationMs;\n\n // Calculate score: each dimension contributes equally\n const dimensions: number[] = [];\n\n if (maxSteps !== undefined) {\n dimensions.push(overStepBudget ? Math.max(0, 1 - (totalSteps - maxSteps) / maxSteps) : 1);\n }\n if (maxTotalTokens !== undefined) {\n dimensions.push(overTokenBudget ? Math.max(0, 1 - (totalTokens - maxTotalTokens) / maxTotalTokens) : 1);\n }\n if (maxTotalDurationMs !== undefined) {\n dimensions.push(\n overDurationBudget ? Math.max(0, 1 - (totalDurationMs - maxTotalDurationMs) / maxTotalDurationMs) : 1,\n );\n }\n if (noRedundantCalls) {\n dimensions.push(redundantCalls.length === 0 ? 1 : Math.max(0, 1 - redundantCalls.length * 0.2));\n }\n\n const score = dimensions.length > 0 ? dimensions.reduce((a, b) => a + b, 0) / dimensions.length : 1;\n\n return {\n score: roundToTwoDecimals(Math.max(0, Math.min(1, score))),\n totalSteps,\n overStepBudget,\n totalTokens,\n overTokenBudget,\n totalDurationMs,\n overDurationBudget,\n redundantCalls,\n };\n}\n\n// ─── Blacklist evaluation ───\n\n/**\n * Result of checking trajectory against a blacklist.\n */\nexport type TrajectoryBlacklistResult = {\n /** Score: 1.0 if clean, 0.0 if any violation found */\n score: number;\n /** Individual blacklisted tools that were found */\n violatedTools: string[];\n /** Blacklisted sequences that were found */\n violatedSequences: string[][];\n};\n\n/**\n * Check if a trajectory violates any blacklist rules.\n * Returns score 0.0 if any violation is found (hard fail).\n */\nexport function checkTrajectoryBlacklist(\n trajectory: Trajectory,\n options: {\n blacklistedTools?: string[];\n blacklistedSequences?: string[][];\n } = {},\n): TrajectoryBlacklistResult {\n const { blacklistedTools = [], blacklistedSequences = [] } = options;\n const violatedTools: string[] = [];\n const violatedSequences: string[][] = [];\n\n const stepNames = trajectory.steps.map(s => s.name);\n\n // Check blacklisted tools\n for (const forbidden of blacklistedTools) {\n if (stepNames.includes(forbidden)) {\n violatedTools.push(forbidden);\n }\n }\n\n // Check blacklisted sequences (contiguous subsequences)\n for (const sequence of blacklistedSequences) {\n if (sequence.length === 0) continue;\n for (let i = 0; i <= stepNames.length - sequence.length; i++) {\n let match = true;\n for (let j = 0; j < sequence.length; j++) {\n if (stepNames[i + j] !== sequence[j]) {\n match = false;\n break;\n }\n }\n if (match) {\n violatedSequences.push(sequence);\n break; // Only report each sequence once\n }\n }\n }\n\n const hasViolations = violatedTools.length > 0 || violatedSequences.length > 0;\n\n return {\n score: hasViolations ? 0 : 1,\n violatedTools,\n violatedSequences,\n };\n}\n\n// ─── Tool failure analysis ───\n\n/**\n * A detected tool failure pattern in the trajectory.\n */\nexport type ToolFailurePattern = {\n /** The tool name that experienced failure */\n toolName: string;\n /** Number of consecutive retries (same tool, same or similar args) */\n retryCount: number;\n /** Whether the agent fell back to a different tool after failures */\n fellBackToAlternative: boolean;\n /** The alternative tool used, if any */\n alternativeTool?: string;\n /** Whether any retry eventually succeeded */\n eventuallySucceeded: boolean;\n};\n\n/**\n * Result of analyzing tool failure patterns in a trajectory.\n */\nexport type ToolFailureAnalysisResult = {\n /** Score from 0 to 1 (lower = more failures/retries) */\n score: number;\n /** Tool failure patterns detected */\n patterns: ToolFailurePattern[];\n /** Total number of retries across all tools */\n totalRetries: number;\n /** Tools that exceeded the retry threshold */\n excessiveRetryTools: string[];\n};\n\n/**\n * Analyze tool failure and retry patterns in a trajectory.\n */\nexport function analyzeToolFailures(\n trajectory: Trajectory,\n options: {\n maxRetriesPerTool?: number;\n } = {},\n): ToolFailureAnalysisResult {\n const { maxRetriesPerTool = 2 } = options;\n const patterns: ToolFailurePattern[] = [];\n let totalRetries = 0;\n\n const toolCallSteps = trajectory.steps.filter(s => s.stepType === 'tool_call' || s.stepType === 'mcp_tool_call');\n\n if (toolCallSteps.length === 0) {\n return { score: 1, patterns: [], totalRetries: 0, excessiveRetryTools: [] };\n }\n\n // Group consecutive calls to the same tool as potential retry sequences\n let i = 0;\n while (i < toolCallSteps.length) {\n const currentTool = toolCallSteps[i]!;\n let retryCount = 0;\n let j = i + 1;\n\n // Count consecutive calls to the same tool\n // (toolCallSteps is pre-filtered to tool_call/mcp_tool_call, so no stepType checks needed)\n while (j < toolCallSteps.length && toolCallSteps[j]!.name === currentTool.name) {\n const prevStep = toolCallSteps[j - 1]! as TrajectoryStep & { success?: boolean };\n if (prevStep.success === false) {\n retryCount++;\n }\n j++;\n }\n\n if (retryCount > 0) {\n // Check if agent fell back to a different tool after retries\n const nextDifferentTool = j < toolCallSteps.length ? toolCallSteps[j] : undefined;\n const lastRetry = toolCallSteps[j - 1]! as TrajectoryStep & { success?: boolean };\n const lastSuccess = lastRetry.success !== false;\n\n patterns.push({\n toolName: currentTool.name,\n retryCount,\n fellBackToAlternative: nextDifferentTool !== undefined && !lastSuccess,\n alternativeTool: nextDifferentTool !== undefined && !lastSuccess ? nextDifferentTool.name : undefined,\n eventuallySucceeded: lastSuccess,\n });\n\n totalRetries += retryCount;\n }\n\n i = j;\n }\n\n // Score: penalize excessive retries\n const excessiveRetryTools = patterns.filter(p => p.retryCount > maxRetriesPerTool).map(p => p.toolName);\n\n let score = 1;\n if (toolCallSteps.length > 0) {\n // Each retry beyond the threshold costs more\n const excessRetries = patterns.reduce((sum, p) => sum + Math.max(0, p.retryCount - maxRetriesPerTool), 0);\n score = Math.max(0, 1 - excessRetries * 0.2);\n }\n\n return {\n score: roundToTwoDecimals(Math.max(0, Math.min(1, score))),\n patterns,\n totalRetries,\n excessiveRetryTools,\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"utils.cjs","names":["RequestContext"],"sources":["../../src/scorers/utils.ts"],"sourcesContent":["import type { MastraDBMessage } from '@mastra/core/agent';\nimport type {\n ExpectedStep,\n ScorerRunInputForAgent,\n ScorerRunOutputForAgent,\n ScoringInput,\n TrajectoryExpectation,\n TrajectoryStep,\n Trajectory,\n} from '@mastra/core/evals';\nimport { RequestContext } from '@mastra/core/request-context';\n\nexport type ScorerRunInputForLLMJudge =\n | ScorerRunInputForAgent\n | string\n | {\n inputMessages?: unknown[];\n messages?: unknown[];\n prompt?: string;\n text?: string;\n content?: unknown;\n input?: unknown;\n user?: unknown;\n [key: string]: unknown;\n };\n\nexport type ScorerRunOutputForLLMJudge =\n | ScorerRunOutputForAgent\n | string\n | unknown[]\n | {\n text?: string;\n content?: unknown;\n role?: string;\n [key: string]: unknown;\n };\n\n/**\n * Extracts text content from a MastraDBMessage or ModelMessage-like object.\n *\n * @param message - The message to extract text from\n * @returns The extracted text content, or an empty string if no text is found\n *\n * @example\n * ```ts\n * const message: MastraDBMessage = {\n * id: 'msg-1',\n * role: 'assistant',\n * content: { format: 2, parts: [{ type: 'text', text: 'Hello!' }] },\n * createdAt: new Date(),\n * };\n * const text = getTextContentFromMastraDBMessage(message); // 'Hello!'\n * ```\n */\nexport function getTextContentFromMastraDBMessage(message: MastraDBMessage): string {\n const content = message.content as any;\n\n if (typeof content === 'string') {\n return content;\n }\n if (Array.isArray(content)) {\n const textParts = content.filter(p => p.type === 'text');\n return textParts.length > 0 ? textParts[textParts.length - 1]?.text || '' : '';\n }\n if (typeof content?.content === 'string' && content.content !== '') {\n return content.content;\n }\n if (typeof content?.text === 'string' && content.text !== '') {\n return content.text;\n }\n if (content?.parts && Array.isArray(content.parts)) {\n // Return only the last text part like AI SDK does\n const textParts = content.parts.filter((p: any) => p.type === 'text');\n return textParts.length > 0 ? textParts[textParts.length - 1]?.text || '' : '';\n }\n return '';\n}\n\nconst isRecord = (value: unknown): value is Record<string, any> => {\n return typeof value === 'object' && value !== null;\n};\n\nconst getTextFromValue = (value: unknown): string | undefined => {\n if (typeof value === 'string') return value === '' ? undefined : value;\n if (Array.isArray(value)) {\n const textParts = value\n .filter(part => isRecord(part) && part.type === 'text' && typeof part.text === 'string')\n .map(part => part.text);\n return textParts.length > 0 ? textParts[textParts.length - 1] : undefined;\n }\n if (!isRecord(value)) return undefined;\n\n const fromParts = Array.isArray(value.parts) ? getTextFromValue(value.parts) : undefined;\n\n return (\n getTextFromValue(value.content) ??\n (typeof value.text === 'string' && value.text !== '' ? value.text : undefined) ??\n (typeof value.body === 'string' && value.body !== '' ? value.body : undefined) ??\n fromParts\n );\n};\n\nexport const isScorerRunInputForAgent = (input: unknown): input is ScorerRunInputForAgent => {\n return (\n isRecord(input) &&\n Array.isArray(input.inputMessages) &&\n Array.isArray(input.rememberedMessages) &&\n Array.isArray(input.systemMessages) &&\n isRecord(input.taggedSystemMessages)\n );\n};\n\nconst isMastraDBMessageLike = (message: unknown): message is MastraDBMessage => {\n return (\n isRecord(message) &&\n typeof message.id === 'string' &&\n typeof message.role === 'string' &&\n 'content' in message &&\n 'createdAt' in message\n );\n};\n\nexport const isScorerRunOutputForAgent = (output: unknown): output is ScorerRunOutputForAgent => {\n return Array.isArray(output) && output.every(isMastraDBMessageLike);\n};\n\n/**\n * Resolves the effective role of a message, accounting for agent signal messages.\n *\n * Messages delivered through the agent subscription / signal API are persisted with\n * `role: 'signal'` and carry their semantic role (e.g. `user`) on `type` and on\n * `content.metadata.signal.{type,tagName}`. Treat those as their underlying role so\n * helpers like `getUserMessageFromRunInput` can find them.\n */\nconst getEffectiveMessageRole = (message: Record<string, any>): string | undefined => {\n if (message.role !== 'signal') return typeof message.role === 'string' ? message.role : undefined;\n\n const signalMeta =\n isRecord(message.content) && isRecord(message.content.metadata) ? message.content.metadata.signal : undefined;\n\n const tagName = isRecord(signalMeta) && typeof signalMeta.tagName === 'string' ? signalMeta.tagName : undefined;\n const signalType = isRecord(signalMeta) && typeof signalMeta.type === 'string' ? signalMeta.type : undefined;\n const topLevelType = typeof message.type === 'string' ? message.type : undefined;\n\n return tagName ?? signalType ?? topLevelType;\n};\n\nconst getTextFromMessages = (messages: unknown, role: string): string | undefined => {\n if (!Array.isArray(messages)) return undefined;\n\n const message = messages.find(message => isRecord(message) && getEffectiveMessageRole(message) === role);\n return message ? getTextFromValue(message) : undefined;\n};\n\n/**\n * Rounds a number to two decimal places.\n *\n * Uses `Number.EPSILON` to handle floating-point precision issues.\n *\n * @param num - The number to round\n * @returns The number rounded to two decimal places\n *\n * @example\n * ```ts\n * roundToTwoDecimals(0.1 + 0.2); // 0.3\n * roundToTwoDecimals(1.005); // 1.01\n * ```\n */\nexport const roundToTwoDecimals = (num: number) => {\n return Math.round((num + Number.EPSILON) * 100) / 100;\n};\n\n/**\n * Determines if a value is closer to the first target than the second.\n *\n * @param value - The value to compare\n * @param target1 - The first target value\n * @param target2 - The second target value\n * @returns `true` if `value` is closer to `target1` than `target2`\n *\n * @example\n * ```ts\n * isCloserTo(0.6, 1, 0); // true (0.6 is closer to 1)\n * isCloserTo(0.3, 1, 0); // false (0.3 is closer to 0)\n * ```\n */\nexport function isCloserTo(value: number, target1: number, target2: number): boolean {\n return Math.abs(value - target1) < Math.abs(value - target2);\n}\n\n/**\n * Represents a test case for scorer evaluation.\n */\nexport type TestCase = {\n /** The input text to evaluate */\n input: string;\n /** The output text to evaluate */\n output: string;\n /** The expected result of the evaluation */\n expectedResult: {\n /** The expected score */\n score: number;\n /** The optional expected reason */\n reason?: string;\n };\n};\n\n/**\n * Represents a test case with additional context for scorer evaluation.\n */\nexport type TestCaseWithContext = TestCase & {\n /** Additional context strings for the evaluation */\n context: string[];\n};\n\n/**\n * Creates a scoring input object for testing purposes.\n *\n * @param input - The user input text\n * @param output - The assistant output text\n * @param additionalContext - Optional additional context data\n * @param requestContext - Optional request context data\n * @returns A ScoringInput object ready for use in scorer tests\n *\n * @example\n * ```ts\n * const run = createTestRun(\n * 'What is 2+2?',\n * 'The answer is 4.',\n * { topic: 'math' }\n * );\n * ```\n */\nexport const createTestRun = (\n input: string,\n output: string,\n additionalContext?: Record<string, any>,\n requestContext?: Record<string, any>,\n): ScoringInput => {\n return {\n input: [{ role: 'user', content: input }],\n output: { role: 'assistant', text: output },\n additionalContext: additionalContext ?? {},\n requestContext: requestContext ?? {},\n };\n};\n\n/**\n * Extracts the user message text from a scorer run input.\n *\n * Accepts the agent shape (`{ inputMessages }`), `ModelMessage[]`\n * (`{ messages }`), workflow input (`{ prompt }`), and a bare string.\n *\n * @param input - The scorer run input\n * @returns The user message text, or `undefined` if none can be extracted\n *\n * @example\n * ```ts\n * const scorer = createScorer({ ... })\n * .preprocess(({ run }) => {\n * const userText = getUserMessageFromRunInput(run.input);\n * return { userText };\n * });\n * ```\n */\nexport const getUserMessageFromRunInput = (input?: unknown): string | undefined => {\n if (typeof input === 'string') return input;\n if (!isRecord(input)) return undefined;\n\n return (\n getTextFromMessages(input.inputMessages, 'user') ??\n getTextFromMessages(input.messages, 'user') ??\n (typeof input.prompt === 'string' ? input.prompt : undefined) ??\n (typeof input.text === 'string' ? input.text : undefined) ??\n getTextFromValue(input.content) ??\n getTextFromValue(input.input) ??\n getTextFromValue(input.user)\n );\n};\n\nconst DEFAULT_CONVERSATION_HISTORY_MESSAGES = 10;\n\n/**\n * Renders the remembered conversation history from an agent scorer run input as a transcript.\n *\n * Only agent run inputs carry `rememberedMessages`; any other input shape (a bare string,\n * a plain `{ prompt }` object, `ModelMessage[]`) returns `undefined` so callers can safely\n * fall back to single-turn behaviour.\n *\n * @param input - The scorer run input\n * @param options.maxMessages - How many of the most recent remembered messages to keep (default 10)\n * @returns A newline separated `role: text` transcript, or `undefined` if there is no history\n *\n * @example\n * ```ts\n * const scorer = createScorer({ ... })\n * .preprocess(({ run }) => {\n * const history = getConversationHistoryFromRunInput(run.input, { maxMessages: 4 });\n * return { history };\n * });\n * ```\n */\nexport const getConversationHistoryFromRunInput = (\n input?: unknown,\n options?: { maxMessages?: number },\n): string | undefined => {\n if (!isScorerRunInputForAgent(input)) return undefined;\n\n const maxMessages = options?.maxMessages ?? DEFAULT_CONVERSATION_HISTORY_MESSAGES;\n if (maxMessages <= 0) return undefined;\n\n const lines = input.rememberedMessages\n .slice(-maxMessages)\n .map(message => {\n if (!isRecord(message)) return undefined;\n\n const role = getEffectiveMessageRole(message);\n const text = getTextContentFromMastraDBMessage(message as MastraDBMessage).trim();\n if (!role || !text) return undefined;\n\n return `${role}: ${text}`;\n })\n .filter((line): line is string => Boolean(line));\n\n return lines.length > 0 ? lines.join('\\n') : undefined;\n};\n\n/**\n * Extracts all system messages from a scorer run input.\n *\n * Collects text from both standard system messages and tagged system messages\n * (specialized system prompts like memory instructions).\n *\n * @param input - The scorer run input containing system messages\n * @returns An array of system message strings\n *\n * @example\n * ```ts\n * const scorer = createScorer({ ... })\n * .preprocess(({ run }) => {\n * const systemMessages = getSystemMessagesFromRunInput(run.input);\n * return { systemPrompt: systemMessages.join('\\n') };\n * });\n * ```\n */\nexport const getSystemMessagesFromRunInput = (input?: unknown): string[] => {\n const systemMessages: string[] = [];\n if (!isRecord(input)) return systemMessages;\n\n // Add standard system messages\n if (Array.isArray(input.systemMessages)) {\n systemMessages.push(\n ...input.systemMessages\n .map(msg => {\n // Handle different content types - extract text if it's an array of parts\n if (typeof msg.content === 'string') {\n return msg.content;\n } else if (Array.isArray(msg.content)) {\n // Extract text from parts array\n return msg.content\n .filter((part: any) => part.type === 'text')\n .map((part: any) => part.text || '')\n .join(' ');\n }\n return '';\n })\n .filter(content => content),\n );\n }\n\n const addSystemMessages = (messages: unknown) => {\n if (!Array.isArray(messages)) return;\n\n systemMessages.push(\n ...messages\n .filter(message => isRecord(message) && message.role === 'system')\n .map(message => getTextFromValue(message))\n .filter((content): content is string => Boolean(content)),\n );\n };\n\n addSystemMessages(input.inputMessages);\n addSystemMessages(input.messages);\n\n // Add tagged system messages (these are specialized system prompts)\n if (isRecord(input.taggedSystemMessages)) {\n Object.values(input.taggedSystemMessages).forEach(messages => {\n if (!Array.isArray(messages)) return;\n messages.forEach(msg => {\n const content = getTextFromValue(msg);\n if (content) {\n systemMessages.push(content);\n }\n });\n });\n }\n\n return systemMessages;\n};\n\n/**\n * Combines all system messages into a single prompt string.\n *\n * Joins all system messages (standard and tagged) with double newlines.\n *\n * @param input - The scorer run input containing system messages\n * @returns A combined system prompt string\n *\n * @example\n * ```ts\n * const scorer = createScorer({ ... })\n * .preprocess(({ run }) => {\n * const systemPrompt = getCombinedSystemPrompt(run.input);\n * return { systemPrompt };\n * });\n * ```\n */\nexport const getCombinedSystemPrompt = (input?: unknown): string => {\n const systemMessages = getSystemMessagesFromRunInput(input);\n return systemMessages.join('\\n\\n');\n};\n\n/**\n * Extracts the assistant message text from a scorer run output.\n *\n * Accepts the agent shape (`MastraDBMessage[]` / `ModelMessage[]`), workflow\n * output (`{ text }`), task output (`{ content }`), a single assistant message\n * object, and a bare string.\n *\n * For array outputs the final assistant message is used, so multi-step agent\n * responses are scored on the last response rather than an intermediate one.\n *\n * @param output - The scorer run output\n * @returns The assistant message text, or `undefined` if none can be extracted\n *\n * @example\n * ```ts\n * const scorer = createScorer({ ... })\n * .preprocess(({ run }) => {\n * const response = getAssistantMessageFromRunOutput(run.output);\n * return { response };\n * });\n * ```\n */\nexport const getAssistantMessageFromRunOutput = (output?: unknown) => {\n if (typeof output === 'string') return output;\n if (Array.isArray(output)) {\n const assistantMessages = output.filter(\n message => isRecord(message) && getEffectiveMessageRole(message) === 'assistant',\n );\n // Prefer the last assistant message that carries text; a trailing assistant\n // message may hold only tool-call or data parts.\n for (let i = assistantMessages.length - 1; i >= 0; i--) {\n const text = getTextFromValue(assistantMessages[i]);\n if (text) return text;\n }\n return undefined;\n }\n if (!isRecord(output)) return undefined;\n\n const isAssistantOutput = output.role === undefined || output.role === 'assistant';\n\n if (isAssistantOutput && typeof output.text === 'string') return output.text;\n if (isAssistantOutput && typeof output.content === 'string') return output.content;\n if (isAssistantOutput && (isRecord(output.content) || Array.isArray(output.content))) {\n return (\n getTextContentFromMastraDBMessage(output as MastraDBMessage) ||\n getTextContentFromMastraDBMessage(output.content as MastraDBMessage) ||\n undefined\n );\n }\n if (output.role === 'assistant') return getTextContentFromMastraDBMessage(output as MastraDBMessage) || undefined;\n\n return undefined;\n};\n\n/**\n * Extracts reasoning text from a scorer run output.\n *\n * This function extracts reasoning content from assistant messages, which is\n * produced by reasoning models like `deepseek-reasoner`. The reasoning can be\n * stored in two places:\n * 1. `content.reasoning` - a string field on the message content\n * 2. `content.parts` - as parts with `type: 'reasoning'` containing `details`\n *\n * @param output - The scorer run output (array of MastraDBMessage)\n * @returns The reasoning text, or `undefined` if no reasoning is present\n *\n * @example\n * ```ts\n * const reasoningScorer = createScorer({\n * id: 'reasoning-scorer',\n * name: 'Reasoning Quality',\n * description: 'Evaluates the quality of model reasoning',\n * type: 'agent',\n * })\n * .preprocess(({ run }) => {\n * const reasoning = getReasoningFromRunOutput(run.output);\n * const response = getAssistantMessageFromRunOutput(run.output);\n * return { reasoning, response };\n * })\n * .generateScore(({ results }) => {\n * // Score based on reasoning quality\n * return results.preprocessStepResult?.reasoning ? 1 : 0;\n * });\n * ```\n */\nexport const getReasoningFromRunOutput = (output?: ScorerRunOutputForAgent): string | undefined => {\n if (!output) return undefined;\n\n const message = output.find(({ role }) => role === 'assistant');\n if (!message) return undefined;\n\n // Check for reasoning in content.reasoning (string format)\n if (message.content.reasoning) {\n return message.content.reasoning;\n }\n\n // Check for reasoning in parts with type 'reasoning'\n // Reasoning models store reasoning in parts as { type: 'reasoning', details: [{ type: 'text', text: '...' }] }\n const reasoningParts = message.content.parts?.filter((p: any) => p.type === 'reasoning');\n if (reasoningParts && reasoningParts.length > 0) {\n const reasoningTexts = reasoningParts\n .map((p: any) => {\n // The reasoning text can be in p.reasoning or in p.details[].text\n if (p.details && Array.isArray(p.details)) {\n return p.details\n .filter((d: any) => d.type === 'text')\n .map((d: any) => d.text)\n .join('');\n }\n return p.reasoning || '';\n })\n .filter(Boolean);\n\n return reasoningTexts.length > 0 ? reasoningTexts.join('\\n') : undefined;\n }\n\n return undefined;\n};\n\n/**\n * Creates a tool invocation object for testing purposes.\n *\n * @param options - The tool invocation configuration\n * @param options.toolCallId - Unique identifier for the tool call\n * @param options.toolName - Name of the tool being called\n * @param options.args - Arguments passed to the tool\n * @param options.result - Result returned by the tool (absent for a thrown call)\n * @param options.state - State of the invocation (default: 'result')\n * @param options.errorText - Error text recorded when a tool threw (`state: 'output-error'`)\n * @param options.isError - Set alongside `state: 'result'` when the executor reported a failure\n * @returns A tool invocation object\n *\n * @example\n * ```ts\n * const invocation = createToolInvocation({\n * toolCallId: 'call-123',\n * toolName: 'weatherTool',\n * args: { location: 'London' },\n * result: { temperature: 20, condition: 'sunny' },\n * });\n *\n * // A tool that threw\n * const thrown = createToolInvocation({\n * toolCallId: 'call-456',\n * toolName: 'saveTool',\n * args: { value: 'x' },\n * state: 'output-error',\n * errorText: 'Save failed',\n * });\n * ```\n */\nexport const createToolInvocation = ({\n toolCallId,\n toolName,\n args,\n result,\n state = 'result',\n errorText,\n isError,\n}: {\n toolCallId: string;\n toolName: string;\n args: Record<string, any>;\n result?: Record<string, any>;\n state?: 'call' | 'partial-call' | 'result' | 'output-error';\n errorText?: string;\n isError?: boolean;\n}): {\n toolCallId: string;\n toolName: string;\n args: Record<string, any>;\n result: Record<string, any> | undefined;\n state: 'call' | 'partial-call' | 'result' | 'output-error';\n errorText?: string;\n isError?: boolean;\n} => {\n return {\n toolCallId,\n toolName,\n args,\n result,\n state,\n ...(errorText !== undefined && { errorText }),\n ...(isError !== undefined && { isError }),\n };\n};\n\n/**\n * Creates a MastraDBMessage object for testing purposes.\n *\n * Supports optional tool invocations for testing tool call scenarios.\n *\n * @param options - The message configuration\n * @param options.content - The text content of the message\n * @param options.role - The role of the message sender ('user', 'assistant', or 'system')\n * @param options.id - Optional message ID (default: 'test-message')\n * @param options.toolInvocations - Optional array of tool invocations\n * @returns A MastraDBMessage object\n *\n * @example\n * ```ts\n * const message = createTestMessage({\n * content: 'Hello, how can I help?',\n * role: 'assistant',\n * });\n *\n * // With tool invocations\n * const messageWithTools = createTestMessage({\n * content: 'Let me check the weather.',\n * role: 'assistant',\n * toolInvocations: [{\n * toolCallId: 'call-1',\n * toolName: 'weatherTool',\n * args: { location: 'Paris' },\n * result: { temp: 22 },\n * state: 'result',\n * }],\n * });\n *\n * // With native V2 parts — the only form that can carry a thrown call\n * const messageWithThrownTool = createTestMessage({\n * content: 'That failed.',\n * role: 'assistant',\n * parts: [\n * { type: 'text', text: 'That failed.' },\n * {\n * type: 'tool-invocation',\n * toolInvocation: createToolInvocation({\n * toolCallId: 'call-2',\n * toolName: 'saveTool',\n * args: { value: 'x' },\n * state: 'output-error',\n * errorText: 'Save failed',\n * }),\n * },\n * ],\n * });\n * ```\n */\nexport function createTestMessage({\n content,\n role,\n id = 'test-message',\n toolInvocations = [],\n parts,\n}: {\n content: string;\n role: 'user' | 'assistant' | 'system';\n id?: string;\n toolInvocations?: Array<{\n toolCallId: string;\n toolName: string;\n args: Record<string, any>;\n result?: Record<string, any>;\n state: any;\n }>;\n /**\n * Native V2 message parts. Replaces the default single text part when provided.\n * Needed to express invocations the legacy array cannot carry, such as a thrown\n * call (`state: 'output-error'` with `errorText`).\n */\n parts?: MastraDBMessage['content']['parts'];\n}): MastraDBMessage {\n return {\n id,\n role,\n content: {\n format: 2,\n parts: parts ?? [{ type: 'text', text: content }],\n content,\n ...(toolInvocations.length > 0 && {\n toolInvocations: toolInvocations.map(ti => ({\n toolCallId: ti.toolCallId,\n toolName: ti.toolName,\n args: ti.args,\n result: ti.result,\n state: ti.state,\n })),\n }),\n },\n createdAt: new Date(),\n };\n}\n\n/**\n * Creates a complete agent test run object for testing scorers.\n *\n * Provides a convenient way to construct the full run object that scorers receive,\n * including input messages, output, system messages, and request context.\n *\n * @param options - The test run configuration\n * @param options.inputMessages - Array of input messages (default: [])\n * @param options.output - The output messages (required)\n * @param options.rememberedMessages - Array of remembered messages from memory (default: [])\n * @param options.systemMessages - Array of system messages (default: [])\n * @param options.taggedSystemMessages - Tagged system messages map (default: {})\n * @param options.requestContext - Request context (default: new RequestContext())\n * @param options.runId - Unique run ID (default: random UUID)\n * @returns A complete test run object\n *\n * @example\n * ```ts\n * const testRun = createAgentTestRun({\n * inputMessages: [createTestMessage({ content: 'Hello', role: 'user' })],\n * output: [createTestMessage({ content: 'Hi there!', role: 'assistant' })],\n * });\n *\n * const result = await scorer.run({\n * input: testRun.input,\n * output: testRun.output,\n * });\n * ```\n */\nexport const createAgentTestRun = ({\n inputMessages = [],\n output,\n rememberedMessages = [],\n systemMessages = [],\n taggedSystemMessages = {},\n requestContext = new RequestContext(),\n runId = crypto.randomUUID(),\n}: {\n inputMessages?: ScorerRunInputForAgent['inputMessages'];\n output: ScorerRunOutputForAgent;\n rememberedMessages?: ScorerRunInputForAgent['rememberedMessages'];\n systemMessages?: ScorerRunInputForAgent['systemMessages'];\n taggedSystemMessages?: ScorerRunInputForAgent['taggedSystemMessages'];\n requestContext?: RequestContext;\n runId?: string;\n}): {\n input: ScorerRunInputForAgent;\n output: ScorerRunOutputForAgent;\n requestContext: RequestContext;\n runId: string;\n} => {\n return {\n input: {\n inputMessages,\n rememberedMessages,\n systemMessages,\n taggedSystemMessages,\n },\n output,\n requestContext,\n runId,\n };\n};\n\n/**\n * Creates a test run for trajectory scorers where `output` is a `Trajectory`\n * (pre-extracted by the `runEvals` pipeline).\n *\n * @example\n * ```ts\n * const testRun = createTrajectoryTestRun({\n * inputMessages: [createTestMessage({ content: 'Do X', role: 'user', id: 'u1' })],\n * trajectory: {\n * steps: [\n * { stepType: 'tool_call', name: 'search', toolArgs: { q: 'test' } },\n * ],\n * },\n * });\n * ```\n */\nexport const createTrajectoryTestRun = ({\n inputMessages = [],\n trajectory,\n rememberedMessages = [],\n systemMessages = [],\n taggedSystemMessages = {},\n requestContext = new RequestContext(),\n runId = crypto.randomUUID(),\n expectedTrajectory,\n}: {\n inputMessages?: ScorerRunInputForAgent['inputMessages'];\n trajectory: Trajectory;\n rememberedMessages?: ScorerRunInputForAgent['rememberedMessages'];\n systemMessages?: ScorerRunInputForAgent['systemMessages'];\n taggedSystemMessages?: ScorerRunInputForAgent['taggedSystemMessages'];\n requestContext?: RequestContext;\n runId?: string;\n expectedTrajectory?: TrajectoryExpectation;\n}): {\n input: ScorerRunInputForAgent;\n output: Trajectory;\n requestContext: RequestContext;\n runId: string;\n expectedTrajectory?: TrajectoryExpectation;\n} => {\n return {\n input: {\n inputMessages,\n rememberedMessages,\n systemMessages,\n taggedSystemMessages,\n },\n output: trajectory,\n expectedTrajectory,\n requestContext,\n runId,\n };\n};\n\n/**\n * Information about a tool call extracted from scorer output.\n */\nexport type ToolCallInfo = {\n /** Name of the tool that was called */\n toolName: string;\n /** Unique identifier for the tool call */\n toolCallId: string;\n /** Index of the message containing this tool call */\n messageIndex: number;\n /** Index of the invocation within the message's tool invocations */\n invocationIndex: number;\n};\n\n/**\n * Merges the two places a message can store tool invocations into one list.\n *\n * A native message may carry `content.parts` tool-invocation entries, the legacy\n * `content.toolInvocations` array, or both. Neither form is a superset of the\n * other: thrown calls are recorded in parts as `state: 'output-error'` and can be\n * missing from the legacy array, while older persisted messages may only carry the\n * legacy array. Reading one form exclusively therefore hides real calls.\n *\n * Parts win when the same `toolCallId` appears in both, since they hold the real\n * terminal state. Legacy-only calls are retained before their next shared anchor so\n * compatible call order survives instead of being appended.\n *\n * Mirrors `extractTrajectory` in `@mastra/core` (`packages/core/src/evals/types.ts`)\n * so tool-use checks and trajectory scorers agree on what a run actually called.\n * Keep the two in step.\n *\n * @param message - A single message from the scorer run output\n * @returns The merged tool invocations for that message\n */\nexport function mergeToolInvocations(message: MastraDBMessage | undefined) {\n const legacy = message?.content?.toolInvocations;\n const fromParts =\n message?.content?.parts\n ?.filter((p): p is Extract<typeof p, { type: 'tool-invocation' }> => p.type === 'tool-invocation')\n .map(p => p.toolInvocation)\n .filter(Boolean) ?? [];\n const partCallIds = new Set(fromParts.map(invocation => invocation.toolCallId).filter(Boolean));\n const legacyInvocations = legacy ?? [];\n const legacyPositions = new Map(legacyInvocations.map((invocation, index) => [invocation?.toolCallId, index]));\n const toolInvocations: typeof fromParts = [];\n let legacyIndex = 0;\n for (const invocation of fromParts) {\n const sharedIndex = invocation.toolCallId ? legacyPositions.get(invocation.toolCallId) : undefined;\n if (sharedIndex !== undefined && sharedIndex >= legacyIndex) {\n // Retain legacy-only calls before their next shared anchor.\n for (; legacyIndex < sharedIndex; legacyIndex++) {\n const previous = legacyInvocations[legacyIndex];\n if (previous && !partCallIds.has(previous.toolCallId)) toolInvocations.push(previous);\n }\n legacyIndex++;\n }\n toolInvocations.push(invocation);\n }\n for (; legacyIndex < legacyInvocations.length; legacyIndex++) {\n const invocation = legacyInvocations[legacyIndex];\n if (invocation && !partCallIds.has(invocation.toolCallId)) toolInvocations.push(invocation);\n }\n return toolInvocations;\n}\n\n/**\n * Extracts all tool calls from a scorer run output.\n *\n * Iterates through all messages and their tool invocations to collect information\n * about tools that were called. A call counts regardless of outcome, so the states\n * `'result'`, `'call'`, and `'output-error'` (a tool that threw) are all included.\n *\n * @param output - The scorer run output (array of MastraDBMessage)\n * @returns An object containing tool names and detailed tool call info\n *\n * @example\n * ```ts\n * const scorer = createScorer({ ... })\n * .preprocess(({ run }) => {\n * const { tools, toolCallInfos } = extractToolCalls(run.output);\n * return {\n * toolsUsed: tools,\n * toolCount: tools.length,\n * };\n * });\n * ```\n */\nexport function extractToolCalls(output: ScorerRunOutputForAgent): { tools: string[]; toolCallInfos: ToolCallInfo[] } {\n const toolCalls: string[] = [];\n const toolCallInfos: ToolCallInfo[] = [];\n\n for (let messageIndex = 0; messageIndex < output.length; messageIndex++) {\n const message = output[messageIndex];\n const toolInvocations = mergeToolInvocations(message);\n\n if (!toolInvocations.length) continue;\n\n for (let invocationIndex = 0; invocationIndex < toolInvocations.length; invocationIndex++) {\n const invocation = toolInvocations[invocationIndex];\n // A thrown call (`output-error`) still counts: the tool ran. Partial calls do not.\n // Same state set core's `extractTrajectory` accepts.\n if (\n invocation &&\n invocation.toolName &&\n (invocation.state === 'result' || invocation.state === 'call' || invocation.state === 'output-error')\n ) {\n toolCalls.push(invocation.toolName);\n toolCallInfos.push({\n toolName: invocation.toolName,\n toolCallId: invocation.toolCallId || `${messageIndex}-${invocationIndex}`,\n messageIndex,\n invocationIndex,\n });\n }\n }\n }\n\n return { tools: toolCalls, toolCallInfos };\n}\n\n/**\n * Extracts text content from all input messages.\n *\n * @param runInput - The scorer run input\n * @returns An array of text strings from each input message\n *\n * @example\n * ```ts\n * const scorer = createScorer({ ... })\n * .preprocess(({ run }) => {\n * const messages = extractInputMessages(run.input);\n * return { allUserMessages: messages.join('\\n') };\n * });\n * ```\n */\nexport const extractInputMessages = (runInput: ScorerRunInputForAgent | undefined): string[] => {\n return runInput?.inputMessages?.map(msg => getTextContentFromMastraDBMessage(msg)) || [];\n};\n\n/**\n * Extracts text content from all assistant response messages.\n *\n * Filters for messages with role 'assistant' and extracts their text content.\n *\n * @param runOutput - The scorer run output (array of MastraDBMessage)\n * @returns An array of text strings from each assistant message\n *\n * @example\n * ```ts\n * const scorer = createScorer({ ... })\n * .preprocess(({ run }) => {\n * const responses = extractAgentResponseMessages(run.output);\n * return { allResponses: responses.join('\\n') };\n * });\n * ```\n */\nexport const extractAgentResponseMessages = (runOutput: ScorerRunOutputForAgent): string[] => {\n return runOutput.filter(msg => msg.role === 'assistant').map(msg => getTextContentFromMastraDBMessage(msg));\n};\n\n/**\n * Information about a tool result extracted from scorer output.\n */\nexport type ToolResultInfo = {\n /** Name of the tool that was called */\n toolName: string;\n /** Unique identifier for the tool call */\n toolCallId: string;\n /** Arguments passed to the tool */\n args: Record<string, any>;\n /** Result returned by the tool */\n result: any;\n};\n\n/**\n * Extracts tool results from a scorer run output.\n *\n * Returns structured objects that can be used with the hallucination scorer's\n * `getContext` hook or for other scorer logic.\n *\n * @param output - The scorer run output (array of MastraDBMessage)\n * @returns An array of ToolResultInfo objects\n *\n * @example\n * ```ts\n * import { extractToolResults } from '@mastra/evals/scorers';\n * import { createHallucinationScorer } from '@mastra/evals/scorers/prebuilt';\n *\n * const scorer = createHallucinationScorer({\n * model: openai('gpt-4o'),\n * options: {\n * getContext: (run) => {\n * const toolResults = extractToolResults(run.output);\n * return toolResults.map(t => JSON.stringify({ tool: t.toolName, result: t.result }));\n * },\n * },\n * });\n * ```\n */\nexport function extractToolResults(output: ScorerRunOutputForAgent): ToolResultInfo[] {\n const results: ToolResultInfo[] = [];\n\n for (const message of output) {\n const toolInvocations = mergeToolInvocations(message);\n\n if (!toolInvocations.length) continue;\n\n for (const invocation of toolInvocations) {\n // A thrown call carries `errorText` rather than a result, so it yields no entry here.\n if (invocation.state === 'result' && invocation.result !== undefined) {\n results.push({\n toolName: invocation.toolName,\n toolCallId: invocation.toolCallId || '',\n args: invocation.args || {},\n result: invocation.result,\n });\n }\n }\n }\n\n return results;\n}\n\n// Re-export extractTrajectory from core — it's called automatically by runEvals\n// for trajectory scorers, but users may still want it for custom use cases.\nexport { extractTrajectory } from '@mastra/core/evals';\n\n/**\n * Compares two trajectories and returns detailed comparison results.\n *\n * This is the core comparison logic used by trajectory scorers. It supports\n * strict and non-strict ordering, optional step data comparison, and loop detection.\n *\n * @param actual - The trajectory the agent actually took\n * @param expected - The expected trajectory to compare against\n * @param options - Comparison configuration options\n * @returns Detailed comparison results including match scores and diagnostics\n *\n * @example\n * ```ts\n * const result = compareTrajectories(\n * { steps: [{ stepType: 'tool_call', name: 'search' }, { stepType: 'tool_call', name: 'summarize' }] },\n * { steps: [{ stepType: 'tool_call', name: 'search' }, { stepType: 'tool_call', name: 'summarize' }] },\n * { ordering: 'strict' }\n * );\n * // result.score = 1.0\n * ```\n */\nexport function compareTrajectories(\n actual: Trajectory,\n expected: Trajectory | { steps: ExpectedStep[] },\n options: {\n ordering?: 'strict' | 'relaxed' | 'unordered';\n allowRepeatedSteps?: boolean;\n } = {},\n): TrajectoryComparisonResult {\n const { allowRepeatedSteps = true, ordering = 'relaxed' } = options;\n\n // Normalize expected to ExpectedStep[]. TrajectoryStep and ExpectedStep share\n // the same field names, so TrajectoryStep[] can be used directly as ExpectedStep[].\n // The only structural difference is `children` (TrajectoryStep[] vs TrajectoryExpectation),\n // but compareTrajectories doesn't recurse into children.\n const normalizedExpected: { steps: ExpectedStep[] } = {\n steps: expected.steps as ExpectedStep[],\n };\n\n if (normalizedExpected.steps.length === 0) {\n return {\n score: actual.steps.length === 0 ? 1 : 0,\n matchedSteps: 0,\n totalExpectedSteps: 0,\n totalActualSteps: actual.steps.length,\n missingSteps: [],\n extraSteps: actual.steps.map((s: TrajectoryStep) => s.name),\n outOfOrderSteps: [],\n repeatedSteps: [],\n };\n }\n\n const actualNames = actual.steps.map((s: TrajectoryStep) => s.name);\n\n // Detect repeated steps\n const nameCounts = new Map<string, number>();\n for (const name of actualNames) {\n nameCounts.set(name, (nameCounts.get(name) || 0) + 1);\n }\n const repeatedSteps = [...nameCounts.entries()]\n .filter(([_, count]: [string, number]) => count > 1)\n .map(([name]: [string, number]) => name);\n\n if (ordering === 'strict') {\n return compareStrictOrder(actual, normalizedExpected, { allowRepeatedSteps, repeatedSteps });\n }\n\n if (ordering === 'unordered') {\n return compareUnorderedPresence(actual, normalizedExpected, { allowRepeatedSteps, repeatedSteps });\n }\n\n return compareRelaxedOrder(actual, normalizedExpected, { allowRepeatedSteps, repeatedSteps });\n}\n\n/**\n * Result of comparing two trajectories.\n */\nexport type TrajectoryComparisonResult = {\n /** Overall match score from 0 to 1 */\n score: number;\n /** Number of expected steps that were matched */\n matchedSteps: number;\n /** Total number of expected steps */\n totalExpectedSteps: number;\n /** Total number of actual steps taken */\n totalActualSteps: number;\n /** Expected steps that were not found in the actual trajectory */\n missingSteps: string[];\n /** Actual steps that were not in the expected trajectory */\n extraSteps: string[];\n /** Steps that appear but not in the expected position */\n outOfOrderSteps: string[];\n /** Steps that were repeated (appeared more than once) */\n repeatedSteps: string[];\n};\n\nfunction compareStrictOrder(\n actual: Trajectory,\n expected: { steps: ExpectedStep[] },\n opts: { allowRepeatedSteps: boolean; repeatedSteps: string[] },\n): TrajectoryComparisonResult {\n const actualNames: string[] = actual.steps.map((s: TrajectoryStep) => s.name);\n const expectedNames: string[] = expected.steps.map((s: ExpectedStep) => s.name);\n\n // Strict: exact same sequence\n let matchedSteps = 0;\n const outOfOrderSteps: string[] = [];\n const matchedExpectedIndices = new Set<number>();\n const maxLen = Math.max(actualNames.length, expectedNames.length);\n\n for (let i = 0; i < maxLen; i++) {\n const actualName = actualNames[i];\n const expectedName = expectedNames[i];\n if (actualName === expectedName) {\n if (actual.steps[i] && expected.steps[i]) {\n if (expectedStepMatches(actual.steps[i]!, expected.steps[i]!)) {\n matchedSteps++;\n matchedExpectedIndices.add(i);\n }\n } else {\n matchedSteps++;\n matchedExpectedIndices.add(i);\n }\n } else if (actualName && expectedNames.includes(actualName)) {\n outOfOrderSteps.push(actualName);\n }\n }\n\n // Missing steps = expected steps that were not matched (accounts for stepType/data mismatches)\n const missingSteps: string[] = expectedNames.filter((_: string, i: number) => !matchedExpectedIndices.has(i));\n const extraSteps: string[] = actualNames.filter((name: string) => !expectedNames.includes(name));\n\n let score = matchedSteps / expected.steps.length;\n\n // Penalize extra steps in strict mode\n if (actualNames.length > expectedNames.length) {\n const extraPenalty = (actualNames.length - expectedNames.length) / expectedNames.length;\n score = Math.max(0, score - extraPenalty * 0.5);\n }\n\n // Penalize repeated steps if not allowed\n if (!opts.allowRepeatedSteps && opts.repeatedSteps.length > 0) {\n score = Math.max(0, score - opts.repeatedSteps.length * 0.1);\n }\n\n return {\n score: roundToTwoDecimals(Math.max(0, Math.min(1, score))),\n matchedSteps,\n totalExpectedSteps: expected.steps.length,\n totalActualSteps: actual.steps.length,\n missingSteps,\n extraSteps,\n outOfOrderSteps,\n repeatedSteps: opts.repeatedSteps,\n };\n}\n\nfunction compareRelaxedOrder(\n actual: Trajectory,\n expected: { steps: ExpectedStep[] },\n opts: { allowRepeatedSteps: boolean; repeatedSteps: string[] },\n): TrajectoryComparisonResult {\n const actualNames: string[] = actual.steps.map((s: TrajectoryStep) => s.name);\n const expectedNames: string[] = expected.steps.map((s: ExpectedStep) => s.name);\n\n // Relaxed: expected steps must appear in order but extra steps are allowed\n let matchedSteps = 0;\n let lastMatchedIndex = -1;\n const outOfOrderSteps: string[] = [];\n const matchedExpectedIndices = new Set<number>();\n\n for (let i = 0; i < expectedNames.length; i++) {\n const expectedName = expectedNames[i];\n let found = false;\n\n for (let j = lastMatchedIndex + 1; j < actualNames.length; j++) {\n if (actualNames[j] === expectedName) {\n if (actual.steps[j] && expected.steps[i]) {\n if (expectedStepMatches(actual.steps[j]!, expected.steps[i]!)) {\n matchedSteps++;\n lastMatchedIndex = j;\n matchedExpectedIndices.add(i);\n found = true;\n break;\n }\n } else {\n matchedSteps++;\n lastMatchedIndex = j;\n matchedExpectedIndices.add(i);\n found = true;\n break;\n }\n }\n }\n\n if (!found) {\n // Check if the step exists but is out of order\n if (actualNames.includes(expectedName!)) {\n outOfOrderSteps.push(expectedName!);\n }\n }\n }\n\n // Missing steps = expected steps that were not matched (by name + stepType + data, not just name)\n const missingSteps = expectedNames.filter((_, i) => !matchedExpectedIndices.has(i));\n const expectedSet = new Set(expectedNames);\n const extraSteps = actualNames.filter(name => !expectedSet.has(name));\n\n let score = matchedSteps / expected.steps.length;\n\n // Penalize repeated steps if not allowed\n if (!opts.allowRepeatedSteps && opts.repeatedSteps.length > 0) {\n score = Math.max(0, score - opts.repeatedSteps.length * 0.1);\n }\n\n return {\n score: roundToTwoDecimals(Math.max(0, Math.min(1, score))),\n matchedSteps,\n totalExpectedSteps: expected.steps.length,\n totalActualSteps: actual.steps.length,\n missingSteps,\n extraSteps,\n outOfOrderSteps,\n repeatedSteps: opts.repeatedSteps,\n };\n}\n\n/**\n * Fields on each ExpectedStep variant that are comparable data (not structural).\n * Used by `expectedStepMatches` to know which fields to compare when `compareData` is true.\n */\nconst COMPARABLE_FIELDS_BY_TYPE: Record<string, string[]> = {\n tool_call: ['toolArgs', 'toolResult', 'success'],\n mcp_tool_call: ['toolArgs', 'toolResult', 'mcpServer', 'success'],\n model_generation: ['modelId', 'promptTokens', 'completionTokens', 'finishReason'],\n agent_run: ['agentId'],\n workflow_step: ['stepId', 'status', 'output'],\n workflow_run: ['workflowId', 'status'],\n workflow_conditional: ['conditionCount', 'selectedSteps'],\n workflow_parallel: ['branchCount', 'parallelSteps'],\n workflow_loop: ['loopType', 'totalIterations'],\n workflow_sleep: ['sleepDurationMs', 'sleepType'],\n workflow_wait_event: ['eventName', 'eventReceived'],\n processor_run: ['processorId'],\n};\n\n/**\n * Check if an actual TrajectoryStep matches an ExpectedStep.\n * Matches by name, optionally by stepType, and auto-compares any variant-specific\n * fields that are present on the expected step.\n */\nfunction expectedStepMatches(actual: TrajectoryStep, expected: ExpectedStep): boolean {\n if (actual.name !== expected.name) return false;\n if (expected.stepType && actual.stepType !== expected.stepType) return false;\n\n if (expected.stepType) {\n const fields = COMPARABLE_FIELDS_BY_TYPE[expected.stepType] ?? [];\n for (const field of fields) {\n const expectedVal = (expected as any)[field];\n if (expectedVal === undefined) continue; // field not specified in expectation, skip\n const actualVal = (actual as any)[field];\n if (actualVal === undefined) return false;\n try {\n if (JSON.stringify(actualVal) !== JSON.stringify(expectedVal)) return false;\n } catch {\n return false;\n }\n }\n }\n\n return true;\n}\n\nfunction compareUnorderedPresence(\n actual: Trajectory,\n expected: { steps: ExpectedStep[] },\n opts: { allowRepeatedSteps: boolean; repeatedSteps: string[] },\n): TrajectoryComparisonResult {\n const actualNames: string[] = actual.steps.map((s: TrajectoryStep) => s.name);\n const expectedNames: string[] = expected.steps.map((s: ExpectedStep) => s.name);\n\n let matchedSteps = 0;\n const matchedExpectedIndices = new Set<number>();\n const usedIndices = new Set<number>();\n for (let i = 0; i < expected.steps.length; i++) {\n const expectedStep = expected.steps[i]!;\n for (let j = 0; j < actual.steps.length; j++) {\n if (!usedIndices.has(j) && expectedStepMatches(actual.steps[j]!, expectedStep)) {\n matchedSteps++;\n matchedExpectedIndices.add(i);\n usedIndices.add(j);\n break;\n }\n }\n }\n\n // Missing steps = expected steps that were not matched (accounts for stepType/data mismatches)\n const missingSteps = expectedNames.filter((_, i) => !matchedExpectedIndices.has(i));\n const expectedSet = new Set(expectedNames);\n const extraSteps = actualNames.filter(name => !expectedSet.has(name));\n\n let score = matchedSteps / expected.steps.length;\n\n // Penalize repeated steps if not allowed\n if (!opts.allowRepeatedSteps && opts.repeatedSteps.length > 0) {\n score = Math.max(0, score - opts.repeatedSteps.length * 0.1);\n }\n\n return {\n score: roundToTwoDecimals(Math.max(0, Math.min(1, score))),\n matchedSteps,\n totalExpectedSteps: expected.steps.length,\n totalActualSteps: actual.steps.length,\n missingSteps,\n extraSteps,\n outOfOrderSteps: [], // ordering not checked in unordered mode\n repeatedSteps: opts.repeatedSteps,\n };\n}\n\n// ─── Efficiency evaluation ───\n\n/**\n * Result of checking trajectory efficiency.\n */\nexport type TrajectoryEfficiencyResult = {\n /** Overall efficiency score from 0 to 1 */\n score: number;\n /** Total number of steps taken */\n totalSteps: number;\n /** Whether the step budget was exceeded */\n overStepBudget: boolean;\n /** Total tokens used across model_generation steps */\n totalTokens: number;\n /** Whether the token budget was exceeded */\n overTokenBudget: boolean;\n /** Total duration in milliseconds */\n totalDurationMs: number;\n /** Whether the duration budget was exceeded */\n overDurationBudget: boolean;\n /** Redundant calls detected (same tool + same args consecutively) */\n redundantCalls: Array<{ name: string; index: number }>;\n};\n\n/**\n * Evaluate trajectory efficiency against budgets and redundancy checks.\n * Throws when a configured budget is invalid or its required measurements are incomplete or invalid.\n */\nexport function checkTrajectoryEfficiency(\n trajectory: Trajectory,\n options: {\n maxSteps?: number;\n maxTotalTokens?: number;\n maxTotalDurationMs?: number;\n noRedundantCalls?: boolean;\n } = {},\n): TrajectoryEfficiencyResult {\n const { maxSteps, maxTotalTokens, maxTotalDurationMs, noRedundantCalls = true } = options;\n\n const isMeasurement = (value: number | undefined): value is number =>\n typeof value === 'number' && Number.isFinite(value) && value >= 0;\n for (const [name, limit] of Object.entries({ maxSteps, maxTotalTokens, maxTotalDurationMs })) {\n if (limit !== undefined && !isMeasurement(limit)) {\n throw new Error(`Invalid ${name} budget: expected a finite nonnegative number`);\n }\n }\n\n const totalSteps = trajectory.steps.length;\n\n // Count each model generation, including nested agents, without counting container aggregates.\n let totalTokens = 0;\n let modelGenerations = 0;\n const remaining = [...trajectory.steps];\n while (remaining.length > 0) {\n const step = remaining.pop()!;\n if (step.stepType === 'model_generation') {\n modelGenerations++;\n if (\n maxTotalTokens !== undefined &&\n (!isMeasurement(step.promptTokens) || !isMeasurement(step.completionTokens))\n ) {\n throw new Error(\n `Cannot evaluate token budget: model generation \"${step.name}\" has missing or invalid token counts`,\n );\n }\n totalTokens += (step.promptTokens ?? 0) + (step.completionTokens ?? 0);\n }\n if (step.children) {\n for (const child of step.children) remaining.push(child);\n }\n }\n if (maxTotalTokens !== undefined && (modelGenerations === 0 || !isMeasurement(totalTokens))) {\n throw new Error('Cannot evaluate token budget: missing model-generation measurements or invalid total tokens');\n }\n\n // A parent duration already covers its children; never add both.\n const totalDurationMs =\n trajectory.totalDurationMs ?? trajectory.steps.reduce((sum, s) => sum + (s.durationMs ?? 0), 0);\n if (maxTotalDurationMs !== undefined) {\n const hasDuration =\n trajectory.totalDurationMs !== undefined\n ? isMeasurement(trajectory.totalDurationMs)\n : trajectory.steps.length > 0 && trajectory.steps.every(step => isMeasurement(step.durationMs));\n if (!hasDuration || !isMeasurement(totalDurationMs)) {\n throw new Error('Cannot evaluate duration budget: missing or invalid duration measurements');\n }\n }\n\n // Detect redundant calls (same tool name + same args in consecutive calls)\n const redundantCalls: Array<{ name: string; index: number }> = [];\n const siblingLists: TrajectoryStep[][] = noRedundantCalls ? [trajectory.steps] : [];\n while (siblingLists.length > 0) {\n const steps = siblingLists.pop()!;\n for (const step of steps) {\n if (step.children?.length) siblingLists.push(step.children);\n }\n for (let i = 1; i < steps.length; i++) {\n const prev = steps[i - 1]!;\n const curr = steps[i]!;\n if (\n prev.name === curr.name &&\n prev.stepType === curr.stepType &&\n (prev.stepType === 'tool_call' || prev.stepType === 'mcp_tool_call')\n ) {\n const prevArgs = (prev as TrajectoryStep & { toolArgs?: Record<string, unknown> }).toolArgs;\n const currArgs = (curr as TrajectoryStep & { toolArgs?: Record<string, unknown> }).toolArgs;\n try {\n if (JSON.stringify(prevArgs) === JSON.stringify(currArgs)) {\n redundantCalls.push({ name: curr.name, index: i });\n }\n } catch {\n // If serialization fails, don't flag as redundant\n }\n }\n }\n }\n\n const overStepBudget = maxSteps !== undefined && totalSteps > maxSteps;\n const overTokenBudget = maxTotalTokens !== undefined && totalTokens > maxTotalTokens;\n const overDurationBudget = maxTotalDurationMs !== undefined && totalDurationMs > maxTotalDurationMs;\n\n // Calculate score: each dimension contributes equally\n const dimensions: number[] = [];\n\n if (maxSteps !== undefined) {\n dimensions.push(overStepBudget ? Math.max(0, 1 - (totalSteps - maxSteps) / maxSteps) : 1);\n }\n if (maxTotalTokens !== undefined) {\n dimensions.push(overTokenBudget ? Math.max(0, 1 - (totalTokens - maxTotalTokens) / maxTotalTokens) : 1);\n }\n if (maxTotalDurationMs !== undefined) {\n dimensions.push(\n overDurationBudget ? Math.max(0, 1 - (totalDurationMs - maxTotalDurationMs) / maxTotalDurationMs) : 1,\n );\n }\n if (noRedundantCalls) {\n dimensions.push(redundantCalls.length === 0 ? 1 : Math.max(0, 1 - redundantCalls.length * 0.2));\n }\n\n const score = dimensions.length > 0 ? dimensions.reduce((a, b) => a + b, 0) / dimensions.length : 1;\n\n return {\n score: roundToTwoDecimals(Math.max(0, Math.min(1, score))),\n totalSteps,\n overStepBudget,\n totalTokens,\n overTokenBudget,\n totalDurationMs,\n overDurationBudget,\n redundantCalls,\n };\n}\n\n// ─── Blacklist evaluation ───\n\n/**\n * Result of checking trajectory against a blacklist.\n */\nexport type TrajectoryBlacklistResult = {\n /** Score: 1.0 if clean, 0.0 if any violation found */\n score: number;\n /** Individual blacklisted tools that were found */\n violatedTools: string[];\n /** Blacklisted sequences that were found */\n violatedSequences: string[][];\n};\n\n/**\n * Check if a trajectory violates any blacklist rules.\n * Returns score 0.0 if any violation is found (hard fail).\n */\nexport function checkTrajectoryBlacklist(\n trajectory: Trajectory,\n options: {\n blacklistedTools?: string[];\n blacklistedSequences?: string[][];\n } = {},\n): TrajectoryBlacklistResult {\n const { blacklistedTools = [], blacklistedSequences = [] } = options;\n const violatedTools: string[] = [];\n const violatedSequences: string[][] = [];\n\n // Collect every sibling list (top level plus each nested `children` list)\n const siblingLists: string[][] = [];\n const allNames = new Set<string>();\n const pending: TrajectoryStep[][] = [trajectory.steps];\n while (pending.length > 0) {\n const steps = pending.pop()!;\n siblingLists.push(steps.map(s => s.name));\n for (const step of steps) {\n allNames.add(step.name);\n if (step.children?.length) pending.push(step.children);\n }\n }\n\n // Check blacklisted tools at any depth\n for (const forbidden of blacklistedTools) {\n if (allNames.has(forbidden)) {\n violatedTools.push(forbidden);\n }\n }\n\n // Check blacklisted sequences (contiguous subsequences within one sibling list)\n const containsSequence = (names: string[], sequence: string[]) => {\n for (let i = 0; i <= names.length - sequence.length; i++) {\n let match = true;\n for (let j = 0; j < sequence.length; j++) {\n if (names[i + j] !== sequence[j]) {\n match = false;\n break;\n }\n }\n if (match) return true;\n }\n return false;\n };\n for (const sequence of blacklistedSequences) {\n if (sequence.length === 0) continue;\n if (siblingLists.some(names => containsSequence(names, sequence))) {\n violatedSequences.push(sequence); // Only report each sequence once\n }\n }\n\n const hasViolations = violatedTools.length > 0 || violatedSequences.length > 0;\n\n return {\n score: hasViolations ? 0 : 1,\n violatedTools,\n violatedSequences,\n };\n}\n\n// ─── Tool failure analysis ───\n\n/**\n * A detected tool failure pattern in the trajectory.\n */\nexport type ToolFailurePattern = {\n /** The tool name that experienced failure */\n toolName: string;\n /** Number of consecutive retries (same tool, same or similar args) */\n retryCount: number;\n /** Whether the agent fell back to a different tool after failures */\n fellBackToAlternative: boolean;\n /** The alternative tool used, if any */\n alternativeTool?: string;\n /** Whether any retry eventually succeeded */\n eventuallySucceeded: boolean;\n};\n\n/**\n * Result of analyzing tool failure patterns in a trajectory.\n */\nexport type ToolFailureAnalysisResult = {\n /** Score from 0 to 1 (lower = more failures/retries) */\n score: number;\n /** Tool failure patterns detected */\n patterns: ToolFailurePattern[];\n /** Total number of retries across all tools */\n totalRetries: number;\n /** Tools that exceeded the retry threshold */\n excessiveRetryTools: string[];\n};\n\n/**\n * Analyze tool failure and retry patterns in a trajectory.\n */\nexport function analyzeToolFailures(\n trajectory: Trajectory,\n options: {\n maxRetriesPerTool?: number;\n } = {},\n): ToolFailureAnalysisResult {\n const { maxRetriesPerTool = 2 } = options;\n const patterns: ToolFailurePattern[] = [];\n let totalRetries = 0;\n\n const toolCallSteps = trajectory.steps.filter(s => s.stepType === 'tool_call' || s.stepType === 'mcp_tool_call');\n\n if (toolCallSteps.length === 0) {\n return { score: 1, patterns: [], totalRetries: 0, excessiveRetryTools: [] };\n }\n\n // Group consecutive calls to the same tool as potential retry sequences\n let i = 0;\n while (i < toolCallSteps.length) {\n const currentTool = toolCallSteps[i]!;\n let retryCount = 0;\n let j = i + 1;\n\n // Count consecutive calls to the same tool\n // (toolCallSteps is pre-filtered to tool_call/mcp_tool_call, so no stepType checks needed)\n while (j < toolCallSteps.length && toolCallSteps[j]!.name === currentTool.name) {\n const prevStep = toolCallSteps[j - 1]! as TrajectoryStep & { success?: boolean };\n if (prevStep.success === false) {\n retryCount++;\n }\n j++;\n }\n\n if (retryCount > 0) {\n // Check if agent fell back to a different tool after retries\n const nextDifferentTool = j < toolCallSteps.length ? toolCallSteps[j] : undefined;\n const lastRetry = toolCallSteps[j - 1]! as TrajectoryStep & { success?: boolean };\n const lastSuccess = lastRetry.success !== false;\n\n patterns.push({\n toolName: currentTool.name,\n retryCount,\n fellBackToAlternative: nextDifferentTool !== undefined && !lastSuccess,\n alternativeTool: nextDifferentTool !== undefined && !lastSuccess ? nextDifferentTool.name : undefined,\n eventuallySucceeded: lastSuccess,\n });\n\n totalRetries += retryCount;\n }\n\n i = j;\n }\n\n // Score: penalize excessive retries\n const excessiveRetryTools = patterns.filter(p => p.retryCount > maxRetriesPerTool).map(p => p.toolName);\n\n let score = 1;\n if (toolCallSteps.length > 0) {\n // Each retry beyond the threshold costs more\n const excessRetries = patterns.reduce((sum, p) => sum + Math.max(0, p.retryCount - maxRetriesPerTool), 0);\n score = Math.max(0, 1 - excessRetries * 0.2);\n }\n\n return {\n score: roundToTwoDecimals(Math.max(0, Math.min(1, score))),\n patterns,\n totalRetries,\n excessiveRetryTools,\n };\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/scorers/utils.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/scorers/utils.ts"],"names":[],"mappings":"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"}
@@ -959,18 +959,23 @@ function checkTrajectoryEfficiency(trajectory, options = {}) {
959
959
  if (!(trajectory.totalDurationMs !== void 0 ? isMeasurement(trajectory.totalDurationMs) : trajectory.steps.length > 0 && trajectory.steps.every((step) => isMeasurement(step.durationMs))) || !isMeasurement(totalDurationMs)) throw new Error("Cannot evaluate duration budget: missing or invalid duration measurements");
960
960
  }
961
961
  const redundantCalls = [];
962
- if (noRedundantCalls) for (let i = 1; i < trajectory.steps.length; i++) {
963
- const prev = trajectory.steps[i - 1];
964
- const curr = trajectory.steps[i];
965
- if (prev.name === curr.name && prev.stepType === curr.stepType && (prev.stepType === "tool_call" || prev.stepType === "mcp_tool_call")) {
966
- const prevArgs = prev.toolArgs;
967
- const currArgs = curr.toolArgs;
968
- try {
969
- if (JSON.stringify(prevArgs) === JSON.stringify(currArgs)) redundantCalls.push({
970
- name: curr.name,
971
- index: i
972
- });
973
- } catch {}
962
+ const siblingLists = noRedundantCalls ? [trajectory.steps] : [];
963
+ while (siblingLists.length > 0) {
964
+ const steps = siblingLists.pop();
965
+ for (const step of steps) if (step.children?.length) siblingLists.push(step.children);
966
+ for (let i = 1; i < steps.length; i++) {
967
+ const prev = steps[i - 1];
968
+ const curr = steps[i];
969
+ if (prev.name === curr.name && prev.stepType === curr.stepType && (prev.stepType === "tool_call" || prev.stepType === "mcp_tool_call")) {
970
+ const prevArgs = prev.toolArgs;
971
+ const currArgs = curr.toolArgs;
972
+ try {
973
+ if (JSON.stringify(prevArgs) === JSON.stringify(currArgs)) redundantCalls.push({
974
+ name: curr.name,
975
+ index: i
976
+ });
977
+ } catch {}
978
+ }
974
979
  }
975
980
  }
976
981
  const overStepBudget = maxSteps !== void 0 && totalSteps > maxSteps;
@@ -1001,21 +1006,32 @@ function checkTrajectoryBlacklist(trajectory, options = {}) {
1001
1006
  const { blacklistedTools = [], blacklistedSequences = [] } = options;
1002
1007
  const violatedTools = [];
1003
1008
  const violatedSequences = [];
1004
- const stepNames = trajectory.steps.map((s) => s.name);
1005
- for (const forbidden of blacklistedTools) if (stepNames.includes(forbidden)) violatedTools.push(forbidden);
1006
- for (const sequence of blacklistedSequences) {
1007
- if (sequence.length === 0) continue;
1008
- for (let i = 0; i <= stepNames.length - sequence.length; i++) {
1009
+ const siblingLists = [];
1010
+ const allNames = /* @__PURE__ */ new Set();
1011
+ const pending = [trajectory.steps];
1012
+ while (pending.length > 0) {
1013
+ const steps = pending.pop();
1014
+ siblingLists.push(steps.map((s) => s.name));
1015
+ for (const step of steps) {
1016
+ allNames.add(step.name);
1017
+ if (step.children?.length) pending.push(step.children);
1018
+ }
1019
+ }
1020
+ for (const forbidden of blacklistedTools) if (allNames.has(forbidden)) violatedTools.push(forbidden);
1021
+ const containsSequence = (names, sequence) => {
1022
+ for (let i = 0; i <= names.length - sequence.length; i++) {
1009
1023
  let match = true;
1010
- for (let j = 0; j < sequence.length; j++) if (stepNames[i + j] !== sequence[j]) {
1024
+ for (let j = 0; j < sequence.length; j++) if (names[i + j] !== sequence[j]) {
1011
1025
  match = false;
1012
1026
  break;
1013
1027
  }
1014
- if (match) {
1015
- violatedSequences.push(sequence);
1016
- break;
1017
- }
1028
+ if (match) return true;
1018
1029
  }
1030
+ return false;
1031
+ };
1032
+ for (const sequence of blacklistedSequences) {
1033
+ if (sequence.length === 0) continue;
1034
+ if (siblingLists.some((names) => containsSequence(names, sequence))) violatedSequences.push(sequence);
1019
1035
  }
1020
1036
  return {
1021
1037
  score: violatedTools.length > 0 || violatedSequences.length > 0 ? 0 : 1,
@@ -1 +1 @@
1
- {"version":3,"file":"utils.js","names":[],"sources":["../../src/scorers/utils.ts"],"sourcesContent":["import type { MastraDBMessage } from '@mastra/core/agent';\nimport type {\n ExpectedStep,\n ScorerRunInputForAgent,\n ScorerRunOutputForAgent,\n ScoringInput,\n TrajectoryExpectation,\n TrajectoryStep,\n Trajectory,\n} from '@mastra/core/evals';\nimport { RequestContext } from '@mastra/core/request-context';\n\nexport type ScorerRunInputForLLMJudge =\n | ScorerRunInputForAgent\n | string\n | {\n inputMessages?: unknown[];\n messages?: unknown[];\n prompt?: string;\n text?: string;\n content?: unknown;\n input?: unknown;\n user?: unknown;\n [key: string]: unknown;\n };\n\nexport type ScorerRunOutputForLLMJudge =\n | ScorerRunOutputForAgent\n | string\n | unknown[]\n | {\n text?: string;\n content?: unknown;\n role?: string;\n [key: string]: unknown;\n };\n\n/**\n * Extracts text content from a MastraDBMessage or ModelMessage-like object.\n *\n * @param message - The message to extract text from\n * @returns The extracted text content, or an empty string if no text is found\n *\n * @example\n * ```ts\n * const message: MastraDBMessage = {\n * id: 'msg-1',\n * role: 'assistant',\n * content: { format: 2, parts: [{ type: 'text', text: 'Hello!' }] },\n * createdAt: new Date(),\n * };\n * const text = getTextContentFromMastraDBMessage(message); // 'Hello!'\n * ```\n */\nexport function getTextContentFromMastraDBMessage(message: MastraDBMessage): string {\n const content = message.content as any;\n\n if (typeof content === 'string') {\n return content;\n }\n if (Array.isArray(content)) {\n const textParts = content.filter(p => p.type === 'text');\n return textParts.length > 0 ? textParts[textParts.length - 1]?.text || '' : '';\n }\n if (typeof content?.content === 'string' && content.content !== '') {\n return content.content;\n }\n if (typeof content?.text === 'string' && content.text !== '') {\n return content.text;\n }\n if (content?.parts && Array.isArray(content.parts)) {\n // Return only the last text part like AI SDK does\n const textParts = content.parts.filter((p: any) => p.type === 'text');\n return textParts.length > 0 ? textParts[textParts.length - 1]?.text || '' : '';\n }\n return '';\n}\n\nconst isRecord = (value: unknown): value is Record<string, any> => {\n return typeof value === 'object' && value !== null;\n};\n\nconst getTextFromValue = (value: unknown): string | undefined => {\n if (typeof value === 'string') return value === '' ? undefined : value;\n if (Array.isArray(value)) {\n const textParts = value\n .filter(part => isRecord(part) && part.type === 'text' && typeof part.text === 'string')\n .map(part => part.text);\n return textParts.length > 0 ? textParts[textParts.length - 1] : undefined;\n }\n if (!isRecord(value)) return undefined;\n\n const fromParts = Array.isArray(value.parts) ? getTextFromValue(value.parts) : undefined;\n\n return (\n getTextFromValue(value.content) ??\n (typeof value.text === 'string' && value.text !== '' ? value.text : undefined) ??\n (typeof value.body === 'string' && value.body !== '' ? value.body : undefined) ??\n fromParts\n );\n};\n\nexport const isScorerRunInputForAgent = (input: unknown): input is ScorerRunInputForAgent => {\n return (\n isRecord(input) &&\n Array.isArray(input.inputMessages) &&\n Array.isArray(input.rememberedMessages) &&\n Array.isArray(input.systemMessages) &&\n isRecord(input.taggedSystemMessages)\n );\n};\n\nconst isMastraDBMessageLike = (message: unknown): message is MastraDBMessage => {\n return (\n isRecord(message) &&\n typeof message.id === 'string' &&\n typeof message.role === 'string' &&\n 'content' in message &&\n 'createdAt' in message\n );\n};\n\nexport const isScorerRunOutputForAgent = (output: unknown): output is ScorerRunOutputForAgent => {\n return Array.isArray(output) && output.every(isMastraDBMessageLike);\n};\n\n/**\n * Resolves the effective role of a message, accounting for agent signal messages.\n *\n * Messages delivered through the agent subscription / signal API are persisted with\n * `role: 'signal'` and carry their semantic role (e.g. `user`) on `type` and on\n * `content.metadata.signal.{type,tagName}`. Treat those as their underlying role so\n * helpers like `getUserMessageFromRunInput` can find them.\n */\nconst getEffectiveMessageRole = (message: Record<string, any>): string | undefined => {\n if (message.role !== 'signal') return typeof message.role === 'string' ? message.role : undefined;\n\n const signalMeta =\n isRecord(message.content) && isRecord(message.content.metadata) ? message.content.metadata.signal : undefined;\n\n const tagName = isRecord(signalMeta) && typeof signalMeta.tagName === 'string' ? signalMeta.tagName : undefined;\n const signalType = isRecord(signalMeta) && typeof signalMeta.type === 'string' ? signalMeta.type : undefined;\n const topLevelType = typeof message.type === 'string' ? message.type : undefined;\n\n return tagName ?? signalType ?? topLevelType;\n};\n\nconst getTextFromMessages = (messages: unknown, role: string): string | undefined => {\n if (!Array.isArray(messages)) return undefined;\n\n const message = messages.find(message => isRecord(message) && getEffectiveMessageRole(message) === role);\n return message ? getTextFromValue(message) : undefined;\n};\n\n/**\n * Rounds a number to two decimal places.\n *\n * Uses `Number.EPSILON` to handle floating-point precision issues.\n *\n * @param num - The number to round\n * @returns The number rounded to two decimal places\n *\n * @example\n * ```ts\n * roundToTwoDecimals(0.1 + 0.2); // 0.3\n * roundToTwoDecimals(1.005); // 1.01\n * ```\n */\nexport const roundToTwoDecimals = (num: number) => {\n return Math.round((num + Number.EPSILON) * 100) / 100;\n};\n\n/**\n * Determines if a value is closer to the first target than the second.\n *\n * @param value - The value to compare\n * @param target1 - The first target value\n * @param target2 - The second target value\n * @returns `true` if `value` is closer to `target1` than `target2`\n *\n * @example\n * ```ts\n * isCloserTo(0.6, 1, 0); // true (0.6 is closer to 1)\n * isCloserTo(0.3, 1, 0); // false (0.3 is closer to 0)\n * ```\n */\nexport function isCloserTo(value: number, target1: number, target2: number): boolean {\n return Math.abs(value - target1) < Math.abs(value - target2);\n}\n\n/**\n * Represents a test case for scorer evaluation.\n */\nexport type TestCase = {\n /** The input text to evaluate */\n input: string;\n /** The output text to evaluate */\n output: string;\n /** The expected result of the evaluation */\n expectedResult: {\n /** The expected score */\n score: number;\n /** The optional expected reason */\n reason?: string;\n };\n};\n\n/**\n * Represents a test case with additional context for scorer evaluation.\n */\nexport type TestCaseWithContext = TestCase & {\n /** Additional context strings for the evaluation */\n context: string[];\n};\n\n/**\n * Creates a scoring input object for testing purposes.\n *\n * @param input - The user input text\n * @param output - The assistant output text\n * @param additionalContext - Optional additional context data\n * @param requestContext - Optional request context data\n * @returns A ScoringInput object ready for use in scorer tests\n *\n * @example\n * ```ts\n * const run = createTestRun(\n * 'What is 2+2?',\n * 'The answer is 4.',\n * { topic: 'math' }\n * );\n * ```\n */\nexport const createTestRun = (\n input: string,\n output: string,\n additionalContext?: Record<string, any>,\n requestContext?: Record<string, any>,\n): ScoringInput => {\n return {\n input: [{ role: 'user', content: input }],\n output: { role: 'assistant', text: output },\n additionalContext: additionalContext ?? {},\n requestContext: requestContext ?? {},\n };\n};\n\n/**\n * Extracts the user message text from a scorer run input.\n *\n * Accepts the agent shape (`{ inputMessages }`), `ModelMessage[]`\n * (`{ messages }`), workflow input (`{ prompt }`), and a bare string.\n *\n * @param input - The scorer run input\n * @returns The user message text, or `undefined` if none can be extracted\n *\n * @example\n * ```ts\n * const scorer = createScorer({ ... })\n * .preprocess(({ run }) => {\n * const userText = getUserMessageFromRunInput(run.input);\n * return { userText };\n * });\n * ```\n */\nexport const getUserMessageFromRunInput = (input?: unknown): string | undefined => {\n if (typeof input === 'string') return input;\n if (!isRecord(input)) return undefined;\n\n return (\n getTextFromMessages(input.inputMessages, 'user') ??\n getTextFromMessages(input.messages, 'user') ??\n (typeof input.prompt === 'string' ? input.prompt : undefined) ??\n (typeof input.text === 'string' ? input.text : undefined) ??\n getTextFromValue(input.content) ??\n getTextFromValue(input.input) ??\n getTextFromValue(input.user)\n );\n};\n\nconst DEFAULT_CONVERSATION_HISTORY_MESSAGES = 10;\n\n/**\n * Renders the remembered conversation history from an agent scorer run input as a transcript.\n *\n * Only agent run inputs carry `rememberedMessages`; any other input shape (a bare string,\n * a plain `{ prompt }` object, `ModelMessage[]`) returns `undefined` so callers can safely\n * fall back to single-turn behaviour.\n *\n * @param input - The scorer run input\n * @param options.maxMessages - How many of the most recent remembered messages to keep (default 10)\n * @returns A newline separated `role: text` transcript, or `undefined` if there is no history\n *\n * @example\n * ```ts\n * const scorer = createScorer({ ... })\n * .preprocess(({ run }) => {\n * const history = getConversationHistoryFromRunInput(run.input, { maxMessages: 4 });\n * return { history };\n * });\n * ```\n */\nexport const getConversationHistoryFromRunInput = (\n input?: unknown,\n options?: { maxMessages?: number },\n): string | undefined => {\n if (!isScorerRunInputForAgent(input)) return undefined;\n\n const maxMessages = options?.maxMessages ?? DEFAULT_CONVERSATION_HISTORY_MESSAGES;\n if (maxMessages <= 0) return undefined;\n\n const lines = input.rememberedMessages\n .slice(-maxMessages)\n .map(message => {\n if (!isRecord(message)) return undefined;\n\n const role = getEffectiveMessageRole(message);\n const text = getTextContentFromMastraDBMessage(message as MastraDBMessage).trim();\n if (!role || !text) return undefined;\n\n return `${role}: ${text}`;\n })\n .filter((line): line is string => Boolean(line));\n\n return lines.length > 0 ? lines.join('\\n') : undefined;\n};\n\n/**\n * Extracts all system messages from a scorer run input.\n *\n * Collects text from both standard system messages and tagged system messages\n * (specialized system prompts like memory instructions).\n *\n * @param input - The scorer run input containing system messages\n * @returns An array of system message strings\n *\n * @example\n * ```ts\n * const scorer = createScorer({ ... })\n * .preprocess(({ run }) => {\n * const systemMessages = getSystemMessagesFromRunInput(run.input);\n * return { systemPrompt: systemMessages.join('\\n') };\n * });\n * ```\n */\nexport const getSystemMessagesFromRunInput = (input?: unknown): string[] => {\n const systemMessages: string[] = [];\n if (!isRecord(input)) return systemMessages;\n\n // Add standard system messages\n if (Array.isArray(input.systemMessages)) {\n systemMessages.push(\n ...input.systemMessages\n .map(msg => {\n // Handle different content types - extract text if it's an array of parts\n if (typeof msg.content === 'string') {\n return msg.content;\n } else if (Array.isArray(msg.content)) {\n // Extract text from parts array\n return msg.content\n .filter((part: any) => part.type === 'text')\n .map((part: any) => part.text || '')\n .join(' ');\n }\n return '';\n })\n .filter(content => content),\n );\n }\n\n const addSystemMessages = (messages: unknown) => {\n if (!Array.isArray(messages)) return;\n\n systemMessages.push(\n ...messages\n .filter(message => isRecord(message) && message.role === 'system')\n .map(message => getTextFromValue(message))\n .filter((content): content is string => Boolean(content)),\n );\n };\n\n addSystemMessages(input.inputMessages);\n addSystemMessages(input.messages);\n\n // Add tagged system messages (these are specialized system prompts)\n if (isRecord(input.taggedSystemMessages)) {\n Object.values(input.taggedSystemMessages).forEach(messages => {\n if (!Array.isArray(messages)) return;\n messages.forEach(msg => {\n const content = getTextFromValue(msg);\n if (content) {\n systemMessages.push(content);\n }\n });\n });\n }\n\n return systemMessages;\n};\n\n/**\n * Combines all system messages into a single prompt string.\n *\n * Joins all system messages (standard and tagged) with double newlines.\n *\n * @param input - The scorer run input containing system messages\n * @returns A combined system prompt string\n *\n * @example\n * ```ts\n * const scorer = createScorer({ ... })\n * .preprocess(({ run }) => {\n * const systemPrompt = getCombinedSystemPrompt(run.input);\n * return { systemPrompt };\n * });\n * ```\n */\nexport const getCombinedSystemPrompt = (input?: unknown): string => {\n const systemMessages = getSystemMessagesFromRunInput(input);\n return systemMessages.join('\\n\\n');\n};\n\n/**\n * Extracts the assistant message text from a scorer run output.\n *\n * Accepts the agent shape (`MastraDBMessage[]` / `ModelMessage[]`), workflow\n * output (`{ text }`), task output (`{ content }`), a single assistant message\n * object, and a bare string.\n *\n * For array outputs the final assistant message is used, so multi-step agent\n * responses are scored on the last response rather than an intermediate one.\n *\n * @param output - The scorer run output\n * @returns The assistant message text, or `undefined` if none can be extracted\n *\n * @example\n * ```ts\n * const scorer = createScorer({ ... })\n * .preprocess(({ run }) => {\n * const response = getAssistantMessageFromRunOutput(run.output);\n * return { response };\n * });\n * ```\n */\nexport const getAssistantMessageFromRunOutput = (output?: unknown) => {\n if (typeof output === 'string') return output;\n if (Array.isArray(output)) {\n const assistantMessages = output.filter(\n message => isRecord(message) && getEffectiveMessageRole(message) === 'assistant',\n );\n // Prefer the last assistant message that carries text; a trailing assistant\n // message may hold only tool-call or data parts.\n for (let i = assistantMessages.length - 1; i >= 0; i--) {\n const text = getTextFromValue(assistantMessages[i]);\n if (text) return text;\n }\n return undefined;\n }\n if (!isRecord(output)) return undefined;\n\n const isAssistantOutput = output.role === undefined || output.role === 'assistant';\n\n if (isAssistantOutput && typeof output.text === 'string') return output.text;\n if (isAssistantOutput && typeof output.content === 'string') return output.content;\n if (isAssistantOutput && (isRecord(output.content) || Array.isArray(output.content))) {\n return (\n getTextContentFromMastraDBMessage(output as MastraDBMessage) ||\n getTextContentFromMastraDBMessage(output.content as MastraDBMessage) ||\n undefined\n );\n }\n if (output.role === 'assistant') return getTextContentFromMastraDBMessage(output as MastraDBMessage) || undefined;\n\n return undefined;\n};\n\n/**\n * Extracts reasoning text from a scorer run output.\n *\n * This function extracts reasoning content from assistant messages, which is\n * produced by reasoning models like `deepseek-reasoner`. The reasoning can be\n * stored in two places:\n * 1. `content.reasoning` - a string field on the message content\n * 2. `content.parts` - as parts with `type: 'reasoning'` containing `details`\n *\n * @param output - The scorer run output (array of MastraDBMessage)\n * @returns The reasoning text, or `undefined` if no reasoning is present\n *\n * @example\n * ```ts\n * const reasoningScorer = createScorer({\n * id: 'reasoning-scorer',\n * name: 'Reasoning Quality',\n * description: 'Evaluates the quality of model reasoning',\n * type: 'agent',\n * })\n * .preprocess(({ run }) => {\n * const reasoning = getReasoningFromRunOutput(run.output);\n * const response = getAssistantMessageFromRunOutput(run.output);\n * return { reasoning, response };\n * })\n * .generateScore(({ results }) => {\n * // Score based on reasoning quality\n * return results.preprocessStepResult?.reasoning ? 1 : 0;\n * });\n * ```\n */\nexport const getReasoningFromRunOutput = (output?: ScorerRunOutputForAgent): string | undefined => {\n if (!output) return undefined;\n\n const message = output.find(({ role }) => role === 'assistant');\n if (!message) return undefined;\n\n // Check for reasoning in content.reasoning (string format)\n if (message.content.reasoning) {\n return message.content.reasoning;\n }\n\n // Check for reasoning in parts with type 'reasoning'\n // Reasoning models store reasoning in parts as { type: 'reasoning', details: [{ type: 'text', text: '...' }] }\n const reasoningParts = message.content.parts?.filter((p: any) => p.type === 'reasoning');\n if (reasoningParts && reasoningParts.length > 0) {\n const reasoningTexts = reasoningParts\n .map((p: any) => {\n // The reasoning text can be in p.reasoning or in p.details[].text\n if (p.details && Array.isArray(p.details)) {\n return p.details\n .filter((d: any) => d.type === 'text')\n .map((d: any) => d.text)\n .join('');\n }\n return p.reasoning || '';\n })\n .filter(Boolean);\n\n return reasoningTexts.length > 0 ? reasoningTexts.join('\\n') : undefined;\n }\n\n return undefined;\n};\n\n/**\n * Creates a tool invocation object for testing purposes.\n *\n * @param options - The tool invocation configuration\n * @param options.toolCallId - Unique identifier for the tool call\n * @param options.toolName - Name of the tool being called\n * @param options.args - Arguments passed to the tool\n * @param options.result - Result returned by the tool (absent for a thrown call)\n * @param options.state - State of the invocation (default: 'result')\n * @param options.errorText - Error text recorded when a tool threw (`state: 'output-error'`)\n * @param options.isError - Set alongside `state: 'result'` when the executor reported a failure\n * @returns A tool invocation object\n *\n * @example\n * ```ts\n * const invocation = createToolInvocation({\n * toolCallId: 'call-123',\n * toolName: 'weatherTool',\n * args: { location: 'London' },\n * result: { temperature: 20, condition: 'sunny' },\n * });\n *\n * // A tool that threw\n * const thrown = createToolInvocation({\n * toolCallId: 'call-456',\n * toolName: 'saveTool',\n * args: { value: 'x' },\n * state: 'output-error',\n * errorText: 'Save failed',\n * });\n * ```\n */\nexport const createToolInvocation = ({\n toolCallId,\n toolName,\n args,\n result,\n state = 'result',\n errorText,\n isError,\n}: {\n toolCallId: string;\n toolName: string;\n args: Record<string, any>;\n result?: Record<string, any>;\n state?: 'call' | 'partial-call' | 'result' | 'output-error';\n errorText?: string;\n isError?: boolean;\n}): {\n toolCallId: string;\n toolName: string;\n args: Record<string, any>;\n result: Record<string, any> | undefined;\n state: 'call' | 'partial-call' | 'result' | 'output-error';\n errorText?: string;\n isError?: boolean;\n} => {\n return {\n toolCallId,\n toolName,\n args,\n result,\n state,\n ...(errorText !== undefined && { errorText }),\n ...(isError !== undefined && { isError }),\n };\n};\n\n/**\n * Creates a MastraDBMessage object for testing purposes.\n *\n * Supports optional tool invocations for testing tool call scenarios.\n *\n * @param options - The message configuration\n * @param options.content - The text content of the message\n * @param options.role - The role of the message sender ('user', 'assistant', or 'system')\n * @param options.id - Optional message ID (default: 'test-message')\n * @param options.toolInvocations - Optional array of tool invocations\n * @returns A MastraDBMessage object\n *\n * @example\n * ```ts\n * const message = createTestMessage({\n * content: 'Hello, how can I help?',\n * role: 'assistant',\n * });\n *\n * // With tool invocations\n * const messageWithTools = createTestMessage({\n * content: 'Let me check the weather.',\n * role: 'assistant',\n * toolInvocations: [{\n * toolCallId: 'call-1',\n * toolName: 'weatherTool',\n * args: { location: 'Paris' },\n * result: { temp: 22 },\n * state: 'result',\n * }],\n * });\n *\n * // With native V2 parts — the only form that can carry a thrown call\n * const messageWithThrownTool = createTestMessage({\n * content: 'That failed.',\n * role: 'assistant',\n * parts: [\n * { type: 'text', text: 'That failed.' },\n * {\n * type: 'tool-invocation',\n * toolInvocation: createToolInvocation({\n * toolCallId: 'call-2',\n * toolName: 'saveTool',\n * args: { value: 'x' },\n * state: 'output-error',\n * errorText: 'Save failed',\n * }),\n * },\n * ],\n * });\n * ```\n */\nexport function createTestMessage({\n content,\n role,\n id = 'test-message',\n toolInvocations = [],\n parts,\n}: {\n content: string;\n role: 'user' | 'assistant' | 'system';\n id?: string;\n toolInvocations?: Array<{\n toolCallId: string;\n toolName: string;\n args: Record<string, any>;\n result?: Record<string, any>;\n state: any;\n }>;\n /**\n * Native V2 message parts. Replaces the default single text part when provided.\n * Needed to express invocations the legacy array cannot carry, such as a thrown\n * call (`state: 'output-error'` with `errorText`).\n */\n parts?: MastraDBMessage['content']['parts'];\n}): MastraDBMessage {\n return {\n id,\n role,\n content: {\n format: 2,\n parts: parts ?? [{ type: 'text', text: content }],\n content,\n ...(toolInvocations.length > 0 && {\n toolInvocations: toolInvocations.map(ti => ({\n toolCallId: ti.toolCallId,\n toolName: ti.toolName,\n args: ti.args,\n result: ti.result,\n state: ti.state,\n })),\n }),\n },\n createdAt: new Date(),\n };\n}\n\n/**\n * Creates a complete agent test run object for testing scorers.\n *\n * Provides a convenient way to construct the full run object that scorers receive,\n * including input messages, output, system messages, and request context.\n *\n * @param options - The test run configuration\n * @param options.inputMessages - Array of input messages (default: [])\n * @param options.output - The output messages (required)\n * @param options.rememberedMessages - Array of remembered messages from memory (default: [])\n * @param options.systemMessages - Array of system messages (default: [])\n * @param options.taggedSystemMessages - Tagged system messages map (default: {})\n * @param options.requestContext - Request context (default: new RequestContext())\n * @param options.runId - Unique run ID (default: random UUID)\n * @returns A complete test run object\n *\n * @example\n * ```ts\n * const testRun = createAgentTestRun({\n * inputMessages: [createTestMessage({ content: 'Hello', role: 'user' })],\n * output: [createTestMessage({ content: 'Hi there!', role: 'assistant' })],\n * });\n *\n * const result = await scorer.run({\n * input: testRun.input,\n * output: testRun.output,\n * });\n * ```\n */\nexport const createAgentTestRun = ({\n inputMessages = [],\n output,\n rememberedMessages = [],\n systemMessages = [],\n taggedSystemMessages = {},\n requestContext = new RequestContext(),\n runId = crypto.randomUUID(),\n}: {\n inputMessages?: ScorerRunInputForAgent['inputMessages'];\n output: ScorerRunOutputForAgent;\n rememberedMessages?: ScorerRunInputForAgent['rememberedMessages'];\n systemMessages?: ScorerRunInputForAgent['systemMessages'];\n taggedSystemMessages?: ScorerRunInputForAgent['taggedSystemMessages'];\n requestContext?: RequestContext;\n runId?: string;\n}): {\n input: ScorerRunInputForAgent;\n output: ScorerRunOutputForAgent;\n requestContext: RequestContext;\n runId: string;\n} => {\n return {\n input: {\n inputMessages,\n rememberedMessages,\n systemMessages,\n taggedSystemMessages,\n },\n output,\n requestContext,\n runId,\n };\n};\n\n/**\n * Creates a test run for trajectory scorers where `output` is a `Trajectory`\n * (pre-extracted by the `runEvals` pipeline).\n *\n * @example\n * ```ts\n * const testRun = createTrajectoryTestRun({\n * inputMessages: [createTestMessage({ content: 'Do X', role: 'user', id: 'u1' })],\n * trajectory: {\n * steps: [\n * { stepType: 'tool_call', name: 'search', toolArgs: { q: 'test' } },\n * ],\n * },\n * });\n * ```\n */\nexport const createTrajectoryTestRun = ({\n inputMessages = [],\n trajectory,\n rememberedMessages = [],\n systemMessages = [],\n taggedSystemMessages = {},\n requestContext = new RequestContext(),\n runId = crypto.randomUUID(),\n expectedTrajectory,\n}: {\n inputMessages?: ScorerRunInputForAgent['inputMessages'];\n trajectory: Trajectory;\n rememberedMessages?: ScorerRunInputForAgent['rememberedMessages'];\n systemMessages?: ScorerRunInputForAgent['systemMessages'];\n taggedSystemMessages?: ScorerRunInputForAgent['taggedSystemMessages'];\n requestContext?: RequestContext;\n runId?: string;\n expectedTrajectory?: TrajectoryExpectation;\n}): {\n input: ScorerRunInputForAgent;\n output: Trajectory;\n requestContext: RequestContext;\n runId: string;\n expectedTrajectory?: TrajectoryExpectation;\n} => {\n return {\n input: {\n inputMessages,\n rememberedMessages,\n systemMessages,\n taggedSystemMessages,\n },\n output: trajectory,\n expectedTrajectory,\n requestContext,\n runId,\n };\n};\n\n/**\n * Information about a tool call extracted from scorer output.\n */\nexport type ToolCallInfo = {\n /** Name of the tool that was called */\n toolName: string;\n /** Unique identifier for the tool call */\n toolCallId: string;\n /** Index of the message containing this tool call */\n messageIndex: number;\n /** Index of the invocation within the message's tool invocations */\n invocationIndex: number;\n};\n\n/**\n * Merges the two places a message can store tool invocations into one list.\n *\n * A native message may carry `content.parts` tool-invocation entries, the legacy\n * `content.toolInvocations` array, or both. Neither form is a superset of the\n * other: thrown calls are recorded in parts as `state: 'output-error'` and can be\n * missing from the legacy array, while older persisted messages may only carry the\n * legacy array. Reading one form exclusively therefore hides real calls.\n *\n * Parts win when the same `toolCallId` appears in both, since they hold the real\n * terminal state. Legacy-only calls are retained before their next shared anchor so\n * compatible call order survives instead of being appended.\n *\n * Mirrors `extractTrajectory` in `@mastra/core` (`packages/core/src/evals/types.ts`)\n * so tool-use checks and trajectory scorers agree on what a run actually called.\n * Keep the two in step.\n *\n * @param message - A single message from the scorer run output\n * @returns The merged tool invocations for that message\n */\nexport function mergeToolInvocations(message: MastraDBMessage | undefined) {\n const legacy = message?.content?.toolInvocations;\n const fromParts =\n message?.content?.parts\n ?.filter((p): p is Extract<typeof p, { type: 'tool-invocation' }> => p.type === 'tool-invocation')\n .map(p => p.toolInvocation)\n .filter(Boolean) ?? [];\n const partCallIds = new Set(fromParts.map(invocation => invocation.toolCallId).filter(Boolean));\n const legacyInvocations = legacy ?? [];\n const legacyPositions = new Map(legacyInvocations.map((invocation, index) => [invocation?.toolCallId, index]));\n const toolInvocations: typeof fromParts = [];\n let legacyIndex = 0;\n for (const invocation of fromParts) {\n const sharedIndex = invocation.toolCallId ? legacyPositions.get(invocation.toolCallId) : undefined;\n if (sharedIndex !== undefined && sharedIndex >= legacyIndex) {\n // Retain legacy-only calls before their next shared anchor.\n for (; legacyIndex < sharedIndex; legacyIndex++) {\n const previous = legacyInvocations[legacyIndex];\n if (previous && !partCallIds.has(previous.toolCallId)) toolInvocations.push(previous);\n }\n legacyIndex++;\n }\n toolInvocations.push(invocation);\n }\n for (; legacyIndex < legacyInvocations.length; legacyIndex++) {\n const invocation = legacyInvocations[legacyIndex];\n if (invocation && !partCallIds.has(invocation.toolCallId)) toolInvocations.push(invocation);\n }\n return toolInvocations;\n}\n\n/**\n * Extracts all tool calls from a scorer run output.\n *\n * Iterates through all messages and their tool invocations to collect information\n * about tools that were called. A call counts regardless of outcome, so the states\n * `'result'`, `'call'`, and `'output-error'` (a tool that threw) are all included.\n *\n * @param output - The scorer run output (array of MastraDBMessage)\n * @returns An object containing tool names and detailed tool call info\n *\n * @example\n * ```ts\n * const scorer = createScorer({ ... })\n * .preprocess(({ run }) => {\n * const { tools, toolCallInfos } = extractToolCalls(run.output);\n * return {\n * toolsUsed: tools,\n * toolCount: tools.length,\n * };\n * });\n * ```\n */\nexport function extractToolCalls(output: ScorerRunOutputForAgent): { tools: string[]; toolCallInfos: ToolCallInfo[] } {\n const toolCalls: string[] = [];\n const toolCallInfos: ToolCallInfo[] = [];\n\n for (let messageIndex = 0; messageIndex < output.length; messageIndex++) {\n const message = output[messageIndex];\n const toolInvocations = mergeToolInvocations(message);\n\n if (!toolInvocations.length) continue;\n\n for (let invocationIndex = 0; invocationIndex < toolInvocations.length; invocationIndex++) {\n const invocation = toolInvocations[invocationIndex];\n // A thrown call (`output-error`) still counts: the tool ran. Partial calls do not.\n // Same state set core's `extractTrajectory` accepts.\n if (\n invocation &&\n invocation.toolName &&\n (invocation.state === 'result' || invocation.state === 'call' || invocation.state === 'output-error')\n ) {\n toolCalls.push(invocation.toolName);\n toolCallInfos.push({\n toolName: invocation.toolName,\n toolCallId: invocation.toolCallId || `${messageIndex}-${invocationIndex}`,\n messageIndex,\n invocationIndex,\n });\n }\n }\n }\n\n return { tools: toolCalls, toolCallInfos };\n}\n\n/**\n * Extracts text content from all input messages.\n *\n * @param runInput - The scorer run input\n * @returns An array of text strings from each input message\n *\n * @example\n * ```ts\n * const scorer = createScorer({ ... })\n * .preprocess(({ run }) => {\n * const messages = extractInputMessages(run.input);\n * return { allUserMessages: messages.join('\\n') };\n * });\n * ```\n */\nexport const extractInputMessages = (runInput: ScorerRunInputForAgent | undefined): string[] => {\n return runInput?.inputMessages?.map(msg => getTextContentFromMastraDBMessage(msg)) || [];\n};\n\n/**\n * Extracts text content from all assistant response messages.\n *\n * Filters for messages with role 'assistant' and extracts their text content.\n *\n * @param runOutput - The scorer run output (array of MastraDBMessage)\n * @returns An array of text strings from each assistant message\n *\n * @example\n * ```ts\n * const scorer = createScorer({ ... })\n * .preprocess(({ run }) => {\n * const responses = extractAgentResponseMessages(run.output);\n * return { allResponses: responses.join('\\n') };\n * });\n * ```\n */\nexport const extractAgentResponseMessages = (runOutput: ScorerRunOutputForAgent): string[] => {\n return runOutput.filter(msg => msg.role === 'assistant').map(msg => getTextContentFromMastraDBMessage(msg));\n};\n\n/**\n * Information about a tool result extracted from scorer output.\n */\nexport type ToolResultInfo = {\n /** Name of the tool that was called */\n toolName: string;\n /** Unique identifier for the tool call */\n toolCallId: string;\n /** Arguments passed to the tool */\n args: Record<string, any>;\n /** Result returned by the tool */\n result: any;\n};\n\n/**\n * Extracts tool results from a scorer run output.\n *\n * Returns structured objects that can be used with the hallucination scorer's\n * `getContext` hook or for other scorer logic.\n *\n * @param output - The scorer run output (array of MastraDBMessage)\n * @returns An array of ToolResultInfo objects\n *\n * @example\n * ```ts\n * import { extractToolResults } from '@mastra/evals/scorers';\n * import { createHallucinationScorer } from '@mastra/evals/scorers/prebuilt';\n *\n * const scorer = createHallucinationScorer({\n * model: openai('gpt-4o'),\n * options: {\n * getContext: (run) => {\n * const toolResults = extractToolResults(run.output);\n * return toolResults.map(t => JSON.stringify({ tool: t.toolName, result: t.result }));\n * },\n * },\n * });\n * ```\n */\nexport function extractToolResults(output: ScorerRunOutputForAgent): ToolResultInfo[] {\n const results: ToolResultInfo[] = [];\n\n for (const message of output) {\n const toolInvocations = mergeToolInvocations(message);\n\n if (!toolInvocations.length) continue;\n\n for (const invocation of toolInvocations) {\n // A thrown call carries `errorText` rather than a result, so it yields no entry here.\n if (invocation.state === 'result' && invocation.result !== undefined) {\n results.push({\n toolName: invocation.toolName,\n toolCallId: invocation.toolCallId || '',\n args: invocation.args || {},\n result: invocation.result,\n });\n }\n }\n }\n\n return results;\n}\n\n// Re-export extractTrajectory from core — it's called automatically by runEvals\n// for trajectory scorers, but users may still want it for custom use cases.\nexport { extractTrajectory } from '@mastra/core/evals';\n\n/**\n * Compares two trajectories and returns detailed comparison results.\n *\n * This is the core comparison logic used by trajectory scorers. It supports\n * strict and non-strict ordering, optional step data comparison, and loop detection.\n *\n * @param actual - The trajectory the agent actually took\n * @param expected - The expected trajectory to compare against\n * @param options - Comparison configuration options\n * @returns Detailed comparison results including match scores and diagnostics\n *\n * @example\n * ```ts\n * const result = compareTrajectories(\n * { steps: [{ stepType: 'tool_call', name: 'search' }, { stepType: 'tool_call', name: 'summarize' }] },\n * { steps: [{ stepType: 'tool_call', name: 'search' }, { stepType: 'tool_call', name: 'summarize' }] },\n * { ordering: 'strict' }\n * );\n * // result.score = 1.0\n * ```\n */\nexport function compareTrajectories(\n actual: Trajectory,\n expected: Trajectory | { steps: ExpectedStep[] },\n options: {\n ordering?: 'strict' | 'relaxed' | 'unordered';\n allowRepeatedSteps?: boolean;\n } = {},\n): TrajectoryComparisonResult {\n const { allowRepeatedSteps = true, ordering = 'relaxed' } = options;\n\n // Normalize expected to ExpectedStep[]. TrajectoryStep and ExpectedStep share\n // the same field names, so TrajectoryStep[] can be used directly as ExpectedStep[].\n // The only structural difference is `children` (TrajectoryStep[] vs TrajectoryExpectation),\n // but compareTrajectories doesn't recurse into children.\n const normalizedExpected: { steps: ExpectedStep[] } = {\n steps: expected.steps as ExpectedStep[],\n };\n\n if (normalizedExpected.steps.length === 0) {\n return {\n score: actual.steps.length === 0 ? 1 : 0,\n matchedSteps: 0,\n totalExpectedSteps: 0,\n totalActualSteps: actual.steps.length,\n missingSteps: [],\n extraSteps: actual.steps.map((s: TrajectoryStep) => s.name),\n outOfOrderSteps: [],\n repeatedSteps: [],\n };\n }\n\n const actualNames = actual.steps.map((s: TrajectoryStep) => s.name);\n\n // Detect repeated steps\n const nameCounts = new Map<string, number>();\n for (const name of actualNames) {\n nameCounts.set(name, (nameCounts.get(name) || 0) + 1);\n }\n const repeatedSteps = [...nameCounts.entries()]\n .filter(([_, count]: [string, number]) => count > 1)\n .map(([name]: [string, number]) => name);\n\n if (ordering === 'strict') {\n return compareStrictOrder(actual, normalizedExpected, { allowRepeatedSteps, repeatedSteps });\n }\n\n if (ordering === 'unordered') {\n return compareUnorderedPresence(actual, normalizedExpected, { allowRepeatedSteps, repeatedSteps });\n }\n\n return compareRelaxedOrder(actual, normalizedExpected, { allowRepeatedSteps, repeatedSteps });\n}\n\n/**\n * Result of comparing two trajectories.\n */\nexport type TrajectoryComparisonResult = {\n /** Overall match score from 0 to 1 */\n score: number;\n /** Number of expected steps that were matched */\n matchedSteps: number;\n /** Total number of expected steps */\n totalExpectedSteps: number;\n /** Total number of actual steps taken */\n totalActualSteps: number;\n /** Expected steps that were not found in the actual trajectory */\n missingSteps: string[];\n /** Actual steps that were not in the expected trajectory */\n extraSteps: string[];\n /** Steps that appear but not in the expected position */\n outOfOrderSteps: string[];\n /** Steps that were repeated (appeared more than once) */\n repeatedSteps: string[];\n};\n\nfunction compareStrictOrder(\n actual: Trajectory,\n expected: { steps: ExpectedStep[] },\n opts: { allowRepeatedSteps: boolean; repeatedSteps: string[] },\n): TrajectoryComparisonResult {\n const actualNames: string[] = actual.steps.map((s: TrajectoryStep) => s.name);\n const expectedNames: string[] = expected.steps.map((s: ExpectedStep) => s.name);\n\n // Strict: exact same sequence\n let matchedSteps = 0;\n const outOfOrderSteps: string[] = [];\n const matchedExpectedIndices = new Set<number>();\n const maxLen = Math.max(actualNames.length, expectedNames.length);\n\n for (let i = 0; i < maxLen; i++) {\n const actualName = actualNames[i];\n const expectedName = expectedNames[i];\n if (actualName === expectedName) {\n if (actual.steps[i] && expected.steps[i]) {\n if (expectedStepMatches(actual.steps[i]!, expected.steps[i]!)) {\n matchedSteps++;\n matchedExpectedIndices.add(i);\n }\n } else {\n matchedSteps++;\n matchedExpectedIndices.add(i);\n }\n } else if (actualName && expectedNames.includes(actualName)) {\n outOfOrderSteps.push(actualName);\n }\n }\n\n // Missing steps = expected steps that were not matched (accounts for stepType/data mismatches)\n const missingSteps: string[] = expectedNames.filter((_: string, i: number) => !matchedExpectedIndices.has(i));\n const extraSteps: string[] = actualNames.filter((name: string) => !expectedNames.includes(name));\n\n let score = matchedSteps / expected.steps.length;\n\n // Penalize extra steps in strict mode\n if (actualNames.length > expectedNames.length) {\n const extraPenalty = (actualNames.length - expectedNames.length) / expectedNames.length;\n score = Math.max(0, score - extraPenalty * 0.5);\n }\n\n // Penalize repeated steps if not allowed\n if (!opts.allowRepeatedSteps && opts.repeatedSteps.length > 0) {\n score = Math.max(0, score - opts.repeatedSteps.length * 0.1);\n }\n\n return {\n score: roundToTwoDecimals(Math.max(0, Math.min(1, score))),\n matchedSteps,\n totalExpectedSteps: expected.steps.length,\n totalActualSteps: actual.steps.length,\n missingSteps,\n extraSteps,\n outOfOrderSteps,\n repeatedSteps: opts.repeatedSteps,\n };\n}\n\nfunction compareRelaxedOrder(\n actual: Trajectory,\n expected: { steps: ExpectedStep[] },\n opts: { allowRepeatedSteps: boolean; repeatedSteps: string[] },\n): TrajectoryComparisonResult {\n const actualNames: string[] = actual.steps.map((s: TrajectoryStep) => s.name);\n const expectedNames: string[] = expected.steps.map((s: ExpectedStep) => s.name);\n\n // Relaxed: expected steps must appear in order but extra steps are allowed\n let matchedSteps = 0;\n let lastMatchedIndex = -1;\n const outOfOrderSteps: string[] = [];\n const matchedExpectedIndices = new Set<number>();\n\n for (let i = 0; i < expectedNames.length; i++) {\n const expectedName = expectedNames[i];\n let found = false;\n\n for (let j = lastMatchedIndex + 1; j < actualNames.length; j++) {\n if (actualNames[j] === expectedName) {\n if (actual.steps[j] && expected.steps[i]) {\n if (expectedStepMatches(actual.steps[j]!, expected.steps[i]!)) {\n matchedSteps++;\n lastMatchedIndex = j;\n matchedExpectedIndices.add(i);\n found = true;\n break;\n }\n } else {\n matchedSteps++;\n lastMatchedIndex = j;\n matchedExpectedIndices.add(i);\n found = true;\n break;\n }\n }\n }\n\n if (!found) {\n // Check if the step exists but is out of order\n if (actualNames.includes(expectedName!)) {\n outOfOrderSteps.push(expectedName!);\n }\n }\n }\n\n // Missing steps = expected steps that were not matched (by name + stepType + data, not just name)\n const missingSteps = expectedNames.filter((_, i) => !matchedExpectedIndices.has(i));\n const expectedSet = new Set(expectedNames);\n const extraSteps = actualNames.filter(name => !expectedSet.has(name));\n\n let score = matchedSteps / expected.steps.length;\n\n // Penalize repeated steps if not allowed\n if (!opts.allowRepeatedSteps && opts.repeatedSteps.length > 0) {\n score = Math.max(0, score - opts.repeatedSteps.length * 0.1);\n }\n\n return {\n score: roundToTwoDecimals(Math.max(0, Math.min(1, score))),\n matchedSteps,\n totalExpectedSteps: expected.steps.length,\n totalActualSteps: actual.steps.length,\n missingSteps,\n extraSteps,\n outOfOrderSteps,\n repeatedSteps: opts.repeatedSteps,\n };\n}\n\n/**\n * Fields on each ExpectedStep variant that are comparable data (not structural).\n * Used by `expectedStepMatches` to know which fields to compare when `compareData` is true.\n */\nconst COMPARABLE_FIELDS_BY_TYPE: Record<string, string[]> = {\n tool_call: ['toolArgs', 'toolResult', 'success'],\n mcp_tool_call: ['toolArgs', 'toolResult', 'mcpServer', 'success'],\n model_generation: ['modelId', 'promptTokens', 'completionTokens', 'finishReason'],\n agent_run: ['agentId'],\n workflow_step: ['stepId', 'status', 'output'],\n workflow_run: ['workflowId', 'status'],\n workflow_conditional: ['conditionCount', 'selectedSteps'],\n workflow_parallel: ['branchCount', 'parallelSteps'],\n workflow_loop: ['loopType', 'totalIterations'],\n workflow_sleep: ['sleepDurationMs', 'sleepType'],\n workflow_wait_event: ['eventName', 'eventReceived'],\n processor_run: ['processorId'],\n};\n\n/**\n * Check if an actual TrajectoryStep matches an ExpectedStep.\n * Matches by name, optionally by stepType, and auto-compares any variant-specific\n * fields that are present on the expected step.\n */\nfunction expectedStepMatches(actual: TrajectoryStep, expected: ExpectedStep): boolean {\n if (actual.name !== expected.name) return false;\n if (expected.stepType && actual.stepType !== expected.stepType) return false;\n\n if (expected.stepType) {\n const fields = COMPARABLE_FIELDS_BY_TYPE[expected.stepType] ?? [];\n for (const field of fields) {\n const expectedVal = (expected as any)[field];\n if (expectedVal === undefined) continue; // field not specified in expectation, skip\n const actualVal = (actual as any)[field];\n if (actualVal === undefined) return false;\n try {\n if (JSON.stringify(actualVal) !== JSON.stringify(expectedVal)) return false;\n } catch {\n return false;\n }\n }\n }\n\n return true;\n}\n\nfunction compareUnorderedPresence(\n actual: Trajectory,\n expected: { steps: ExpectedStep[] },\n opts: { allowRepeatedSteps: boolean; repeatedSteps: string[] },\n): TrajectoryComparisonResult {\n const actualNames: string[] = actual.steps.map((s: TrajectoryStep) => s.name);\n const expectedNames: string[] = expected.steps.map((s: ExpectedStep) => s.name);\n\n let matchedSteps = 0;\n const matchedExpectedIndices = new Set<number>();\n const usedIndices = new Set<number>();\n for (let i = 0; i < expected.steps.length; i++) {\n const expectedStep = expected.steps[i]!;\n for (let j = 0; j < actual.steps.length; j++) {\n if (!usedIndices.has(j) && expectedStepMatches(actual.steps[j]!, expectedStep)) {\n matchedSteps++;\n matchedExpectedIndices.add(i);\n usedIndices.add(j);\n break;\n }\n }\n }\n\n // Missing steps = expected steps that were not matched (accounts for stepType/data mismatches)\n const missingSteps = expectedNames.filter((_, i) => !matchedExpectedIndices.has(i));\n const expectedSet = new Set(expectedNames);\n const extraSteps = actualNames.filter(name => !expectedSet.has(name));\n\n let score = matchedSteps / expected.steps.length;\n\n // Penalize repeated steps if not allowed\n if (!opts.allowRepeatedSteps && opts.repeatedSteps.length > 0) {\n score = Math.max(0, score - opts.repeatedSteps.length * 0.1);\n }\n\n return {\n score: roundToTwoDecimals(Math.max(0, Math.min(1, score))),\n matchedSteps,\n totalExpectedSteps: expected.steps.length,\n totalActualSteps: actual.steps.length,\n missingSteps,\n extraSteps,\n outOfOrderSteps: [], // ordering not checked in unordered mode\n repeatedSteps: opts.repeatedSteps,\n };\n}\n\n// ─── Efficiency evaluation ───\n\n/**\n * Result of checking trajectory efficiency.\n */\nexport type TrajectoryEfficiencyResult = {\n /** Overall efficiency score from 0 to 1 */\n score: number;\n /** Total number of steps taken */\n totalSteps: number;\n /** Whether the step budget was exceeded */\n overStepBudget: boolean;\n /** Total tokens used across model_generation steps */\n totalTokens: number;\n /** Whether the token budget was exceeded */\n overTokenBudget: boolean;\n /** Total duration in milliseconds */\n totalDurationMs: number;\n /** Whether the duration budget was exceeded */\n overDurationBudget: boolean;\n /** Redundant calls detected (same tool + same args consecutively) */\n redundantCalls: Array<{ name: string; index: number }>;\n};\n\n/**\n * Evaluate trajectory efficiency against budgets and redundancy checks.\n * Throws when a configured budget is invalid or its required measurements are incomplete or invalid.\n */\nexport function checkTrajectoryEfficiency(\n trajectory: Trajectory,\n options: {\n maxSteps?: number;\n maxTotalTokens?: number;\n maxTotalDurationMs?: number;\n noRedundantCalls?: boolean;\n } = {},\n): TrajectoryEfficiencyResult {\n const { maxSteps, maxTotalTokens, maxTotalDurationMs, noRedundantCalls = true } = options;\n\n const isMeasurement = (value: number | undefined): value is number =>\n typeof value === 'number' && Number.isFinite(value) && value >= 0;\n for (const [name, limit] of Object.entries({ maxSteps, maxTotalTokens, maxTotalDurationMs })) {\n if (limit !== undefined && !isMeasurement(limit)) {\n throw new Error(`Invalid ${name} budget: expected a finite nonnegative number`);\n }\n }\n\n const totalSteps = trajectory.steps.length;\n\n // Count each model generation, including nested agents, without counting container aggregates.\n let totalTokens = 0;\n let modelGenerations = 0;\n const remaining = [...trajectory.steps];\n while (remaining.length > 0) {\n const step = remaining.pop()!;\n if (step.stepType === 'model_generation') {\n modelGenerations++;\n if (\n maxTotalTokens !== undefined &&\n (!isMeasurement(step.promptTokens) || !isMeasurement(step.completionTokens))\n ) {\n throw new Error(\n `Cannot evaluate token budget: model generation \"${step.name}\" has missing or invalid token counts`,\n );\n }\n totalTokens += (step.promptTokens ?? 0) + (step.completionTokens ?? 0);\n }\n if (step.children) {\n for (const child of step.children) remaining.push(child);\n }\n }\n if (maxTotalTokens !== undefined && (modelGenerations === 0 || !isMeasurement(totalTokens))) {\n throw new Error('Cannot evaluate token budget: missing model-generation measurements or invalid total tokens');\n }\n\n // A parent duration already covers its children; never add both.\n const totalDurationMs =\n trajectory.totalDurationMs ?? trajectory.steps.reduce((sum, s) => sum + (s.durationMs ?? 0), 0);\n if (maxTotalDurationMs !== undefined) {\n const hasDuration =\n trajectory.totalDurationMs !== undefined\n ? isMeasurement(trajectory.totalDurationMs)\n : trajectory.steps.length > 0 && trajectory.steps.every(step => isMeasurement(step.durationMs));\n if (!hasDuration || !isMeasurement(totalDurationMs)) {\n throw new Error('Cannot evaluate duration budget: missing or invalid duration measurements');\n }\n }\n\n // Detect redundant calls (same tool name + same args in consecutive calls)\n const redundantCalls: Array<{ name: string; index: number }> = [];\n if (noRedundantCalls) {\n for (let i = 1; i < trajectory.steps.length; i++) {\n const prev = trajectory.steps[i - 1]!;\n const curr = trajectory.steps[i]!;\n if (\n prev.name === curr.name &&\n prev.stepType === curr.stepType &&\n (prev.stepType === 'tool_call' || prev.stepType === 'mcp_tool_call')\n ) {\n const prevArgs = (prev as TrajectoryStep & { toolArgs?: Record<string, unknown> }).toolArgs;\n const currArgs = (curr as TrajectoryStep & { toolArgs?: Record<string, unknown> }).toolArgs;\n try {\n if (JSON.stringify(prevArgs) === JSON.stringify(currArgs)) {\n redundantCalls.push({ name: curr.name, index: i });\n }\n } catch {\n // If serialization fails, don't flag as redundant\n }\n }\n }\n }\n\n const overStepBudget = maxSteps !== undefined && totalSteps > maxSteps;\n const overTokenBudget = maxTotalTokens !== undefined && totalTokens > maxTotalTokens;\n const overDurationBudget = maxTotalDurationMs !== undefined && totalDurationMs > maxTotalDurationMs;\n\n // Calculate score: each dimension contributes equally\n const dimensions: number[] = [];\n\n if (maxSteps !== undefined) {\n dimensions.push(overStepBudget ? Math.max(0, 1 - (totalSteps - maxSteps) / maxSteps) : 1);\n }\n if (maxTotalTokens !== undefined) {\n dimensions.push(overTokenBudget ? Math.max(0, 1 - (totalTokens - maxTotalTokens) / maxTotalTokens) : 1);\n }\n if (maxTotalDurationMs !== undefined) {\n dimensions.push(\n overDurationBudget ? Math.max(0, 1 - (totalDurationMs - maxTotalDurationMs) / maxTotalDurationMs) : 1,\n );\n }\n if (noRedundantCalls) {\n dimensions.push(redundantCalls.length === 0 ? 1 : Math.max(0, 1 - redundantCalls.length * 0.2));\n }\n\n const score = dimensions.length > 0 ? dimensions.reduce((a, b) => a + b, 0) / dimensions.length : 1;\n\n return {\n score: roundToTwoDecimals(Math.max(0, Math.min(1, score))),\n totalSteps,\n overStepBudget,\n totalTokens,\n overTokenBudget,\n totalDurationMs,\n overDurationBudget,\n redundantCalls,\n };\n}\n\n// ─── Blacklist evaluation ───\n\n/**\n * Result of checking trajectory against a blacklist.\n */\nexport type TrajectoryBlacklistResult = {\n /** Score: 1.0 if clean, 0.0 if any violation found */\n score: number;\n /** Individual blacklisted tools that were found */\n violatedTools: string[];\n /** Blacklisted sequences that were found */\n violatedSequences: string[][];\n};\n\n/**\n * Check if a trajectory violates any blacklist rules.\n * Returns score 0.0 if any violation is found (hard fail).\n */\nexport function checkTrajectoryBlacklist(\n trajectory: Trajectory,\n options: {\n blacklistedTools?: string[];\n blacklistedSequences?: string[][];\n } = {},\n): TrajectoryBlacklistResult {\n const { blacklistedTools = [], blacklistedSequences = [] } = options;\n const violatedTools: string[] = [];\n const violatedSequences: string[][] = [];\n\n const stepNames = trajectory.steps.map(s => s.name);\n\n // Check blacklisted tools\n for (const forbidden of blacklistedTools) {\n if (stepNames.includes(forbidden)) {\n violatedTools.push(forbidden);\n }\n }\n\n // Check blacklisted sequences (contiguous subsequences)\n for (const sequence of blacklistedSequences) {\n if (sequence.length === 0) continue;\n for (let i = 0; i <= stepNames.length - sequence.length; i++) {\n let match = true;\n for (let j = 0; j < sequence.length; j++) {\n if (stepNames[i + j] !== sequence[j]) {\n match = false;\n break;\n }\n }\n if (match) {\n violatedSequences.push(sequence);\n break; // Only report each sequence once\n }\n }\n }\n\n const hasViolations = violatedTools.length > 0 || violatedSequences.length > 0;\n\n return {\n score: hasViolations ? 0 : 1,\n violatedTools,\n violatedSequences,\n };\n}\n\n// ─── Tool failure analysis ───\n\n/**\n * A detected tool failure pattern in the trajectory.\n */\nexport type ToolFailurePattern = {\n /** The tool name that experienced failure */\n toolName: string;\n /** Number of consecutive retries (same tool, same or similar args) */\n retryCount: number;\n /** Whether the agent fell back to a different tool after failures */\n fellBackToAlternative: boolean;\n /** The alternative tool used, if any */\n alternativeTool?: string;\n /** Whether any retry eventually succeeded */\n eventuallySucceeded: boolean;\n};\n\n/**\n * Result of analyzing tool failure patterns in a trajectory.\n */\nexport type ToolFailureAnalysisResult = {\n /** Score from 0 to 1 (lower = more failures/retries) */\n score: number;\n /** Tool failure patterns detected */\n patterns: ToolFailurePattern[];\n /** Total number of retries across all tools */\n totalRetries: number;\n /** Tools that exceeded the retry threshold */\n excessiveRetryTools: string[];\n};\n\n/**\n * Analyze tool failure and retry patterns in a trajectory.\n */\nexport function analyzeToolFailures(\n trajectory: Trajectory,\n options: {\n maxRetriesPerTool?: number;\n } = {},\n): ToolFailureAnalysisResult {\n const { maxRetriesPerTool = 2 } = options;\n const patterns: ToolFailurePattern[] = [];\n let totalRetries = 0;\n\n const toolCallSteps = trajectory.steps.filter(s => s.stepType === 'tool_call' || s.stepType === 'mcp_tool_call');\n\n if (toolCallSteps.length === 0) {\n return { score: 1, patterns: [], totalRetries: 0, excessiveRetryTools: [] };\n }\n\n // Group consecutive calls to the same tool as potential retry sequences\n let i = 0;\n while (i < toolCallSteps.length) {\n const currentTool = toolCallSteps[i]!;\n let retryCount = 0;\n let j = i + 1;\n\n // Count consecutive calls to the same tool\n // (toolCallSteps is pre-filtered to tool_call/mcp_tool_call, so no stepType checks needed)\n while (j < toolCallSteps.length && toolCallSteps[j]!.name === currentTool.name) {\n const prevStep = toolCallSteps[j - 1]! as TrajectoryStep & { success?: boolean };\n if (prevStep.success === false) {\n retryCount++;\n }\n j++;\n }\n\n if (retryCount > 0) {\n // Check if agent fell back to a different tool after retries\n const nextDifferentTool = j < toolCallSteps.length ? toolCallSteps[j] : undefined;\n const lastRetry = toolCallSteps[j - 1]! as TrajectoryStep & { success?: boolean };\n const lastSuccess = lastRetry.success !== false;\n\n patterns.push({\n toolName: currentTool.name,\n retryCount,\n fellBackToAlternative: nextDifferentTool !== undefined && !lastSuccess,\n alternativeTool: nextDifferentTool !== undefined && !lastSuccess ? nextDifferentTool.name : undefined,\n eventuallySucceeded: lastSuccess,\n });\n\n totalRetries += retryCount;\n }\n\n i = j;\n }\n\n // Score: penalize excessive retries\n const excessiveRetryTools = patterns.filter(p => p.retryCount > maxRetriesPerTool).map(p => p.toolName);\n\n let score = 1;\n if (toolCallSteps.length > 0) {\n // Each retry beyond the threshold costs more\n const excessRetries = patterns.reduce((sum, p) => sum + Math.max(0, p.retryCount - maxRetriesPerTool), 0);\n score = Math.max(0, 1 - excessRetries * 0.2);\n }\n\n return {\n score: roundToTwoDecimals(Math.max(0, Math.min(1, score))),\n patterns,\n totalRetries,\n excessiveRetryTools,\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"utils.js","names":[],"sources":["../../src/scorers/utils.ts"],"sourcesContent":["import type { MastraDBMessage } from '@mastra/core/agent';\nimport type {\n ExpectedStep,\n ScorerRunInputForAgent,\n ScorerRunOutputForAgent,\n ScoringInput,\n TrajectoryExpectation,\n TrajectoryStep,\n Trajectory,\n} from '@mastra/core/evals';\nimport { RequestContext } from '@mastra/core/request-context';\n\nexport type ScorerRunInputForLLMJudge =\n | ScorerRunInputForAgent\n | string\n | {\n inputMessages?: unknown[];\n messages?: unknown[];\n prompt?: string;\n text?: string;\n content?: unknown;\n input?: unknown;\n user?: unknown;\n [key: string]: unknown;\n };\n\nexport type ScorerRunOutputForLLMJudge =\n | ScorerRunOutputForAgent\n | string\n | unknown[]\n | {\n text?: string;\n content?: unknown;\n role?: string;\n [key: string]: unknown;\n };\n\n/**\n * Extracts text content from a MastraDBMessage or ModelMessage-like object.\n *\n * @param message - The message to extract text from\n * @returns The extracted text content, or an empty string if no text is found\n *\n * @example\n * ```ts\n * const message: MastraDBMessage = {\n * id: 'msg-1',\n * role: 'assistant',\n * content: { format: 2, parts: [{ type: 'text', text: 'Hello!' }] },\n * createdAt: new Date(),\n * };\n * const text = getTextContentFromMastraDBMessage(message); // 'Hello!'\n * ```\n */\nexport function getTextContentFromMastraDBMessage(message: MastraDBMessage): string {\n const content = message.content as any;\n\n if (typeof content === 'string') {\n return content;\n }\n if (Array.isArray(content)) {\n const textParts = content.filter(p => p.type === 'text');\n return textParts.length > 0 ? textParts[textParts.length - 1]?.text || '' : '';\n }\n if (typeof content?.content === 'string' && content.content !== '') {\n return content.content;\n }\n if (typeof content?.text === 'string' && content.text !== '') {\n return content.text;\n }\n if (content?.parts && Array.isArray(content.parts)) {\n // Return only the last text part like AI SDK does\n const textParts = content.parts.filter((p: any) => p.type === 'text');\n return textParts.length > 0 ? textParts[textParts.length - 1]?.text || '' : '';\n }\n return '';\n}\n\nconst isRecord = (value: unknown): value is Record<string, any> => {\n return typeof value === 'object' && value !== null;\n};\n\nconst getTextFromValue = (value: unknown): string | undefined => {\n if (typeof value === 'string') return value === '' ? undefined : value;\n if (Array.isArray(value)) {\n const textParts = value\n .filter(part => isRecord(part) && part.type === 'text' && typeof part.text === 'string')\n .map(part => part.text);\n return textParts.length > 0 ? textParts[textParts.length - 1] : undefined;\n }\n if (!isRecord(value)) return undefined;\n\n const fromParts = Array.isArray(value.parts) ? getTextFromValue(value.parts) : undefined;\n\n return (\n getTextFromValue(value.content) ??\n (typeof value.text === 'string' && value.text !== '' ? value.text : undefined) ??\n (typeof value.body === 'string' && value.body !== '' ? value.body : undefined) ??\n fromParts\n );\n};\n\nexport const isScorerRunInputForAgent = (input: unknown): input is ScorerRunInputForAgent => {\n return (\n isRecord(input) &&\n Array.isArray(input.inputMessages) &&\n Array.isArray(input.rememberedMessages) &&\n Array.isArray(input.systemMessages) &&\n isRecord(input.taggedSystemMessages)\n );\n};\n\nconst isMastraDBMessageLike = (message: unknown): message is MastraDBMessage => {\n return (\n isRecord(message) &&\n typeof message.id === 'string' &&\n typeof message.role === 'string' &&\n 'content' in message &&\n 'createdAt' in message\n );\n};\n\nexport const isScorerRunOutputForAgent = (output: unknown): output is ScorerRunOutputForAgent => {\n return Array.isArray(output) && output.every(isMastraDBMessageLike);\n};\n\n/**\n * Resolves the effective role of a message, accounting for agent signal messages.\n *\n * Messages delivered through the agent subscription / signal API are persisted with\n * `role: 'signal'` and carry their semantic role (e.g. `user`) on `type` and on\n * `content.metadata.signal.{type,tagName}`. Treat those as their underlying role so\n * helpers like `getUserMessageFromRunInput` can find them.\n */\nconst getEffectiveMessageRole = (message: Record<string, any>): string | undefined => {\n if (message.role !== 'signal') return typeof message.role === 'string' ? message.role : undefined;\n\n const signalMeta =\n isRecord(message.content) && isRecord(message.content.metadata) ? message.content.metadata.signal : undefined;\n\n const tagName = isRecord(signalMeta) && typeof signalMeta.tagName === 'string' ? signalMeta.tagName : undefined;\n const signalType = isRecord(signalMeta) && typeof signalMeta.type === 'string' ? signalMeta.type : undefined;\n const topLevelType = typeof message.type === 'string' ? message.type : undefined;\n\n return tagName ?? signalType ?? topLevelType;\n};\n\nconst getTextFromMessages = (messages: unknown, role: string): string | undefined => {\n if (!Array.isArray(messages)) return undefined;\n\n const message = messages.find(message => isRecord(message) && getEffectiveMessageRole(message) === role);\n return message ? getTextFromValue(message) : undefined;\n};\n\n/**\n * Rounds a number to two decimal places.\n *\n * Uses `Number.EPSILON` to handle floating-point precision issues.\n *\n * @param num - The number to round\n * @returns The number rounded to two decimal places\n *\n * @example\n * ```ts\n * roundToTwoDecimals(0.1 + 0.2); // 0.3\n * roundToTwoDecimals(1.005); // 1.01\n * ```\n */\nexport const roundToTwoDecimals = (num: number) => {\n return Math.round((num + Number.EPSILON) * 100) / 100;\n};\n\n/**\n * Determines if a value is closer to the first target than the second.\n *\n * @param value - The value to compare\n * @param target1 - The first target value\n * @param target2 - The second target value\n * @returns `true` if `value` is closer to `target1` than `target2`\n *\n * @example\n * ```ts\n * isCloserTo(0.6, 1, 0); // true (0.6 is closer to 1)\n * isCloserTo(0.3, 1, 0); // false (0.3 is closer to 0)\n * ```\n */\nexport function isCloserTo(value: number, target1: number, target2: number): boolean {\n return Math.abs(value - target1) < Math.abs(value - target2);\n}\n\n/**\n * Represents a test case for scorer evaluation.\n */\nexport type TestCase = {\n /** The input text to evaluate */\n input: string;\n /** The output text to evaluate */\n output: string;\n /** The expected result of the evaluation */\n expectedResult: {\n /** The expected score */\n score: number;\n /** The optional expected reason */\n reason?: string;\n };\n};\n\n/**\n * Represents a test case with additional context for scorer evaluation.\n */\nexport type TestCaseWithContext = TestCase & {\n /** Additional context strings for the evaluation */\n context: string[];\n};\n\n/**\n * Creates a scoring input object for testing purposes.\n *\n * @param input - The user input text\n * @param output - The assistant output text\n * @param additionalContext - Optional additional context data\n * @param requestContext - Optional request context data\n * @returns A ScoringInput object ready for use in scorer tests\n *\n * @example\n * ```ts\n * const run = createTestRun(\n * 'What is 2+2?',\n * 'The answer is 4.',\n * { topic: 'math' }\n * );\n * ```\n */\nexport const createTestRun = (\n input: string,\n output: string,\n additionalContext?: Record<string, any>,\n requestContext?: Record<string, any>,\n): ScoringInput => {\n return {\n input: [{ role: 'user', content: input }],\n output: { role: 'assistant', text: output },\n additionalContext: additionalContext ?? {},\n requestContext: requestContext ?? {},\n };\n};\n\n/**\n * Extracts the user message text from a scorer run input.\n *\n * Accepts the agent shape (`{ inputMessages }`), `ModelMessage[]`\n * (`{ messages }`), workflow input (`{ prompt }`), and a bare string.\n *\n * @param input - The scorer run input\n * @returns The user message text, or `undefined` if none can be extracted\n *\n * @example\n * ```ts\n * const scorer = createScorer({ ... })\n * .preprocess(({ run }) => {\n * const userText = getUserMessageFromRunInput(run.input);\n * return { userText };\n * });\n * ```\n */\nexport const getUserMessageFromRunInput = (input?: unknown): string | undefined => {\n if (typeof input === 'string') return input;\n if (!isRecord(input)) return undefined;\n\n return (\n getTextFromMessages(input.inputMessages, 'user') ??\n getTextFromMessages(input.messages, 'user') ??\n (typeof input.prompt === 'string' ? input.prompt : undefined) ??\n (typeof input.text === 'string' ? input.text : undefined) ??\n getTextFromValue(input.content) ??\n getTextFromValue(input.input) ??\n getTextFromValue(input.user)\n );\n};\n\nconst DEFAULT_CONVERSATION_HISTORY_MESSAGES = 10;\n\n/**\n * Renders the remembered conversation history from an agent scorer run input as a transcript.\n *\n * Only agent run inputs carry `rememberedMessages`; any other input shape (a bare string,\n * a plain `{ prompt }` object, `ModelMessage[]`) returns `undefined` so callers can safely\n * fall back to single-turn behaviour.\n *\n * @param input - The scorer run input\n * @param options.maxMessages - How many of the most recent remembered messages to keep (default 10)\n * @returns A newline separated `role: text` transcript, or `undefined` if there is no history\n *\n * @example\n * ```ts\n * const scorer = createScorer({ ... })\n * .preprocess(({ run }) => {\n * const history = getConversationHistoryFromRunInput(run.input, { maxMessages: 4 });\n * return { history };\n * });\n * ```\n */\nexport const getConversationHistoryFromRunInput = (\n input?: unknown,\n options?: { maxMessages?: number },\n): string | undefined => {\n if (!isScorerRunInputForAgent(input)) return undefined;\n\n const maxMessages = options?.maxMessages ?? DEFAULT_CONVERSATION_HISTORY_MESSAGES;\n if (maxMessages <= 0) return undefined;\n\n const lines = input.rememberedMessages\n .slice(-maxMessages)\n .map(message => {\n if (!isRecord(message)) return undefined;\n\n const role = getEffectiveMessageRole(message);\n const text = getTextContentFromMastraDBMessage(message as MastraDBMessage).trim();\n if (!role || !text) return undefined;\n\n return `${role}: ${text}`;\n })\n .filter((line): line is string => Boolean(line));\n\n return lines.length > 0 ? lines.join('\\n') : undefined;\n};\n\n/**\n * Extracts all system messages from a scorer run input.\n *\n * Collects text from both standard system messages and tagged system messages\n * (specialized system prompts like memory instructions).\n *\n * @param input - The scorer run input containing system messages\n * @returns An array of system message strings\n *\n * @example\n * ```ts\n * const scorer = createScorer({ ... })\n * .preprocess(({ run }) => {\n * const systemMessages = getSystemMessagesFromRunInput(run.input);\n * return { systemPrompt: systemMessages.join('\\n') };\n * });\n * ```\n */\nexport const getSystemMessagesFromRunInput = (input?: unknown): string[] => {\n const systemMessages: string[] = [];\n if (!isRecord(input)) return systemMessages;\n\n // Add standard system messages\n if (Array.isArray(input.systemMessages)) {\n systemMessages.push(\n ...input.systemMessages\n .map(msg => {\n // Handle different content types - extract text if it's an array of parts\n if (typeof msg.content === 'string') {\n return msg.content;\n } else if (Array.isArray(msg.content)) {\n // Extract text from parts array\n return msg.content\n .filter((part: any) => part.type === 'text')\n .map((part: any) => part.text || '')\n .join(' ');\n }\n return '';\n })\n .filter(content => content),\n );\n }\n\n const addSystemMessages = (messages: unknown) => {\n if (!Array.isArray(messages)) return;\n\n systemMessages.push(\n ...messages\n .filter(message => isRecord(message) && message.role === 'system')\n .map(message => getTextFromValue(message))\n .filter((content): content is string => Boolean(content)),\n );\n };\n\n addSystemMessages(input.inputMessages);\n addSystemMessages(input.messages);\n\n // Add tagged system messages (these are specialized system prompts)\n if (isRecord(input.taggedSystemMessages)) {\n Object.values(input.taggedSystemMessages).forEach(messages => {\n if (!Array.isArray(messages)) return;\n messages.forEach(msg => {\n const content = getTextFromValue(msg);\n if (content) {\n systemMessages.push(content);\n }\n });\n });\n }\n\n return systemMessages;\n};\n\n/**\n * Combines all system messages into a single prompt string.\n *\n * Joins all system messages (standard and tagged) with double newlines.\n *\n * @param input - The scorer run input containing system messages\n * @returns A combined system prompt string\n *\n * @example\n * ```ts\n * const scorer = createScorer({ ... })\n * .preprocess(({ run }) => {\n * const systemPrompt = getCombinedSystemPrompt(run.input);\n * return { systemPrompt };\n * });\n * ```\n */\nexport const getCombinedSystemPrompt = (input?: unknown): string => {\n const systemMessages = getSystemMessagesFromRunInput(input);\n return systemMessages.join('\\n\\n');\n};\n\n/**\n * Extracts the assistant message text from a scorer run output.\n *\n * Accepts the agent shape (`MastraDBMessage[]` / `ModelMessage[]`), workflow\n * output (`{ text }`), task output (`{ content }`), a single assistant message\n * object, and a bare string.\n *\n * For array outputs the final assistant message is used, so multi-step agent\n * responses are scored on the last response rather than an intermediate one.\n *\n * @param output - The scorer run output\n * @returns The assistant message text, or `undefined` if none can be extracted\n *\n * @example\n * ```ts\n * const scorer = createScorer({ ... })\n * .preprocess(({ run }) => {\n * const response = getAssistantMessageFromRunOutput(run.output);\n * return { response };\n * });\n * ```\n */\nexport const getAssistantMessageFromRunOutput = (output?: unknown) => {\n if (typeof output === 'string') return output;\n if (Array.isArray(output)) {\n const assistantMessages = output.filter(\n message => isRecord(message) && getEffectiveMessageRole(message) === 'assistant',\n );\n // Prefer the last assistant message that carries text; a trailing assistant\n // message may hold only tool-call or data parts.\n for (let i = assistantMessages.length - 1; i >= 0; i--) {\n const text = getTextFromValue(assistantMessages[i]);\n if (text) return text;\n }\n return undefined;\n }\n if (!isRecord(output)) return undefined;\n\n const isAssistantOutput = output.role === undefined || output.role === 'assistant';\n\n if (isAssistantOutput && typeof output.text === 'string') return output.text;\n if (isAssistantOutput && typeof output.content === 'string') return output.content;\n if (isAssistantOutput && (isRecord(output.content) || Array.isArray(output.content))) {\n return (\n getTextContentFromMastraDBMessage(output as MastraDBMessage) ||\n getTextContentFromMastraDBMessage(output.content as MastraDBMessage) ||\n undefined\n );\n }\n if (output.role === 'assistant') return getTextContentFromMastraDBMessage(output as MastraDBMessage) || undefined;\n\n return undefined;\n};\n\n/**\n * Extracts reasoning text from a scorer run output.\n *\n * This function extracts reasoning content from assistant messages, which is\n * produced by reasoning models like `deepseek-reasoner`. The reasoning can be\n * stored in two places:\n * 1. `content.reasoning` - a string field on the message content\n * 2. `content.parts` - as parts with `type: 'reasoning'` containing `details`\n *\n * @param output - The scorer run output (array of MastraDBMessage)\n * @returns The reasoning text, or `undefined` if no reasoning is present\n *\n * @example\n * ```ts\n * const reasoningScorer = createScorer({\n * id: 'reasoning-scorer',\n * name: 'Reasoning Quality',\n * description: 'Evaluates the quality of model reasoning',\n * type: 'agent',\n * })\n * .preprocess(({ run }) => {\n * const reasoning = getReasoningFromRunOutput(run.output);\n * const response = getAssistantMessageFromRunOutput(run.output);\n * return { reasoning, response };\n * })\n * .generateScore(({ results }) => {\n * // Score based on reasoning quality\n * return results.preprocessStepResult?.reasoning ? 1 : 0;\n * });\n * ```\n */\nexport const getReasoningFromRunOutput = (output?: ScorerRunOutputForAgent): string | undefined => {\n if (!output) return undefined;\n\n const message = output.find(({ role }) => role === 'assistant');\n if (!message) return undefined;\n\n // Check for reasoning in content.reasoning (string format)\n if (message.content.reasoning) {\n return message.content.reasoning;\n }\n\n // Check for reasoning in parts with type 'reasoning'\n // Reasoning models store reasoning in parts as { type: 'reasoning', details: [{ type: 'text', text: '...' }] }\n const reasoningParts = message.content.parts?.filter((p: any) => p.type === 'reasoning');\n if (reasoningParts && reasoningParts.length > 0) {\n const reasoningTexts = reasoningParts\n .map((p: any) => {\n // The reasoning text can be in p.reasoning or in p.details[].text\n if (p.details && Array.isArray(p.details)) {\n return p.details\n .filter((d: any) => d.type === 'text')\n .map((d: any) => d.text)\n .join('');\n }\n return p.reasoning || '';\n })\n .filter(Boolean);\n\n return reasoningTexts.length > 0 ? reasoningTexts.join('\\n') : undefined;\n }\n\n return undefined;\n};\n\n/**\n * Creates a tool invocation object for testing purposes.\n *\n * @param options - The tool invocation configuration\n * @param options.toolCallId - Unique identifier for the tool call\n * @param options.toolName - Name of the tool being called\n * @param options.args - Arguments passed to the tool\n * @param options.result - Result returned by the tool (absent for a thrown call)\n * @param options.state - State of the invocation (default: 'result')\n * @param options.errorText - Error text recorded when a tool threw (`state: 'output-error'`)\n * @param options.isError - Set alongside `state: 'result'` when the executor reported a failure\n * @returns A tool invocation object\n *\n * @example\n * ```ts\n * const invocation = createToolInvocation({\n * toolCallId: 'call-123',\n * toolName: 'weatherTool',\n * args: { location: 'London' },\n * result: { temperature: 20, condition: 'sunny' },\n * });\n *\n * // A tool that threw\n * const thrown = createToolInvocation({\n * toolCallId: 'call-456',\n * toolName: 'saveTool',\n * args: { value: 'x' },\n * state: 'output-error',\n * errorText: 'Save failed',\n * });\n * ```\n */\nexport const createToolInvocation = ({\n toolCallId,\n toolName,\n args,\n result,\n state = 'result',\n errorText,\n isError,\n}: {\n toolCallId: string;\n toolName: string;\n args: Record<string, any>;\n result?: Record<string, any>;\n state?: 'call' | 'partial-call' | 'result' | 'output-error';\n errorText?: string;\n isError?: boolean;\n}): {\n toolCallId: string;\n toolName: string;\n args: Record<string, any>;\n result: Record<string, any> | undefined;\n state: 'call' | 'partial-call' | 'result' | 'output-error';\n errorText?: string;\n isError?: boolean;\n} => {\n return {\n toolCallId,\n toolName,\n args,\n result,\n state,\n ...(errorText !== undefined && { errorText }),\n ...(isError !== undefined && { isError }),\n };\n};\n\n/**\n * Creates a MastraDBMessage object for testing purposes.\n *\n * Supports optional tool invocations for testing tool call scenarios.\n *\n * @param options - The message configuration\n * @param options.content - The text content of the message\n * @param options.role - The role of the message sender ('user', 'assistant', or 'system')\n * @param options.id - Optional message ID (default: 'test-message')\n * @param options.toolInvocations - Optional array of tool invocations\n * @returns A MastraDBMessage object\n *\n * @example\n * ```ts\n * const message = createTestMessage({\n * content: 'Hello, how can I help?',\n * role: 'assistant',\n * });\n *\n * // With tool invocations\n * const messageWithTools = createTestMessage({\n * content: 'Let me check the weather.',\n * role: 'assistant',\n * toolInvocations: [{\n * toolCallId: 'call-1',\n * toolName: 'weatherTool',\n * args: { location: 'Paris' },\n * result: { temp: 22 },\n * state: 'result',\n * }],\n * });\n *\n * // With native V2 parts — the only form that can carry a thrown call\n * const messageWithThrownTool = createTestMessage({\n * content: 'That failed.',\n * role: 'assistant',\n * parts: [\n * { type: 'text', text: 'That failed.' },\n * {\n * type: 'tool-invocation',\n * toolInvocation: createToolInvocation({\n * toolCallId: 'call-2',\n * toolName: 'saveTool',\n * args: { value: 'x' },\n * state: 'output-error',\n * errorText: 'Save failed',\n * }),\n * },\n * ],\n * });\n * ```\n */\nexport function createTestMessage({\n content,\n role,\n id = 'test-message',\n toolInvocations = [],\n parts,\n}: {\n content: string;\n role: 'user' | 'assistant' | 'system';\n id?: string;\n toolInvocations?: Array<{\n toolCallId: string;\n toolName: string;\n args: Record<string, any>;\n result?: Record<string, any>;\n state: any;\n }>;\n /**\n * Native V2 message parts. Replaces the default single text part when provided.\n * Needed to express invocations the legacy array cannot carry, such as a thrown\n * call (`state: 'output-error'` with `errorText`).\n */\n parts?: MastraDBMessage['content']['parts'];\n}): MastraDBMessage {\n return {\n id,\n role,\n content: {\n format: 2,\n parts: parts ?? [{ type: 'text', text: content }],\n content,\n ...(toolInvocations.length > 0 && {\n toolInvocations: toolInvocations.map(ti => ({\n toolCallId: ti.toolCallId,\n toolName: ti.toolName,\n args: ti.args,\n result: ti.result,\n state: ti.state,\n })),\n }),\n },\n createdAt: new Date(),\n };\n}\n\n/**\n * Creates a complete agent test run object for testing scorers.\n *\n * Provides a convenient way to construct the full run object that scorers receive,\n * including input messages, output, system messages, and request context.\n *\n * @param options - The test run configuration\n * @param options.inputMessages - Array of input messages (default: [])\n * @param options.output - The output messages (required)\n * @param options.rememberedMessages - Array of remembered messages from memory (default: [])\n * @param options.systemMessages - Array of system messages (default: [])\n * @param options.taggedSystemMessages - Tagged system messages map (default: {})\n * @param options.requestContext - Request context (default: new RequestContext())\n * @param options.runId - Unique run ID (default: random UUID)\n * @returns A complete test run object\n *\n * @example\n * ```ts\n * const testRun = createAgentTestRun({\n * inputMessages: [createTestMessage({ content: 'Hello', role: 'user' })],\n * output: [createTestMessage({ content: 'Hi there!', role: 'assistant' })],\n * });\n *\n * const result = await scorer.run({\n * input: testRun.input,\n * output: testRun.output,\n * });\n * ```\n */\nexport const createAgentTestRun = ({\n inputMessages = [],\n output,\n rememberedMessages = [],\n systemMessages = [],\n taggedSystemMessages = {},\n requestContext = new RequestContext(),\n runId = crypto.randomUUID(),\n}: {\n inputMessages?: ScorerRunInputForAgent['inputMessages'];\n output: ScorerRunOutputForAgent;\n rememberedMessages?: ScorerRunInputForAgent['rememberedMessages'];\n systemMessages?: ScorerRunInputForAgent['systemMessages'];\n taggedSystemMessages?: ScorerRunInputForAgent['taggedSystemMessages'];\n requestContext?: RequestContext;\n runId?: string;\n}): {\n input: ScorerRunInputForAgent;\n output: ScorerRunOutputForAgent;\n requestContext: RequestContext;\n runId: string;\n} => {\n return {\n input: {\n inputMessages,\n rememberedMessages,\n systemMessages,\n taggedSystemMessages,\n },\n output,\n requestContext,\n runId,\n };\n};\n\n/**\n * Creates a test run for trajectory scorers where `output` is a `Trajectory`\n * (pre-extracted by the `runEvals` pipeline).\n *\n * @example\n * ```ts\n * const testRun = createTrajectoryTestRun({\n * inputMessages: [createTestMessage({ content: 'Do X', role: 'user', id: 'u1' })],\n * trajectory: {\n * steps: [\n * { stepType: 'tool_call', name: 'search', toolArgs: { q: 'test' } },\n * ],\n * },\n * });\n * ```\n */\nexport const createTrajectoryTestRun = ({\n inputMessages = [],\n trajectory,\n rememberedMessages = [],\n systemMessages = [],\n taggedSystemMessages = {},\n requestContext = new RequestContext(),\n runId = crypto.randomUUID(),\n expectedTrajectory,\n}: {\n inputMessages?: ScorerRunInputForAgent['inputMessages'];\n trajectory: Trajectory;\n rememberedMessages?: ScorerRunInputForAgent['rememberedMessages'];\n systemMessages?: ScorerRunInputForAgent['systemMessages'];\n taggedSystemMessages?: ScorerRunInputForAgent['taggedSystemMessages'];\n requestContext?: RequestContext;\n runId?: string;\n expectedTrajectory?: TrajectoryExpectation;\n}): {\n input: ScorerRunInputForAgent;\n output: Trajectory;\n requestContext: RequestContext;\n runId: string;\n expectedTrajectory?: TrajectoryExpectation;\n} => {\n return {\n input: {\n inputMessages,\n rememberedMessages,\n systemMessages,\n taggedSystemMessages,\n },\n output: trajectory,\n expectedTrajectory,\n requestContext,\n runId,\n };\n};\n\n/**\n * Information about a tool call extracted from scorer output.\n */\nexport type ToolCallInfo = {\n /** Name of the tool that was called */\n toolName: string;\n /** Unique identifier for the tool call */\n toolCallId: string;\n /** Index of the message containing this tool call */\n messageIndex: number;\n /** Index of the invocation within the message's tool invocations */\n invocationIndex: number;\n};\n\n/**\n * Merges the two places a message can store tool invocations into one list.\n *\n * A native message may carry `content.parts` tool-invocation entries, the legacy\n * `content.toolInvocations` array, or both. Neither form is a superset of the\n * other: thrown calls are recorded in parts as `state: 'output-error'` and can be\n * missing from the legacy array, while older persisted messages may only carry the\n * legacy array. Reading one form exclusively therefore hides real calls.\n *\n * Parts win when the same `toolCallId` appears in both, since they hold the real\n * terminal state. Legacy-only calls are retained before their next shared anchor so\n * compatible call order survives instead of being appended.\n *\n * Mirrors `extractTrajectory` in `@mastra/core` (`packages/core/src/evals/types.ts`)\n * so tool-use checks and trajectory scorers agree on what a run actually called.\n * Keep the two in step.\n *\n * @param message - A single message from the scorer run output\n * @returns The merged tool invocations for that message\n */\nexport function mergeToolInvocations(message: MastraDBMessage | undefined) {\n const legacy = message?.content?.toolInvocations;\n const fromParts =\n message?.content?.parts\n ?.filter((p): p is Extract<typeof p, { type: 'tool-invocation' }> => p.type === 'tool-invocation')\n .map(p => p.toolInvocation)\n .filter(Boolean) ?? [];\n const partCallIds = new Set(fromParts.map(invocation => invocation.toolCallId).filter(Boolean));\n const legacyInvocations = legacy ?? [];\n const legacyPositions = new Map(legacyInvocations.map((invocation, index) => [invocation?.toolCallId, index]));\n const toolInvocations: typeof fromParts = [];\n let legacyIndex = 0;\n for (const invocation of fromParts) {\n const sharedIndex = invocation.toolCallId ? legacyPositions.get(invocation.toolCallId) : undefined;\n if (sharedIndex !== undefined && sharedIndex >= legacyIndex) {\n // Retain legacy-only calls before their next shared anchor.\n for (; legacyIndex < sharedIndex; legacyIndex++) {\n const previous = legacyInvocations[legacyIndex];\n if (previous && !partCallIds.has(previous.toolCallId)) toolInvocations.push(previous);\n }\n legacyIndex++;\n }\n toolInvocations.push(invocation);\n }\n for (; legacyIndex < legacyInvocations.length; legacyIndex++) {\n const invocation = legacyInvocations[legacyIndex];\n if (invocation && !partCallIds.has(invocation.toolCallId)) toolInvocations.push(invocation);\n }\n return toolInvocations;\n}\n\n/**\n * Extracts all tool calls from a scorer run output.\n *\n * Iterates through all messages and their tool invocations to collect information\n * about tools that were called. A call counts regardless of outcome, so the states\n * `'result'`, `'call'`, and `'output-error'` (a tool that threw) are all included.\n *\n * @param output - The scorer run output (array of MastraDBMessage)\n * @returns An object containing tool names and detailed tool call info\n *\n * @example\n * ```ts\n * const scorer = createScorer({ ... })\n * .preprocess(({ run }) => {\n * const { tools, toolCallInfos } = extractToolCalls(run.output);\n * return {\n * toolsUsed: tools,\n * toolCount: tools.length,\n * };\n * });\n * ```\n */\nexport function extractToolCalls(output: ScorerRunOutputForAgent): { tools: string[]; toolCallInfos: ToolCallInfo[] } {\n const toolCalls: string[] = [];\n const toolCallInfos: ToolCallInfo[] = [];\n\n for (let messageIndex = 0; messageIndex < output.length; messageIndex++) {\n const message = output[messageIndex];\n const toolInvocations = mergeToolInvocations(message);\n\n if (!toolInvocations.length) continue;\n\n for (let invocationIndex = 0; invocationIndex < toolInvocations.length; invocationIndex++) {\n const invocation = toolInvocations[invocationIndex];\n // A thrown call (`output-error`) still counts: the tool ran. Partial calls do not.\n // Same state set core's `extractTrajectory` accepts.\n if (\n invocation &&\n invocation.toolName &&\n (invocation.state === 'result' || invocation.state === 'call' || invocation.state === 'output-error')\n ) {\n toolCalls.push(invocation.toolName);\n toolCallInfos.push({\n toolName: invocation.toolName,\n toolCallId: invocation.toolCallId || `${messageIndex}-${invocationIndex}`,\n messageIndex,\n invocationIndex,\n });\n }\n }\n }\n\n return { tools: toolCalls, toolCallInfos };\n}\n\n/**\n * Extracts text content from all input messages.\n *\n * @param runInput - The scorer run input\n * @returns An array of text strings from each input message\n *\n * @example\n * ```ts\n * const scorer = createScorer({ ... })\n * .preprocess(({ run }) => {\n * const messages = extractInputMessages(run.input);\n * return { allUserMessages: messages.join('\\n') };\n * });\n * ```\n */\nexport const extractInputMessages = (runInput: ScorerRunInputForAgent | undefined): string[] => {\n return runInput?.inputMessages?.map(msg => getTextContentFromMastraDBMessage(msg)) || [];\n};\n\n/**\n * Extracts text content from all assistant response messages.\n *\n * Filters for messages with role 'assistant' and extracts their text content.\n *\n * @param runOutput - The scorer run output (array of MastraDBMessage)\n * @returns An array of text strings from each assistant message\n *\n * @example\n * ```ts\n * const scorer = createScorer({ ... })\n * .preprocess(({ run }) => {\n * const responses = extractAgentResponseMessages(run.output);\n * return { allResponses: responses.join('\\n') };\n * });\n * ```\n */\nexport const extractAgentResponseMessages = (runOutput: ScorerRunOutputForAgent): string[] => {\n return runOutput.filter(msg => msg.role === 'assistant').map(msg => getTextContentFromMastraDBMessage(msg));\n};\n\n/**\n * Information about a tool result extracted from scorer output.\n */\nexport type ToolResultInfo = {\n /** Name of the tool that was called */\n toolName: string;\n /** Unique identifier for the tool call */\n toolCallId: string;\n /** Arguments passed to the tool */\n args: Record<string, any>;\n /** Result returned by the tool */\n result: any;\n};\n\n/**\n * Extracts tool results from a scorer run output.\n *\n * Returns structured objects that can be used with the hallucination scorer's\n * `getContext` hook or for other scorer logic.\n *\n * @param output - The scorer run output (array of MastraDBMessage)\n * @returns An array of ToolResultInfo objects\n *\n * @example\n * ```ts\n * import { extractToolResults } from '@mastra/evals/scorers';\n * import { createHallucinationScorer } from '@mastra/evals/scorers/prebuilt';\n *\n * const scorer = createHallucinationScorer({\n * model: openai('gpt-4o'),\n * options: {\n * getContext: (run) => {\n * const toolResults = extractToolResults(run.output);\n * return toolResults.map(t => JSON.stringify({ tool: t.toolName, result: t.result }));\n * },\n * },\n * });\n * ```\n */\nexport function extractToolResults(output: ScorerRunOutputForAgent): ToolResultInfo[] {\n const results: ToolResultInfo[] = [];\n\n for (const message of output) {\n const toolInvocations = mergeToolInvocations(message);\n\n if (!toolInvocations.length) continue;\n\n for (const invocation of toolInvocations) {\n // A thrown call carries `errorText` rather than a result, so it yields no entry here.\n if (invocation.state === 'result' && invocation.result !== undefined) {\n results.push({\n toolName: invocation.toolName,\n toolCallId: invocation.toolCallId || '',\n args: invocation.args || {},\n result: invocation.result,\n });\n }\n }\n }\n\n return results;\n}\n\n// Re-export extractTrajectory from core — it's called automatically by runEvals\n// for trajectory scorers, but users may still want it for custom use cases.\nexport { extractTrajectory } from '@mastra/core/evals';\n\n/**\n * Compares two trajectories and returns detailed comparison results.\n *\n * This is the core comparison logic used by trajectory scorers. It supports\n * strict and non-strict ordering, optional step data comparison, and loop detection.\n *\n * @param actual - The trajectory the agent actually took\n * @param expected - The expected trajectory to compare against\n * @param options - Comparison configuration options\n * @returns Detailed comparison results including match scores and diagnostics\n *\n * @example\n * ```ts\n * const result = compareTrajectories(\n * { steps: [{ stepType: 'tool_call', name: 'search' }, { stepType: 'tool_call', name: 'summarize' }] },\n * { steps: [{ stepType: 'tool_call', name: 'search' }, { stepType: 'tool_call', name: 'summarize' }] },\n * { ordering: 'strict' }\n * );\n * // result.score = 1.0\n * ```\n */\nexport function compareTrajectories(\n actual: Trajectory,\n expected: Trajectory | { steps: ExpectedStep[] },\n options: {\n ordering?: 'strict' | 'relaxed' | 'unordered';\n allowRepeatedSteps?: boolean;\n } = {},\n): TrajectoryComparisonResult {\n const { allowRepeatedSteps = true, ordering = 'relaxed' } = options;\n\n // Normalize expected to ExpectedStep[]. TrajectoryStep and ExpectedStep share\n // the same field names, so TrajectoryStep[] can be used directly as ExpectedStep[].\n // The only structural difference is `children` (TrajectoryStep[] vs TrajectoryExpectation),\n // but compareTrajectories doesn't recurse into children.\n const normalizedExpected: { steps: ExpectedStep[] } = {\n steps: expected.steps as ExpectedStep[],\n };\n\n if (normalizedExpected.steps.length === 0) {\n return {\n score: actual.steps.length === 0 ? 1 : 0,\n matchedSteps: 0,\n totalExpectedSteps: 0,\n totalActualSteps: actual.steps.length,\n missingSteps: [],\n extraSteps: actual.steps.map((s: TrajectoryStep) => s.name),\n outOfOrderSteps: [],\n repeatedSteps: [],\n };\n }\n\n const actualNames = actual.steps.map((s: TrajectoryStep) => s.name);\n\n // Detect repeated steps\n const nameCounts = new Map<string, number>();\n for (const name of actualNames) {\n nameCounts.set(name, (nameCounts.get(name) || 0) + 1);\n }\n const repeatedSteps = [...nameCounts.entries()]\n .filter(([_, count]: [string, number]) => count > 1)\n .map(([name]: [string, number]) => name);\n\n if (ordering === 'strict') {\n return compareStrictOrder(actual, normalizedExpected, { allowRepeatedSteps, repeatedSteps });\n }\n\n if (ordering === 'unordered') {\n return compareUnorderedPresence(actual, normalizedExpected, { allowRepeatedSteps, repeatedSteps });\n }\n\n return compareRelaxedOrder(actual, normalizedExpected, { allowRepeatedSteps, repeatedSteps });\n}\n\n/**\n * Result of comparing two trajectories.\n */\nexport type TrajectoryComparisonResult = {\n /** Overall match score from 0 to 1 */\n score: number;\n /** Number of expected steps that were matched */\n matchedSteps: number;\n /** Total number of expected steps */\n totalExpectedSteps: number;\n /** Total number of actual steps taken */\n totalActualSteps: number;\n /** Expected steps that were not found in the actual trajectory */\n missingSteps: string[];\n /** Actual steps that were not in the expected trajectory */\n extraSteps: string[];\n /** Steps that appear but not in the expected position */\n outOfOrderSteps: string[];\n /** Steps that were repeated (appeared more than once) */\n repeatedSteps: string[];\n};\n\nfunction compareStrictOrder(\n actual: Trajectory,\n expected: { steps: ExpectedStep[] },\n opts: { allowRepeatedSteps: boolean; repeatedSteps: string[] },\n): TrajectoryComparisonResult {\n const actualNames: string[] = actual.steps.map((s: TrajectoryStep) => s.name);\n const expectedNames: string[] = expected.steps.map((s: ExpectedStep) => s.name);\n\n // Strict: exact same sequence\n let matchedSteps = 0;\n const outOfOrderSteps: string[] = [];\n const matchedExpectedIndices = new Set<number>();\n const maxLen = Math.max(actualNames.length, expectedNames.length);\n\n for (let i = 0; i < maxLen; i++) {\n const actualName = actualNames[i];\n const expectedName = expectedNames[i];\n if (actualName === expectedName) {\n if (actual.steps[i] && expected.steps[i]) {\n if (expectedStepMatches(actual.steps[i]!, expected.steps[i]!)) {\n matchedSteps++;\n matchedExpectedIndices.add(i);\n }\n } else {\n matchedSteps++;\n matchedExpectedIndices.add(i);\n }\n } else if (actualName && expectedNames.includes(actualName)) {\n outOfOrderSteps.push(actualName);\n }\n }\n\n // Missing steps = expected steps that were not matched (accounts for stepType/data mismatches)\n const missingSteps: string[] = expectedNames.filter((_: string, i: number) => !matchedExpectedIndices.has(i));\n const extraSteps: string[] = actualNames.filter((name: string) => !expectedNames.includes(name));\n\n let score = matchedSteps / expected.steps.length;\n\n // Penalize extra steps in strict mode\n if (actualNames.length > expectedNames.length) {\n const extraPenalty = (actualNames.length - expectedNames.length) / expectedNames.length;\n score = Math.max(0, score - extraPenalty * 0.5);\n }\n\n // Penalize repeated steps if not allowed\n if (!opts.allowRepeatedSteps && opts.repeatedSteps.length > 0) {\n score = Math.max(0, score - opts.repeatedSteps.length * 0.1);\n }\n\n return {\n score: roundToTwoDecimals(Math.max(0, Math.min(1, score))),\n matchedSteps,\n totalExpectedSteps: expected.steps.length,\n totalActualSteps: actual.steps.length,\n missingSteps,\n extraSteps,\n outOfOrderSteps,\n repeatedSteps: opts.repeatedSteps,\n };\n}\n\nfunction compareRelaxedOrder(\n actual: Trajectory,\n expected: { steps: ExpectedStep[] },\n opts: { allowRepeatedSteps: boolean; repeatedSteps: string[] },\n): TrajectoryComparisonResult {\n const actualNames: string[] = actual.steps.map((s: TrajectoryStep) => s.name);\n const expectedNames: string[] = expected.steps.map((s: ExpectedStep) => s.name);\n\n // Relaxed: expected steps must appear in order but extra steps are allowed\n let matchedSteps = 0;\n let lastMatchedIndex = -1;\n const outOfOrderSteps: string[] = [];\n const matchedExpectedIndices = new Set<number>();\n\n for (let i = 0; i < expectedNames.length; i++) {\n const expectedName = expectedNames[i];\n let found = false;\n\n for (let j = lastMatchedIndex + 1; j < actualNames.length; j++) {\n if (actualNames[j] === expectedName) {\n if (actual.steps[j] && expected.steps[i]) {\n if (expectedStepMatches(actual.steps[j]!, expected.steps[i]!)) {\n matchedSteps++;\n lastMatchedIndex = j;\n matchedExpectedIndices.add(i);\n found = true;\n break;\n }\n } else {\n matchedSteps++;\n lastMatchedIndex = j;\n matchedExpectedIndices.add(i);\n found = true;\n break;\n }\n }\n }\n\n if (!found) {\n // Check if the step exists but is out of order\n if (actualNames.includes(expectedName!)) {\n outOfOrderSteps.push(expectedName!);\n }\n }\n }\n\n // Missing steps = expected steps that were not matched (by name + stepType + data, not just name)\n const missingSteps = expectedNames.filter((_, i) => !matchedExpectedIndices.has(i));\n const expectedSet = new Set(expectedNames);\n const extraSteps = actualNames.filter(name => !expectedSet.has(name));\n\n let score = matchedSteps / expected.steps.length;\n\n // Penalize repeated steps if not allowed\n if (!opts.allowRepeatedSteps && opts.repeatedSteps.length > 0) {\n score = Math.max(0, score - opts.repeatedSteps.length * 0.1);\n }\n\n return {\n score: roundToTwoDecimals(Math.max(0, Math.min(1, score))),\n matchedSteps,\n totalExpectedSteps: expected.steps.length,\n totalActualSteps: actual.steps.length,\n missingSteps,\n extraSteps,\n outOfOrderSteps,\n repeatedSteps: opts.repeatedSteps,\n };\n}\n\n/**\n * Fields on each ExpectedStep variant that are comparable data (not structural).\n * Used by `expectedStepMatches` to know which fields to compare when `compareData` is true.\n */\nconst COMPARABLE_FIELDS_BY_TYPE: Record<string, string[]> = {\n tool_call: ['toolArgs', 'toolResult', 'success'],\n mcp_tool_call: ['toolArgs', 'toolResult', 'mcpServer', 'success'],\n model_generation: ['modelId', 'promptTokens', 'completionTokens', 'finishReason'],\n agent_run: ['agentId'],\n workflow_step: ['stepId', 'status', 'output'],\n workflow_run: ['workflowId', 'status'],\n workflow_conditional: ['conditionCount', 'selectedSteps'],\n workflow_parallel: ['branchCount', 'parallelSteps'],\n workflow_loop: ['loopType', 'totalIterations'],\n workflow_sleep: ['sleepDurationMs', 'sleepType'],\n workflow_wait_event: ['eventName', 'eventReceived'],\n processor_run: ['processorId'],\n};\n\n/**\n * Check if an actual TrajectoryStep matches an ExpectedStep.\n * Matches by name, optionally by stepType, and auto-compares any variant-specific\n * fields that are present on the expected step.\n */\nfunction expectedStepMatches(actual: TrajectoryStep, expected: ExpectedStep): boolean {\n if (actual.name !== expected.name) return false;\n if (expected.stepType && actual.stepType !== expected.stepType) return false;\n\n if (expected.stepType) {\n const fields = COMPARABLE_FIELDS_BY_TYPE[expected.stepType] ?? [];\n for (const field of fields) {\n const expectedVal = (expected as any)[field];\n if (expectedVal === undefined) continue; // field not specified in expectation, skip\n const actualVal = (actual as any)[field];\n if (actualVal === undefined) return false;\n try {\n if (JSON.stringify(actualVal) !== JSON.stringify(expectedVal)) return false;\n } catch {\n return false;\n }\n }\n }\n\n return true;\n}\n\nfunction compareUnorderedPresence(\n actual: Trajectory,\n expected: { steps: ExpectedStep[] },\n opts: { allowRepeatedSteps: boolean; repeatedSteps: string[] },\n): TrajectoryComparisonResult {\n const actualNames: string[] = actual.steps.map((s: TrajectoryStep) => s.name);\n const expectedNames: string[] = expected.steps.map((s: ExpectedStep) => s.name);\n\n let matchedSteps = 0;\n const matchedExpectedIndices = new Set<number>();\n const usedIndices = new Set<number>();\n for (let i = 0; i < expected.steps.length; i++) {\n const expectedStep = expected.steps[i]!;\n for (let j = 0; j < actual.steps.length; j++) {\n if (!usedIndices.has(j) && expectedStepMatches(actual.steps[j]!, expectedStep)) {\n matchedSteps++;\n matchedExpectedIndices.add(i);\n usedIndices.add(j);\n break;\n }\n }\n }\n\n // Missing steps = expected steps that were not matched (accounts for stepType/data mismatches)\n const missingSteps = expectedNames.filter((_, i) => !matchedExpectedIndices.has(i));\n const expectedSet = new Set(expectedNames);\n const extraSteps = actualNames.filter(name => !expectedSet.has(name));\n\n let score = matchedSteps / expected.steps.length;\n\n // Penalize repeated steps if not allowed\n if (!opts.allowRepeatedSteps && opts.repeatedSteps.length > 0) {\n score = Math.max(0, score - opts.repeatedSteps.length * 0.1);\n }\n\n return {\n score: roundToTwoDecimals(Math.max(0, Math.min(1, score))),\n matchedSteps,\n totalExpectedSteps: expected.steps.length,\n totalActualSteps: actual.steps.length,\n missingSteps,\n extraSteps,\n outOfOrderSteps: [], // ordering not checked in unordered mode\n repeatedSteps: opts.repeatedSteps,\n };\n}\n\n// ─── Efficiency evaluation ───\n\n/**\n * Result of checking trajectory efficiency.\n */\nexport type TrajectoryEfficiencyResult = {\n /** Overall efficiency score from 0 to 1 */\n score: number;\n /** Total number of steps taken */\n totalSteps: number;\n /** Whether the step budget was exceeded */\n overStepBudget: boolean;\n /** Total tokens used across model_generation steps */\n totalTokens: number;\n /** Whether the token budget was exceeded */\n overTokenBudget: boolean;\n /** Total duration in milliseconds */\n totalDurationMs: number;\n /** Whether the duration budget was exceeded */\n overDurationBudget: boolean;\n /** Redundant calls detected (same tool + same args consecutively) */\n redundantCalls: Array<{ name: string; index: number }>;\n};\n\n/**\n * Evaluate trajectory efficiency against budgets and redundancy checks.\n * Throws when a configured budget is invalid or its required measurements are incomplete or invalid.\n */\nexport function checkTrajectoryEfficiency(\n trajectory: Trajectory,\n options: {\n maxSteps?: number;\n maxTotalTokens?: number;\n maxTotalDurationMs?: number;\n noRedundantCalls?: boolean;\n } = {},\n): TrajectoryEfficiencyResult {\n const { maxSteps, maxTotalTokens, maxTotalDurationMs, noRedundantCalls = true } = options;\n\n const isMeasurement = (value: number | undefined): value is number =>\n typeof value === 'number' && Number.isFinite(value) && value >= 0;\n for (const [name, limit] of Object.entries({ maxSteps, maxTotalTokens, maxTotalDurationMs })) {\n if (limit !== undefined && !isMeasurement(limit)) {\n throw new Error(`Invalid ${name} budget: expected a finite nonnegative number`);\n }\n }\n\n const totalSteps = trajectory.steps.length;\n\n // Count each model generation, including nested agents, without counting container aggregates.\n let totalTokens = 0;\n let modelGenerations = 0;\n const remaining = [...trajectory.steps];\n while (remaining.length > 0) {\n const step = remaining.pop()!;\n if (step.stepType === 'model_generation') {\n modelGenerations++;\n if (\n maxTotalTokens !== undefined &&\n (!isMeasurement(step.promptTokens) || !isMeasurement(step.completionTokens))\n ) {\n throw new Error(\n `Cannot evaluate token budget: model generation \"${step.name}\" has missing or invalid token counts`,\n );\n }\n totalTokens += (step.promptTokens ?? 0) + (step.completionTokens ?? 0);\n }\n if (step.children) {\n for (const child of step.children) remaining.push(child);\n }\n }\n if (maxTotalTokens !== undefined && (modelGenerations === 0 || !isMeasurement(totalTokens))) {\n throw new Error('Cannot evaluate token budget: missing model-generation measurements or invalid total tokens');\n }\n\n // A parent duration already covers its children; never add both.\n const totalDurationMs =\n trajectory.totalDurationMs ?? trajectory.steps.reduce((sum, s) => sum + (s.durationMs ?? 0), 0);\n if (maxTotalDurationMs !== undefined) {\n const hasDuration =\n trajectory.totalDurationMs !== undefined\n ? isMeasurement(trajectory.totalDurationMs)\n : trajectory.steps.length > 0 && trajectory.steps.every(step => isMeasurement(step.durationMs));\n if (!hasDuration || !isMeasurement(totalDurationMs)) {\n throw new Error('Cannot evaluate duration budget: missing or invalid duration measurements');\n }\n }\n\n // Detect redundant calls (same tool name + same args in consecutive calls)\n const redundantCalls: Array<{ name: string; index: number }> = [];\n const siblingLists: TrajectoryStep[][] = noRedundantCalls ? [trajectory.steps] : [];\n while (siblingLists.length > 0) {\n const steps = siblingLists.pop()!;\n for (const step of steps) {\n if (step.children?.length) siblingLists.push(step.children);\n }\n for (let i = 1; i < steps.length; i++) {\n const prev = steps[i - 1]!;\n const curr = steps[i]!;\n if (\n prev.name === curr.name &&\n prev.stepType === curr.stepType &&\n (prev.stepType === 'tool_call' || prev.stepType === 'mcp_tool_call')\n ) {\n const prevArgs = (prev as TrajectoryStep & { toolArgs?: Record<string, unknown> }).toolArgs;\n const currArgs = (curr as TrajectoryStep & { toolArgs?: Record<string, unknown> }).toolArgs;\n try {\n if (JSON.stringify(prevArgs) === JSON.stringify(currArgs)) {\n redundantCalls.push({ name: curr.name, index: i });\n }\n } catch {\n // If serialization fails, don't flag as redundant\n }\n }\n }\n }\n\n const overStepBudget = maxSteps !== undefined && totalSteps > maxSteps;\n const overTokenBudget = maxTotalTokens !== undefined && totalTokens > maxTotalTokens;\n const overDurationBudget = maxTotalDurationMs !== undefined && totalDurationMs > maxTotalDurationMs;\n\n // Calculate score: each dimension contributes equally\n const dimensions: number[] = [];\n\n if (maxSteps !== undefined) {\n dimensions.push(overStepBudget ? Math.max(0, 1 - (totalSteps - maxSteps) / maxSteps) : 1);\n }\n if (maxTotalTokens !== undefined) {\n dimensions.push(overTokenBudget ? Math.max(0, 1 - (totalTokens - maxTotalTokens) / maxTotalTokens) : 1);\n }\n if (maxTotalDurationMs !== undefined) {\n dimensions.push(\n overDurationBudget ? Math.max(0, 1 - (totalDurationMs - maxTotalDurationMs) / maxTotalDurationMs) : 1,\n );\n }\n if (noRedundantCalls) {\n dimensions.push(redundantCalls.length === 0 ? 1 : Math.max(0, 1 - redundantCalls.length * 0.2));\n }\n\n const score = dimensions.length > 0 ? dimensions.reduce((a, b) => a + b, 0) / dimensions.length : 1;\n\n return {\n score: roundToTwoDecimals(Math.max(0, Math.min(1, score))),\n totalSteps,\n overStepBudget,\n totalTokens,\n overTokenBudget,\n totalDurationMs,\n overDurationBudget,\n redundantCalls,\n };\n}\n\n// ─── Blacklist evaluation ───\n\n/**\n * Result of checking trajectory against a blacklist.\n */\nexport type TrajectoryBlacklistResult = {\n /** Score: 1.0 if clean, 0.0 if any violation found */\n score: number;\n /** Individual blacklisted tools that were found */\n violatedTools: string[];\n /** Blacklisted sequences that were found */\n violatedSequences: string[][];\n};\n\n/**\n * Check if a trajectory violates any blacklist rules.\n * Returns score 0.0 if any violation is found (hard fail).\n */\nexport function checkTrajectoryBlacklist(\n trajectory: Trajectory,\n options: {\n blacklistedTools?: string[];\n blacklistedSequences?: string[][];\n } = {},\n): TrajectoryBlacklistResult {\n const { blacklistedTools = [], blacklistedSequences = [] } = options;\n const violatedTools: string[] = [];\n const violatedSequences: string[][] = [];\n\n // Collect every sibling list (top level plus each nested `children` list)\n const siblingLists: string[][] = [];\n const allNames = new Set<string>();\n const pending: TrajectoryStep[][] = [trajectory.steps];\n while (pending.length > 0) {\n const steps = pending.pop()!;\n siblingLists.push(steps.map(s => s.name));\n for (const step of steps) {\n allNames.add(step.name);\n if (step.children?.length) pending.push(step.children);\n }\n }\n\n // Check blacklisted tools at any depth\n for (const forbidden of blacklistedTools) {\n if (allNames.has(forbidden)) {\n violatedTools.push(forbidden);\n }\n }\n\n // Check blacklisted sequences (contiguous subsequences within one sibling list)\n const containsSequence = (names: string[], sequence: string[]) => {\n for (let i = 0; i <= names.length - sequence.length; i++) {\n let match = true;\n for (let j = 0; j < sequence.length; j++) {\n if (names[i + j] !== sequence[j]) {\n match = false;\n break;\n }\n }\n if (match) return true;\n }\n return false;\n };\n for (const sequence of blacklistedSequences) {\n if (sequence.length === 0) continue;\n if (siblingLists.some(names => containsSequence(names, sequence))) {\n violatedSequences.push(sequence); // Only report each sequence once\n }\n }\n\n const hasViolations = violatedTools.length > 0 || violatedSequences.length > 0;\n\n return {\n score: hasViolations ? 0 : 1,\n violatedTools,\n violatedSequences,\n };\n}\n\n// ─── Tool failure analysis ───\n\n/**\n * A detected tool failure pattern in the trajectory.\n */\nexport type ToolFailurePattern = {\n /** The tool name that experienced failure */\n toolName: string;\n /** Number of consecutive retries (same tool, same or similar args) */\n retryCount: number;\n /** Whether the agent fell back to a different tool after failures */\n fellBackToAlternative: boolean;\n /** The alternative tool used, if any */\n alternativeTool?: string;\n /** Whether any retry eventually succeeded */\n eventuallySucceeded: boolean;\n};\n\n/**\n * Result of analyzing tool failure patterns in a trajectory.\n */\nexport type ToolFailureAnalysisResult = {\n /** Score from 0 to 1 (lower = more failures/retries) */\n score: number;\n /** Tool failure patterns detected */\n patterns: ToolFailurePattern[];\n /** Total number of retries across all tools */\n totalRetries: number;\n /** Tools that exceeded the retry threshold */\n excessiveRetryTools: string[];\n};\n\n/**\n * Analyze tool failure and retry patterns in a trajectory.\n */\nexport function analyzeToolFailures(\n trajectory: Trajectory,\n options: {\n maxRetriesPerTool?: number;\n } = {},\n): ToolFailureAnalysisResult {\n const { maxRetriesPerTool = 2 } = options;\n const patterns: ToolFailurePattern[] = [];\n let totalRetries = 0;\n\n const toolCallSteps = trajectory.steps.filter(s => s.stepType === 'tool_call' || s.stepType === 'mcp_tool_call');\n\n if (toolCallSteps.length === 0) {\n return { score: 1, patterns: [], totalRetries: 0, excessiveRetryTools: [] };\n }\n\n // Group consecutive calls to the same tool as potential retry sequences\n let i = 0;\n while (i < toolCallSteps.length) {\n const currentTool = toolCallSteps[i]!;\n let retryCount = 0;\n let j = i + 1;\n\n // Count consecutive calls to the same tool\n // (toolCallSteps is pre-filtered to tool_call/mcp_tool_call, so no stepType checks needed)\n while (j < toolCallSteps.length && toolCallSteps[j]!.name === currentTool.name) {\n const prevStep = toolCallSteps[j - 1]! as TrajectoryStep & { success?: boolean };\n if (prevStep.success === false) {\n retryCount++;\n }\n j++;\n }\n\n if (retryCount > 0) {\n // Check if agent fell back to a different tool after retries\n const nextDifferentTool = j < toolCallSteps.length ? toolCallSteps[j] : undefined;\n const lastRetry = toolCallSteps[j - 1]! as TrajectoryStep & { success?: boolean };\n const lastSuccess = lastRetry.success !== false;\n\n patterns.push({\n toolName: currentTool.name,\n retryCount,\n fellBackToAlternative: nextDifferentTool !== undefined && !lastSuccess,\n alternativeTool: nextDifferentTool !== undefined && !lastSuccess ? nextDifferentTool.name : undefined,\n eventuallySucceeded: lastSuccess,\n });\n\n totalRetries += retryCount;\n }\n\n i = j;\n }\n\n // Score: penalize excessive retries\n const excessiveRetryTools = patterns.filter(p => p.retryCount > maxRetriesPerTool).map(p => p.toolName);\n\n let score = 1;\n if (toolCallSteps.length > 0) {\n // Each retry beyond the threshold costs more\n const excessRetries = patterns.reduce((sum, p) => sum + Math.max(0, p.retryCount - maxRetriesPerTool), 0);\n score = Math.max(0, 1 - excessRetries * 0.2);\n }\n\n return {\n score: roundToTwoDecimals(Math.max(0, Math.min(1, score))),\n patterns,\n totalRetries,\n excessiveRetryTools,\n };\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mastra/evals",
3
- "version": "1.10.3",
3
+ "version": "1.10.4-alpha.0",
4
4
  "description": "",
5
5
  "type": "module",
6
6
  "files": [
@@ -113,12 +113,12 @@
113
113
  "typescript": "^7.0.2",
114
114
  "vitest": "4.1.11",
115
115
  "zod": "^4.6.4",
116
- "@internal/ai-sdk-v5": "0.0.84",
117
116
  "@internal/lint": "0.0.137",
117
+ "@internal/ai-sdk-v5": "0.0.84",
118
118
  "@internal/llm-recorder": "0.0.73",
119
119
  "@internal/types-builder": "0.0.112",
120
- "@internal/test-utils": "0.0.73",
121
- "@mastra/core": "1.71.0"
120
+ "@mastra/core": "1.72.0-alpha.7",
121
+ "@internal/test-utils": "0.0.73"
122
122
  },
123
123
  "engines": {
124
124
  "node": ">=22.13.0"