{"version":3,"sources":["../../../src/voice/testing/fake_llm.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2026 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport type { ChatContext } from '../../llm/chat_context.js';\nimport { FunctionCall } from '../../llm/chat_context.js';\nimport { LLMStream as BaseLLMStream, LLM, type LLMStream } from '../../llm/llm.js';\nimport type { ToolChoice, ToolContextLike } from '../../llm/tool_context.js';\nimport { type APIConnectOptions, DEFAULT_API_CONNECT_OPTIONS } from '../../types.js';\nimport { delay } from '../../utils.js';\n\nexport interface FakeLLMResponse {\n  input: string;\n  type?: 'llm';\n  content?: string;\n  ttft?: number;\n  duration?: number;\n  toolCalls?: Array<{ name: string; args: Record<string, unknown> }>;\n}\n\nexport class FakeLLM extends LLM {\n  private readonly responseMap = new Map<string, FakeLLMResponse>();\n\n  constructor(responses: FakeLLMResponse[] = []) {\n    super();\n    for (const response of responses) {\n      this.responseMap.set(response.input, {\n        type: 'llm',\n        ttft: 0,\n        duration: 0,\n        ...response,\n      });\n    }\n  }\n\n  label(): string {\n    return 'fake-llm';\n  }\n\n  chat({\n    chatCtx,\n    toolCtx,\n    connOptions = DEFAULT_API_CONNECT_OPTIONS,\n  }: {\n    chatCtx: ChatContext;\n    toolCtx?: ToolContextLike;\n    connOptions?: APIConnectOptions;\n    parallelToolCalls?: boolean;\n    toolChoice?: ToolChoice;\n    extraKwargs?: Record<string, unknown>;\n  }): LLMStream {\n    return new FakeLLMStream(this, {\n      chatCtx,\n      toolCtx,\n      connOptions,\n    });\n  }\n\n  lookup(input: string): FakeLLMResponse | undefined {\n    return this.responseMap.get(input);\n  }\n}\n\nclass FakeLLMStream extends BaseLLMStream {\n  private readonly fake: FakeLLM;\n\n  constructor(\n    fake: FakeLLM,\n    params: { chatCtx: ChatContext; toolCtx?: ToolContextLike; connOptions: APIConnectOptions },\n  ) {\n    super(fake, params);\n    this.fake = fake;\n  }\n\n  protected async run(): Promise<void> {\n    const input = this.getInputText();\n    if (input === undefined) {\n      return;\n    }\n    const decision = this.fake.lookup(input);\n    if (!decision) {\n      return;\n    }\n\n    const startedAt = Date.now();\n    if ((decision.ttft ?? 0) > 0) {\n      await delay(decision.ttft!);\n    }\n\n    const content = decision.content ?? '';\n    const chunkSize = 3;\n    for (let i = 0; i < content.length; i += chunkSize) {\n      this.queue.put({\n        id: 'fake',\n        delta: { role: 'assistant', content: content.slice(i, i + chunkSize) },\n      });\n    }\n\n    if (decision.toolCalls && decision.toolCalls.length > 0) {\n      const calls = decision.toolCalls.map((tc, index) =>\n        FunctionCall.create({\n          callId: `fake_call_${index}`,\n          name: tc.name,\n          args: JSON.stringify(tc.args),\n        }),\n      );\n      this.queue.put({\n        id: 'fake',\n        delta: { role: 'assistant', toolCalls: calls },\n      });\n    }\n\n    const elapsed = Date.now() - startedAt;\n    const waitMs = Math.max(0, (decision.duration ?? 0) - elapsed);\n    if (waitMs > 0) {\n      await delay(waitMs);\n    }\n  }\n\n  private getInputText(): string | undefined {\n    const items = this.chatCtx.items;\n    if (items.length === 0) {\n      throw new Error('No input text found');\n    }\n\n    for (const item of items) {\n      if (item.type === 'message' && item.role === 'system') {\n        const text = item.textContent ?? '';\n        const lines = text.split('\\n');\n        const tail = lines[lines.length - 1] ?? '';\n        if (lines.length > 1 && tail.startsWith('instructions:')) {\n          return tail;\n        }\n      }\n    }\n\n    const last = items[items.length - 1]!;\n    if (last.type === 'message') {\n      return last.role === 'user' ? last.textContent ?? '' : undefined;\n    }\n    if (last.type === 'function_call_output') return last.output;\n    if (\n      last.type === 'function_call' ||\n      last.type === 'agent_handoff' ||\n      last.type === 'agent_config_update'\n    ) {\n      return undefined;\n    }\n  }\n}\n"],"mappings":"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","names":["BaseLLMStream"]}