{"version":3,"file":"stream.cjs","names":[],"sources":["../../../src/utils/testing/stream.ts"],"sourcesContent":["import type { AIMessage } from \"../../messages/index.js\";\nimport type { UsageMetadata } from \"../../messages/metadata.js\";\nimport { ChatModelStream } from \"../../language_models/stream.js\";\n\n/**\n * The `this` context that Vitest provides to custom matchers via `expect.extend`.\n * @see https://vitest.dev/guide/extending-matchers.html\n */\ninterface ExpectExtendThis {\n  isNot?: boolean;\n  equals(a: unknown, b: unknown): boolean;\n  utils: {\n    matcherHint(\n      name: string,\n      received?: string,\n      expected?: string,\n      options?: { isNot?: boolean }\n    ): string;\n    printReceived(value: unknown): string;\n    printExpected(value: unknown): string;\n  };\n}\n\ninterface ExpectationResult {\n  pass: boolean;\n  message: () => string;\n  actual?: unknown;\n  expected?: unknown;\n}\n\nfunction isChatModelStream(received: unknown): received is ChatModelStream {\n  if (received == null || typeof received !== \"object\") {\n    return false;\n  }\n  const stream = received as ChatModelStream;\n  return (\n    typeof stream.text !== \"undefined\" &&\n    typeof stream.toolCalls !== \"undefined\" &&\n    typeof stream.reasoning !== \"undefined\" &&\n    typeof stream.usage !== \"undefined\" &&\n    typeof stream.output !== \"undefined\" &&\n    typeof stream[Symbol.asyncIterator] === \"function\"\n  );\n}\n\nfunction matchesPartialObject(\n  actual: Record<string, unknown> | undefined,\n  expected: Record<string, unknown>,\n  equals: ExpectExtendThis[\"equals\"]\n): boolean {\n  if (actual == null) {\n    return false;\n  }\n  return Object.entries(expected).every(([key, value]) =>\n    equals(actual[key], value)\n  );\n}\n\nfunction matchesStreamUsage(\n  actual: UsageMetadata | undefined,\n  expected: StreamUsageExpectation,\n  equals: ExpectExtendThis[\"equals\"]\n): boolean {\n  if (actual == null) {\n    return false;\n  }\n  return matchesPartialObject(\n    actual as Record<string, unknown>,\n    expected as Record<string, unknown>,\n    equals\n  );\n}\n\nfunction getOutputText(message: AIMessage): string | undefined {\n  const content = message.content as Array<{ type: string; text?: string }>;\n  return content.find((block) => block.type === \"text\")?.text;\n}\n\nfunction matchesStreamOutput(\n  message: AIMessage,\n  expected: StreamOutputExpectation,\n  equals: ExpectExtendThis[\"equals\"]\n): boolean {\n  if (expected.id !== undefined && message.id !== expected.id) {\n    return false;\n  }\n  if (expected.text !== undefined && getOutputText(message) !== expected.text) {\n    return false;\n  }\n  if (expected.toolCalls !== undefined) {\n    const calls = message.tool_calls ?? [];\n    if (calls.length !== expected.toolCalls.length) {\n      return false;\n    }\n    for (let i = 0; i < expected.toolCalls.length; i++) {\n      const call = calls[i];\n      const exp = expected.toolCalls[i]!;\n      if (call?.name !== exp.name || !equals(call.args, exp.args)) {\n        return false;\n      }\n    }\n  }\n  if (\n    expected.usage !== undefined &&\n    !matchesStreamUsage(message.usage_metadata, expected.usage, equals)\n  ) {\n    return false;\n  }\n  if (\n    expected.responseMetadata !== undefined &&\n    !matchesPartialObject(\n      message.response_metadata as Record<string, unknown>,\n      expected.responseMetadata,\n      equals\n    )\n  ) {\n    return false;\n  }\n  return true;\n}\n\nfunction invalidStreamResult(\n  received: unknown,\n  matcherName: string,\n  utils: ExpectExtendThis[\"utils\"]\n): ExpectationResult {\n  return {\n    pass: false,\n    message: () =>\n      `${utils.matcherHint(matcherName)}\\n\\n` +\n      `Expected: ChatModelStream (return value of model.streamEvents(\"Hello\"))\\n` +\n      `Received: ${utils.printReceived(received)}`,\n    actual: received,\n    expected: \"ChatModelStream\",\n  };\n}\n\nfunction applyNot(pass: boolean, isNot?: boolean): boolean {\n  return isNot ? !pass : pass;\n}\n\nexport async function toHaveStreamText(\n  this: ExpectExtendThis,\n  received: unknown,\n  expected: string\n): Promise<ExpectationResult> {\n  const { isNot, utils } = this;\n  const matcherName = \"toHaveStreamText\";\n\n  if (!isChatModelStream(received)) {\n    return invalidStreamResult(received, matcherName, utils);\n  }\n\n  const actual = await received.text;\n  const pass = applyNot(actual === expected, isNot);\n\n  return {\n    pass,\n    message: () =>\n      `${utils.matcherHint(matcherName, undefined, undefined, { isNot })}\\n\\n` +\n      `Expected stream text: ${isNot ? \"not \" : \"\"}${utils.printExpected(expected)}\\n` +\n      `Received stream text: ${utils.printReceived(actual)}`,\n    actual,\n    expected,\n  };\n}\n\nexport async function toHaveStreamReasoning(\n  this: ExpectExtendThis,\n  received: unknown,\n  expected: string\n): Promise<ExpectationResult> {\n  const { isNot, utils } = this;\n  const matcherName = \"toHaveStreamReasoning\";\n\n  if (!isChatModelStream(received)) {\n    return invalidStreamResult(received, matcherName, utils);\n  }\n\n  const actual = await received.reasoning;\n  const pass = applyNot(actual === expected, isNot);\n\n  return {\n    pass,\n    message: () =>\n      `${utils.matcherHint(matcherName, undefined, undefined, { isNot })}\\n\\n` +\n      `Expected stream reasoning: ${isNot ? \"not \" : \"\"}${utils.printExpected(expected)}\\n` +\n      `Received stream reasoning: ${utils.printReceived(actual)}`,\n    actual,\n    expected,\n  };\n}\n\nexport type StreamToolCallExpectation = {\n  name: string;\n  args: Record<string, unknown>;\n};\n\nexport type StreamUsageExpectation = {\n  input_tokens?: number;\n  output_tokens?: number;\n  total_tokens?: number;\n  input_token_details?: Record<string, unknown>;\n  output_token_details?: Record<string, unknown>;\n};\n\nexport type StreamOutputExpectation = {\n  id?: string;\n  text?: string;\n  toolCalls?: StreamToolCallExpectation[];\n  usage?: StreamUsageExpectation;\n  responseMetadata?: Record<string, unknown>;\n};\n\nexport async function toHaveStreamToolCalls(\n  this: ExpectExtendThis,\n  received: unknown,\n  expected: StreamToolCallExpectation[]\n): Promise<ExpectationResult> {\n  const { isNot, utils } = this;\n  const matcherName = \"toHaveStreamToolCalls\";\n\n  if (!isChatModelStream(received)) {\n    return invalidStreamResult(received, matcherName, utils);\n  }\n\n  const actual = await received.toolCalls;\n\n  let pass =\n    actual.length === expected.length &&\n    expected.every((exp, i) => {\n      const call = actual[i];\n      return call?.name === exp.name && this.equals(call.args, exp.args);\n    });\n  pass = applyNot(pass, isNot);\n\n  return {\n    pass,\n    message: () =>\n      `${utils.matcherHint(matcherName, undefined, undefined, { isNot })}\\n\\n` +\n      `Expected stream tool calls: ${utils.printExpected(expected)}\\n` +\n      `Received stream tool calls: ${utils.printReceived(\n        actual.map((tc) => ({ name: tc.name, args: tc.args }))\n      )}`,\n    actual: actual.map((tc) => ({ name: tc.name, args: tc.args })),\n    expected,\n  };\n}\n\nexport async function toHaveStreamUsage(\n  this: ExpectExtendThis,\n  received: unknown,\n  expected: StreamUsageExpectation\n): Promise<ExpectationResult> {\n  const { isNot, utils } = this;\n  const matcherName = \"toHaveStreamUsage\";\n\n  if (!isChatModelStream(received)) {\n    return invalidStreamResult(received, matcherName, utils);\n  }\n\n  const actual = await received.usage;\n  const pass = applyNot(\n    matchesStreamUsage(actual, expected, this.equals),\n    isNot\n  );\n\n  return {\n    pass,\n    message: () =>\n      `${utils.matcherHint(matcherName, undefined, undefined, { isNot })}\\n\\n` +\n      `Expected stream usage: ${utils.printExpected(expected)}\\n` +\n      `Received stream usage: ${utils.printReceived(actual)}`,\n    actual,\n    expected,\n  };\n}\n\nexport async function toHaveStreamOutput(\n  this: ExpectExtendThis,\n  received: unknown,\n  expected: StreamOutputExpectation\n): Promise<ExpectationResult> {\n  const { isNot, utils } = this;\n  const matcherName = \"toHaveStreamOutput\";\n\n  if (!isChatModelStream(received)) {\n    return invalidStreamResult(received, matcherName, utils);\n  }\n\n  const message = await received.output;\n  const pass = applyNot(\n    matchesStreamOutput(message, expected, this.equals),\n    isNot\n  );\n\n  return {\n    pass,\n    message: () =>\n      `${utils.matcherHint(matcherName, undefined, undefined, { isNot })}\\n\\n` +\n      `Expected stream output: ${utils.printExpected(expected)}\\n` +\n      `Received stream output: ${utils.printReceived({\n        id: message.id,\n        text: getOutputText(message),\n        tool_calls: message.tool_calls?.map((tc) => ({\n          name: tc.name,\n          args: tc.args,\n        })),\n        usage_metadata: message.usage_metadata,\n        response_metadata: message.response_metadata,\n      })}`,\n    actual: message,\n    expected,\n  };\n}\n\n/** Stream matchers for `expect.extend()`. */\nexport const streamMatchers = {\n  toHaveStreamText,\n  toHaveStreamReasoning,\n  toHaveStreamToolCalls,\n  toHaveStreamUsage,\n  toHaveStreamOutput,\n};\n\n/**\n * Custom assertion helpers for values returned by `BaseChatModel.streamEvents()`.\n *\n * These matchers consume the stream lazily through the corresponding\n * `ChatModelStream` promise-backed properties.\n *\n * @typeParam R - The assertion return type provided by the test framework.\n */\nexport interface StreamMatchers<R = unknown> {\n  /**\n   * Asserts that the stream resolves to the expected concatenated text.\n   *\n   * @param expected - The exact text expected from `ChatModelStream.text`.\n   */\n  toHaveStreamText(expected: string): R;\n\n  /**\n   * Asserts that the stream resolves to the expected concatenated reasoning text.\n   *\n   * @param expected - The exact reasoning text expected from `ChatModelStream.reasoning`.\n   */\n  toHaveStreamReasoning(expected: string): R;\n\n  /**\n   * Asserts that the stream resolves to the expected ordered tool calls.\n   *\n   * @param expected - Tool call names and arguments expected from `ChatModelStream.toolCalls`.\n   */\n  toHaveStreamToolCalls(expected: StreamToolCallExpectation[]): R;\n\n  /**\n   * Asserts that the stream resolves to usage metadata matching the expected fields.\n   *\n   * @param expected - A partial usage metadata object expected from `ChatModelStream.usage`.\n   */\n  toHaveStreamUsage(expected: StreamUsageExpectation): R;\n\n  /**\n   * Asserts that the final streamed output message matches the expected fields.\n   *\n   * @param expected - A partial output expectation checked against `ChatModelStream.output`.\n   */\n  toHaveStreamOutput(expected: StreamOutputExpectation): R;\n}\n"],"mappings":"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"}