{"version":3,"sources":["../../src/testing/mock-model.ts"],"sourcesContent":["/**\n * Copyright 2026 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type { HasRegistry, Registry } from '@genkit-ai/core/registry';\nimport { Message } from '../message.mjs';\nimport {\n  defineModel,\n  type GenerateRequest,\n  type GenerateResponseChunkData,\n  type GenerateResponseData,\n  type ModelAction,\n  type ModelInfo,\n  type Part,\n} from '../model.mjs';\n\n/**\n * A streamed chunk a mock model may emit. A bare string is shorthand for a\n * single text part (`{ content: [{ text }] }`).\n */\nexport type MockChunk = string | GenerateResponseChunkData;\n\n/** Context passed to a {@link MockModelOptions.respond} callback. */\nexport interface MockContext {\n  /**\n   * Emit a streamed chunk. A no-op unless the caller used `generateStream` /\n   * `{ onChunk }`. A string is shorthand for a text part.\n   */\n  sendChunk: (chunk: MockChunk) => void;\n}\n\n/**\n * What a {@link MockModelOptions.respond} callback may return. From lightest to\n * fullest control:\n * - a `string` — shorthand for a single text response;\n * - an object with any of `text` / `toolRequests` / `content` — the common\n *   cases, assembled into a well-formed response for you;\n * - a full {@link GenerateResponseData} (anything with a `message`) — used as-is.\n */\nexport interface MockResponseObject {\n  /** Text content of the model message. */\n  text?: string;\n  /** Tool/function calls to emit, by tool name. */\n  toolRequests?: Array<{ name: string; input?: unknown; ref?: string }>;\n  /** Escape hatch: raw message parts, prepended before `text`/`toolRequests`. */\n  content?: Part[];\n  finishReason?: GenerateResponseData['finishReason'];\n  usage?: GenerateResponseData['usage'];\n}\n\nexport type MockResponse = string | MockResponseObject | GenerateResponseData;\n\n/** A `respond` callback: given the request, returns the response to emit. */\nexport type MockRespondFn = (\n  request: GenerateRequest,\n  context: MockContext\n) => MockResponse | Promise<MockResponse>;\n\n/** Options for {@link mockModel}. */\nexport interface MockModelOptions {\n  /** Registered model name. Defaults to `'mockModel'`. */\n  name?: string;\n  /**\n   * Model metadata (e.g. `supports`, `versions`).\n   *\n   * `supports.constrained` defaults to `'all'` (native constrained generation),\n   * so a `generate` with an `output.schema` reaches `respond` with that schema\n   * on `request.output.schema`. Pass `supports: { constrained: 'none' }` to\n   * instead exercise the framework's simulated path, where the schema is\n   * injected into the prompt (and thus visible in `lastRequestText`) and\n   * stripped from what `respond` sees.\n   */\n  info?: ModelInfo;\n  /**\n   * What to respond with on each `generate` call. Defaults to empty text.\n   *\n   * - A **single response** (string / object) is returned on every call.\n   * - A **callback** `(request, { sendChunk }) => response` is invoked once per\n   *   call, so you can branch on request history (for tool loops) and stream via\n   *   `sendChunk`.\n   * - An **array** is a queue consumed one item per call, with the last item\n   *   repeating once exhausted — handy for scripted multi-turn tests. A queued\n   *   `Error` is thrown when reached, to inject a failure on a given turn.\n   *   (Queued items are static, so streaming needs the callback form.)\n   *\n   * ```ts\n   * mockModel(ai, { respond: 'always this' });\n   * mockModel(ai, { respond: ['first', 'second'] });\n   * mockModel(ai, { respond: ['ok', new Error('rate limited')] });\n   * ```\n   */\n  respond?: MockRespond;\n}\n\n/**\n * Anything accepted as respond behavior: a single {@link MockResponse}, a\n * per-call callback, or a queue of responses/errors. See\n * {@link MockModelOptions.respond}.\n */\nexport type MockRespond =\n  | MockRespondFn\n  | MockResponse\n  | Array<MockResponse | Error>;\n\n/**\n * A mock model with typed inspection of the calls it received. The extra\n * members are read-only views over the recorded calls.\n */\nexport type MockModel = ModelAction & {\n  /** The request from the most recent call, or `undefined` if never called. */\n  readonly lastRequest: GenerateRequest | undefined;\n  /**\n   * The final message of the most recent request, wrapped as a {@link Message}\n   * so you can read it the same way you read a response — `.text`, `.media`,\n   * `.toolRequests`, etc. `undefined` if the model was never called.\n   *\n   * ```ts\n   * assert.match(model.lastRequestMessage!.text, /Summarize: long text/);\n   * ```\n   */\n  readonly lastRequestMessage: Message | undefined;\n  /**\n   * The full assembled conversation of the most recent request, flattened to a\n   * single string (system + every message, in order). Use it for prompt-\n   * assembly assertions on any mock — including ones returning structured\n   * output, where {@link echoModel} can't be used. `undefined` if never called.\n   *\n   * ```ts\n   * assert.match(model.lastRequestText!, /system: Be terse/);\n   * ```\n   */\n  readonly lastRequestText: string | undefined;\n  /**\n   * The tool results fed back to the model in the most recent request, in order.\n   * Use it to assert which tools ran and what they returned, without digging\n   * through message content yourself. Empty if the model was never called or saw\n   * no tool results.\n   *\n   * ```ts\n   * assert.deepStrictEqual(model.toolResponses.map((t) => t.name), ['lookup']);\n   * ```\n   */\n  readonly toolResponses: Array<{\n    name: string;\n    ref?: string;\n    output: unknown;\n  }>;\n  /** Every request this model received, oldest first. */\n  readonly requests: GenerateRequest[];\n  /** How many times this model was called. */\n  readonly requestCount: number;\n  /**\n   * Replaces the respond behavior for subsequent calls. Accepts everything\n   * {@link MockModelOptions.respond} does; an array re-arms as a fresh queue.\n   * Recorded history is untouched — use {@link MockModel.reset} for that.\n   *\n   * Together with `reset()` this supports the register-once idiom: define the\n   * mock once per test file, then give each test its own behavior.\n   *\n   * ```ts\n   * const model = mockModel(ai, { name: 'menuModel' });\n   * beforeEach(() => model.reset());\n   *\n   * test('...', async () => {\n   *   model.respondWith({ text: 'scripted' });\n   *   // ...\n   * });\n   * ```\n   */\n  respondWith(respond: MockRespond): void;\n  /**\n   * Clears recorded history (`requests`, `requestCount`, …) and restores the\n   * respond behavior given at construction, re-arming a queued `respond` from\n   * its first item. Call it in `beforeEach` so tests sharing a mock stay\n   * order-independent.\n   */\n  reset(): void;\n};\n\nfunction resolveRegistry(registry: Registry | HasRegistry): Registry {\n  return (registry as HasRegistry).registry ?? (registry as Registry);\n}\n\n/**\n * Snapshots a request so later mutation can't alter recorded history. Prefers a\n * deep `structuredClone`, but falls back to a message/part-level copy when the\n * request carries non-serializable values (e.g. a function or class instance in\n * `config`), which would otherwise throw a `DataCloneError`.\n */\nfunction cloneRequest(request: GenerateRequest): GenerateRequest {\n  try {\n    return structuredClone(request);\n  } catch {\n    return {\n      ...request,\n      messages: request.messages.map((m) => ({\n        ...m,\n        content: m.content.map((c) => ({ ...c })),\n      })),\n    };\n  }\n}\n\nfunction toChunkData(chunk: MockChunk): GenerateResponseChunkData {\n  return typeof chunk === 'string' ? { content: [{ text: chunk }] } : chunk;\n}\n\n/**\n * Renders a single request part to text for {@link renderRequestText}. Non-text\n * parts (media, tool requests/responses, reasoning, resource, data, custom) are\n * rendered as a labelled placeholder rather than silently dropped.\n */\nfunction renderPart(part: Part): string {\n  if (part.text !== undefined) return part.text;\n  if (part.media) {\n    const type = part.media.contentType ? ` ${part.media.contentType}` : '';\n    return `[media${type}: ${part.media.url}]`;\n  }\n  if (part.toolRequest) {\n    const { name, input } = part.toolRequest;\n    return `[toolRequest ${name}(${JSON.stringify(input)})]`;\n  }\n  if (part.toolResponse) {\n    const { name, output } = part.toolResponse;\n    return `[toolResponse ${name}: ${JSON.stringify(output)}]`;\n  }\n  if (part.reasoning !== undefined) return `[reasoning: ${part.reasoning}]`;\n  if (part.resource) return `[resource: ${part.resource.uri}]`;\n  if (part.data !== undefined) return `[data: ${JSON.stringify(part.data)}]`;\n  if (part.custom !== undefined)\n    return `[custom: ${JSON.stringify(part.custom)}]`;\n  return '';\n}\n\n/**\n * Flattens a request's full message list to text — system and tool messages are\n * prefixed with their role; `user`/`model` are not. Messages are newline-\n * separated so adjacent messages' text can't fuse into a single token (which\n * would silently break boundary-spanning assertions). This is the assembled\n * conversation the model would have seen, used by both {@link echoModel} and\n * {@link MockModel.lastRequestText}.\n */\nfunction renderRequestText(request: GenerateRequest): string {\n  return request.messages\n    .map(\n      (m) =>\n        (m.role === 'user' || m.role === 'model' ? '' : `${m.role}: `) +\n        m.content.map(renderPart).join('')\n    )\n    .join('\\n');\n}\n\n/**\n * Normalizes the `respond` option into a single callback. A single response is\n * returned on every call; an array becomes a queue consumed one item per call,\n * with the last item repeating once exhausted; a queued `Error` is thrown when\n * reached.\n */\nfunction toRespondFn(respond: MockRespond | undefined): MockRespondFn {\n  if (respond === undefined) {\n    return () => ({ text: '' });\n  }\n  if (typeof respond === 'function') {\n    return respond;\n  }\n  const queue = Array.isArray(respond) ? respond : [respond];\n  if (queue.length === 0) {\n    return () => ({ text: '' });\n  }\n  let i = 0;\n  return () => {\n    const item = queue[Math.min(i, queue.length - 1)];\n    i++;\n    if (item instanceof Error) throw item;\n    return item;\n  };\n}\n\nfunction toResponseData(response: MockResponse): GenerateResponseData {\n  // A `respond` that streams but returns nothing (void) yields an empty message\n  // rather than throwing on the `'message' in response` check below. Checked\n  // against null/undefined only, so `respond: ''` still means empty *text*.\n  if (response === undefined || response === null) {\n    return { message: { role: 'model', content: [] }, finishReason: 'stop' };\n  }\n  if (typeof response === 'string') {\n    return {\n      message: { role: 'model', content: [{ text: response }] },\n      finishReason: 'stop',\n    };\n  }\n  if ('message' in response && response.message) {\n    const data = response as GenerateResponseData;\n    // Default finishReason without letting an explicit `undefined` clobber it.\n    return { ...data, finishReason: data.finishReason ?? 'stop' };\n  }\n  const obj = response as MockResponseObject;\n  const content: Part[] = [...(obj.content ?? [])];\n  if (obj.text !== undefined) {\n    content.push({ text: obj.text });\n  }\n  for (const tool of obj.toolRequests ?? []) {\n    content.push({\n      toolRequest: { name: tool.name, input: tool.input, ref: tool.ref },\n    });\n  }\n  return {\n    message: { role: 'model', content },\n    finishReason: obj.finishReason ?? 'stop',\n    usage: obj.usage,\n  };\n}\n\n/**\n * Defines a programmable mock model for tests. Drive each call's response with\n * `respond`, and inspect what the model was called with via `lastRequest` /\n * `requests` / `requestCount`.\n *\n * ```ts\n * const model = mockModel(ai, { respond: () => ({ text: 'a summary' }) });\n * const out = (await ai.generate({ model, prompt: 'Summarize: ...' })).text;\n * assert.match(model.lastRequest!.messages.at(-1)!.content[0].text!, /Summarize/);\n * ```\n *\n * Streaming and tool calls are first-class:\n *\n * ```ts\n * mockModel(ai, {\n *   respond: (req, { sendChunk }) => {\n *     sendChunk('hel');\n *     sendChunk('lo');\n *     return { text: 'hello' };\n *   },\n * });\n *\n * mockModel(ai, {\n *   respond: () => ({ toolRequests: [{ name: 'lookup', input: { id: 1 } }] }),\n * });\n * ```\n *\n * For structured output, the mock defaults to native constrained generation\n * (`supports.constrained: 'all'`), so `respond` sees `request.output.schema`\n * and no schema blob is injected into the prompt. Override with\n * `supports: { constrained: 'none' }` to test the simulated path.\n *\n * @param registry a `Genkit` instance (or anything holding a `Registry`).\n * @param options model name, metadata, and the `respond` callback.\n */\nexport function mockModel(\n  registry: Registry | HasRegistry,\n  options: MockModelOptions = {}\n): MockModel {\n  const requests: GenerateRequest[] = [];\n  let respond = toRespondFn(options.respond);\n\n  const model = defineModel(\n    resolveRegistry(registry),\n    {\n      apiVersion: 'v2',\n      name: options.name ?? 'mockModel',\n      // Forward only the metadata fields defineModel accepts; ModelInfo's\n      // `configSchema`/`stage` aren't part of DefineModelOptions.\n      versions: options.info?.versions,\n      label: options.info?.label,\n      // Default to native constrained generation (like modern provider models),\n      // so a structured-output request reaches `respond` with `output.schema`\n      // intact instead of the framework injecting a schema blob into the prompt\n      // and stripping it. Spread last so callers can opt out with\n      // `supports: { constrained: 'none' }` to exercise the simulated path.\n      supports: { constrained: 'all', ...options.info?.supports },\n    },\n    async (request, { sendChunk }) => {\n      // Snapshot so later mutation of the request can't alter recorded history.\n      requests.push(cloneRequest(request));\n      const context: MockContext = {\n        sendChunk: (chunk) => sendChunk?.(toChunkData(chunk)),\n      };\n      return toResponseData(await respond(request, context));\n    }\n  ) as MockModel;\n\n  Object.defineProperties(model, {\n    // Return clones so callers can't mutate recorded history through a view.\n    requests: { get: () => requests.map((r) => cloneRequest(r)) },\n    lastRequest: {\n      get: () => {\n        const last = requests[requests.length - 1];\n        return last ? cloneRequest(last) : undefined;\n      },\n    },\n    lastRequestMessage: {\n      get: () => {\n        const last = requests[requests.length - 1]?.messages.at(-1);\n        return last ? new Message(last) : undefined;\n      },\n    },\n    lastRequestText: {\n      get: () => {\n        const last = requests[requests.length - 1];\n        return last ? renderRequestText(last) : undefined;\n      },\n    },\n    toolResponses: {\n      get: () =>\n        (requests[requests.length - 1]?.messages ?? [])\n          .flatMap((m) => m.content)\n          .filter((p) => p.toolResponse)\n          .map((p) => ({\n            name: p.toolResponse!.name,\n            ref: p.toolResponse!.ref,\n            output: p.toolResponse!.output,\n          })),\n    },\n    requestCount: { get: () => requests.length },\n    respondWith: {\n      value: (next: MockRespond) => {\n        respond = toRespondFn(next);\n      },\n    },\n    reset: {\n      value: () => {\n        requests.length = 0;\n        respond = toRespondFn(options.respond);\n      },\n    },\n  });\n  return model;\n}\n\n/** Options for {@link echoModel}. */\nexport interface EchoModelOptions {\n  /** Registered model name. Defaults to `'echoModel'`. */\n  name?: string;\n  /** Model metadata. */\n  info?: ModelInfo;\n}\n\n/**\n * A {@link mockModel} preset for *text* paths: a zero-config model that echoes\n * the rendered request back as text, for asserting prompt and message assembly\n * (what the model *would have seen*). Supports the same inspection members as\n * {@link mockModel}.\n *\n * ```ts\n * const model = echoModel(ai);\n * const res = await ai.generate({ model, system: 'Be terse', prompt: 'hi' });\n * assert.match(res.text, /system: Be terse/);\n * ```\n *\n * Because it returns text, it can't satisfy a structured **output schema** —\n * Genkit derives `output` by parsing the response text and validating it, which\n * prose can't pass. If the request carries an output schema, `echoModel` throws\n * an explanatory error. For structured-output paths, use {@link mockModel} with\n * a conforming response and assert assembly via {@link MockModel.lastRequestText}\n * / {@link MockModel.lastRequest} instead.\n *\n * @param registry a `Genkit` instance (or anything holding a `Registry`).\n * @param options model name and metadata.\n */\nexport function echoModel(\n  registry: Registry | HasRegistry,\n  options: EchoModelOptions = {}\n): MockModel {\n  return mockModel(registry, {\n    name: options.name ?? 'echoModel',\n    // Declare native constrained support so the framework hands the output\n    // schema to the model directly (in `request.output.schema`) rather than\n    // injecting it as prompt text — that lets the guard below detect it\n    // reliably, and keeps the echo free of framework-injected schema blobs.\n    info: {\n      ...options.info,\n      supports: { ...options.info?.supports, constrained: 'all' },\n    },\n    respond: (request) => {\n      if (request.output?.schema) {\n        throw new Error(\n          \"echoModel returns text and can't satisfy an output schema: this \" +\n            'request asks for structured output. Either move `output: { schema }` ' +\n            'to the generate()/flow call site so the prompt stays text-only, or ' +\n            'use mockModel(...) with a conforming response and assert prompt ' +\n            'assembly via model.lastRequestText / model.lastRequest.'\n        );\n      }\n      return {\n        content: [\n          { text: 'Echo: ' + renderRequestText(request) },\n          { text: '; config: ' + JSON.stringify(request.config) },\n        ],\n      };\n    },\n  });\n}\n"],"mappings":"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","names":[]}