{"version":3,"file":"fake_model_builder.cjs","names":["BaseChatModel","BaseMessage","RunnableLambda","AIMessage"],"sources":["../../src/testing/fake_model_builder.ts"],"sourcesContent":["/* oxlint-disable @typescript-eslint/no-explicit-any */\nimport { CallbackManagerForLLMRun } from \"../callbacks/manager.js\";\nimport {\n  BaseChatModel,\n  BaseChatModelCallOptions,\n} from \"../language_models/chat_models.js\";\nimport {\n  BaseLanguageModelInput,\n  StructuredOutputMethodOptions,\n  StructuredOutputMethodParams,\n} from \"../language_models/base.js\";\nimport { BaseMessage, AIMessage } from \"../messages/index.js\";\nimport type { ToolCall } from \"../messages/tool.js\";\nimport type { ChatResult } from \"../outputs.js\";\nimport { Runnable, RunnableLambda } from \"../runnables/base.js\";\nimport { StructuredTool } from \"../tools/index.js\";\nimport type { InteropZodType } from \"../utils/types/zod.js\";\nimport type { ToolSpec } from \"../utils/testing/chat_models.js\";\n\ntype ResponseFactory = (messages: BaseMessage[]) => BaseMessage | Error;\n\ntype QueueEntry =\n  | { kind: \"message\"; message: BaseMessage }\n  | { kind: \"toolCalls\"; toolCalls: ToolCall[] }\n  | { kind: \"error\"; error: Error }\n  | { kind: \"factory\"; factory: ResponseFactory };\n\ninterface FakeModelCall {\n  messages: BaseMessage[];\n  options: any;\n}\n\nfunction deriveContent(messages: BaseMessage[]): string {\n  return messages\n    .map((m) => m.text)\n    .filter(Boolean)\n    .join(\"-\");\n}\n\nlet idCounter = 0;\nfunction nextToolCallId(): string {\n  idCounter += 1;\n  return `fake_tc_${idCounter}`;\n}\n\n/**\n * A fake chat model for testing, created via {@link fakeModel}.\n *\n * Queue responses with `.respond()` and `.respondWithTools()`, then\n * pass the instance directly wherever a chat model is expected.\n * Responses are consumed in first-in-first-out order — one per `invoke()` call.\n * When all queued responses are consumed, further invocations throw.\n */\nexport class FakeBuiltModel extends BaseChatModel {\n  private queue: QueueEntry[] = [];\n\n  private _alwaysThrowError: Error | undefined;\n\n  private _structuredResponseValue: any;\n\n  private _tools: (StructuredTool | ToolSpec)[] = [];\n\n  private _callIndex = 0;\n\n  private _calls: FakeModelCall[] = [];\n\n  /**\n   * All invocations recorded by this model, in order.\n   * Each entry contains the `messages` array and `options` that were\n   * passed to `invoke()`.\n   */\n  get calls(): FakeModelCall[] {\n    return this._calls;\n  }\n\n  /**\n   * The number of times this model has been invoked.\n   */\n  get callCount(): number {\n    return this._calls.length;\n  }\n\n  constructor() {\n    super({});\n  }\n\n  _llmType(): string {\n    return \"fake-model-builder\";\n  }\n\n  _combineLLMOutput() {\n    return [];\n  }\n\n  /**\n   * Enqueue a response that the model will return on its next invocation.\n   * @param entry A {@link BaseMessage} to return, an `Error` to throw, or\n   *   a factory `(messages) => BaseMessage | Error` for dynamic responses.\n   * @returns `this`, for chaining.\n   */\n  respond(entry: BaseMessage | Error | ResponseFactory): this {\n    if (typeof entry === \"function\") {\n      this.queue.push({ kind: \"factory\", factory: entry });\n    } else if (BaseMessage.isInstance(entry)) {\n      this.queue.push({ kind: \"message\", message: entry });\n    } else {\n      this.queue.push({ kind: \"error\", error: entry });\n    }\n    return this;\n  }\n\n  /**\n   * Enqueue an {@link AIMessage} that carries the given tool calls.\n   * Content is derived from the input messages at invocation time.\n   * @param toolCalls Array of tool calls. Each entry needs `name` and\n   *   `args`; `id` is optional and auto-generated when omitted.\n   * @returns `this`, for chaining.\n   */\n  respondWithTools(\n    toolCalls: Array<{ name: string; args: Record<string, any>; id?: string }>\n  ): this {\n    this.queue.push({\n      kind: \"toolCalls\",\n      toolCalls: toolCalls.map((tc) => ({\n        name: tc.name,\n        args: tc.args,\n        id: tc.id ?? nextToolCallId(),\n        type: \"tool_call\" as const,\n      })),\n    });\n    return this;\n  }\n\n  /**\n   * Make every invocation throw the given error, regardless of the queue.\n   * @param error The error to throw.\n   * @returns `this`, for chaining.\n   */\n  alwaysThrow(error: Error): this {\n    this._alwaysThrowError = error;\n    return this;\n  }\n\n  /**\n   * Set the value that {@link withStructuredOutput} will resolve to.\n   * @param value The structured object to return.\n   * @returns `this`, for chaining.\n   */\n  structuredResponse(value: Record<string, any>): this {\n    this._structuredResponseValue = value;\n    return this;\n  }\n\n  /**\n   * Bind tools to the model. Returns a new model that shares the same\n   * response queue and call history.\n   * @param tools The tools to bind, as {@link StructuredTool} instances or\n   *   plain {@link ToolSpec} objects.\n   * @returns A new RunnableBinding with the tools bound.\n   */\n  bindTools(tools: (StructuredTool | ToolSpec)[]) {\n    const merged = [...this._tools, ...tools];\n    const next = new FakeBuiltModel();\n    next.queue = this.queue;\n    next._alwaysThrowError = this._alwaysThrowError;\n    next._structuredResponseValue = this._structuredResponseValue;\n    next._tools = merged;\n    next._calls = this._calls;\n    next._callIndex = this._callIndex;\n\n    return next.withConfig({} as BaseChatModelCallOptions);\n  }\n\n  /**\n   * Returns a {@link Runnable} that produces the {@link structuredResponse}\n   * value. The schema argument is accepted for compatibility but ignored.\n   * @param _params Schema or params (ignored).\n   * @param _config Options (ignored).\n   * @returns A Runnable that resolves to the structured response value.\n   */\n  withStructuredOutput<\n    RunOutput extends Record<string, any> = Record<string, any>,\n  >(\n    _params:\n      | StructuredOutputMethodParams<RunOutput, boolean>\n      | InteropZodType<RunOutput>\n      | Record<string, any>,\n    _config?: StructuredOutputMethodOptions<boolean>\n  ):\n    | Runnable<BaseLanguageModelInput, RunOutput>\n    | Runnable<\n        BaseLanguageModelInput,\n        { raw: BaseMessage; parsed: RunOutput }\n      > {\n    const { _structuredResponseValue } = this;\n    return RunnableLambda.from(async () => {\n      return _structuredResponseValue as RunOutput;\n    }) as Runnable;\n  }\n\n  async _generate(\n    messages: BaseMessage[],\n    options?: this[\"ParsedCallOptions\"],\n    _runManager?: CallbackManagerForLLMRun\n  ): Promise<ChatResult> {\n    this._calls.push({ messages: [...messages], options });\n\n    const currentCallIndex = this._callIndex;\n    this._callIndex += 1;\n\n    if (this._alwaysThrowError) {\n      throw this._alwaysThrowError;\n    }\n\n    const entry = this.queue[currentCallIndex];\n    if (!entry) {\n      throw new Error(\n        `FakeModel: no response queued for invocation ${currentCallIndex} (${this.queue.length} total queued).`\n      );\n    }\n\n    if (entry.kind === \"error\") {\n      throw entry.error;\n    }\n\n    if (entry.kind === \"factory\") {\n      const result = entry.factory(messages);\n      if (!BaseMessage.isInstance(result)) {\n        throw result;\n      }\n      return {\n        generations: [{ text: \"\", message: result }],\n      };\n    }\n\n    if (entry.kind === \"message\") {\n      return {\n        generations: [{ text: \"\", message: entry.message }],\n      };\n    }\n\n    const content = deriveContent(messages);\n    const message = new AIMessage({\n      content,\n      id: currentCallIndex.toString(),\n      tool_calls:\n        entry.toolCalls.length > 0\n          ? entry.toolCalls.map((tc) => ({\n              ...tc,\n              type: \"tool_call\" as const,\n            }))\n          : undefined,\n    });\n\n    return {\n      generations: [{ text: content, message }],\n      llmOutput: {},\n    };\n  }\n}\n\n/**\n * Creates a new {@link FakeBuiltModel} for testing.\n *\n * Returns a chainable builder — queue responses, then pass the model\n * anywhere a chat model is expected. Responses are consumed in FIFO\n * order, one per `invoke()` call.\n *\n * ## API summary\n *\n * | Method | Description |\n * | --- | --- |\n * | `fakeModel()` | Creates a new fake chat model. Returns a chainable builder. |\n * | `.respond(message)` | Queue an `AIMessage` (or any `BaseMessage`) to return on the next invocation. |\n * | `.respond(error)` | Queue an `Error` to throw on the next invocation. |\n * | `.respond(factory)` | Queue a function `(messages) => BaseMessage \\| Error` for dynamic responses. |\n * | `.respondWithTools(toolCalls)` | Shorthand for `.respond()` with tool calls. Each entry needs `name` and `args`; `id` is optional. |\n * | `.alwaysThrow(error)` | Make every invocation throw this error, regardless of the queue. |\n * | `.structuredResponse(value)` | Set the value returned by `.withStructuredOutput()`. |\n * | `.bindTools(tools)` | Bind tools to the model. Returns a `RunnableBinding` that shares the response queue and call recording. |\n * | `.withStructuredOutput(schema)` | Returns a runnable that produces the `.structuredResponse()` value. |\n * | `.calls` | Array of `{ messages, options }` for every invocation (read-only). |\n * | `.callCount` | Number of times the model has been invoked. |\n *\n * @example\n * ```typescript\n * const model = fakeModel()\n *   .respondWithTools([{ name: \"search\", args: { query: \"weather\" } }])\n *   .respond(new AIMessage(\"Sunny and warm.\"));\n *\n * const r1 = await model.invoke([new HumanMessage(\"What's the weather?\")]);\n * // r1.tool_calls[0].name === \"search\"\n *\n * const r2 = await model.invoke([new HumanMessage(\"Thanks\")]);\n * // r2.content === \"Sunny and warm.\"\n * ```\n */\nexport function fakeModel(): FakeBuiltModel {\n  return new FakeBuiltModel();\n}\n"],"mappings":"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"}