{"version":3,"sources":["../../../src/inference/interruption/interruption_failover.test.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2026 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\n//\n// Tests for interruption detection failover (transport error production + error-emission)\n// behavior. Ported from the Python `test_interruption_failover.py` suite.\n//\n// Covers, for the WebSocket-only transport (HTTP transport was dropped):\n//   - connection timeout                -> non-retryable APITimeoutError\n//   - connection 429                    -> non-retryable APIStatusError\n//   - cache-based inference timeout     -> non-retryable APIStatusError (408)\n// and that a non-retryable transport error surfaces as exactly one unrecoverable\n// InterruptionDetectionError (zero recoverable) through AudioRecognition's retry loop.\n//\n// Also covers in-place reconnect on updateOptions: it opens a fresh socket with the updated\n// settings without erroring the stream (so it never consumes a failover retry), while a genuine\n// reconnect failure still surfaces as a stream error.\nimport { AudioFrame } from '@livekit/rtc-node';\nimport { ReadableStream } from 'node:stream/web';\nimport { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';\nimport type { APIError } from '../../_exceptions.js';\nimport { APIStatusError, APITimeoutError } from '../../_exceptions.js';\nimport { ChatContext } from '../../llm/chat_context.js';\nimport { initializeLogger } from '../../log.js';\nimport { AudioRecognition, type RecognitionHooks } from '../../voice/audio_recognition.js';\nimport { MockWebSocket } from './_mock_ws.js';\nimport { apiConnectDefaults } from './defaults.js';\nimport { AdaptiveInterruptionDetector } from './interruption_detector.js';\nimport { InterruptionStreamBase, InterruptionStreamSentinel } from './interruption_stream.js';\n\n// ---------------------------------------------------------------------------\n// Mock `ws` so the WebSocket transport can be driven deterministically.\n// ---------------------------------------------------------------------------\n\nvi.mock('ws', async () => {\n  const { MockWebSocket } = await import('./_mock_ws.js');\n  return { default: MockWebSocket, WebSocket: MockWebSocket };\n});\n\ntype MockSocket = MockWebSocket;\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\ninitializeLogger({ pretty: false, level: 'silent' });\n\nconst sleep = (ms: number) => new Promise<void>((resolve) => setTimeout(resolve, ms));\n\nasync function waitForInstance(timeoutMs = 2000): Promise<MockSocket> {\n  const start = performance.now();\n  while (MockWebSocket.instances.length === 0) {\n    if (performance.now() - start > timeoutMs) {\n      throw new Error('WebSocket instance was never constructed');\n    }\n    await sleep(5);\n  }\n  return MockWebSocket.instances[MockWebSocket.instances.length - 1]!;\n}\n\n/** Wait until at least `n` sockets have been constructed and return the nth (1-indexed). */\nasync function waitForInstanceCount(n: number, timeoutMs = 2000): Promise<MockSocket> {\n  const start = performance.now();\n  while (MockWebSocket.instances.length < n) {\n    if (performance.now() - start > timeoutMs) {\n      throw new Error(`expected ${n} WebSocket instances, saw ${MockWebSocket.instances.length}`);\n    }\n    await sleep(5);\n  }\n  return MockWebSocket.instances[n - 1]!;\n}\n\nasync function waitFor(predicate: () => boolean, timeoutMs = 2000): Promise<void> {\n  const start = performance.now();\n  while (!predicate()) {\n    if (performance.now() - start > timeoutMs) {\n      throw new Error('condition not met within timeout');\n    }\n    await sleep(5);\n  }\n}\n\n/** Resolves to 'alive' if the stream has not errored/ended within `ms`. */\nfunction stillAlive(errPromise: Promise<unknown>, ms = 30): Promise<'alive' | unknown> {\n  return Promise.race([errPromise, sleep(ms).then(() => 'alive' as const)]);\n}\n\nfunction sessionCreateSettings(ws: MockSocket): Record<string, unknown> {\n  return JSON.parse(String(ws.sent[0])).settings;\n}\n\nfunction makeAudioFrame(numSamples = 1600, sampleRate = 16000): AudioFrame {\n  const data = new Int16Array(numSamples);\n  return new AudioFrame(data, sampleRate, 1, numSamples);\n}\n\nfunction createDetector(opts: { inferenceTimeout?: number } = {}): AdaptiveInterruptionDetector {\n  return new AdaptiveInterruptionDetector({\n    baseUrl: 'http://localhost:9999',\n    apiKey: 'test-key',\n    apiSecret: 'test-secret',\n    ...opts,\n  });\n}\n\n/** Drain a stream's event side and return the rejection (or undefined on clean end). */\nasync function readError(stream: InterruptionStreamBase): Promise<unknown> {\n  const reader = stream.stream().getReader();\n  try {\n    for (;;) {\n      const { done } = await reader.read();\n      if (done) return undefined;\n    }\n  } catch (e) {\n    return e;\n  } finally {\n    try {\n      reader.releaseLock();\n    } catch {\n      // already released\n    }\n  }\n}\n\nbeforeEach(() => {\n  MockWebSocket.instances.length = 0;\n});\n\nafterEach(() => {\n  vi.clearAllMocks();\n});\n\n// ---------------------------------------------------------------------------\n// WebSocket transport error production\n// ---------------------------------------------------------------------------\n\ndescribe('interruption WebSocket transport failover', () => {\n  it('surfaces a non-retryable APITimeoutError on connection timeout', async () => {\n    const detector = createDetector();\n    // Short connect timeout so the test does not wait on the default.\n    const stream = new InterruptionStreamBase(detector, { timeout: 50 });\n\n    const err = await readError(stream);\n\n    expect(err).toBeInstanceOf(APITimeoutError);\n    expect((err as APIError).retryable).toBe(false);\n\n    await stream.close();\n  });\n\n  it('surfaces a non-retryable APIStatusError on connection 429', async () => {\n    const detector = createDetector();\n    const stream = new InterruptionStreamBase(detector, {});\n\n    const errPromise = readError(stream);\n    const ws = await waitForInstance();\n    ws.simulateUnexpectedResponse(429);\n\n    const err = await errPromise;\n\n    expect(err).toBeInstanceOf(APIStatusError);\n    expect((err as APIStatusError).statusCode).toBe(429);\n    expect((err as APIError).retryable).toBe(false);\n\n    await stream.close();\n  });\n\n  it('surfaces a non-retryable 408 APIStatusError when inference responses time out', async () => {\n    const inferenceTimeout = 50;\n    const detector = createDetector({ inferenceTimeout });\n    const stream = new InterruptionStreamBase(detector, {});\n\n    const errPromise = readError(stream);\n    const ws = await waitForInstance();\n    ws.simulateOpen();\n    // let ensureConnection() resolve and send session.create\n    await sleep(5);\n\n    // Drive overlap audio so the transport sends a request and caches it, then never\n    // answers — the next slice must trip the cache-timeout guard.\n    await stream.pushFrame(InterruptionStreamSentinel.agentSpeechStarted());\n    await stream.pushFrame(InterruptionStreamSentinel.overlapSpeechStarted(500, Date.now()));\n    await stream.pushFrame(makeAudioFrame());\n    await sleep(inferenceTimeout + 40);\n    await stream.pushFrame(makeAudioFrame());\n\n    const err = await errPromise;\n\n    expect(err).toBeInstanceOf(APIStatusError);\n    expect((err as APIStatusError).statusCode).toBe(408);\n    expect((err as APIError).retryable).toBe(false);\n\n    await stream.close();\n  });\n\n  it('errors when session.created reports no threshold and the user did not override', async () => {\n    const detector = createDetector();\n    const stream = new InterruptionStreamBase(detector, {});\n\n    const errPromise = readError(stream);\n    const ws = await waitForInstance();\n    ws.simulateOpen();\n    await sleep(5);\n    ws.simulateMessage({ type: 'session.created' });\n\n    const err = await errPromise;\n\n    expect(err).toBeInstanceOf(APIStatusError);\n    expect((err as APIStatusError).statusCode).toBe(500);\n    expect((err as APIError).retryable).toBe(false);\n\n    await stream.close();\n  });\n\n  it('does not error when session.created carries a default_threshold', async () => {\n    const detector = createDetector();\n    const stream = new InterruptionStreamBase(detector, {});\n\n    const errPromise = readError(stream);\n    const ws = await waitForInstance();\n    ws.simulateOpen();\n    await sleep(5);\n    ws.simulateMessage({ type: 'session.created', default_threshold: 0.42 });\n    await sleep(5);\n\n    await stream.close();\n    expect(await errPromise).toBeUndefined();\n  });\n\n  it('closes the underlying WebSocket on teardown even after the transport errored', async () => {\n    // The transport's flush() only runs on graceful stream completion. When the stream is torn\n    // down via an error (here a 408 inference timeout) the socket is still open, so close() must\n    // tear it down directly — otherwise the WebSocket leaks.\n    const inferenceTimeout = 50;\n    const detector = createDetector({ inferenceTimeout });\n    const stream = new InterruptionStreamBase(detector, {});\n\n    const errPromise = readError(stream);\n    const ws = await waitForInstance();\n    ws.simulateOpen();\n    await sleep(5);\n\n    await stream.pushFrame(InterruptionStreamSentinel.agentSpeechStarted());\n    await stream.pushFrame(InterruptionStreamSentinel.overlapSpeechStarted(500, Date.now()));\n    await stream.pushFrame(makeAudioFrame());\n    await sleep(inferenceTimeout + 40);\n    await stream.pushFrame(makeAudioFrame());\n\n    await errPromise;\n    expect(ws.readyState).toBe(MockWebSocket.OPEN); // socket still open after the stream errored\n\n    await stream.close();\n\n    expect(ws.readyState).toBe(3); // CLOSED — close() tore the socket down despite the error\n  });\n});\n\n// ---------------------------------------------------------------------------\n// In-place reconnect on updateOptions\n// ---------------------------------------------------------------------------\n\ndescribe('interruption updateOptions reconnect', () => {\n  it('reconnects in place with the updated threshold without erroring the stream', async () => {\n    const detector = createDetector();\n    const stream = new InterruptionStreamBase(detector, {});\n    const errPromise = readError(stream);\n\n    const ws1 = await waitForInstance();\n    ws1.simulateOpen();\n    await waitFor(() => ws1.sent.length > 0); // session.create #1\n    // No user override → the first session.create omits threshold (server applies its default).\n    expect('threshold' in sessionCreateSettings(ws1)).toBe(false);\n\n    await stream.updateOptions({ threshold: 0.7 });\n\n    // A fresh socket is opened in place and the old one is closed — no error on the stream.\n    const ws2 = await waitForInstanceCount(2);\n    expect(ws2).not.toBe(ws1);\n    expect(ws1.readyState).toBe(3); // old socket closed\n    ws2.simulateOpen();\n    await waitFor(() => ws2.sent.length > 0); // session.create #2\n\n    expect(sessionCreateSettings(ws2).threshold).toBe(0.7);\n    await expect(stillAlive(errPromise)).resolves.toBe('alive');\n\n    await stream.close();\n  });\n\n  it('does not error the stream across more reconnects than the failover budget', async () => {\n    // The original bug: each updateOptions errored the stream and burned one (never-reset) retry,\n    // so exceeding maxRetries killed detection. In-place reconnect must keep the stream live.\n    const detector = createDetector();\n    const stream = new InterruptionStreamBase(detector, {});\n    const errPromise = readError(stream);\n\n    const ws1 = await waitForInstance();\n    ws1.simulateOpen();\n    await waitFor(() => ws1.sent.length > 0);\n\n    const rounds = apiConnectDefaults.maxRetries + 2; // deliberately exceed the retry budget\n    for (let i = 0; i < rounds; i++) {\n      await stream.updateOptions({ threshold: 0.5 + (i + 1) * 0.01 });\n      const ws = await waitForInstanceCount(i + 2);\n      ws.simulateOpen();\n      await waitFor(() => ws.sent.length > 0);\n    }\n\n    expect(MockWebSocket.instances.length).toBe(rounds + 1);\n    await expect(stillAlive(errPromise)).resolves.toBe('alive');\n\n    await stream.close();\n  });\n\n  it('errors the stream when the post-updateOptions reconnect genuinely fails', async () => {\n    // A real reconnect failure (here connection 429) must still surface as a stream error so the\n    // failover path runs.\n    const detector = createDetector();\n    const stream = new InterruptionStreamBase(detector, {});\n    const errPromise = readError(stream);\n\n    const ws1 = await waitForInstance();\n    ws1.simulateOpen();\n    await waitFor(() => ws1.sent.length > 0);\n\n    await stream.updateOptions({ threshold: 0.7 });\n    const ws2 = await waitForInstanceCount(2);\n    ws2.simulateUnexpectedResponse(429); // the reconnect's connect attempt is rejected\n\n    const err = await errPromise;\n    expect(err).toBeInstanceOf(APIStatusError);\n    expect((err as APIStatusError).statusCode).toBe(429);\n\n    await stream.close();\n  });\n});\n\n// ---------------------------------------------------------------------------\n// AudioRecognition error-emission classification\n// ---------------------------------------------------------------------------\n\nfunction createHooks(): RecognitionHooks {\n  return {\n    onInterruption: vi.fn(),\n    onBackchannelConfirmed: vi.fn(),\n    onStartOfSpeech: vi.fn(),\n    onVADInferenceDone: vi.fn(),\n    onEndOfSpeech: vi.fn(),\n    onInterimTranscript: vi.fn(),\n    onFinalTranscript: vi.fn(),\n    onPreemptiveGeneration: vi.fn(),\n    onAgentBackchannelOpportunity: vi.fn(),\n    retrieveChatCtx: () => ChatContext.empty(),\n    onEndOfTurn: vi.fn(async () => true),\n  };\n}\n\ndescribe('interruption failover error emission', () => {\n  it('emits exactly one unrecoverable error for a non-retryable transport failure', async () => {\n    // Mirrors how ws_transport constructs the connection-rejected error (retryable forced off).\n    const transportError = new APIStatusError({\n      message: 'WebSocket connection rejected with status 429',\n      options: { statusCode: 429, retryable: false },\n    });\n    expect(transportError.retryable).toBe(false);\n\n    const errors: Array<{ recoverable: boolean }> = [];\n    const erroringStream = {\n      stream: () =>\n        new ReadableStream({\n          start(controller) {\n            controller.error(transportError);\n          },\n        }),\n      pushFrame: async () => {},\n      close: async () => {},\n    };\n    const mockDetector = {\n      label: 'mock-detector',\n      createStream: () => erroringStream,\n      emitError: (e: { recoverable: boolean }) => errors.push(e),\n    };\n\n    const recognition = new AudioRecognition({\n      recognitionHooks: createHooks(),\n      interruptionDetection: mockDetector as unknown as AdaptiveInterruptionDetector,\n    });\n\n    const ac = new AbortController();\n    const task = (\n      recognition as unknown as {\n        createInterruptionTask: (\n          d: AdaptiveInterruptionDetector,\n          signal: AbortSignal,\n        ) => Promise<void>;\n      }\n    ).createInterruptionTask(mockDetector as unknown as AdaptiveInterruptionDetector, ac.signal);\n\n    // The non-retryable path emits the unrecoverable error then blocks in `finally`\n    // awaiting the (idle) input-forwarding task; abort to let it wind down.\n    const start = performance.now();\n    while (errors.length === 0 && performance.now() - start < 2000) {\n      await sleep(5);\n    }\n    ac.abort();\n    await task;\n\n    expect(errors.filter((e) => e.recoverable)).toHaveLength(0);\n    expect(errors.filter((e) => !e.recoverable)).toHaveLength(1);\n  });\n});\n"],"mappings":"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","names":["MockWebSocket"]}