{"version":3,"sources":["../../../src/inference/eot/detector.test.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2026 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\n\n/**\n * Tests for the unified `TurnDetector` (auto-select + fallback + server defaults).\n *\n * Covers:\n *\n * - Auto-select via `LIVEKIT_REMOTE_EOT_URL` env var (with creds present,\n *   with creds missing → silent downgrade).\n * - Explicit-cloud missing creds throws.\n * - Cloud → local fallback triggers (transport raise, predict timeout).\n * - Fallback persistence across turns.\n * - Local-failure handling (default 1.0, retry on next turn).\n * - Per-session warning dedupe (one warning per failure mode).\n * - Server-provided default thresholds adopted from `SessionCreated`.\n * - Override resolution (scalar / dict / none) against the server defaults, the\n *   override warning, runtime `updateOptions`, and the degenerate\n *   (no usable thresholds) → fallback path.\n * - Threshold rescaling against the server defaults on actual fallback.\n */\nimport { AudioFrame } from '@livekit/rtc-node';\nimport { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';\nimport { APIConnectionError, APIError } from '../../_exceptions.js';\nimport type { InferenceExecutor } from '../../ipc/inference_executor.js';\nimport { log } from '../../log.js';\nimport { DEFAULT_API_CONNECT_OPTIONS } from '../../types.js';\nimport type { BaseStreamingTurnDetectorStream } from './base.js';\nimport {\n  type BaseStreamingTurnDetectorOptions,\n  type FlushSentinel,\n  type StreamingTurnDetectionTransport,\n} from './base.js';\nimport { TurnDetector, TurnDetectorStreamImpl } from './detector.js';\nimport { LOCAL_LANGUAGES, ThresholdOptions } from './languages.js';\nimport { EOT_INFERENCE_METHOD } from './runner.js';\nimport { LocalTransport } from './transports.js';\n\n// Stand-in for the per-language defaults a gateway returns in `SessionCreated`.\nconst SERVER_THRESHOLDS: Record<string, number> = { en: 0.56, ja: 0.37, fr: 0.575 };\nconst SERVER_DEFAULT_THRESHOLD = 0.5;\n\n// Backchannel defaults a gateway returns alongside the EOT defaults.\nconst SERVER_BACKCHANNEL_THRESHOLDS: Record<string, number> = { en: 0.62, ja: 0.7 };\nconst SERVER_BACKCHANNEL_DEFAULT = 0.6;\n\nasync function waitFor(predicate: () => boolean, ticks = 50): Promise<void> {\n  for (let i = 0; i < ticks; i++) {\n    if (predicate()) return;\n    await new Promise<void>((r) => setImmediate(r));\n  }\n}\n\ninterface ScriptedTransportOptions {\n  runBehavior?: 'idle' | 'raise' | 'return';\n  runExc?: Error;\n}\n\nclass ScriptedTransport implements StreamingTurnDetectionTransport {\n  runBehavior: 'idle' | 'raise' | 'return';\n  runExc: Error | undefined;\n  runCalls = 0;\n  events: Array<[string, unknown]> = [];\n  private _stream: BaseStreamingTurnDetectorStream | undefined;\n\n  constructor(opts: ScriptedTransportOptions = {}) {\n    this.runBehavior = opts.runBehavior ?? 'idle';\n    this.runExc = opts.runExc;\n  }\n\n  attach(stream: BaseStreamingTurnDetectorStream): void {\n    this._stream = stream;\n  }\n  async run(): Promise<void> {\n    this.runCalls += 1;\n    if (this.runBehavior === 'raise') {\n      if (!this.runExc) throw new Error('runExc not set');\n      throw this.runExc;\n    }\n    if (this.runBehavior === 'return') {\n      return;\n    }\n    // idle — wait until cancelled (resolved by `detach()` via the\n    // scripted transport's no-op; in our tests the parent stream\n    // cancels via `aclose`).\n    await new Promise(() => undefined);\n  }\n  runInference(requestId: string): void {\n    this.events.push(['run_inference', requestId]);\n  }\n  async pushFrame(frame: AudioFrame): Promise<void> {\n    this.events.push(['push_frame', frame]);\n  }\n  async flush(sentinel: FlushSentinel): Promise<void> {\n    this.events.push(['flush', sentinel]);\n  }\n  detach(): void {\n    this.events.push(['detach', null]);\n  }\n}\n\nfunction detectorOpts(detector: TurnDetector): BaseStreamingTurnDetectorOptions {\n  return (detector as unknown as { _opts: BaseStreamingTurnDetectorOptions })._opts;\n}\n\ninterface MakeStreamOpts {\n  model?: 'turn-detector-v1' | 'turn-detector-v1-mini';\n  userThreshold?: number | Record<string, number>;\n  detector?: TurnDetector;\n}\n\n/**\n * Construct a stream wired to a scripted transport. The detector and stream\n * share one `ThresholdOptions` (as in production). The cloud model starts with\n * empty thresholds (its defaults arrive via `SessionCreated` — call\n * `stream.thresholdsOptions._updateDefaults` to simulate that). The local mini\n * model resolves its thresholds against `LOCAL_LANGUAGES` up front.\n */\nfunction makeStreamWithTransport(\n  transport: StreamingTurnDetectionTransport,\n  opts: MakeStreamOpts = {},\n): TurnDetectorStreamImpl {\n  const model = opts.model ?? 'turn-detector-v1';\n  const detector = opts.detector ?? makeMockDetector(model, opts.userThreshold);\n  const stream = new TurnDetectorStreamImpl({\n    detector,\n    opts: detectorOpts(detector),\n    cloudOpts:\n      model === 'turn-detector-v1'\n        ? {\n            baseUrl: 'ws://test',\n            apiKey: 'x',\n            apiSecret: 'x',\n            connOptions: DEFAULT_API_CONNECT_OPTIONS,\n          }\n        : undefined,\n    model,\n    transport,\n  });\n  return stream;\n}\n\n/** Build a `TurnDetector` for assertions without going through env\n * resolution — seed a specific model + threshold override for a stream we'll\n * build separately. */\nfunction makeMockDetector(\n  model: 'turn-detector-v1' | 'turn-detector-v1-mini',\n  userThreshold?: number | Record<string, number>,\n): TurnDetector {\n  // Construct via the public constructor, then override the internal model +\n  // shared threshold options to match what we want for the assertion.\n  const originalEnv = { ...process.env };\n  if (model === 'turn-detector-v1-mini') {\n    delete process.env.LIVEKIT_REMOTE_EOT_URL;\n  } else {\n    process.env.LIVEKIT_REMOTE_EOT_URL = 'ws://test';\n    process.env.LIVEKIT_API_KEY = 'x';\n    process.env.LIVEKIT_API_SECRET = 'x';\n  }\n  const det = new TurnDetector();\n  process.env = originalEnv;\n  const internals = det as unknown as {\n    _model: typeof model;\n    _opts: BaseStreamingTurnDetectorOptions;\n  };\n  internals._model = model;\n  internals._opts = { ...internals._opts, thresholds: new ThresholdOptions(model, userThreshold) };\n  return det;\n}\n\nfunction withEnv(\n  overrides: Record<string, string | undefined>,\n  fn: () => void | Promise<void>,\n): void | Promise<void> {\n  const original = { ...process.env };\n  for (const [k, v] of Object.entries(overrides)) {\n    if (v === undefined) delete process.env[k];\n    else process.env[k] = v;\n  }\n  try {\n    const result = fn();\n    if (result instanceof Promise) {\n      return result.finally(() => {\n        process.env = original;\n      });\n    }\n    process.env = original;\n    return result;\n  } catch (err) {\n    process.env = original;\n    throw err;\n  }\n}\n\n// Stub `LocalTransport.run` so the fallback FSM doesn't hang on a real\n// drain loop. The behavior under test is the swap, not the post-swap I/O.\nlet runSpy: ReturnType<typeof vi.spyOn>;\nbeforeEach(() => {\n  runSpy = vi.spyOn(LocalTransport.prototype, 'run').mockImplementation(async () => undefined);\n});\nafterEach(() => {\n  runSpy.mockRestore();\n});\n\ndescribe('AutoSelect', () => {\n  it('selects local when no remote EOT url', () => {\n    void withEnv({ LIVEKIT_REMOTE_EOT_URL: undefined }, () => {\n      const detector = new TurnDetector();\n      expect(detector.model).toBe('turn-detector-v1-mini');\n    });\n  });\n\n  it('selects cloud when remote EOT url set', () => {\n    void withEnv(\n      {\n        LIVEKIT_REMOTE_EOT_URL: 'ws://gateway',\n        LIVEKIT_API_KEY: 'k',\n        LIVEKIT_API_SECRET: 's',\n      },\n      () => {\n        const detector = new TurnDetector();\n        expect(detector.model).toBe('turn-detector-v1');\n      },\n    );\n  });\n\n  it('downgrades to local when creds missing', () => {\n    void withEnv(\n      {\n        LIVEKIT_REMOTE_EOT_URL: 'ws://gateway',\n        LIVEKIT_API_KEY: undefined,\n        LIVEKIT_API_SECRET: undefined,\n        LIVEKIT_INFERENCE_API_KEY: undefined,\n        LIVEKIT_INFERENCE_API_SECRET: undefined,\n      },\n      () => {\n        const detector = new TurnDetector();\n        expect(detector.model).toBe('turn-detector-v1-mini');\n      },\n    );\n  });\n});\n\ndescribe('Redaction', () => {\n  it('omits cloud credentials from JSON serialization', () => {\n    void withEnv(\n      {\n        LIVEKIT_INFERENCE_URL: 'ws://gateway',\n        LIVEKIT_API_KEY: 'APIsecretkey123',\n        LIVEKIT_API_SECRET: 'topsecretvalue456',\n        LIVEKIT_INFERENCE_API_KEY: undefined,\n        LIVEKIT_INFERENCE_API_SECRET: undefined,\n      },\n      () => {\n        const detector = new TurnDetector({ version: 'v1' });\n        const json = JSON.stringify(detector);\n        expect(json).not.toContain('APIsecretkey123');\n        expect(json).not.toContain('topsecretvalue456');\n        const parsed = JSON.parse(json);\n        expect(parsed.model).toBe('turn-detector-v1');\n        expect(parsed.cloud.baseUrl).toBe('ws://gateway');\n        expect(parsed.cloud).not.toHaveProperty('apiKey');\n        expect(parsed.cloud).not.toHaveProperty('apiSecret');\n        expect(parsed.thresholds).not.toHaveProperty('_serverThresholds');\n        expect(parsed.thresholds).toHaveProperty('model');\n      },\n    );\n  });\n});\n\ndescribe('ExplicitModelErrors', () => {\n  it('explicit cloud missing creds throws', () => {\n    void withEnv(\n      {\n        LIVEKIT_REMOTE_EOT_URL: undefined,\n        LIVEKIT_API_KEY: undefined,\n        LIVEKIT_API_SECRET: undefined,\n        LIVEKIT_INFERENCE_API_KEY: undefined,\n        LIVEKIT_INFERENCE_API_SECRET: undefined,\n      },\n      () => {\n        expect(() => new TurnDetector({ version: 'v1' })).toThrow();\n      },\n    );\n  });\n});\n\ndescribe('Fallback', () => {\n  it('fallback on transport error swaps to local', async () => {\n    const transport = new ScriptedTransport({\n      runBehavior: 'raise',\n      runExc: new APIConnectionError({ message: 'boom' }),\n    });\n    const stream = makeStreamWithTransport(transport);\n    await waitFor(() => stream.model === 'turn-detector-v1-mini');\n    expect(stream.model).toBe('turn-detector-v1-mini');\n    expect(stream.isFallback).toBe(true);\n    expect(stream.warnedCloudFailure).toBe(true);\n    expect(transport.events).toContainEqual(['detach', null]);\n    await stream.aclose();\n  });\n\n  it('fallback on timed-out cancelInference', async () => {\n    const transport = new ScriptedTransport({ runBehavior: 'idle' });\n    const stream = makeStreamWithTransport(transport);\n    const fut = stream.predict();\n    // A timed-out cancel (driven by AudioRecognition's eou bounce) closes the\n    // request and promotes the cloud→local fallback.\n    stream.cancelInference({ timedOut: true });\n    expect((await fut.await).endOfTurnProbability).toBe(0.0);\n    await waitFor(() => stream.model === 'turn-detector-v1-mini');\n    expect(stream.model).toBe('turn-detector-v1-mini');\n    expect(stream.isFallback).toBe(true);\n    await stream.aclose();\n  });\n\n  it('fallback persists across turns', async () => {\n    const transport = new ScriptedTransport({\n      runBehavior: 'raise',\n      runExc: new APIConnectionError({ message: 'boom' }),\n    });\n    const stream = makeStreamWithTransport(transport);\n    await waitFor(() => stream.model === 'turn-detector-v1-mini');\n    expect(transport.runCalls).toBe(1);\n    stream.predict();\n    expect(stream.model).toBe('turn-detector-v1-mini');\n    await stream.aclose();\n  });\n});\n\ndescribe('MultiStreamOwnership', () => {\n  it('multiple streams can be opened off one detector', async () => {\n    let detector!: TurnDetector;\n    withEnv({ LIVEKIT_REMOTE_EOT_URL: undefined }, () => {\n      detector = new TurnDetector({ version: 'v1-mini' });\n    });\n    // Only one stream is active at a time in production; the detector still\n    // permits constructing several (they share its `ThresholdOptions`).\n    const s1 = detector.stream();\n    const s2 = detector.stream();\n    await s1.aclose();\n    await s2.aclose();\n  });\n});\n\ndescribe('DetectorViewAfterFallback', () => {\n  it('detector model + threshold follow the fallback (shared ThresholdOptions)', async () => {\n    let detector!: TurnDetector;\n    withEnv(\n      {\n        LIVEKIT_REMOTE_EOT_URL: 'ws://gateway',\n        LIVEKIT_API_KEY: 'k',\n        LIVEKIT_API_SECRET: 's',\n      },\n      () => {\n        detector = new TurnDetector({ unlikelyThreshold: 0.5 });\n      },\n    );\n    expect(detector.model).toBe('turn-detector-v1');\n    // scalar override is resolvable pre-session via the catch-all\n    expect(await detector.unlikelyThreshold('en')).toBeCloseTo(0.5);\n\n    const transport = new ScriptedTransport({ runBehavior: 'idle' });\n    const stream = new TurnDetectorStreamImpl({\n      detector,\n      opts: detectorOpts(detector),\n      cloudOpts: undefined,\n      model: 'turn-detector-v1',\n      transport,\n    });\n    // server defaults arrive, then the cloud session fails\n    stream.thresholdsOptions._updateDefaults({ ...SERVER_THRESHOLDS }, SERVER_DEFAULT_THRESHOLD);\n    stream._fallBackToLocal(new APIConnectionError({ message: 'boom' }));\n    await waitFor(() => stream.model === 'turn-detector-v1-mini');\n\n    // Both the stream and the detector (sharing one ThresholdOptions) reflect it.\n    expect(stream.model).toBe('turn-detector-v1-mini');\n    expect(detector.model).toBe('turn-detector-v1-mini');\n    const expected = LOCAL_LANGUAGES.en! * (0.5 / SERVER_THRESHOLDS.en!);\n    expect(await detector.unlikelyThreshold('en')).toBeCloseTo(expected);\n    await stream.aclose();\n  });\n});\n\ndescribe('LocalFailureRetry', () => {\n  it('local failure emits default and retries on next turn', async () => {\n    const transport = new ScriptedTransport({\n      runBehavior: 'raise',\n      runExc: new Error('local boom'),\n    });\n    const stream = makeStreamWithTransport(transport, { model: 'turn-detector-v1-mini' });\n    await waitFor(() => stream.warnedLocalFailure);\n    expect(stream.model).toBe('turn-detector-v1-mini');\n    expect(stream.isFallback).toBe(false);\n    expect(stream.warnedLocalFailure).toBe(true);\n    expect(stream.transport).toBe(transport);\n    await stream.aclose();\n  });\n});\n\ndescribe('WarningDedupe', () => {\n  it('cloud→local warning logged once per session', async () => {\n    const transport = new ScriptedTransport({\n      runBehavior: 'raise',\n      runExc: new APIConnectionError({ message: 'boom' }),\n    });\n    const stream = makeStreamWithTransport(transport);\n    await waitFor(() => stream.model === 'turn-detector-v1-mini');\n    // Trigger a second fallback path directly.\n    stream._fallBackToLocal(new APIConnectionError({ message: 'boom2' }));\n    // Across both invocations only one warning was emitted — tracked by\n    // the `warnedCloudFailure` flag staying flipped after the first call.\n    expect(stream.warnedCloudFailure).toBe(true);\n    await stream.aclose();\n  });\n\n  it('local warning logged once per session', async () => {\n    const transport = new ScriptedTransport({ runBehavior: 'idle' });\n    const stream = makeStreamWithTransport(transport, { model: 'turn-detector-v1-mini' });\n    stream._onLocalFailure(new Error('a'));\n    stream._onLocalFailure(new Error('b'));\n    expect(stream.warnedLocalFailure).toBe(true);\n    await stream.aclose();\n  });\n});\n\ndescribe('ResolveThresholds', () => {\n  // Cloud-override resolution against the server defaults, via ThresholdOptions.\n  function cloud(overrides?: number | Record<string, number>): ThresholdOptions {\n    const opts = new ThresholdOptions('turn-detector-v1', overrides);\n    opts._updateDefaults({ ...SERVER_THRESHOLDS }, SERVER_DEFAULT_THRESHOLD);\n    return opts;\n  }\n\n  it('no override adopts server map + fallback default', () => {\n    const opts = cloud();\n    expect(opts.thresholds).toEqual(SERVER_THRESHOLDS);\n    expect(opts.defaultThreshold).toBeCloseTo(SERVER_DEFAULT_THRESHOLD);\n  });\n\n  it('scalar override replaces with empty map', () => {\n    const opts = cloud(0.8);\n    // empty map → every language resolves through the scalar fallback\n    expect(opts.thresholds).toEqual({});\n    expect(opts.defaultThreshold).toBeCloseTo(0.8);\n  });\n\n  it('dict override layers on server map', () => {\n    const opts = cloud({ en: 0.7 });\n    expect(opts.thresholds.en).toBeCloseTo(0.7);\n    // unmapped languages keep the server values + server fallback\n    expect(opts.thresholds.ja).toBeCloseTo(SERVER_THRESHOLDS.ja!);\n    expect(opts.defaultThreshold).toBeCloseTo(SERVER_DEFAULT_THRESHOLD);\n  });\n\n  it('dict keys normalized', () => {\n    const opts = cloud({ English: 0.7, 'en-US': 0.7 });\n    expect(opts.thresholds.en).toBeCloseTo(0.7);\n  });\n});\n\ndescribe('BackchannelThresholds', () => {\n  // Server-provided backchannel defaults, disabled on the mini model and after\n  // fallback (see ResolveBackchannelThresholds for override layering).\n  function cloud(): ThresholdOptions {\n    const opts = new ThresholdOptions('turn-detector-v1');\n    opts._updateDefaults(\n      { ...SERVER_THRESHOLDS },\n      SERVER_DEFAULT_THRESHOLD,\n      { ...SERVER_BACKCHANNEL_THRESHOLDS },\n      SERVER_BACKCHANNEL_DEFAULT,\n    );\n    return opts;\n  }\n\n  it('lookup per-language and default', () => {\n    const opts = cloud();\n    expect(opts.lookupBackchannel('en')).toBeCloseTo(SERVER_BACKCHANNEL_THRESHOLDS.en!);\n    // absent language → catch-all backchannel default\n    expect(opts.lookupBackchannel('de')).toBeCloseTo(SERVER_BACKCHANNEL_DEFAULT);\n    // undefined language defaults to \"en\"\n    expect(opts.lookupBackchannel(undefined)).toBeCloseTo(SERVER_BACKCHANNEL_THRESHOLDS.en!);\n  });\n\n  it('disabled when server omits backchannel', () => {\n    const opts = new ThresholdOptions('turn-detector-v1');\n    opts._updateDefaults({ ...SERVER_THRESHOLDS }, SERVER_DEFAULT_THRESHOLD);\n    expect(opts.lookupBackchannel('en')).toBeUndefined();\n  });\n\n  it('disabled for the local mini model', () => {\n    const opts = new ThresholdOptions('turn-detector-v1-mini');\n    expect(opts.lookupBackchannel('en')).toBeUndefined();\n  });\n\n  it('non-positive threshold treated as disabled', () => {\n    const opts = new ThresholdOptions('turn-detector-v1');\n    opts._updateDefaults({ ...SERVER_THRESHOLDS }, SERVER_DEFAULT_THRESHOLD, { en: 0.0 }, 0.6);\n    // en explicitly 0 → disabled for en, but the positive default still applies elsewhere\n    expect(opts.lookupBackchannel('en')).toBeUndefined();\n    expect(opts.lookupBackchannel('de')).toBeCloseTo(0.6);\n  });\n\n  it('cleared on local fallback', () => {\n    const opts = cloud();\n    opts._toLocalFallback();\n    expect(opts.lookupBackchannel('en')).toBeUndefined();\n  });\n});\n\ndescribe('ResolveBackchannelThresholds', () => {\n  // User backchannel-threshold overrides layered against the server defaults,\n  // mirroring the EOT override resolution in ResolveThresholds.\n  function cloud(overrides?: number | Record<string, number>): ThresholdOptions {\n    const opts = new ThresholdOptions('turn-detector-v1', undefined, overrides);\n    opts._updateDefaults(\n      { ...SERVER_THRESHOLDS },\n      SERVER_DEFAULT_THRESHOLD,\n      { ...SERVER_BACKCHANNEL_THRESHOLDS },\n      SERVER_BACKCHANNEL_DEFAULT,\n    );\n    return opts;\n  }\n\n  it('no override adopts server backchannel', () => {\n    const opts = cloud();\n    expect(opts.lookupBackchannel('en')).toBeCloseTo(SERVER_BACKCHANNEL_THRESHOLDS.en!);\n  });\n\n  it('scalar override applies to every language', () => {\n    const opts = cloud(0.8);\n    expect(opts.lookupBackchannel('en')).toBeCloseTo(0.8);\n    expect(opts.lookupBackchannel('ja')).toBeCloseTo(0.8);\n  });\n\n  it('dict override layers on server map', () => {\n    const opts = cloud({ en: 0.5 });\n    expect(opts.lookupBackchannel('en')).toBeCloseTo(0.5);\n    // unmapped languages keep the server values + server default\n    expect(opts.lookupBackchannel('ja')).toBeCloseTo(SERVER_BACKCHANNEL_THRESHOLDS.ja!);\n    expect(opts.lookupBackchannel('de')).toBeCloseTo(SERVER_BACKCHANNEL_DEFAULT);\n  });\n\n  it('dict keys normalized', () => {\n    const opts = cloud({ English: 0.5 });\n    expect(opts.lookupBackchannel('en')).toBeCloseTo(0.5);\n  });\n\n  it('scalar override enables before server defaults', () => {\n    // an explicit scalar override resolves up front, even though the server\n    // backchannel defaults haven't arrived yet\n    const opts = new ThresholdOptions('turn-detector-v1', undefined, 0.8);\n    expect(opts.lookupBackchannel('en')).toBeCloseTo(0.8);\n  });\n\n  it('updateBackchannelOverrides re-resolves', () => {\n    const opts = cloud();\n    opts.updateBackchannelOverrides(0.45);\n    expect(opts.lookupBackchannel('ja')).toBeCloseTo(0.45);\n  });\n});\n\ndescribe('ServerDefaults', () => {\n  it('cloud thresholds pending before session created', async () => {\n    const transport = new ScriptedTransport({ runBehavior: 'idle' });\n    const stream = makeStreamWithTransport(transport);\n    // A cloud detector has no per-language threshold until `SessionCreated`,\n    // but reports the language as supported so the first turn isn't skipped.\n    expect(await stream.unlikelyThreshold('en')).toBeUndefined();\n    expect(await stream.supportsLanguage('en')).toBe(true);\n    await stream.aclose();\n  });\n\n  it('cloud adopts server defaults', async () => {\n    const transport = new ScriptedTransport({ runBehavior: 'idle' });\n    const stream = makeStreamWithTransport(transport);\n    stream.thresholdsOptions._updateDefaults({ ...SERVER_THRESHOLDS }, SERVER_DEFAULT_THRESHOLD);\n    expect(await stream.unlikelyThreshold('en')).toBeCloseTo(SERVER_THRESHOLDS.en!);\n    // language absent from the server map → catch-all default\n    expect(await stream.unlikelyThreshold('de')).toBeCloseTo(SERVER_DEFAULT_THRESHOLD);\n    await stream.aclose();\n  });\n\n  it('dict override layers on server defaults', async () => {\n    const transport = new ScriptedTransport({ runBehavior: 'idle' });\n    const stream = makeStreamWithTransport(transport, { userThreshold: { en: 0.7, ja: 0.2 } });\n    stream.thresholdsOptions._updateDefaults({ ...SERVER_THRESHOLDS }, SERVER_DEFAULT_THRESHOLD);\n    expect(await stream.unlikelyThreshold('en')).toBeCloseTo(0.7);\n    expect(await stream.unlikelyThreshold('ja')).toBeCloseTo(0.2);\n    // fr not overridden → server default for fr\n    expect(await stream.unlikelyThreshold('fr')).toBeCloseTo(SERVER_THRESHOLDS.fr!);\n    await stream.aclose();\n  });\n\n  it('degenerate session created throws without override', async () => {\n    const transport = new ScriptedTransport({ runBehavior: 'idle' });\n    const stream = makeStreamWithTransport(transport);\n    expect(() => stream.thresholdsOptions._updateDefaults({}, 0.0)).toThrow(APIError);\n    await stream.aclose();\n  });\n\n  it('degenerate session created throws even with override', async () => {\n    const transport = new ScriptedTransport({ runBehavior: 'idle' });\n    const stream = makeStreamWithTransport(transport, { userThreshold: 0.8 });\n    expect(() => stream.thresholdsOptions._updateDefaults({}, 0.0)).toThrow(APIError);\n    await stream.aclose();\n  });\n});\n\ndescribe('OverrideWarning', () => {\n  it('warns on construction with override', () => {\n    const warnSpy = vi.spyOn(log(), 'warn');\n    try {\n      withEnv({ LIVEKIT_REMOTE_EOT_URL: undefined }, () => {\n        new TurnDetector({ unlikelyThreshold: 0.5 });\n      });\n      const warned = warnSpy.mock.calls.some((c) =>\n        JSON.stringify(c).includes('non-default turn detection threshold'),\n      );\n      expect(warned).toBe(true);\n    } finally {\n      warnSpy.mockRestore();\n    }\n  });\n\n  it('warns on construction with backchannel override', () => {\n    const warnSpy = vi.spyOn(log(), 'warn');\n    try {\n      withEnv({ LIVEKIT_REMOTE_EOT_URL: undefined }, () => {\n        new TurnDetector({ backchannelThreshold: 0.7 });\n      });\n      const warned = warnSpy.mock.calls.some((c) =>\n        JSON.stringify(c).includes('non-default backchannel threshold'),\n      );\n      expect(warned).toBe(true);\n    } finally {\n      warnSpy.mockRestore();\n    }\n  });\n\n  it('no warning without override', () => {\n    const warnSpy = vi.spyOn(log(), 'warn');\n    try {\n      withEnv({ LIVEKIT_REMOTE_EOT_URL: undefined }, () => {\n        new TurnDetector();\n      });\n      const warned = warnSpy.mock.calls.some((c) =>\n        JSON.stringify(c).includes('non-default turn detection threshold'),\n      );\n      expect(warned).toBe(false);\n    } finally {\n      warnSpy.mockRestore();\n    }\n  });\n});\n\ndescribe('UpdateOptions', () => {\n  it('re-resolves an active cloud stream against cached server defaults', async () => {\n    let detector!: TurnDetector;\n    withEnv(\n      {\n        LIVEKIT_REMOTE_EOT_URL: 'ws://gateway',\n        LIVEKIT_API_KEY: 'k',\n        LIVEKIT_API_SECRET: 's',\n      },\n      () => {\n        detector = new TurnDetector();\n      },\n    );\n    const transport = new ScriptedTransport({ runBehavior: 'idle' });\n    const stream = new TurnDetectorStreamImpl({\n      detector,\n      opts: detectorOpts(detector),\n      cloudOpts: undefined,\n      model: 'turn-detector-v1',\n      transport,\n    });\n    stream.thresholdsOptions._updateDefaults({ ...SERVER_THRESHOLDS }, SERVER_DEFAULT_THRESHOLD);\n    expect(await stream.unlikelyThreshold('en')).toBeCloseTo(SERVER_THRESHOLDS.en!);\n\n    detector.updateOptions({ unlikelyThreshold: 0.7 });\n    // the shared resolver re-resolves against the cached server defaults\n    expect(await stream.unlikelyThreshold('en')).toBeCloseTo(0.7);\n    await stream.aclose();\n  });\n\n  it('local model updateOptions', async () => {\n    let detector!: TurnDetector;\n    withEnv({ LIVEKIT_REMOTE_EOT_URL: undefined }, () => {\n      detector = new TurnDetector();\n    });\n    expect(detector.model).toBe('turn-detector-v1-mini');\n    detector.updateOptions({ unlikelyThreshold: 0.42 });\n    expect(await detector.unlikelyThreshold('en')).toBeCloseTo(0.42);\n    await detector.aclose();\n  });\n});\n\ndescribe('ThresholdRescaleOnFallback', () => {\n  it('scalar override rescaled against server on fallback', async () => {\n    const transport = new ScriptedTransport({ runBehavior: 'idle' });\n    const stream = makeStreamWithTransport(transport, { userThreshold: 0.5 });\n    stream.thresholdsOptions._updateDefaults({ ...SERVER_THRESHOLDS }, SERVER_DEFAULT_THRESHOLD);\n    stream._fallBackToLocal(new APIConnectionError({ message: 'boom' }));\n    await waitFor(() => stream.model === 'turn-detector-v1-mini');\n    expect(stream.isFallback).toBe(true);\n    expect(await stream.unlikelyThreshold('en')).toBeCloseTo(\n      LOCAL_LANGUAGES.en! * (0.5 / SERVER_THRESHOLDS.en!),\n    );\n    await stream.aclose();\n  });\n\n  it('no override fallback uses local table', async () => {\n    const transport = new ScriptedTransport({ runBehavior: 'idle' });\n    const stream = makeStreamWithTransport(transport);\n    stream.thresholdsOptions._updateDefaults({ ...SERVER_THRESHOLDS }, SERVER_DEFAULT_THRESHOLD);\n    stream._fallBackToLocal(new APIConnectionError({ message: 'boom' }));\n    await waitFor(() => stream.model === 'turn-detector-v1-mini');\n    // ratio 1.0 → local table unchanged\n    expect(await stream.unlikelyThreshold('en')).toBeCloseTo(LOCAL_LANGUAGES.en!);\n    await stream.aclose();\n  });\n\n  it('dict override rescaled per language on fallback', async () => {\n    const transport = new ScriptedTransport({ runBehavior: 'idle' });\n    const stream = makeStreamWithTransport(transport, { userThreshold: { en: 0.55, ja: 0.25 } });\n    stream.thresholdsOptions._updateDefaults({ ...SERVER_THRESHOLDS }, SERVER_DEFAULT_THRESHOLD);\n    stream._fallBackToLocal(new APIConnectionError({ message: 'boom' }));\n    await waitFor(() => stream.model === 'turn-detector-v1-mini');\n    expect(stream.isFallback).toBe(true);\n    expect(await stream.unlikelyThreshold('en')).toBeCloseTo(\n      LOCAL_LANGUAGES.en! * (0.55 / SERVER_THRESHOLDS.en!),\n    );\n    expect(await stream.unlikelyThreshold('ja')).toBeCloseTo(\n      LOCAL_LANGUAGES.ja! * (0.25 / SERVER_THRESHOLDS.ja!),\n    );\n    // fr not in dict → server value as effective → plain local default\n    expect(await stream.unlikelyThreshold('fr')).toBeCloseTo(LOCAL_LANGUAGES.fr!);\n    await stream.aclose();\n  });\n\n  it('fallback before session created uses local table with override applied', async () => {\n    // Cloud fails before any `SessionCreated` → no server map to rescale\n    // against, so the local table (with the override applied) is used directly.\n    const transport = new ScriptedTransport({\n      runBehavior: 'raise',\n      runExc: new APIConnectionError({ message: 'boom' }),\n    });\n    const stream = makeStreamWithTransport(transport, { userThreshold: 0.42 });\n    await waitFor(() => stream.model === 'turn-detector-v1-mini');\n    expect(stream.isFallback).toBe(true);\n    // scalar 0.42 → 0.42 for every language via the catch-all\n    expect(await stream.unlikelyThreshold('en')).toBeCloseTo(0.42);\n    await stream.aclose();\n  });\n});\n\ndescribe('LocalModelExecutor', () => {\n  function pcmFrame(samples = 320): AudioFrame {\n    return new AudioFrame(new Int16Array(samples), 16000, 1, samples);\n  }\n\n  it('routes local predict through the injected executor (base64 PCM)', async () => {\n    const doInference = vi.fn(async (method: string, data: unknown) => {\n      expect(method).toBe(EOT_INFERENCE_METHOD);\n      expect(typeof (data as { pcm: string }).pcm).toBe('string');\n      return { probability: 0.7, inferenceDurationMs: 5 };\n    });\n    const executor: InferenceExecutor = { doInference };\n    const detector = new TurnDetector({ version: 'v1-mini', executor });\n    const stream = detector.stream();\n    try {\n      stream.pushAudio(pcmFrame());\n      const ev = await stream.predict().await;\n      expect(ev.endOfTurnProbability).toBe(0.7);\n      expect(doInference).toHaveBeenCalledWith(EOT_INFERENCE_METHOD, expect.anything());\n    } finally {\n      await stream.aclose();\n    }\n  });\n\n  it('degrades to a positive default when no executor is available', async () => {\n    // explicit undefined → constructor falls through to getJobContext()\n    // (throws outside a job) → executor stays undefined.\n    const detector = new TurnDetector({ version: 'v1-mini', executor: undefined });\n    const stream = detector.stream();\n    try {\n      const ev = await stream.predict().await;\n      expect(ev.endOfTurnProbability).toBe(1.0);\n    } finally {\n      await stream.aclose();\n    }\n  });\n});\n"],"mappings":"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","names":[]}