{"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\ninterface FakeModelState {\n  callIndex: number;\n  calls: FakeModelCall[];\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 _state: FakeModelState = { callIndex: 0, calls: [] };\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._state.calls;\n  }\n\n  /**\n   * The number of times this model has been invoked.\n   */\n  get callCount(): number {\n    return this._state.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._state = this._state;\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._state.calls.push({ messages: [...messages], options });\n\n    const currentCallIndex = this._state.callIndex;\n    this._state.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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"}