"use strict";
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
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 __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
  // If the importer is in node compatibility mode or this is not an ESM
  // file that has been converted to a CommonJS file using a Babel-
  // compatible transform (i.e. "__esModule" has not been set), then set
  // "default" to the CommonJS "module.exports" for node compatibility.
  isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
  mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var utils_exports = {};
__export(utils_exports, {
  computeChatCtxDiff: () => computeChatCtxDiff,
  createToolOptions: () => createToolOptions,
  executeToolCall: () => executeToolCall,
  formatChatHistory: () => formatChatHistory,
  oaiBuildFunctionInfo: () => oaiBuildFunctionInfo,
  oaiParams: () => oaiParams,
  parseFunctionArguments: () => parseFunctionArguments,
  serializeImage: () => serializeImage,
  toJsonSchema: () => toJsonSchema,
  validateChatContextStructure: () => validateChatContextStructure
});
module.exports = __toCommonJS(utils_exports);
var import_rtc_node = require("@livekit/rtc-node");
var import_jsonrepair = require("jsonrepair");
var import_sharp = __toESM(require("sharp"), 1);
var import_chat_context = require("./chat_context.cjs");
var import_zod_utils = require("./zod-utils.cjs");
function getChannelsFromVideoBufferType(type) {
  switch (type) {
    case import_rtc_node.VideoBufferType.RGBA:
    case import_rtc_node.VideoBufferType.ABGR:
    case import_rtc_node.VideoBufferType.ARGB:
    case import_rtc_node.VideoBufferType.BGRA:
      return 4;
    case import_rtc_node.VideoBufferType.RGB24:
      return 3;
    default:
      throw new Error(`Unsupported VideoBufferType: ${type}. Only RGB/RGBA formats are supported.`);
  }
}
function ensureRGBCompatible(frame) {
  if (frame.type === import_rtc_node.VideoBufferType.RGBA || frame.type === import_rtc_node.VideoBufferType.BGRA || frame.type === import_rtc_node.VideoBufferType.ARGB || frame.type === import_rtc_node.VideoBufferType.ABGR || frame.type === import_rtc_node.VideoBufferType.RGB24) {
    return frame;
  }
  try {
    return frame.convert(import_rtc_node.VideoBufferType.RGBA);
  } catch (error) {
    throw new Error(
      `Failed to convert format ${frame.type} to RGB: ${error}. Consider using RGB/RGBA formats or converting on the client side.`
    );
  }
}
async function serializeImage(image) {
  var _a;
  if (typeof image.image === "string") {
    if (image.image.startsWith("data:")) {
      const [header, base64Data] = image.image.split(",", 2);
      const headerParts = header.split(";");
      const mimeParts = (_a = headerParts[0]) == null ? void 0 : _a.split(":");
      const headerMime = mimeParts == null ? void 0 : mimeParts[1];
      if (!headerMime) {
        throw new Error("Invalid data URL format");
      }
      let mimeType;
      if (image.mimeType && image.mimeType !== headerMime) {
        console.warn(
          `Provided mimeType '${image.mimeType}' does not match data URL mime type '${headerMime}'. Using provided mimeType.`
        );
        mimeType = image.mimeType;
      } else {
        mimeType = headerMime;
      }
      const supportedTypes = /* @__PURE__ */ new Set(["image/jpeg", "image/png", "image/webp", "image/gif"]);
      if (!supportedTypes.has(mimeType)) {
        throw new Error(`Unsupported mimeType ${mimeType}. Must be jpeg, png, webp, or gif`);
      }
      return {
        base64Data,
        mimeType,
        inferenceDetail: image.inferenceDetail
      };
    }
    return {
      mimeType: image.mimeType,
      inferenceDetail: image.inferenceDetail,
      externalUrl: image.image
    };
  } else if (image.image instanceof import_rtc_node.VideoFrame) {
    const frame = ensureRGBCompatible(image.image);
    const channels = getChannelsFromVideoBufferType(frame.type);
    let encoded = (0, import_sharp.default)(frame.data, {
      raw: {
        width: frame.width,
        height: frame.height,
        channels
      }
    });
    if (image.inferenceWidth && image.inferenceHeight) {
      encoded = encoded.resize(image.inferenceWidth, image.inferenceHeight);
    }
    const base64Data = await encoded.png().toBuffer().then((buffer) => buffer.toString("base64"));
    return {
      base64Data,
      mimeType: "image/png",
      inferenceDetail: image.inferenceDetail
    };
  } else {
    throw new Error("Unsupported image type");
  }
}
const createToolOptions = (toolCallId, userData = {}) => {
  return {
    ctx: { userData },
    toolCallId,
    abortSignal: new AbortController().signal
  };
};
const oaiParams = (schema, isOpenai = true) => {
  const jsonSchema = (0, import_zod_utils.zodSchemaToJsonSchema)(schema, isOpenai);
  const { properties, required, additionalProperties } = jsonSchema;
  return {
    type: "object",
    properties,
    required,
    additionalProperties
  };
};
const oaiBuildFunctionInfo = (toolCtx, toolCallId, toolName, rawArgs) => {
  const tool = toolCtx[toolName];
  if (!tool) {
    throw new Error(`AI tool ${toolName} not found`);
  }
  return import_chat_context.FunctionCall.create({
    callId: toolCallId,
    name: toolName,
    args: rawArgs
  });
};
const templateTokenPatterns = [
  /<\|[^<>|]{0,40}\|>/g,
  /<\|[^<>a-zA-Z0-9_]{0,10}/g,
  /[^<>a-zA-Z0-9_]{0,10}\|>/g,
  /<(?:start|end)_of_turn>/g
];
function stripTemplateTokens(value) {
  if (typeof value === "string") {
    return templateTokenPatterns.reduce((out, pattern) => out.replace(pattern, ""), value).trim();
  }
  if (Array.isArray(value)) {
    return value.map(stripTemplateTokens).filter((item) => item !== "" && item !== null);
  }
  if (value && typeof value === "object") {
    return Object.fromEntries(
      Object.entries(value).map(([key, nestedValue]) => [key, stripTemplateTokens(nestedValue)])
    );
  }
  return value;
}
function parseFunctionArguments(rawArgs) {
  let args;
  const rawForError = typeof rawArgs === "string" ? rawArgs : JSON.stringify(rawArgs);
  if (typeof rawArgs === "string") {
    try {
      args = JSON.parse(rawArgs);
    } catch (strictError) {
      let repaired;
      try {
        repaired = JSON.parse((0, import_jsonrepair.jsonrepair)(rawArgs));
      } catch {
        throw new Error(
          `could not parse function arguments as JSON: ${strictError}: ${rawForError.slice(0, 200)}`
        );
      }
      args = stripTemplateTokens(repaired);
    }
  } else {
    args = rawArgs;
  }
  while (typeof args === "string") {
    const stringArgs = args;
    try {
      args = JSON.parse(stringArgs);
    } catch {
      throw new Error(
        `function arguments decoded to a non-JSON string: ${stringArgs.slice(0, 200)}`
      );
    }
  }
  if (args === null) {
    return {};
  }
  if (typeof args !== "object" || Array.isArray(args)) {
    throw new Error(`expected object from function arguments: ${rawForError.slice(0, 200)}`);
  }
  return args;
}
async function executeToolCall(toolCall, toolCtx) {
  const tool = toolCtx[toolCall.name];
  let args;
  let params;
  try {
    const rawArgs = toolCall.args || "{}";
    args = parseFunctionArguments(rawArgs);
    const canonicalArgs = JSON.stringify(args);
    if (canonicalArgs !== rawArgs) {
      toolCall.args = canonicalArgs;
    }
  } catch (error) {
    return import_chat_context.FunctionCallOutput.create({
      callId: toolCall.callId,
      output: `Invalid JSON: ${error}`,
      isError: true
    });
  }
  try {
    if ((0, import_zod_utils.isZodSchema)(tool.parameters)) {
      const result = await (0, import_zod_utils.parseZodSchema)(tool.parameters, args);
      if (result.success) {
        params = result.data;
      } else {
        return import_chat_context.FunctionCallOutput.create({
          callId: toolCall.callId,
          output: `Arguments parsing failed: ${result.error}`,
          isError: true
        });
      }
    } else {
      params = args;
    }
  } catch (error) {
    return import_chat_context.FunctionCallOutput.create({
      callId: toolCall.callId,
      output: `Arguments parsing failed: ${error}`,
      isError: true
    });
  }
  try {
    const result = await tool.execute(params, createToolOptions(toolCall.callId));
    return import_chat_context.FunctionCallOutput.create({
      callId: toolCall.callId,
      output: JSON.stringify(result),
      isError: false
    });
  } catch (error) {
    return import_chat_context.FunctionCallOutput.create({
      callId: toolCall.callId,
      output: `Tool execution failed: ${error}`,
      isError: true
    });
  }
}
function formatChatHistory(chatCtx, options = {}) {
  const { includeIds = false, includeTimestamps = false } = options;
  if (chatCtx.items.length === 0) {
    return "Chat history (0 items)";
  }
  const formattedItems = chatCtx.items.map(
    (item, index) => formatChatHistoryItem(item, index, {
      includeIds,
      includeTimestamps
    })
  );
  return [
    `Chat history (${chatCtx.items.length} items)`,
    ...formattedItems.flatMap((item) => ["", item])
  ].join("\n");
}
function validateChatContextStructure(chatCtx) {
  const issues = [];
  const ids = /* @__PURE__ */ new Set();
  const seenFunctionCallIds = /* @__PURE__ */ new Set();
  let previousCreatedAt = -Infinity;
  const pushIssue = (issue) => {
    issues.push(issue);
  };
  for (let index = 0; index < chatCtx.items.length; index += 1) {
    const item = chatCtx.items[index];
    if (ids.has(item.id)) {
      pushIssue({
        severity: "error",
        code: "duplicate_id",
        index,
        itemId: item.id,
        message: `Duplicate item id '${item.id}'`
      });
    } else {
      ids.add(item.id);
    }
    if (item.createdAt < previousCreatedAt) {
      pushIssue({
        severity: "error",
        code: "timestamp_order",
        index,
        itemId: item.id,
        message: `Item createdAt (${item.createdAt}) is older than previous item (${previousCreatedAt})`
      });
    }
    previousCreatedAt = item.createdAt;
    if (item.type === "message") {
      if (item.content.length === 0) {
        pushIssue({
          severity: "warning",
          code: "empty_message_content",
          index,
          itemId: item.id,
          message: "Message has empty content array"
        });
      }
      item.content.forEach((term, termIndex) => {
        if (typeof term === "string") {
          if (term.trim().length === 0) {
            pushIssue({
              severity: "warning",
              code: "empty_text_term",
              index,
              itemId: item.id,
              message: `Message term[${termIndex}] is empty text`
            });
          }
          return;
        }
        if (term.type === "instructions") {
          return;
        }
        if (term.type === "image_content") {
          if (!term.id || term.image === void 0 || term.image === null) {
            pushIssue({
              severity: "error",
              code: "missing_image_term",
              index,
              itemId: item.id,
              message: `Message term[${termIndex}] has invalid image content`
            });
          }
          return;
        }
        if (!Array.isArray(term.frame)) {
          pushIssue({
            severity: "error",
            code: "invalid_audio_term",
            index,
            itemId: item.id,
            message: `Message term[${termIndex}] has invalid audio content`
          });
        }
      });
    } else if (item.type === "function_call") {
      if (!item.name || !item.callId) {
        pushIssue({
          severity: "error",
          code: "invalid_function_call",
          index,
          itemId: item.id,
          message: "Function call is missing name or callId"
        });
      } else {
        seenFunctionCallIds.add(item.callId);
      }
      try {
        JSON.parse(item.args);
      } catch {
        pushIssue({
          severity: "warning",
          code: "invalid_function_call_args",
          index,
          itemId: item.id,
          message: "Function call args are not valid JSON"
        });
      }
    } else if (item.type === "function_call_output") {
      if (!item.callId) {
        pushIssue({
          severity: "error",
          code: "invalid_function_call_output",
          index,
          itemId: item.id,
          message: "Function call output is missing callId"
        });
      } else if (!seenFunctionCallIds.has(item.callId)) {
        pushIssue({
          severity: "warning",
          code: "orphan_function_call_output",
          index,
          itemId: item.id,
          message: `Function call output references unknown callId '${item.callId}'`
        });
      }
    }
  }
  const errors = issues.filter((issue) => issue.severity === "error").length;
  const warnings = issues.length - errors;
  return {
    valid: errors === 0,
    errors,
    warnings,
    issues
  };
}
function formatChatHistoryItem(item, index, options) {
  const headerParts = [`[${index}]`];
  if (item.type === "message") {
    headerParts.push("message", item.role);
  } else if (item.type === "function_call") {
    headerParts.push("function_call", item.name, `call_id=${item.callId}`);
  } else if (item.type === "function_call_output") {
    headerParts.push("function_call_output", item.name || "(unnamed)", `call_id=${item.callId}`);
    if (item.isError) {
      headerParts.push("error=true");
    }
  } else if (item.type === "agent_handoff") {
    headerParts.push("agent_handoff");
  } else {
    headerParts.push("agent_config_update");
  }
  if (options.includeIds) {
    headerParts.push(`id=${item.id}`);
  }
  if (options.includeTimestamps) {
    headerParts.push(`created_at=${item.createdAt.toFixed(3)}`);
  }
  const body = formatChatHistoryItemBody(item);
  if (!body) {
    return headerParts.join(" ");
  }
  return `${headerParts.join(" ")}
${indentBlock(body, "  ")}`;
}
function formatChatHistoryItemBody(item) {
  if (item.type === "message") {
    const content = item.content.map((part) => formatMessageContentPart(part)).join("\n");
    return content.trim() ? content : "(empty)";
  }
  if (item.type === "function_call") {
    return prettyJsonText(item.args);
  }
  if (item.type === "function_call_output") {
    return prettyJsonText(item.output);
  }
  if (item.type === "agent_handoff") {
    return `${item.oldAgentId ?? "(none)"} -> ${item.newAgentId}`;
  }
  return JSON.stringify(
    {
      instructions: item.instructions,
      toolsAdded: item.toolsAdded,
      toolsRemoved: item.toolsRemoved
    },
    null,
    2
  );
}
function formatMessageContentPart(part) {
  if (typeof part === "string") {
    return part;
  }
  if (part.type === "instructions") {
    return part.value;
  }
  if (part.type === "image_content") {
    if (typeof part.image === "string") {
      return `[image url=${truncateText(part.image, 120)}]`;
    }
    return `[image frame=${part.image.width}x${part.image.height}]`;
  }
  if (part.transcript) {
    return `[audio transcript=${JSON.stringify(truncateText(part.transcript, 120))}]`;
  }
  return `[audio frames=${part.frame.length}]`;
}
function prettyJsonText(text) {
  try {
    return JSON.stringify(JSON.parse(text), null, 2);
  } catch {
    return text;
  }
}
function truncateText(text, maxLength) {
  if (text.length <= maxLength) {
    return text;
  }
  return `${text.slice(0, Math.max(0, maxLength - 3))}...`;
}
function indentBlock(text, indent) {
  return text.split("\n").map((line) => `${indent}${line}`).join("\n");
}
function computeLCS(oldIds, newIds) {
  const n = oldIds.length;
  const m = newIds.length;
  const dp = Array(n + 1).fill(null).map(() => Array(m + 1).fill(0));
  for (let i2 = 1; i2 <= n; i2++) {
    for (let j2 = 1; j2 <= m; j2++) {
      if (oldIds[i2 - 1] === newIds[j2 - 1]) {
        dp[i2][j2] = dp[i2 - 1][j2 - 1] + 1;
      } else {
        dp[i2][j2] = Math.max(dp[i2 - 1][j2], dp[i2][j2 - 1]);
      }
    }
  }
  const lcsIds = [];
  let i = n;
  let j = m;
  while (i > 0 && j > 0) {
    if (oldIds[i - 1] === newIds[j - 1]) {
      lcsIds.push(oldIds[i - 1]);
      i--;
      j--;
    } else if (dp[i - 1][j] > dp[i][j - 1]) {
      i--;
    } else {
      j--;
    }
  }
  return lcsIds.reverse();
}
function computeChatCtxDiff(oldCtx, newCtx) {
  const oldIds = oldCtx.items.map((item) => item.id);
  const newIds = newCtx.items.map((item) => item.id);
  const lcsIds = new Set(computeLCS(oldIds, newIds));
  const toRemove = oldCtx.items.filter((msg) => !lcsIds.has(msg.id)).map((msg) => msg.id);
  const toCreate = [];
  let lastIdInSequence = null;
  for (const newItem of newCtx.items) {
    if (lcsIds.has(newItem.id)) {
      lastIdInSequence = newItem.id;
    } else {
      const prevId = lastIdInSequence;
      toCreate.push([prevId, newItem.id]);
      lastIdInSequence = newItem.id;
    }
  }
  return {
    toRemove,
    toCreate
  };
}
function toJsonSchema(schema, isOpenai = true, strict = false) {
  if ((0, import_zod_utils.isZodSchema)(schema)) {
    return (0, import_zod_utils.zodSchemaToJsonSchema)(schema, isOpenai, strict);
  }
  return schema;
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
  computeChatCtxDiff,
  createToolOptions,
  executeToolCall,
  formatChatHistory,
  oaiBuildFunctionInfo,
  oaiParams,
  parseFunctionArguments,
  serializeImage,
  toJsonSchema,
  validateChatContextStructure
});
//# sourceMappingURL=utils.cjs.map