UNPKG

@langchain/core

Version:
141 lines (140 loc) 6 kB
//#region src/utils/testing/stream.ts function isChatModelStream(received) { if (received == null || typeof received !== "object") return false; const stream = received; return typeof stream.text !== "undefined" && typeof stream.toolCalls !== "undefined" && typeof stream.reasoning !== "undefined" && typeof stream.usage !== "undefined" && typeof stream.output !== "undefined" && typeof stream[Symbol.asyncIterator] === "function"; } function matchesPartialObject(actual, expected, equals) { if (actual == null) return false; return Object.entries(expected).every(([key, value]) => equals(actual[key], value)); } function matchesStreamUsage(actual, expected, equals) { if (actual == null) return false; return matchesPartialObject(actual, expected, equals); } function getOutputText(message) { return message.content.find((block) => block.type === "text")?.text; } function matchesStreamOutput(message, expected, equals) { if (expected.id !== void 0 && message.id !== expected.id) return false; if (expected.text !== void 0 && getOutputText(message) !== expected.text) return false; if (expected.toolCalls !== void 0) { const calls = message.tool_calls ?? []; if (calls.length !== expected.toolCalls.length) return false; for (let i = 0; i < expected.toolCalls.length; i++) { const call = calls[i]; const exp = expected.toolCalls[i]; if (call?.name !== exp.name || !equals(call.args, exp.args)) return false; } } if (expected.usage !== void 0 && !matchesStreamUsage(message.usage_metadata, expected.usage, equals)) return false; if (expected.responseMetadata !== void 0 && !matchesPartialObject(message.response_metadata, expected.responseMetadata, equals)) return false; return true; } function invalidStreamResult(received, matcherName, utils) { return { pass: false, message: () => `${utils.matcherHint(matcherName)}\n\nExpected: ChatModelStream (return value of model.streamEvents("Hello"))\nReceived: ${utils.printReceived(received)}`, actual: received, expected: "ChatModelStream" }; } function applyNot(pass, isNot) { return isNot ? !pass : pass; } async function toHaveStreamText(received, expected) { const { isNot, utils } = this; const matcherName = "toHaveStreamText"; if (!isChatModelStream(received)) return invalidStreamResult(received, matcherName, utils); const actual = await received.text; return { pass: applyNot(actual === expected, isNot), message: () => `${utils.matcherHint(matcherName, void 0, void 0, { isNot })}\n\nExpected stream text: ${isNot ? "not " : ""}${utils.printExpected(expected)}\nReceived stream text: ${utils.printReceived(actual)}`, actual, expected }; } async function toHaveStreamReasoning(received, expected) { const { isNot, utils } = this; const matcherName = "toHaveStreamReasoning"; if (!isChatModelStream(received)) return invalidStreamResult(received, matcherName, utils); const actual = await received.reasoning; return { pass: applyNot(actual === expected, isNot), message: () => `${utils.matcherHint(matcherName, void 0, void 0, { isNot })}\n\nExpected stream reasoning: ${isNot ? "not " : ""}${utils.printExpected(expected)}\nReceived stream reasoning: ${utils.printReceived(actual)}`, actual, expected }; } async function toHaveStreamToolCalls(received, expected) { const { isNot, utils } = this; const matcherName = "toHaveStreamToolCalls"; if (!isChatModelStream(received)) return invalidStreamResult(received, matcherName, utils); const actual = await received.toolCalls; let pass = actual.length === expected.length && expected.every((exp, i) => { const call = actual[i]; return call?.name === exp.name && this.equals(call.args, exp.args); }); pass = applyNot(pass, isNot); return { pass, message: () => `${utils.matcherHint(matcherName, void 0, void 0, { isNot })}\n\nExpected stream tool calls: ${utils.printExpected(expected)}\nReceived stream tool calls: ${utils.printReceived(actual.map((tc) => ({ name: tc.name, args: tc.args })))}`, actual: actual.map((tc) => ({ name: tc.name, args: tc.args })), expected }; } async function toHaveStreamUsage(received, expected) { const { isNot, utils } = this; const matcherName = "toHaveStreamUsage"; if (!isChatModelStream(received)) return invalidStreamResult(received, matcherName, utils); const actual = await received.usage; return { pass: applyNot(matchesStreamUsage(actual, expected, this.equals), isNot), message: () => `${utils.matcherHint(matcherName, void 0, void 0, { isNot })}\n\nExpected stream usage: ${utils.printExpected(expected)}\nReceived stream usage: ${utils.printReceived(actual)}`, actual, expected }; } async function toHaveStreamOutput(received, expected) { const { isNot, utils } = this; const matcherName = "toHaveStreamOutput"; if (!isChatModelStream(received)) return invalidStreamResult(received, matcherName, utils); const message = await received.output; return { pass: applyNot(matchesStreamOutput(message, expected, this.equals), isNot), message: () => `${utils.matcherHint(matcherName, void 0, void 0, { isNot })}\n\nExpected stream output: ${utils.printExpected(expected)}\nReceived stream output: ${utils.printReceived({ id: message.id, text: getOutputText(message), tool_calls: message.tool_calls?.map((tc) => ({ name: tc.name, args: tc.args })), usage_metadata: message.usage_metadata, response_metadata: message.response_metadata })}`, actual: message, expected }; } /** Stream matchers for `expect.extend()`. */ const streamMatchers = { toHaveStreamText, toHaveStreamReasoning, toHaveStreamToolCalls, toHaveStreamUsage, toHaveStreamOutput }; //#endregion exports.streamMatchers = streamMatchers; exports.toHaveStreamOutput = toHaveStreamOutput; exports.toHaveStreamReasoning = toHaveStreamReasoning; exports.toHaveStreamText = toHaveStreamText; exports.toHaveStreamToolCalls = toHaveStreamToolCalls; exports.toHaveStreamUsage = toHaveStreamUsage; //# sourceMappingURL=stream.cjs.map