"use strict";
var import_rtc_node = require("@livekit/rtc-node");
var import_vitest = require("vitest");
var import_exceptions = require("../../_exceptions.cjs");
var import_log = require("../../log.cjs");
var import_types = require("../../types.cjs");
var import_base = require("./base.cjs");
var import_detector = require("./detector.cjs");
var import_languages = require("./languages.cjs");
var import_runner = require("./runner.cjs");
var import_transports = require("./transports.cjs");
const SERVER_THRESHOLDS = { en: 0.56, ja: 0.37, fr: 0.575 };
const SERVER_DEFAULT_THRESHOLD = 0.5;
const SERVER_BACKCHANNEL_THRESHOLDS = { en: 0.62, ja: 0.7 };
const SERVER_BACKCHANNEL_DEFAULT = 0.6;
async function waitFor(predicate, ticks = 50) {
  for (let i = 0; i < ticks; i++) {
    if (predicate()) return;
    await new Promise((r) => setImmediate(r));
  }
}
class ScriptedTransport {
  runBehavior;
  runExc;
  runCalls = 0;
  events = [];
  _stream;
  constructor(opts = {}) {
    this.runBehavior = opts.runBehavior ?? "idle";
    this.runExc = opts.runExc;
  }
  attach(stream) {
    this._stream = stream;
  }
  async run() {
    this.runCalls += 1;
    if (this.runBehavior === "raise") {
      if (!this.runExc) throw new Error("runExc not set");
      throw this.runExc;
    }
    if (this.runBehavior === "return") {
      return;
    }
    await new Promise(() => void 0);
  }
  runInference(requestId) {
    this.events.push(["run_inference", requestId]);
  }
  async pushFrame(frame) {
    this.events.push(["push_frame", frame]);
  }
  async flush(sentinel) {
    this.events.push(["flush", sentinel]);
  }
  detach() {
    this.events.push(["detach", null]);
  }
}
function detectorOpts(detector) {
  return detector._opts;
}
function makeStreamWithTransport(transport, opts = {}) {
  const model = opts.model ?? "turn-detector-v1";
  const detector = opts.detector ?? makeMockDetector(model, opts.userThreshold);
  const stream = new import_detector.TurnDetectorStreamImpl({
    detector,
    opts: detectorOpts(detector),
    cloudOpts: model === "turn-detector-v1" ? {
      baseUrl: "ws://test",
      apiKey: "x",
      apiSecret: "x",
      connOptions: import_types.DEFAULT_API_CONNECT_OPTIONS
    } : void 0,
    model,
    transport
  });
  return stream;
}
function makeMockDetector(model, userThreshold) {
  const originalEnv = { ...process.env };
  if (model === "turn-detector-v1-mini") {
    delete process.env.LIVEKIT_REMOTE_EOT_URL;
  } else {
    process.env.LIVEKIT_REMOTE_EOT_URL = "ws://test";
    process.env.LIVEKIT_API_KEY = "x";
    process.env.LIVEKIT_API_SECRET = "x";
  }
  const det = new import_detector.TurnDetector();
  process.env = originalEnv;
  const internals = det;
  internals._model = model;
  internals._opts = { ...internals._opts, thresholds: new import_languages.ThresholdOptions(model, userThreshold) };
  return det;
}
function withEnv(overrides, fn) {
  const original = { ...process.env };
  for (const [k, v] of Object.entries(overrides)) {
    if (v === void 0) delete process.env[k];
    else process.env[k] = v;
  }
  try {
    const result = fn();
    if (result instanceof Promise) {
      return result.finally(() => {
        process.env = original;
      });
    }
    process.env = original;
    return result;
  } catch (err) {
    process.env = original;
    throw err;
  }
}
let runSpy;
(0, import_vitest.beforeEach)(() => {
  runSpy = import_vitest.vi.spyOn(import_transports.LocalTransport.prototype, "run").mockImplementation(async () => void 0);
});
(0, import_vitest.afterEach)(() => {
  runSpy.mockRestore();
});
(0, import_vitest.describe)("AutoSelect", () => {
  (0, import_vitest.it)("selects local when no remote EOT url", () => {
    void withEnv({ LIVEKIT_REMOTE_EOT_URL: void 0 }, () => {
      const detector = new import_detector.TurnDetector();
      (0, import_vitest.expect)(detector.model).toBe("turn-detector-v1-mini");
    });
  });
  (0, import_vitest.it)("selects cloud when remote EOT url set", () => {
    void withEnv(
      {
        LIVEKIT_REMOTE_EOT_URL: "ws://gateway",
        LIVEKIT_API_KEY: "k",
        LIVEKIT_API_SECRET: "s"
      },
      () => {
        const detector = new import_detector.TurnDetector();
        (0, import_vitest.expect)(detector.model).toBe("turn-detector-v1");
      }
    );
  });
  (0, import_vitest.it)("downgrades to local when creds missing", () => {
    void withEnv(
      {
        LIVEKIT_REMOTE_EOT_URL: "ws://gateway",
        LIVEKIT_API_KEY: void 0,
        LIVEKIT_API_SECRET: void 0,
        LIVEKIT_INFERENCE_API_KEY: void 0,
        LIVEKIT_INFERENCE_API_SECRET: void 0
      },
      () => {
        const detector = new import_detector.TurnDetector();
        (0, import_vitest.expect)(detector.model).toBe("turn-detector-v1-mini");
      }
    );
  });
});
(0, import_vitest.describe)("Redaction", () => {
  (0, import_vitest.it)("omits cloud credentials from JSON serialization", () => {
    void withEnv(
      {
        LIVEKIT_INFERENCE_URL: "ws://gateway",
        LIVEKIT_API_KEY: "APIsecretkey123",
        LIVEKIT_API_SECRET: "topsecretvalue456",
        LIVEKIT_INFERENCE_API_KEY: void 0,
        LIVEKIT_INFERENCE_API_SECRET: void 0
      },
      () => {
        const detector = new import_detector.TurnDetector({ version: "v1" });
        const json = JSON.stringify(detector);
        (0, import_vitest.expect)(json).not.toContain("APIsecretkey123");
        (0, import_vitest.expect)(json).not.toContain("topsecretvalue456");
        const parsed = JSON.parse(json);
        (0, import_vitest.expect)(parsed.model).toBe("turn-detector-v1");
        (0, import_vitest.expect)(parsed.cloud.baseUrl).toBe("ws://gateway");
        (0, import_vitest.expect)(parsed.cloud).not.toHaveProperty("apiKey");
        (0, import_vitest.expect)(parsed.cloud).not.toHaveProperty("apiSecret");
        (0, import_vitest.expect)(parsed.thresholds).not.toHaveProperty("_serverThresholds");
        (0, import_vitest.expect)(parsed.thresholds).toHaveProperty("model");
      }
    );
  });
});
(0, import_vitest.describe)("ExplicitModelErrors", () => {
  (0, import_vitest.it)("explicit cloud missing creds throws", () => {
    void withEnv(
      {
        LIVEKIT_REMOTE_EOT_URL: void 0,
        LIVEKIT_API_KEY: void 0,
        LIVEKIT_API_SECRET: void 0,
        LIVEKIT_INFERENCE_API_KEY: void 0,
        LIVEKIT_INFERENCE_API_SECRET: void 0
      },
      () => {
        (0, import_vitest.expect)(() => new import_detector.TurnDetector({ version: "v1" })).toThrow();
      }
    );
  });
});
(0, import_vitest.describe)("Fallback", () => {
  (0, import_vitest.it)("fallback on transport error swaps to local", async () => {
    const transport = new ScriptedTransport({
      runBehavior: "raise",
      runExc: new import_exceptions.APIConnectionError({ message: "boom" })
    });
    const stream = makeStreamWithTransport(transport);
    await waitFor(() => stream.model === "turn-detector-v1-mini");
    (0, import_vitest.expect)(stream.model).toBe("turn-detector-v1-mini");
    (0, import_vitest.expect)(stream.isFallback).toBe(true);
    (0, import_vitest.expect)(stream.warnedCloudFailure).toBe(true);
    (0, import_vitest.expect)(transport.events).toContainEqual(["detach", null]);
    await stream.aclose();
  });
  (0, import_vitest.it)("fallback on timed-out cancelInference", async () => {
    const transport = new ScriptedTransport({ runBehavior: "idle" });
    const stream = makeStreamWithTransport(transport);
    const fut = stream.predict();
    stream.cancelInference({ timedOut: true });
    (0, import_vitest.expect)((await fut.await).endOfTurnProbability).toBe(0);
    await waitFor(() => stream.model === "turn-detector-v1-mini");
    (0, import_vitest.expect)(stream.model).toBe("turn-detector-v1-mini");
    (0, import_vitest.expect)(stream.isFallback).toBe(true);
    await stream.aclose();
  });
  (0, import_vitest.it)("fallback persists across turns", async () => {
    const transport = new ScriptedTransport({
      runBehavior: "raise",
      runExc: new import_exceptions.APIConnectionError({ message: "boom" })
    });
    const stream = makeStreamWithTransport(transport);
    await waitFor(() => stream.model === "turn-detector-v1-mini");
    (0, import_vitest.expect)(transport.runCalls).toBe(1);
    stream.predict();
    (0, import_vitest.expect)(stream.model).toBe("turn-detector-v1-mini");
    await stream.aclose();
  });
});
(0, import_vitest.describe)("MultiStreamOwnership", () => {
  (0, import_vitest.it)("multiple streams can be opened off one detector", async () => {
    let detector;
    withEnv({ LIVEKIT_REMOTE_EOT_URL: void 0 }, () => {
      detector = new import_detector.TurnDetector({ version: "v1-mini" });
    });
    const s1 = detector.stream();
    const s2 = detector.stream();
    await s1.aclose();
    await s2.aclose();
  });
});
(0, import_vitest.describe)("DetectorViewAfterFallback", () => {
  (0, import_vitest.it)("detector model + threshold follow the fallback (shared ThresholdOptions)", async () => {
    let detector;
    withEnv(
      {
        LIVEKIT_REMOTE_EOT_URL: "ws://gateway",
        LIVEKIT_API_KEY: "k",
        LIVEKIT_API_SECRET: "s"
      },
      () => {
        detector = new import_detector.TurnDetector({ unlikelyThreshold: 0.5 });
      }
    );
    (0, import_vitest.expect)(detector.model).toBe("turn-detector-v1");
    (0, import_vitest.expect)(await detector.unlikelyThreshold("en")).toBeCloseTo(0.5);
    const transport = new ScriptedTransport({ runBehavior: "idle" });
    const stream = new import_detector.TurnDetectorStreamImpl({
      detector,
      opts: detectorOpts(detector),
      cloudOpts: void 0,
      model: "turn-detector-v1",
      transport
    });
    stream.thresholdsOptions._updateDefaults({ ...SERVER_THRESHOLDS }, SERVER_DEFAULT_THRESHOLD);
    stream._fallBackToLocal(new import_exceptions.APIConnectionError({ message: "boom" }));
    await waitFor(() => stream.model === "turn-detector-v1-mini");
    (0, import_vitest.expect)(stream.model).toBe("turn-detector-v1-mini");
    (0, import_vitest.expect)(detector.model).toBe("turn-detector-v1-mini");
    const expected = import_languages.LOCAL_LANGUAGES.en * (0.5 / SERVER_THRESHOLDS.en);
    (0, import_vitest.expect)(await detector.unlikelyThreshold("en")).toBeCloseTo(expected);
    await stream.aclose();
  });
});
(0, import_vitest.describe)("LocalFailureRetry", () => {
  (0, import_vitest.it)("local failure emits default and retries on next turn", async () => {
    const transport = new ScriptedTransport({
      runBehavior: "raise",
      runExc: new Error("local boom")
    });
    const stream = makeStreamWithTransport(transport, { model: "turn-detector-v1-mini" });
    await waitFor(() => stream.warnedLocalFailure);
    (0, import_vitest.expect)(stream.model).toBe("turn-detector-v1-mini");
    (0, import_vitest.expect)(stream.isFallback).toBe(false);
    (0, import_vitest.expect)(stream.warnedLocalFailure).toBe(true);
    (0, import_vitest.expect)(stream.transport).toBe(transport);
    await stream.aclose();
  });
});
(0, import_vitest.describe)("WarningDedupe", () => {
  (0, import_vitest.it)("cloud\u2192local warning logged once per session", async () => {
    const transport = new ScriptedTransport({
      runBehavior: "raise",
      runExc: new import_exceptions.APIConnectionError({ message: "boom" })
    });
    const stream = makeStreamWithTransport(transport);
    await waitFor(() => stream.model === "turn-detector-v1-mini");
    stream._fallBackToLocal(new import_exceptions.APIConnectionError({ message: "boom2" }));
    (0, import_vitest.expect)(stream.warnedCloudFailure).toBe(true);
    await stream.aclose();
  });
  (0, import_vitest.it)("local warning logged once per session", async () => {
    const transport = new ScriptedTransport({ runBehavior: "idle" });
    const stream = makeStreamWithTransport(transport, { model: "turn-detector-v1-mini" });
    stream._onLocalFailure(new Error("a"));
    stream._onLocalFailure(new Error("b"));
    (0, import_vitest.expect)(stream.warnedLocalFailure).toBe(true);
    await stream.aclose();
  });
});
(0, import_vitest.describe)("ResolveThresholds", () => {
  function cloud(overrides) {
    const opts = new import_languages.ThresholdOptions("turn-detector-v1", overrides);
    opts._updateDefaults({ ...SERVER_THRESHOLDS }, SERVER_DEFAULT_THRESHOLD);
    return opts;
  }
  (0, import_vitest.it)("no override adopts server map + fallback default", () => {
    const opts = cloud();
    (0, import_vitest.expect)(opts.thresholds).toEqual(SERVER_THRESHOLDS);
    (0, import_vitest.expect)(opts.defaultThreshold).toBeCloseTo(SERVER_DEFAULT_THRESHOLD);
  });
  (0, import_vitest.it)("scalar override replaces with empty map", () => {
    const opts = cloud(0.8);
    (0, import_vitest.expect)(opts.thresholds).toEqual({});
    (0, import_vitest.expect)(opts.defaultThreshold).toBeCloseTo(0.8);
  });
  (0, import_vitest.it)("dict override layers on server map", () => {
    const opts = cloud({ en: 0.7 });
    (0, import_vitest.expect)(opts.thresholds.en).toBeCloseTo(0.7);
    (0, import_vitest.expect)(opts.thresholds.ja).toBeCloseTo(SERVER_THRESHOLDS.ja);
    (0, import_vitest.expect)(opts.defaultThreshold).toBeCloseTo(SERVER_DEFAULT_THRESHOLD);
  });
  (0, import_vitest.it)("dict keys normalized", () => {
    const opts = cloud({ English: 0.7, "en-US": 0.7 });
    (0, import_vitest.expect)(opts.thresholds.en).toBeCloseTo(0.7);
  });
});
(0, import_vitest.describe)("BackchannelThresholds", () => {
  function cloud() {
    const opts = new import_languages.ThresholdOptions("turn-detector-v1");
    opts._updateDefaults(
      { ...SERVER_THRESHOLDS },
      SERVER_DEFAULT_THRESHOLD,
      { ...SERVER_BACKCHANNEL_THRESHOLDS },
      SERVER_BACKCHANNEL_DEFAULT
    );
    return opts;
  }
  (0, import_vitest.it)("lookup per-language and default", () => {
    const opts = cloud();
    (0, import_vitest.expect)(opts.lookupBackchannel("en")).toBeCloseTo(SERVER_BACKCHANNEL_THRESHOLDS.en);
    (0, import_vitest.expect)(opts.lookupBackchannel("de")).toBeCloseTo(SERVER_BACKCHANNEL_DEFAULT);
    (0, import_vitest.expect)(opts.lookupBackchannel(void 0)).toBeCloseTo(SERVER_BACKCHANNEL_THRESHOLDS.en);
  });
  (0, import_vitest.it)("disabled when server omits backchannel", () => {
    const opts = new import_languages.ThresholdOptions("turn-detector-v1");
    opts._updateDefaults({ ...SERVER_THRESHOLDS }, SERVER_DEFAULT_THRESHOLD);
    (0, import_vitest.expect)(opts.lookupBackchannel("en")).toBeUndefined();
  });
  (0, import_vitest.it)("disabled for the local mini model", () => {
    const opts = new import_languages.ThresholdOptions("turn-detector-v1-mini");
    (0, import_vitest.expect)(opts.lookupBackchannel("en")).toBeUndefined();
  });
  (0, import_vitest.it)("non-positive threshold treated as disabled", () => {
    const opts = new import_languages.ThresholdOptions("turn-detector-v1");
    opts._updateDefaults({ ...SERVER_THRESHOLDS }, SERVER_DEFAULT_THRESHOLD, { en: 0 }, 0.6);
    (0, import_vitest.expect)(opts.lookupBackchannel("en")).toBeUndefined();
    (0, import_vitest.expect)(opts.lookupBackchannel("de")).toBeCloseTo(0.6);
  });
  (0, import_vitest.it)("cleared on local fallback", () => {
    const opts = cloud();
    opts._toLocalFallback();
    (0, import_vitest.expect)(opts.lookupBackchannel("en")).toBeUndefined();
  });
});
(0, import_vitest.describe)("ResolveBackchannelThresholds", () => {
  function cloud(overrides) {
    const opts = new import_languages.ThresholdOptions("turn-detector-v1", void 0, overrides);
    opts._updateDefaults(
      { ...SERVER_THRESHOLDS },
      SERVER_DEFAULT_THRESHOLD,
      { ...SERVER_BACKCHANNEL_THRESHOLDS },
      SERVER_BACKCHANNEL_DEFAULT
    );
    return opts;
  }
  (0, import_vitest.it)("no override adopts server backchannel", () => {
    const opts = cloud();
    (0, import_vitest.expect)(opts.lookupBackchannel("en")).toBeCloseTo(SERVER_BACKCHANNEL_THRESHOLDS.en);
  });
  (0, import_vitest.it)("scalar override applies to every language", () => {
    const opts = cloud(0.8);
    (0, import_vitest.expect)(opts.lookupBackchannel("en")).toBeCloseTo(0.8);
    (0, import_vitest.expect)(opts.lookupBackchannel("ja")).toBeCloseTo(0.8);
  });
  (0, import_vitest.it)("dict override layers on server map", () => {
    const opts = cloud({ en: 0.5 });
    (0, import_vitest.expect)(opts.lookupBackchannel("en")).toBeCloseTo(0.5);
    (0, import_vitest.expect)(opts.lookupBackchannel("ja")).toBeCloseTo(SERVER_BACKCHANNEL_THRESHOLDS.ja);
    (0, import_vitest.expect)(opts.lookupBackchannel("de")).toBeCloseTo(SERVER_BACKCHANNEL_DEFAULT);
  });
  (0, import_vitest.it)("dict keys normalized", () => {
    const opts = cloud({ English: 0.5 });
    (0, import_vitest.expect)(opts.lookupBackchannel("en")).toBeCloseTo(0.5);
  });
  (0, import_vitest.it)("scalar override enables before server defaults", () => {
    const opts = new import_languages.ThresholdOptions("turn-detector-v1", void 0, 0.8);
    (0, import_vitest.expect)(opts.lookupBackchannel("en")).toBeCloseTo(0.8);
  });
  (0, import_vitest.it)("updateBackchannelOverrides re-resolves", () => {
    const opts = cloud();
    opts.updateBackchannelOverrides(0.45);
    (0, import_vitest.expect)(opts.lookupBackchannel("ja")).toBeCloseTo(0.45);
  });
});
(0, import_vitest.describe)("ServerDefaults", () => {
  (0, import_vitest.it)("cloud thresholds pending before session created", async () => {
    const transport = new ScriptedTransport({ runBehavior: "idle" });
    const stream = makeStreamWithTransport(transport);
    (0, import_vitest.expect)(await stream.unlikelyThreshold("en")).toBeUndefined();
    (0, import_vitest.expect)(await stream.supportsLanguage("en")).toBe(true);
    await stream.aclose();
  });
  (0, import_vitest.it)("cloud adopts server defaults", async () => {
    const transport = new ScriptedTransport({ runBehavior: "idle" });
    const stream = makeStreamWithTransport(transport);
    stream.thresholdsOptions._updateDefaults({ ...SERVER_THRESHOLDS }, SERVER_DEFAULT_THRESHOLD);
    (0, import_vitest.expect)(await stream.unlikelyThreshold("en")).toBeCloseTo(SERVER_THRESHOLDS.en);
    (0, import_vitest.expect)(await stream.unlikelyThreshold("de")).toBeCloseTo(SERVER_DEFAULT_THRESHOLD);
    await stream.aclose();
  });
  (0, import_vitest.it)("dict override layers on server defaults", async () => {
    const transport = new ScriptedTransport({ runBehavior: "idle" });
    const stream = makeStreamWithTransport(transport, { userThreshold: { en: 0.7, ja: 0.2 } });
    stream.thresholdsOptions._updateDefaults({ ...SERVER_THRESHOLDS }, SERVER_DEFAULT_THRESHOLD);
    (0, import_vitest.expect)(await stream.unlikelyThreshold("en")).toBeCloseTo(0.7);
    (0, import_vitest.expect)(await stream.unlikelyThreshold("ja")).toBeCloseTo(0.2);
    (0, import_vitest.expect)(await stream.unlikelyThreshold("fr")).toBeCloseTo(SERVER_THRESHOLDS.fr);
    await stream.aclose();
  });
  (0, import_vitest.it)("degenerate session created throws without override", async () => {
    const transport = new ScriptedTransport({ runBehavior: "idle" });
    const stream = makeStreamWithTransport(transport);
    (0, import_vitest.expect)(() => stream.thresholdsOptions._updateDefaults({}, 0)).toThrow(import_exceptions.APIError);
    await stream.aclose();
  });
  (0, import_vitest.it)("degenerate session created throws even with override", async () => {
    const transport = new ScriptedTransport({ runBehavior: "idle" });
    const stream = makeStreamWithTransport(transport, { userThreshold: 0.8 });
    (0, import_vitest.expect)(() => stream.thresholdsOptions._updateDefaults({}, 0)).toThrow(import_exceptions.APIError);
    await stream.aclose();
  });
});
(0, import_vitest.describe)("OverrideWarning", () => {
  (0, import_vitest.it)("warns on construction with override", () => {
    const warnSpy = import_vitest.vi.spyOn((0, import_log.log)(), "warn");
    try {
      withEnv({ LIVEKIT_REMOTE_EOT_URL: void 0 }, () => {
        new import_detector.TurnDetector({ unlikelyThreshold: 0.5 });
      });
      const warned = warnSpy.mock.calls.some(
        (c) => JSON.stringify(c).includes("non-default turn detection threshold")
      );
      (0, import_vitest.expect)(warned).toBe(true);
    } finally {
      warnSpy.mockRestore();
    }
  });
  (0, import_vitest.it)("warns on construction with backchannel override", () => {
    const warnSpy = import_vitest.vi.spyOn((0, import_log.log)(), "warn");
    try {
      withEnv({ LIVEKIT_REMOTE_EOT_URL: void 0 }, () => {
        new import_detector.TurnDetector({ backchannelThreshold: 0.7 });
      });
      const warned = warnSpy.mock.calls.some(
        (c) => JSON.stringify(c).includes("non-default backchannel threshold")
      );
      (0, import_vitest.expect)(warned).toBe(true);
    } finally {
      warnSpy.mockRestore();
    }
  });
  (0, import_vitest.it)("no warning without override", () => {
    const warnSpy = import_vitest.vi.spyOn((0, import_log.log)(), "warn");
    try {
      withEnv({ LIVEKIT_REMOTE_EOT_URL: void 0 }, () => {
        new import_detector.TurnDetector();
      });
      const warned = warnSpy.mock.calls.some(
        (c) => JSON.stringify(c).includes("non-default turn detection threshold")
      );
      (0, import_vitest.expect)(warned).toBe(false);
    } finally {
      warnSpy.mockRestore();
    }
  });
});
(0, import_vitest.describe)("UpdateOptions", () => {
  (0, import_vitest.it)("re-resolves an active cloud stream against cached server defaults", async () => {
    let detector;
    withEnv(
      {
        LIVEKIT_REMOTE_EOT_URL: "ws://gateway",
        LIVEKIT_API_KEY: "k",
        LIVEKIT_API_SECRET: "s"
      },
      () => {
        detector = new import_detector.TurnDetector();
      }
    );
    const transport = new ScriptedTransport({ runBehavior: "idle" });
    const stream = new import_detector.TurnDetectorStreamImpl({
      detector,
      opts: detectorOpts(detector),
      cloudOpts: void 0,
      model: "turn-detector-v1",
      transport
    });
    stream.thresholdsOptions._updateDefaults({ ...SERVER_THRESHOLDS }, SERVER_DEFAULT_THRESHOLD);
    (0, import_vitest.expect)(await stream.unlikelyThreshold("en")).toBeCloseTo(SERVER_THRESHOLDS.en);
    detector.updateOptions({ unlikelyThreshold: 0.7 });
    (0, import_vitest.expect)(await stream.unlikelyThreshold("en")).toBeCloseTo(0.7);
    await stream.aclose();
  });
  (0, import_vitest.it)("local model updateOptions", async () => {
    let detector;
    withEnv({ LIVEKIT_REMOTE_EOT_URL: void 0 }, () => {
      detector = new import_detector.TurnDetector();
    });
    (0, import_vitest.expect)(detector.model).toBe("turn-detector-v1-mini");
    detector.updateOptions({ unlikelyThreshold: 0.42 });
    (0, import_vitest.expect)(await detector.unlikelyThreshold("en")).toBeCloseTo(0.42);
    await detector.aclose();
  });
});
(0, import_vitest.describe)("ThresholdRescaleOnFallback", () => {
  (0, import_vitest.it)("scalar override rescaled against server on fallback", async () => {
    const transport = new ScriptedTransport({ runBehavior: "idle" });
    const stream = makeStreamWithTransport(transport, { userThreshold: 0.5 });
    stream.thresholdsOptions._updateDefaults({ ...SERVER_THRESHOLDS }, SERVER_DEFAULT_THRESHOLD);
    stream._fallBackToLocal(new import_exceptions.APIConnectionError({ message: "boom" }));
    await waitFor(() => stream.model === "turn-detector-v1-mini");
    (0, import_vitest.expect)(stream.isFallback).toBe(true);
    (0, import_vitest.expect)(await stream.unlikelyThreshold("en")).toBeCloseTo(
      import_languages.LOCAL_LANGUAGES.en * (0.5 / SERVER_THRESHOLDS.en)
    );
    await stream.aclose();
  });
  (0, import_vitest.it)("no override fallback uses local table", async () => {
    const transport = new ScriptedTransport({ runBehavior: "idle" });
    const stream = makeStreamWithTransport(transport);
    stream.thresholdsOptions._updateDefaults({ ...SERVER_THRESHOLDS }, SERVER_DEFAULT_THRESHOLD);
    stream._fallBackToLocal(new import_exceptions.APIConnectionError({ message: "boom" }));
    await waitFor(() => stream.model === "turn-detector-v1-mini");
    (0, import_vitest.expect)(await stream.unlikelyThreshold("en")).toBeCloseTo(import_languages.LOCAL_LANGUAGES.en);
    await stream.aclose();
  });
  (0, import_vitest.it)("dict override rescaled per language on fallback", async () => {
    const transport = new ScriptedTransport({ runBehavior: "idle" });
    const stream = makeStreamWithTransport(transport, { userThreshold: { en: 0.55, ja: 0.25 } });
    stream.thresholdsOptions._updateDefaults({ ...SERVER_THRESHOLDS }, SERVER_DEFAULT_THRESHOLD);
    stream._fallBackToLocal(new import_exceptions.APIConnectionError({ message: "boom" }));
    await waitFor(() => stream.model === "turn-detector-v1-mini");
    (0, import_vitest.expect)(stream.isFallback).toBe(true);
    (0, import_vitest.expect)(await stream.unlikelyThreshold("en")).toBeCloseTo(
      import_languages.LOCAL_LANGUAGES.en * (0.55 / SERVER_THRESHOLDS.en)
    );
    (0, import_vitest.expect)(await stream.unlikelyThreshold("ja")).toBeCloseTo(
      import_languages.LOCAL_LANGUAGES.ja * (0.25 / SERVER_THRESHOLDS.ja)
    );
    (0, import_vitest.expect)(await stream.unlikelyThreshold("fr")).toBeCloseTo(import_languages.LOCAL_LANGUAGES.fr);
    await stream.aclose();
  });
  (0, import_vitest.it)("fallback before session created uses local table with override applied", async () => {
    const transport = new ScriptedTransport({
      runBehavior: "raise",
      runExc: new import_exceptions.APIConnectionError({ message: "boom" })
    });
    const stream = makeStreamWithTransport(transport, { userThreshold: 0.42 });
    await waitFor(() => stream.model === "turn-detector-v1-mini");
    (0, import_vitest.expect)(stream.isFallback).toBe(true);
    (0, import_vitest.expect)(await stream.unlikelyThreshold("en")).toBeCloseTo(0.42);
    await stream.aclose();
  });
});
(0, import_vitest.describe)("LocalModelExecutor", () => {
  function pcmFrame(samples = 320) {
    return new import_rtc_node.AudioFrame(new Int16Array(samples), 16e3, 1, samples);
  }
  (0, import_vitest.it)("routes local predict through the injected executor (base64 PCM)", async () => {
    const doInference = import_vitest.vi.fn(async (method, data) => {
      (0, import_vitest.expect)(method).toBe(import_runner.EOT_INFERENCE_METHOD);
      (0, import_vitest.expect)(typeof data.pcm).toBe("string");
      return { probability: 0.7, inferenceDurationMs: 5 };
    });
    const executor = { doInference };
    const detector = new import_detector.TurnDetector({ version: "v1-mini", executor });
    const stream = detector.stream();
    try {
      stream.pushAudio(pcmFrame());
      const ev = await stream.predict().await;
      (0, import_vitest.expect)(ev.endOfTurnProbability).toBe(0.7);
      (0, import_vitest.expect)(doInference).toHaveBeenCalledWith(import_runner.EOT_INFERENCE_METHOD, import_vitest.expect.anything());
    } finally {
      await stream.aclose();
    }
  });
  (0, import_vitest.it)("degrades to a positive default when no executor is available", async () => {
    const detector = new import_detector.TurnDetector({ version: "v1-mini", executor: void 0 });
    const stream = detector.stream();
    try {
      const ev = await stream.predict().await;
      (0, import_vitest.expect)(ev.endOfTurnProbability).toBe(1);
    } finally {
      await stream.aclose();
    }
  });
});
//# sourceMappingURL=detector.test.cjs.map