import {
  describeImageFromDataUrl
} from "./chunk-SZKEGZIB.js";
import {
  logger
} from "./chunk-K44MW7JJ.js";

// src/server/llm/backend.ts
var BACKEND_CAPABILITIES = {
  vllm: {
    supportsReasoningField: true,
    supportsChatTemplateKwargs: true,
    supportsTopK: true
  },
  sglang: {
    supportsReasoningField: true,
    supportsChatTemplateKwargs: true,
    supportsTopK: true
  },
  ollama: {
    supportsReasoningField: false,
    supportsChatTemplateKwargs: false,
    supportsTopK: false
  },
  llamacpp: {
    supportsReasoningField: false,
    supportsChatTemplateKwargs: false,
    supportsTopK: true
  },
  "opencode-go": {
    supportsReasoningField: false,
    supportsChatTemplateKwargs: false,
    supportsTopK: true
  },
  unknown: {
    // Assume vLLM-like for unknown backends
    supportsReasoningField: true,
    supportsChatTemplateKwargs: true,
    supportsTopK: true
  }
};
function getBackendCapabilities(backend) {
  return BACKEND_CAPABILITIES[backend];
}
async function detectBackend(baseUrl, explicitBackend, silent = false) {
  if (explicitBackend && explicitBackend !== "unknown") {
    if (silent) {
      logger.debug("Using explicit backend", { backend: explicitBackend });
    } else {
      logger.info("Using explicit backend", { backend: explicitBackend });
    }
    return explicitBackend;
  }
  const probeUrl = baseUrl.replace(/\/v1\/?$/, "");
  try {
    if (await probeOllama(probeUrl)) {
      if (silent) {
        logger.debug("Detected Ollama backend", { url: probeUrl });
      } else {
        logger.info("Detected Ollama backend", { url: probeUrl });
      }
      return "ollama";
    }
    if (await probeLlamaCpp(probeUrl)) {
      if (silent) {
        logger.debug("Detected llama.cpp backend", { url: probeUrl });
      } else {
        logger.info("Detected llama.cpp backend", { url: probeUrl });
      }
      return "llamacpp";
    }
    if (await probeSGLang(probeUrl)) {
      if (silent) {
        logger.debug("Detected SGLang backend", { url: probeUrl });
      } else {
        logger.info("Detected SGLang backend", { url: probeUrl });
      }
      return "sglang";
    }
    if (await probeOpenAI(baseUrl)) {
      if (silent) {
        logger.debug("Detected vLLM backend (OpenAI-compatible)", { url: baseUrl });
      } else {
        logger.info("Detected vLLM backend (OpenAI-compatible)", { url: baseUrl });
      }
      return "vllm";
    }
    if (silent) {
      logger.debug("Could not detect backend, using unknown", { url: baseUrl });
    } else {
      logger.warn("Could not detect backend, using unknown", { url: baseUrl });
    }
    return "unknown";
  } catch (error) {
    if (silent) {
      logger.debug("Backend detection failed", {
        url: baseUrl,
        error: error instanceof Error ? error.message : String(error)
      });
    } else {
      logger.warn("Backend detection failed", {
        url: baseUrl,
        error: error instanceof Error ? error.message : String(error)
      });
    }
    return "unknown";
  }
}
async function probeOllama(baseUrl) {
  try {
    const response = await fetch(`${baseUrl}/api/tags`, {
      signal: AbortSignal.timeout(5e3)
    });
    return response.ok;
  } catch {
    return false;
  }
}
async function probeLlamaCpp(baseUrl) {
  try {
    const response = await fetch(`${baseUrl}/health`, {
      signal: AbortSignal.timeout(5e3)
    });
    if (!response.ok) return false;
    const data = await response.json();
    return "slots_idle" in data || "slots_processing" in data;
  } catch {
    return false;
  }
}
async function probeSGLang(baseUrl) {
  try {
    const response = await fetch(`${baseUrl}/get_model_info`, {
      signal: AbortSignal.timeout(5e3)
    });
    return response.ok;
  } catch {
    return false;
  }
}
async function probeOpenAI(baseUrl) {
  try {
    const url = baseUrl.includes("/v1") ? baseUrl : `${baseUrl}/v1`;
    const response = await fetch(`${url}/models`, {
      signal: AbortSignal.timeout(5e3)
    });
    return response.ok;
  } catch {
    return false;
  }
}
function getBackendDisplayName(backend) {
  switch (backend) {
    case "vllm":
      return "vLLM";
    case "sglang":
      return "SGLang";
    case "ollama":
      return "Ollama";
    case "llamacpp":
      return "llama.cpp";
    case "opencode-go":
      return "OpenCode Go";
    case "unknown":
      return "Unknown";
  }
}

// src/server/llm/profiles.ts
var DEFAULT_PROFILE = {
  name: "default",
  temperature: 0.7,
  topP: 0.9,
  supportsReasoning: false,
  reasoningAsContent: false,
  defaultMaxTokens: 4096,
  supportsVision: true
};
var MOCK_PROFILE = {
  name: "mock",
  temperature: 0.7,
  topP: 0.9,
  supportsReasoning: false,
  reasoningAsContent: false,
  defaultMaxTokens: 1024,
  supportsVision: false
};
var MODEL_PROFILES = [
  {
    pattern: "mistral",
    profile: {
      name: "Mistral",
      temperature: 0.7,
      topP: 0.9,
      supportsReasoning: false,
      reasoningAsContent: false,
      defaultMaxTokens: 16384,
      supportsVision: false
    }
  },
  {
    pattern: "qwen3-coder-next",
    profile: {
      name: "Qwen3-Coder-Next",
      // Per Qwen docs: "temperature=1.0, top_p=0.95, top_k=40"
      temperature: 1,
      topP: 0.95,
      topK: 40,
      // "This model supports only non-thinking mode and does not generate <think></think> blocks"
      supportsReasoning: false,
      reasoningAsContent: false,
      defaultMaxTokens: 16384,
      supportsVision: false
    }
  },
  {
    pattern: "qwen3",
    profile: {
      name: "Qwen3",
      temperature: 0.7,
      topP: 0.9,
      supportsReasoning: true,
      reasoningAsContent: false,
      defaultMaxTokens: 16384,
      supportsVision: false
    }
  },
  {
    pattern: "qwen3-vl",
    profile: {
      name: "Qwen3-VL",
      temperature: 0.7,
      topP: 0.9,
      supportsReasoning: true,
      reasoningAsContent: false,
      defaultMaxTokens: 16384,
      supportsVision: true
    }
  },
  {
    pattern: "deepseek",
    profile: {
      name: "DeepSeek",
      temperature: 0.6,
      topP: 0.95,
      supportsReasoning: true,
      reasoningAsContent: false,
      defaultMaxTokens: 16384,
      supportsVision: false
    }
  },
  {
    pattern: "minimax-m2.5",
    profile: {
      name: "MiniMax-M2.5",
      temperature: 1,
      topP: 0.95,
      topK: 40,
      supportsReasoning: true,
      reasoningAsContent: false,
      defaultMaxTokens: 16384,
      supportsVision: false
    }
  },
  {
    pattern: "minimax-m2.7",
    profile: {
      name: "MiniMax-M2.7",
      temperature: 1,
      topP: 0.95,
      topK: 40,
      supportsReasoning: true,
      reasoningAsContent: false,
      defaultMaxTokens: 16384,
      supportsVision: false
    }
  },
  {
    pattern: "minimax",
    profile: {
      name: "MiniMax",
      temperature: 1,
      topP: 0.95,
      topK: 40,
      supportsReasoning: true,
      reasoningAsContent: false,
      defaultMaxTokens: 16384,
      supportsVision: false
    }
  },
  {
    pattern: "minimax-m3",
    profile: {
      name: "MiniMax-M3",
      temperature: 1,
      topP: 0.95,
      topK: 40,
      supportsReasoning: true,
      reasoningAsContent: false,
      defaultMaxTokens: 16384,
      supportsVision: true
    }
  },
  {
    pattern: "llava",
    profile: {
      name: "LLaVA",
      temperature: 0.7,
      topP: 0.9,
      supportsReasoning: false,
      reasoningAsContent: false,
      defaultMaxTokens: 16384,
      supportsVision: true
    }
  },
  {
    pattern: "llama",
    profile: {
      name: "Llama",
      temperature: 0.7,
      topP: 0.9,
      supportsReasoning: false,
      reasoningAsContent: false,
      defaultMaxTokens: 16384,
      supportsVision: false
    }
  },
  {
    pattern: "claude",
    profile: {
      name: "Claude",
      temperature: 0.7,
      topP: 0.9,
      supportsReasoning: true,
      reasoningAsContent: false,
      defaultMaxTokens: 16384,
      supportsVision: false
    }
  },
  {
    pattern: "gemma-4",
    profile: {
      name: "Gemma 4",
      temperature: 0.7,
      topP: 0.9,
      supportsReasoning: true,
      reasoningAsContent: false,
      defaultMaxTokens: 16384,
      supportsVision: true
    }
  }
];
function getModelProfile(modelName) {
  const lowerName = modelName.toLowerCase();
  if (lowerName.includes("mock")) {
    return MOCK_PROFILE;
  }
  for (const { pattern, profile } of MODEL_PROFILES) {
    if (lowerName.includes(pattern.toLowerCase())) {
      return profile;
    }
  }
  return DEFAULT_PROFILE;
}

// src/server/llm/client-pure.ts
function buildModelParams(params) {
  return {
    ...params.temperature !== void 0 && { temperature: params.temperature },
    ...params.topP !== void 0 && { topP: params.topP },
    ...params.topK !== void 0 && { topK: params.topK },
    ...params.maxTokens !== void 0 && { maxTokens: params.maxTokens }
  };
}
function buildAttachmentContent(msgContent, attachments, modelSupportsVision) {
  const content = [];
  if (msgContent?.trim()) {
    content.push({ type: "text", text: msgContent });
  }
  for (const attachment of attachments) {
    content.push(convertAttachmentSync(attachment, modelSupportsVision));
  }
  return content;
}
async function convertMessagesWithOptions(messages, profile, visionFallbackEnabled, userVisionOverride, signal, onVisionFallbackStart, onVisionFallbackDone) {
  const modelSupportsVision = userVisionOverride ?? profile.supportsVision ?? false;
  const options = {
    modelSupportsVision,
    visionFallbackEnabled,
    signal,
    onVisionFallbackStart,
    onVisionFallbackDone
  };
  return needsVisionFallback(messages, modelSupportsVision, visionFallbackEnabled) ? await convertMessagesWithFallback(messages, options) : convertMessages(messages, { modelSupportsVision, visionFallbackEnabled: false });
}
function convertToolCalls(toolCalls) {
  return toolCalls.map((toolCall) => ({
    id: toolCall.id,
    type: "function",
    function: {
      name: toolCall.name,
      arguments: JSON.stringify(toolCall.arguments)
    }
  }));
}
function buildAssistantMessage(msg) {
  const result = {
    role: "assistant",
    content: msg.content || null
  };
  if (msg.toolCalls?.length) {
    result["tool_calls"] = convertToolCalls(msg.toolCalls);
  }
  if (msg.thinkingContent) {
    result["reasoning_content"] = msg.thinkingContent;
  }
  return result;
}
function convertAttachmentSync(attachment, modelSupportsVision) {
  if (modelSupportsVision) {
    return {
      type: "image_url",
      image_url: { url: attachment.data }
    };
  }
  return {
    type: "text",
    text: `[Image: ${attachment.filename || "image"}] (vision not supported, cannot describe)`
  };
}
function createAttachmentForConversion(data, filename, id) {
  return {
    data,
    ...filename !== void 0 && { filename },
    ...id !== void 0 && { id }
  };
}
async function convertAttachmentWithFallback(attachment, options) {
  logger.debug("[VisionFallback] convertAttachmentWithFallback called", {
    filename: attachment.filename,
    id: attachment.id,
    hasCallbacks: !!options.onVisionFallbackStart
  });
  if (options.modelSupportsVision) {
    logger.debug("[VisionFallback] Model supports vision - passing image directly");
    return {
      type: "image_url",
      image_url: { url: attachment.data }
    };
  }
  if (!options.visionFallbackEnabled) {
    logger.debug("[VisionFallback] Fallback disabled - returning placeholder");
    return {
      type: "text",
      text: `[Image: ${attachment.filename || "image"}] (vision not supported)`
    };
  }
  const attachmentId = attachment.id ?? crypto.randomUUID();
  const filename = attachment.filename;
  logger.debug("[VisionFallback] Starting delegation for:", { attachmentId, filename });
  options.onVisionFallbackStart?.(attachmentId, filename);
  const context = filename ? `File: ${filename}` : void 0;
  const description = await describeImageFromDataUrl(attachment.data, { context, signal: options.signal });
  logger.debug("[VisionFallback] Delegation complete:", { attachmentId, descriptionLength: description.length });
  options.onVisionFallbackDone?.(attachmentId, description);
  return {
    type: "text",
    text: `[Image: ${attachment.filename || "image"}] ${description}`
  };
}
async function buildAttachmentContentWithFallback(msgContent, attachments, options) {
  const content = [];
  if (msgContent?.trim()) {
    content.push({ type: "text", text: msgContent });
  }
  for (const attachment of attachments) {
    const convertedContent = await convertAttachmentWithFallback(
      createAttachmentForConversion(attachment.data, attachment.filename, attachment.id),
      options
    );
    content.push(convertedContent);
  }
  return content;
}
function convertMessages(messages, options) {
  const filtered = messages.filter((msg) => {
    return !(msg.role === "assistant" && !msg.content?.trim() && (!msg.toolCalls || msg.toolCalls.length === 0));
  });
  return filtered.map((msg) => {
    if (msg.role === "tool") {
      if (msg.attachments && msg.attachments.length > 0) {
        const content = buildAttachmentContent(msg.content, msg.attachments, options.modelSupportsVision);
        return {
          role: "tool",
          content,
          tool_call_id: msg.toolCallId
        };
      }
      return {
        role: "tool",
        content: msg.content,
        tool_call_id: msg.toolCallId
      };
    }
    if (msg.role === "assistant") {
      return buildAssistantMessage(msg);
    }
    if (msg.role === "user" && msg.attachments && msg.attachments.length > 0) {
      const content = buildAttachmentContent(msg.content, msg.attachments, options.modelSupportsVision);
      return {
        role: "user",
        content
      };
    }
    return {
      role: msg.role,
      content: msg.content
    };
  });
}
async function convertMessagesWithFallback(messages, options) {
  logger.debug("[VisionFallback] convertMessagesWithFallback called", { messageCount: messages.length });
  const filtered = messages.filter((msg) => {
    return !(msg.role === "assistant" && !msg.content?.trim() && (!msg.toolCalls || msg.toolCalls.length === 0));
  });
  const converted = [];
  for (const msg of filtered) {
    if (msg.role === "tool") {
      if (msg.attachments && msg.attachments.length > 0) {
        const content = await buildAttachmentContentWithFallback(msg.content, msg.attachments, options);
        converted.push({
          role: "tool",
          content,
          tool_call_id: msg.toolCallId
        });
      } else {
        converted.push({
          role: "tool",
          content: msg.content,
          tool_call_id: msg.toolCallId
        });
      }
      continue;
    }
    if (msg.role === "assistant") {
      converted.push(buildAssistantMessage(msg));
      continue;
    }
    if (msg.role === "user" && msg.attachments && msg.attachments.length > 0) {
      const content = await buildAttachmentContentWithFallback(msg.content, msg.attachments, options);
      converted.push({
        role: "user",
        content
      });
      continue;
    }
    converted.push({
      role: msg.role,
      content: msg.content
    });
  }
  return converted;
}
function convertTools(tools) {
  return tools.map((tool) => ({
    type: "function",
    function: {
      name: tool.function.name,
      description: tool.function.description,
      parameters: tool.function.parameters
    }
  }));
}
function needsVisionFallback(messages, modelSupportsVision, visionFallbackEnabled) {
  const hasAttachments = messages.some(
    (msg) => msg.attachments && msg.attachments.length > 0 || msg.role === "tool" && msg.attachments && msg.attachments.length > 0
  );
  const result = hasAttachments && !modelSupportsVision && visionFallbackEnabled;
  logger.debug("[VisionFallback] needsVisionFallback check", {
    hasAttachments,
    modelSupportsVision,
    visionFallbackEnabled,
    result
  });
  return result;
}
async function buildChatCompletionCreateParams(model, request, profile, capabilities, disableThinking, visionFallbackEnabled, isStreaming, onVisionFallbackStart, onVisionFallbackDone) {
  const userVisionOverride = request.modelSettings?.supportsVision;
  const convertedMessages = await convertMessagesWithOptions(
    request.messages,
    profile,
    visionFallbackEnabled,
    userVisionOverride,
    request.signal,
    onVisionFallbackStart,
    onVisionFallbackDone
  );
  const temperature = request.modelSettings?.temperature ?? request.temperature ?? profile.temperature;
  const maxTokens = request.modelSettings?.maxTokens ?? request.maxTokens ?? profile.defaultMaxTokens;
  const topP = request.modelSettings?.topP ?? profile.topP;
  const topK = capabilities.supportsTopK ? profile.topK : void 0;
  const params = {
    model,
    messages: convertedMessages,
    ...request.tools?.length ? { tools: convertTools(request.tools) } : {},
    ...request.toolChoice ? { tool_choice: request.toolChoice } : {},
    temperature,
    max_tokens: maxTokens,
    top_p: topP,
    stream: isStreaming,
    ...isStreaming ? { stream_options: { include_usage: true } } : {}
  };
  if (topK !== void 0) {
    ;
    params["top_k"] = topK;
  }
  const shouldDisableThinking = disableThinking || request.disableThinking;
  if (capabilities.supportsChatTemplateKwargs && profile.supportsReasoning && shouldDisableThinking) {
    ;
    params["chat_template_kwargs"] = { enable_thinking: false };
  }
  const modelParams = buildModelParams({ temperature, topP, topK, maxTokens });
  return { params, modelParams };
}
async function buildCreateParamsFromInput(input, isStreaming) {
  const {
    model,
    request,
    profile,
    capabilities,
    disableThinking,
    visionFallbackEnabled = false,
    onVisionFallbackStart,
    onVisionFallbackDone
  } = input;
  return buildChatCompletionCreateParams(
    model,
    request,
    profile,
    capabilities,
    !!disableThinking,
    visionFallbackEnabled,
    isStreaming,
    onVisionFallbackStart,
    onVisionFallbackDone
  );
}
var buildNonStreamingCreateParams = (input) => buildCreateParamsFromInput(input, false);
var buildStreamingCreateParams = (input) => buildCreateParamsFromInput(input, true);
function mapFinishReason(reason) {
  switch (reason) {
    case "stop":
      return "stop";
    case "tool_calls":
      return "tool_calls";
    case "length":
      return "length";
    case "content_filter":
      return "content_filter";
    default:
      return "stop";
  }
}
function extractThinking(content) {
  const thinkRegex = /<think>([\s\S]*?)<\/think>/g;
  let thinkingContent = "";
  let cleanContent = content;
  let match;
  while ((match = thinkRegex.exec(content)) !== null) {
    thinkingContent += match[1];
    cleanContent = cleanContent.replace(match[0], "");
  }
  return {
    content: cleanContent.trim(),
    thinkingContent: thinkingContent.trim() || null
  };
}

// src/server/llm/streaming.ts
var XML_TOOL_PATTERNS = ["<tool_call>", "<function=", "</tool_call>", "<parameter="];
function hasXmlToolPattern(text) {
  return XML_TOOL_PATTERNS.some((p) => text.includes(p));
}
async function* streamWithSegments(client, request) {
  const xmlAbortController = new AbortController();
  const combinedSignal = request.signal ? AbortSignal.any([request.signal, xmlAbortController.signal]) : xmlAbortController.signal;
  let content = "";
  let thinkingContent = "";
  let response = null;
  const segments = [];
  let currentTextSegment = "";
  let currentThinkingSegment = "";
  const startTime = performance.now();
  let firstTokenTime = null;
  const flushText = () => {
    if (currentTextSegment.trim()) {
      segments.push({ type: "text", content: currentTextSegment });
    }
    currentTextSegment = "";
  };
  const flushThinking = () => {
    if (currentThinkingSegment.trim()) {
      segments.push({ type: "thinking", content: currentThinkingSegment });
    }
    currentThinkingSegment = "";
  };
  try {
    for await (const event of client.stream({ ...request, signal: combinedSignal })) {
      switch (event.type) {
        case "text_delta":
          if (firstTokenTime === null) {
            firstTokenTime = performance.now();
          }
          flushThinking();
          content += event.content;
          currentTextSegment += event.content;
          if (!request.disableXmlProtection && hasXmlToolPattern(content)) {
            xmlAbortController.abort();
            yield { type: "xml_tool_abort" };
            return null;
          }
          yield { type: "text_delta", content: event.content };
          break;
        case "thinking_delta":
          if (firstTokenTime === null) {
            firstTokenTime = performance.now();
          }
          flushText();
          thinkingContent += event.content;
          currentThinkingSegment += event.content;
          if (!request.disableXmlProtection && hasXmlToolPattern(thinkingContent)) {
            xmlAbortController.abort();
            yield { type: "xml_tool_abort" };
            return null;
          }
          yield { type: "thinking_delta", content: event.content };
          break;
        case "tool_call_delta":
          yield {
            type: "tool_call_delta",
            index: event.index,
            ...event.id !== void 0 ? { id: event.id } : {},
            ...event.name !== void 0 ? { name: event.name } : {},
            ...event.arguments !== void 0 ? { arguments: event.arguments } : {}
          };
          break;
        case "done":
          flushThinking();
          flushText();
          response = event.response;
          yield { type: "done", response: event.response };
          break;
        case "error":
          yield { type: "error", error: event.error };
          return null;
      }
    }
  } catch (error) {
    if (error instanceof Error && error.name === "AbortError") {
      return null;
    }
    yield { type: "error", error: error instanceof Error ? error.message : "Unknown error" };
    return null;
  }
  if (!response) {
    return null;
  }
  const toolCalls = response.toolCalls ?? [];
  for (const tc of toolCalls) {
    segments.push({ type: "tool_call", toolCallId: tc.id });
  }
  const endTime = performance.now();
  const ttft = ((firstTokenTime ?? endTime) - startTime) / 1e3;
  const completionTime = (endTime - (firstTokenTime ?? startTime)) / 1e3;
  const { promptTokens, completionTokens } = response.usage;
  const effectiveThinkingContent = thinkingContent || content === "" && response.reasoning_content || "";
  return {
    content,
    thinkingContent: effectiveThinkingContent,
    toolCalls,
    response,
    segments,
    timing: {
      ttft,
      completionTime,
      tps: completionTime > 0 ? completionTokens / completionTime : 0,
      prefillTps: ttft > 0 ? promptTokens / ttft : 0
    }
  };
}

export {
  getModelProfile,
  getBackendCapabilities,
  detectBackend,
  getBackendDisplayName,
  buildModelParams,
  buildNonStreamingCreateParams,
  buildStreamingCreateParams,
  mapFinishReason,
  extractThinking,
  streamWithSegments
};
//# sourceMappingURL=chunk-HGAMIPFO.js.map