"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
  for (var name in all)
    __defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
  if (from && typeof from === "object" || typeof from === "function") {
    for (let key of __getOwnPropNames(from))
      if (!__hasOwnProp.call(to, key) && key !== except)
        __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
  }
  return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var detector_exports = {};
__export(detector_exports, {
  TurnDetector: () => TurnDetector,
  TurnDetectorStreamImpl: () => TurnDetectorStreamImpl
});
module.exports = __toCommonJS(detector_exports);
var import_job = require("../../job.cjs");
var import_log = require("../../log.cjs");
var import_types = require("../../types.cjs");
var import_utils = require("../../utils.cjs");
var import_utils2 = require("../utils.cjs");
var import_base = require("./base.cjs");
var import_languages = require("./languages.cjs");
var import_transports = require("./transports.cjs");
class TurnDetector extends import_base.BaseStreamingTurnDetector {
  _model;
  _cloudOpts;
  _executor;
  constructor(opts = {}) {
    const auto = opts.version === void 0;
    const resolvedVersion = opts.version ?? ((0, import_utils.isHosted)() || (0, import_utils.isDevMode)() ? "v1" : "v1-mini");
    let resolvedModel = `turn-detector-${resolvedVersion}`;
    let cloudOpts;
    if (resolvedVersion === "v1") {
      const baseUrl = (0, import_utils.resolveEnvVar)(
        opts.baseUrl,
        ["LIVEKIT_INFERENCE_URL"],
        (0, import_utils2.getDefaultInferenceUrl)()
      );
      const apiKey = (0, import_utils.resolveEnvVar)(opts.apiKey, ["LIVEKIT_INFERENCE_API_KEY", "LIVEKIT_API_KEY"]);
      const apiSecret = (0, import_utils.resolveEnvVar)(opts.apiSecret, [
        "LIVEKIT_INFERENCE_API_SECRET",
        "LIVEKIT_API_SECRET"
      ]);
      const missing = [];
      if (!baseUrl) missing.push("LIVEKIT_INFERENCE_URL");
      if (!apiKey) missing.push("LIVEKIT_API_KEY");
      if (!apiSecret) missing.push("LIVEKIT_API_SECRET");
      if (missing.length > 0) {
        if (auto) {
          (0, import_log.log)().warn(
            { missing },
            "LIVEKIT_INFERENCE_URL is set but creds are missing; falling back to 'v1-mini'"
          );
          resolvedModel = "turn-detector-v1-mini";
        } else {
          throw new Error(
            `TurnDetector(version='v1') requires ${missing.join(", ")} (env or constructor argument).`
          );
        }
      } else {
        cloudOpts = {
          baseUrl,
          apiKey,
          apiSecret,
          connOptions: opts.connOptions ?? import_types.DEFAULT_API_CONNECT_OPTIONS
        };
      }
    }
    const detectorOpts = {
      sampleRate: opts.sampleRate ?? import_base.DEFAULT_SAMPLE_RATE,
      thresholds: new import_languages.ThresholdOptions(
        resolvedModel,
        opts.unlikelyThreshold,
        opts.backchannelThreshold
      )
    };
    super(detectorOpts);
    this._model = resolvedModel;
    this._cloudOpts = cloudOpts;
    this._warnThresholdOverride();
    if (opts.executor !== void 0) {
      this._executor = opts.executor;
    } else {
      try {
        this._executor = (0, import_job.getJobContext)().inferenceExecutor;
      } catch {
        this._executor = void 0;
      }
    }
  }
  /** Current model name. Starts at the construction-time selection and flips to
   * `'turn-detector-v1-mini'` after a cloud→local fallback: the detector and its
   * (single) active stream share one mutable `ThresholdOptions`, and the
   * stream writes the swap back here so EOU metrics and `audio_recognition`
   * see a consistent view. The fallback is one-way and sticky. */
  get model() {
    return this._model;
  }
  /** @internal Written by the active stream on cloud→local fallback. */
  _setModel(model) {
    this._model = model;
  }
  /**
   * Plain config snapshot for serialization and observability.
   */
  toJSON() {
    return {
      model: this._model,
      sampleRate: this._opts.sampleRate,
      thresholds: this._opts.thresholds.toJSON(),
      cloud: this._cloudOpts ? { baseUrl: this._cloudOpts.baseUrl, connOptions: this._cloudOpts.connOptions } : void 0
    };
  }
  _warnThresholdOverride() {
    const overrides = this._opts.thresholds.overrides;
    if (overrides !== void 0) {
      (0, import_log.log)().warn(
        { unlikelyThreshold: overrides },
        "a non-default turn detection threshold was provided; the server provides calibrated defaults and overriding them may be suboptimal"
      );
    }
    const bcOverrides = this._opts.thresholds.backchannelOverrides;
    if (bcOverrides !== void 0) {
      (0, import_log.log)().warn(
        { backchannelThreshold: bcOverrides },
        "a non-default backchannel threshold was provided; the server provides calibrated defaults and overriding them may be suboptimal"
      );
    }
  }
  /** Replace the user threshold override at runtime. The shared
   * `ThresholdOptions` re-resolves against the current (server or shipped)
   * defaults, so an active stream picks it up immediately. */
  updateOptions(opts = {}) {
    if (opts.unlikelyThreshold !== void 0) {
      this._opts.thresholds.updateOverrides(opts.unlikelyThreshold);
    }
    if (opts.backchannelThreshold !== void 0) {
      this._opts.thresholds.updateBackchannelOverrides(opts.backchannelThreshold);
    }
    this._warnThresholdOverride();
  }
  stream(opts = {}) {
    const cloudOpts = this._cloudOpts !== void 0 ? { ...this._cloudOpts, connOptions: opts.connOptions ?? this._cloudOpts.connOptions } : void 0;
    const stream = new TurnDetectorStreamImpl({
      detector: this,
      opts: this._opts,
      cloudOpts,
      model: this._model,
      executor: this._executor
    });
    this._streams.add(stream);
    return stream;
  }
}
class TurnDetectorStreamImpl extends import_base.BaseStreamingTurnDetectorStream {
  _model;
  _cloudOpts;
  _executor;
  _isFallback = false;
  _warnedCloudFailure = false;
  _warnedLocalFailure = false;
  _detLogger = (0, import_log.log)();
  constructor(args) {
    const transport = args.transport ?? (args.model === "turn-detector-v1" ? new import_transports.CloudTransport({
      detector: args.detector,
      opts: args.opts,
      cloudOpts: args.cloudOpts
    }) : new import_transports.LocalTransport({ opts: args.opts, executor: args.executor }));
    super({ detector: args.detector, opts: args.opts, transport });
    this._model = args.model;
    this._cloudOpts = args.cloudOpts;
    this._executor = args.executor;
  }
  /** This stream's *current* model name (flips to `'turn-detector-v1-mini'`
   * after a cloud→local fallback). The swap is also written back to the owning
   * detector, which shares this stream's mutable `ThresholdOptions`. */
  get model() {
    return this._model;
  }
  get isFallback() {
    return this._isFallback;
  }
  /** @internal Test-visible. */
  get warnedCloudFailure() {
    return this._warnedCloudFailure;
  }
  /** @internal Test-visible. */
  get warnedLocalFailure() {
    return this._warnedLocalFailure;
  }
  /** @internal Test-visible. */
  get transport() {
    return this._transport;
  }
  /** @internal Test-visible: same logic as the path taken when `_run` catches
   * a cloud transport error. Tests call this directly to verify the warning
   * dedupe across multiple invocations on the same stream. */
  _fallBackToLocal(reason) {
    if (!this._warnedCloudFailure) {
      this._detLogger.warn(
        { reason: reason.message },
        "cloud turn detector failed; falling back to local mini model"
      );
      this._warnedCloudFailure = true;
    }
    this._emitDefaultForInflight();
    try {
      this._transport.detach();
    } catch {
    }
    this._opts.thresholds._toLocalFallback();
    if (this._detector instanceof TurnDetector) {
      this._detector._setModel("turn-detector-v1-mini");
    }
    this._transport = new import_transports.LocalTransport({ opts: this._opts, executor: this._executor });
    this._transport.attach(this);
    this._model = "turn-detector-v1-mini";
    this._isFallback = true;
  }
  /** @internal Test-visible: same logic as the path taken when `_run` sees a
   * local transport error. */
  _onLocalFailure(reason) {
    if (!this._warnedLocalFailure) {
      this._detLogger.warn(
        { reason: reason.message },
        "local audio turn detector failed; defaulting to 1.0 and retrying on next turn"
      );
      this._warnedLocalFailure = true;
    }
    this._emitDefaultForInflight();
  }
  _emitDefaultForInflight() {
    const requestId = this._requestId;
    if (requestId !== void 0) {
      this._resolvePrediction(requestId, 1);
    }
  }
  async aclose() {
    try {
      this._transport.detach();
    } catch {
    }
    await super.aclose();
  }
  async _run() {
    while (true) {
      try {
        await this._raceWithSwap(this._transport.run());
        return;
      } catch (err) {
        if (err instanceof import_base.SwapAbortError) {
          if (this._closing) return;
          continue;
        }
        const e = err instanceof Error ? err : new Error(String(err));
        if (this._model === "turn-detector-v1") {
          this._fallBackToLocal(e);
          continue;
        }
        this._onLocalFailure(e);
        return;
      }
    }
  }
  _onPredictTimeout() {
    if (this._model === "turn-detector-v1") {
      this._signalSwap();
      this._fallBackToLocal(new Error("predict_end_of_turn"));
    }
  }
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
  TurnDetector,
  TurnDetectorStreamImpl
});
//# sourceMappingURL=detector.cjs.map