{"version":3,"file":"index.cjs","sources":["../src/constants.ts","../src/llm.ts","../src/openai.ts","../src/mcp.tsx","../src/vector.ts"],"sourcesContent":["import { LiveChannelAddress, LiveChannelScope } from \"@grafana/data\";\nimport { logWarning } from \"@grafana/runtime\";\n\nimport { SemVer } from \"semver\";\n\nexport const LLM_PLUGIN_ID = \"grafana-llm-app\";\nexport const LLM_PLUGIN_ROUTE = `/api/plugins/${LLM_PLUGIN_ID}`;\n\n// Grafana 12.4 renamed `LiveChannelAddress.namespace` to `stream` and now\n// builds the channel id from `stream`, ignoring `namespace`. Older versions do\n// the reverse. We must set both so a single plugin build subscribes to the\n// correct channel regardless of which Grafana version is running; omitting\n// `stream` on 12.4+ yields a `plugin/undefined/...` channel that never routes.\n// The intersection adds `stream` to the type without losing type checking on\n// the other fields, even when building against a pre-12.4 `@grafana/data`.\ntype PluginLiveChannelAddress = LiveChannelAddress & { stream: string };\n\nexport function pluginLiveChannel(\n  path: string,\n  data?: unknown,\n): PluginLiveChannelAddress {\n  return {\n    scope: LiveChannelScope.Plugin,\n    namespace: LLM_PLUGIN_ID,\n    stream: LLM_PLUGIN_ID,\n    path,\n    data,\n  };\n}\n\n// The LLM app was at version 0.2.0 before we added the health check.\n// If the health check fails, or the details don't exist on the response,\n// we should assume it's this older version.\nexport let LLM_PLUGIN_VERSION = new SemVer(\"0.2.0\");\n\nexport function setLLMPluginVersion(version: string) {\n  try {\n    LLM_PLUGIN_VERSION = new SemVer(version);\n  } catch (e) {\n    logWarning(\n      \"Failed to parse version of grafana-llm-app; assuming old version is present.\",\n    );\n  }\n}\n","/**\n * LLM API client.\n *\n * This module contains functions used to make requests to the LLM provider API via\n * the Grafana LLM app plugin. That plugin must be installed, enabled and configured\n * in order for these functions to work.\n *\n * The {@link enabled} function can be used to check if the plugin is enabled and configured.\n */\n\nimport {\n  isLiveChannelMessageEvent,\n  LiveChannelMessageEvent,\n} from \"@grafana/data\";\nimport {\n  getBackendSrv,\n  getGrafanaLiveSrv,\n  logDebug /* logError */,\n} from \"@grafana/runtime\";\n\nimport React, { useEffect, useCallback, useState } from \"react\";\nimport { useAsync } from \"react-use\";\nimport { pipe, Observable, UnaryFunction, Subscription } from \"rxjs\";\nimport { filter, map, scan, takeWhile, tap, toArray } from \"rxjs/operators\";\nimport { v4 as uuidv4 } from \"uuid\";\n\nimport {\n  LLM_PLUGIN_ROUTE,\n  pluginLiveChannel,\n  setLLMPluginVersion,\n} from \"./constants\";\nimport { HealthCheckResponse, LLMProviderHealthDetails } from \"./types\";\n\nconst LLM_CHAT_COMPLETIONS_PATH = \"llm/v1/chat/completions\";\n\n/** The role of a message's author. */\nexport type Role = \"system\" | \"user\" | \"assistant\" | \"function\" | \"tool\";\n\n/** A message in a conversation. */\nexport interface Message {\n  /** The role of the message's author. */\n  role: Role;\n\n  /** The contents of the message. content is required for all messages, and may be null for assistant messages with function calls. */\n  content?: string;\n\n  /** The ID of the tool call, if this message is a function call. */\n  tool_call_id?: string;\n\n  /**\n   * The name of the author of this message.\n   *\n   * This is required if role is 'function', and it should be the name of the function whose response is in the content.\n   *\n   * May contain a-z, A-Z, 0-9, and underscores, with a maximum length of 64 characters.\n   */\n  name?: string;\n\n  /**\n   * The name and arguments of a function that should be called, as generated by the model.\n   *\n   * @deprecated Use tool_calls instead.\n   */\n  function_call?: Object;\n\n  /**\n   * The tool calls generated by the model, such as function calls.\n   */\n  tool_calls?: ToolCall[];\n}\n\n/** A tool call the model may generate. */\nexport interface ToolCall {\n  id: string;\n  index?: number;\n  type: \"function\";\n  function: FunctionCall;\n}\n\n/** A function call generated by the model. */\ninterface FunctionCall {\n  /**\n   * The name of the tool to call.\n   */\n  name: string;\n\n  /**\n   * The arguments to call the function with, as generated by the model in JSON format.\n   *\n   * Note that the model does not always generate valid JSON, and may hallucinate\n   * parameters not defined by your function schema. Validate the arguments in\n   * your code before calling your function.\n   */\n  arguments: string;\n}\n\n/** A function the model may generate JSON inputs for. */\nexport interface Function {\n  /**\n   * The name of the function to be called.\n   *\n   * Must be a-z, A-Z, 0-9, or contain underscores and dashes, with a maximum length of 64.\n   */\n  name: string;\n  /**\n   * A description of what the function does, used by the model to choose when and how to call the function.\n   */\n  description?: string;\n  /*\n   * The parameters the functions accepts, described as a JSON Schema object. See the provider's guide for examples, and the JSON Schema reference for documentation about the format.\n   *\n   * Omitting `parameters` defines a function with an empty parameter list.\n   */\n  parameters?: Object;\n  /**\n   * Whether to enable strict schema adherence when generating the function call.\n   *\n   * If set to true, the model will follow the exact schema defined in the parameters field.\n   * Only a subset of JSON Schema is supported when strict is true.\n   */\n  strict?: boolean;\n}\n\n/**\n * Enum representing abstracted models used by the backend app.\n * @enum {string}\n */\nexport enum Model {\n  BASE = \"base\",\n  LARGE = \"large\",\n}\n\n/**\n * @deprecated Use {@link Model} instead.\n */\ntype DeprecatedString = string;\n\nexport interface ChatCompletionsRequest {\n  /**\n   * Model abstraction to use. These abstractions are then translated back into specific models based on the users settings.\n   *\n   * If not specified, defaults to `Model.BASE`.\n   */\n  model?: Model | DeprecatedString;\n  /** A list of messages comprising the conversation so far. */\n  messages: Message[];\n  /**\n   * What sampling temperature to use, between 0 and 2.\n   * Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic.\n   *\n   * We generally recommend altering this or top_p but not both.\n   */\n  temperature?: number;\n  /**\n   * An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass.\n   * So 0.1 means only the tokens comprising the top 10% probability mass are considered.\n   *\n   * We generally recommend altering this or temperature but not both.\n   */\n  top_p?: number;\n  /**\n   * How many chat completion choices to generate for each input message.\n   */\n  n?: number;\n  /**\n   * Up to 4 sequences where the API will stop generating further tokens.\n   */\n  stop?: string | string[];\n  /**\n   * The maximum number of tokens to generate in the chat completion.\n   *\n   * This value is now deprecated in favor of `max_completion_tokens`.\n   */\n  max_tokens?: number;\n  /**\n   * An upper bound for the number of tokens that can be generated for a completion, including visible output tokens and reasoning tokens.\n   */\n  max_completion_tokens?: number;\n  /**\n   * Number between -2.0 and 2.0.\n   *\n   * Positive values penalize new tokens based on whether they appear in the text so far, increasing the model's likelihood to talk about new topics.\n   */\n  presence_penalty?: number;\n  /**\n   * Number between -2.0 and 2.0.\n   *\n   * Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim.\n   */\n  frequency_penalty?: number;\n  /**\n   * Modify the likelihood of specified tokens appearing in the completion.\n   *\n   * Accepts a json object that maps tokens (specified by their token ID in the tokenizer) to an associated bias value from -100 to 100.\n   * Mathematically, the bias is added to the logits generated by the model prior to sampling. The exact effect will vary per model,\n   * but values between -1 and 1 should decrease or increase likelihood of selection; values like -100 or 100 should result in a ban\n   * or exclusive selection of the relevant token.\n   */\n  logit_bias?: { [key: string]: number };\n  /**\n   * A unique identifier representing your end-user, which can help monitor and detect abuse.\n   */\n  user?: string;\n\n  /** A list of tools that the model may use. */\n  tools?: Tool[];\n}\n\n/** A tool that the model may use. */\nexport interface Tool {\n  type: \"function\";\n  /** The function that the model may use. */\n  function: Function;\n}\n\n/** A completion object from the LLM provider. */\nexport interface Choice {\n  /** The message object generated by the model. */\n  message: Message;\n  /**\n   * The reason the model stopped generating text.\n   *\n   * This may be one of:\n   *  - stop: API returned complete message, or a message terminated by one of the stop sequences provided via the stop parameter\n   *  - length: incomplete model output due to max_tokens parameter or token limit\n   *  - function_call: the model decided to call a function\n   *  - content_filter: omitted content due to a flag from our content filters\n   *  - null: API response still in progress or incomplete\n   */\n  finish_reason: string;\n  /** The index of the completion in the list of choices. */\n  index: number;\n}\n\n/** The usage statistics for a request to the LLM provider. */\nexport interface Usage {\n  /** The number of tokens in the prompt. */\n  prompt_tokens: number;\n  /** The number of tokens in the completion. */\n  completion_tokens: number;\n  /** The total number of tokens. */\n  total_tokens: number;\n}\n\n/** The error response from the Grafana LLM app when trying to call the chat completions API. */\ninterface ChatCompletionsErrorResponse {\n  /** The error message. */\n  error: string;\n}\n\n/** A response from the LLM provider Chat Completions API. */\nexport interface ChatCompletionsResponse<T = Choice> {\n  /** The ID of the request. */\n  id: string;\n  /** The type of object returned (e.g. 'chat.completion'). */\n  object: string;\n  /** The timestamp of the request, as a UNIX timestamp. */\n  created: number;\n  /** The name of the model used to generate the response. */\n  model: string;\n  /** A list of completion objects (only one, unless `n > 1` in the request). */\n  choices: T[];\n  /** The number of tokens used to generate the replies, counting prompt, completion, and total. */\n  usage: Usage;\n}\n\n/** A content message returned from the model. */\nexport interface ContentMessage {\n  /** The content of the message. */\n  content: string;\n  /** The role of the author of this message. */\n  role: Role;\n}\n\n/** A message returned from the model indicating that it is done. */\nexport interface DoneMessage {\n  done: boolean;\n}\n\n/** A function call message returned from the model. */\nexport interface FunctionCallMessage {\n  /** The name of the function to call. */\n  name: string;\n  /** The arguments to the function call. */\n  arguments: any[];\n}\n\n/** A tool calls message returned from the model. */\nexport interface ToolCallsMessage {\n  /** The tool calls generated by the model. */\n  tool_calls: ToolCall[];\n  /** The role of the author of this message. */\n  role: Role;\n}\n\n/**\n * A delta returned from a stream of chat completion responses.\n *\n * In practice this will be either a content message or a function call;\n * done messages are filtered out by the `streamChatCompletions` function.\n */\nexport type ChatCompletionsDelta =\n  | ContentMessage\n  | FunctionCallMessage\n  | DoneMessage\n  | ToolCallsMessage;\n\n/** A chunk included in a chat completion response. */\nexport interface ChatCompletionsChunk {\n  /** The delta since the previous chunk. */\n  delta: ChatCompletionsDelta;\n}\n\n/** Return true if the message is a 'content' message. */\nexport function isContentMessage(\n  message: ChatCompletionsDelta,\n): message is ContentMessage {\n  return \"content\" in message;\n}\n\n/** Return true if the message is a 'done' message. */\nexport function isDoneMessage(\n  message: ChatCompletionsDelta,\n): message is DoneMessage {\n  return \"done\" in message && message.done != null;\n}\n\n/** Return true if the response is an error response. */\nexport function isErrorResponse<T>(\n  response: ChatCompletionsResponse<T> | ChatCompletionsErrorResponse,\n): response is ChatCompletionsErrorResponse {\n  return \"error\" in response;\n}\n\n/** Return true if the message is a function call message. */\nexport function isFunctionCallMessage(\n  message: ChatCompletionsDelta,\n): message is FunctionCallMessage {\n  return \"name\" in message && \"arguments\" in message;\n}\n\n/** Return true if the message is a tool calls message. */\nexport function isToolCallsMessage(\n  message: ChatCompletionsDelta,\n): message is ToolCallsMessage {\n  return \"tool_calls\" in message && message.tool_calls != null;\n}\n\n/**\n * An rxjs operator that extracts the content messages from a stream of chat completion responses.\n *\n * @returns An observable that emits the content messages. Each emission will be a string containing the\n *         token emitted by the model.\n * @example <caption>Example of reading all tokens in a stream.</caption>\n * const stream = streamChatCompletions({ model: Model.BASE, messages: [\n *   { role: 'system', content: 'You are a great bot.' },\n *   { role: 'user', content: 'Hello, bot.' },\n * ]}).pipe(extractContent());\n * stream.subscribe({ next: console.log, error: console.error });\n * // Output:\n * // ['Hello', '? ', 'How ', 'are ', 'you', '?']\n */\nexport function extractContent(): UnaryFunction<\n  Observable<ChatCompletionsResponse<ChatCompletionsChunk>>,\n  Observable<string>\n> {\n  return pipe(\n    filter(\n      (response: ChatCompletionsResponse<ChatCompletionsChunk>) =>\n        response.choices.length > 0 &&\n        isContentMessage(response.choices[0].delta),\n    ),\n    // The type assertion is needed here because the type predicate above doesn't seem to propagate.\n    map(\n      (response: ChatCompletionsResponse<ChatCompletionsChunk>) =>\n        (response.choices[0].delta as ContentMessage).content,\n    ),\n  );\n}\n\n/**\n * An rxjs operator that accumulates the content messages from a stream of chat completion responses.\n *\n * @returns An observable that emits the accumulated content messages. Each emission will be a string containing the\n *         content of all messages received so far.\n * @example\n * const stream = streamChatCompletions({ model: Model.BASE, messages: [\n *   { role: 'system', content: 'You are a great bot.' },\n *   { role: 'user', content: 'Hello, bot.' },\n * ]}).pipe(accumulateContent());\n * stream.subscribe({ next: console.log, error: console.error });\n * // Output:\n * // ['Hello', 'Hello! ', 'Hello! How ', 'Hello! How are ', 'Hello! How are you', 'Hello! How are you?']\n */\nexport function accumulateContent(): UnaryFunction<\n  Observable<ChatCompletionsResponse<ChatCompletionsChunk>>,\n  Observable<string>\n> {\n  return pipe(\n    extractContent(),\n    scan((acc, curr) => acc + curr, \"\"),\n  );\n}\n\n/**\n * Make a request to the chat-completions API via the Grafana LLM plugin proxy.\n */\nexport async function chatCompletions(\n  request: ChatCompletionsRequest,\n): Promise<ChatCompletionsResponse> {\n  const response = await getBackendSrv().post<ChatCompletionsResponse>(\n    `/api/plugins/grafana-llm-app/resources/${LLM_CHAT_COMPLETIONS_PATH}`,\n    request,\n    {\n      headers: { \"Content-Type\": \"application/json\" },\n    },\n  );\n  return response;\n}\n\n/**\n * Make a streaming request to the chat-completions API via the Grafana LLM plugin proxy.\n *\n * A stream of tokens will be returned as an `Observable<string>`. Use the `extractContent` operator to\n * filter the stream to only content messages, or the `accumulateContent` operator to obtain a stream of\n * accumulated content messages.\n *\n * The 'done' message will not be emitted; the stream will simply end when this message is encountered.\n *\n * @example <caption>Example of reading all tokens in a stream.</caption>\n * const stream = streamChatCompletions({ model: Model.BASE, messages: [\n *   { role: 'system', content: 'You are a great bot.' },\n *   { role: 'user', content: 'Hello, bot.' },\n * ]}).pipe(extractContent());\n * stream.subscribe({ next: console.log, error: console.error });\n * // Output:\n * // ['Hello', '? ', 'How ', 'are ', 'you', '?']\n *\n * @example <caption>Example of accumulating tokens in a stream.</caption>\n * const stream = streamChatCompletions({ model: Model.BASE, messages: [\n *   { role: 'system', content: 'You are a great bot.' },\n *   { role: 'user', content: 'Hello, bot.' },\n * ]}).pipe(accumulateContent());\n * stream.subscribe({ next: console.log, error: console.error });\n * // Output:\n * // ['Hello', 'Hello! ', 'Hello! How ', 'Hello! How are ', 'Hello! How are you', 'Hello! How are you?']\n */\nexport function streamChatCompletions(\n  request: ChatCompletionsRequest,\n): Observable<ChatCompletionsResponse<ChatCompletionsChunk>> {\n  const channel = pluginLiveChannel(\n    LLM_CHAT_COMPLETIONS_PATH + \"/\" + uuidv4(),\n    request,\n  );\n  const messages = getGrafanaLiveSrv()\n    .getStream(channel)\n    .pipe(filter((event) => isLiveChannelMessageEvent(event))) as Observable<\n    LiveChannelMessageEvent<ChatCompletionsResponse<ChatCompletionsChunk>>\n  >;\n  return messages.pipe(\n    // Filter out messages that don't have the expected structure\n    filter((event) => {\n      // Skip messages with null choices\n      if (!event.message.choices) {\n        return false;\n      }\n      return true;\n    }),\n    tap((event) => {\n      if (isErrorResponse(event.message)) {\n        throw new Error(event.message.error);\n      }\n    }),\n    // Stop the stream when we get a done message or when the finish_reason is \"stop\"\n    takeWhile((event) => {\n      // If it's an error response, we should continue to let the tap operator handle it\n      if (isErrorResponse(event.message)) {\n        return true;\n      }\n\n      // Check for the explicit done message\n      if (\n        event.message.choices &&\n        event.message.choices[0].delta &&\n        \"done\" in event.message.choices[0].delta &&\n        event.message.choices[0].delta.done === true\n      ) {\n        return false;\n      }\n\n      // Check for finish_reason = \"stop\"\n      if (\n        event.message.choices &&\n        \"finish_reason\" in event.message.choices[0] &&\n        event.message.choices[0].finish_reason === \"stop\"\n      ) {\n        return false;\n      }\n\n      return true;\n    }),\n    map((event) => event.message),\n  );\n}\n\nlet loggedWarning = false;\n\n/** Check if the LLM provider API is enabled via the LLM plugin. */\nexport const health = async (): Promise<LLMProviderHealthDetails> => {\n  // First check if the plugin is enabled.\n  try {\n    const settings = await getBackendSrv().get(\n      `${LLM_PLUGIN_ROUTE}/settings`,\n      undefined,\n      undefined,\n      {\n        showSuccessAlert: false,\n        showErrorAlert: false,\n      },\n    );\n    if (!settings.enabled) {\n      return {\n        configured: false,\n        ok: false,\n        error: \"The Grafana LLM plugin is not enabled.\",\n      };\n    }\n  } catch (e) {\n    logDebug(String(e));\n    logDebug(\n      \"Failed to check if LLM provider is enabled. This is expected if the Grafana LLM plugin is not installed, and the above error can be ignored.\",\n    );\n    loggedWarning = true;\n    return {\n      configured: false,\n      ok: false,\n      error: \"The Grafana LLM plugin is not installed.\",\n    };\n  }\n\n  // Run a health check to see if the LLM provider is configured on the plugin.\n  let response: HealthCheckResponse;\n  try {\n    response = await getBackendSrv().get(\n      `${LLM_PLUGIN_ROUTE}/health`,\n      undefined,\n      undefined,\n      {\n        showSuccessAlert: false,\n        showErrorAlert: false,\n      },\n    );\n  } catch (e) {\n    if (!loggedWarning) {\n      logDebug(String(e));\n      logDebug(\n        \"Failed to check if LLM provider is enabled. This is expected if the Grafana LLM plugin is not installed, and the above error can be ignored.\",\n      );\n      loggedWarning = true;\n    }\n    return {\n      configured: false,\n      ok: false,\n      error: \"The Grafana LLM plugin is not installed.\",\n    };\n  }\n\n  const { details } = response;\n  // Update the version if it's present on the response.\n  if (details?.version !== undefined) {\n    setLLMPluginVersion(details.version);\n  }\n  if (details?.llmProvider === undefined) {\n    return {\n      configured: false,\n      ok: false,\n      error: \"The Grafana LLM plugin is outdated; please update it.\",\n    };\n  }\n  return typeof details.llmProvider === \"boolean\"\n    ? { configured: details.llmProvider, ok: details.llmProvider }\n    : details.llmProvider;\n};\n\nexport const enabled = async (): Promise<boolean> => {\n  const healthDetails = await health();\n  return healthDetails.configured && healthDetails.ok;\n};\n\n/**\n * Enum representing different states for a stream.\n * @enum {string}\n */\nexport enum StreamStatus {\n  IDLE = \"idle\",\n  GENERATING = \"generating\",\n  COMPLETED = \"completed\",\n}\n\n/**\n * A constant representing the timeout value in milliseconds.\n * @type {number}\n */\nexport const TIMEOUT = 60000;\n\n/**\n * A type representing the state of an LLM stream.\n * @typedef {Object} LLMStreamState\n * @property {React.Dispatch<React.SetStateAction<Message[]>} setMessages - A function to set messages.\n * @property {string} reply - The reply associated with the stream.\n * @property {typeof StreamStatus} streamStatus - The current status of the stream.\n * @property {Error|undefined} error - An optional error associated with the stream.\n * @property {{\n *    enabled: boolean|undefined;\n *    stream?: undefined;\n *  }|{\n *    enabled: boolean|undefined;\n *    stream: Subscription;\n *  }|undefined} value - A value that can be an object with 'enabled' and 'stream' properties or undefined.\n */\nexport type LLMStreamState = {\n  setMessages: React.Dispatch<React.SetStateAction<Message[]>>;\n  reply: string;\n  streamStatus: StreamStatus;\n  error: Error | undefined;\n  value:\n    | {\n        enabled: boolean | undefined;\n        stream?: undefined;\n      }\n    | {\n        enabled: boolean | undefined;\n        stream: Subscription;\n      }\n    | undefined;\n};\n\n/**\n * A custom React hook for managing an LLM stream that communicates with the provided model.\n *\n * @param {string} [model=Model.LARGE] - The LLM model to use for communication.\n * @param {number} [temperature=1] - The temperature value for text generation (default is 1).\n * @param {function} [notifyError] - A callback function for handling errors.\n * @param {number} [timeout=TIMEOUT] - Timeout in milliseconds for the initial response before the stream is considered failed.\n *\n * @returns {LLMStreamState} - An object containing the state of the LLM stream.\n * @property {function} setMessages - A function to update the list of messages in the stream.\n * @property {string} reply - The most recent reply received from the LLM stream.\n * @property {StreamStatus} streamStatus - The status of the stream (\"idle\", \"generating\" or \"completed\").\n * @property {Error|undefined} error - An error object if an error occurs, or undefined if no error.\n * @property {object|undefined} value - The current value of the stream.\n * @property {boolean|undefined} value.enabled - Indicates whether the stream is enabled (true or false).\n * @property {Subscription|undefined} value.stream - The stream subscription object if the stream is active, or undefined if not.\n */\nexport function useLLMStream(\n  model = Model.LARGE,\n  temperature = 1,\n  notifyError: (\n    title: string,\n    text?: string,\n    traceId?: string,\n  ) => void = () => {},\n  timeout = TIMEOUT,\n): LLMStreamState {\n  // The messages array to send to the LLM.\n  const [messages, setMessages] = useState<Message[]>([]);\n  // The latest reply from the LLM.\n  const [reply, setReply] = useState(\"\");\n  const [streamStatus, setStreamStatus] = useState<StreamStatus>(\n    StreamStatus.IDLE,\n  );\n  const [error, setError] = useState<Error>();\n\n  const onError = useCallback(\n    (e: Error) => {\n      setStreamStatus(StreamStatus.IDLE);\n      setMessages([]);\n      setError(e);\n      notifyError(\n        \"Failed to generate content using LLM provider\",\n        `Please try again or if the problem persists, contact your organization admin.`,\n      );\n      console.error(e);\n    },\n    [notifyError],\n  );\n\n  const { error: enabledError, value: isEnabled } = useAsync(\n    async () => await enabled(),\n    [enabled],\n  );\n\n  const { error: asyncError, value } = useAsync(async () => {\n    if (!isEnabled || !messages.length) {\n      return { enabled: isEnabled };\n    }\n\n    setStreamStatus(StreamStatus.GENERATING);\n    setError(undefined);\n    // Stream the completions. Each element is the next stream chunk.\n    const stream = streamChatCompletions({\n      model,\n      temperature,\n      messages,\n    }).pipe(\n      // Accumulate the stream content into a stream of strings, where each\n      // element contains the accumulated message so far.\n      accumulateContent(),\n      // The stream is just a regular Observable, so we can use standard rxjs\n      // functionality to update state, e.g. recording when the stream\n      // has completed.\n      // The operator decision tree on the rxjs website is a useful resource:\n      // https://rxjs.dev/operator-decision-tree.)\n    );\n    // Subscribe to the stream and update the state for each returned value.\n    return {\n      enabled: isEnabled,\n      stream: stream.subscribe({\n        next: setReply,\n        error: onError,\n        complete: () => {\n          setStreamStatus(StreamStatus.COMPLETED);\n          setTimeout(() => {\n            setStreamStatus(StreamStatus.IDLE);\n          });\n          setMessages([]);\n          setError(undefined);\n        },\n      }),\n    };\n  }, [messages, isEnabled]);\n\n  // Unsubscribe from the stream when the component unmounts.\n  useEffect(() => {\n    return () => {\n      if (value?.stream) {\n        value.stream.unsubscribe();\n      }\n    };\n  }, [value]);\n\n  // If the stream is generating and we haven't received a reply, it times out.\n  useEffect(() => {\n    let timeout_: NodeJS.Timeout | undefined;\n    if (streamStatus === StreamStatus.GENERATING && reply === \"\") {\n      timeout_ = setTimeout(() => {\n        onError(new Error(`LLM stream timed out after ${timeout}ms`));\n      }, timeout);\n    }\n    return () => {\n      timeout_ && clearTimeout(timeout_);\n    };\n  }, [streamStatus, reply, onError, timeout]);\n\n  if (asyncError || enabledError) {\n    setError(asyncError || enabledError);\n  }\n\n  return {\n    setMessages,\n    reply,\n    streamStatus,\n    error,\n    value,\n  };\n}\n\n/**\n * An rxjs operator that accumulates tool call messages from a stream of chat completion responses into a complete tool call message.\n *\n * @returns An observable that emits the accumulated tool call message when complete.\n * @example\n * const stream = streamChatCompletions({...}).pipe(\n *   accumulateToolCalls()\n * );\n * stream.subscribe({\n *   next: (toolCallMessage) => console.log('Received complete tool call:', toolCallMessage),\n *   error: console.error\n * });\n */\nexport function accumulateToolCalls(): UnaryFunction<\n  Observable<ChatCompletionsResponse<ChatCompletionsChunk>>,\n  Observable<ToolCallsMessage>\n> {\n  return pipe(\n    filter((response: ChatCompletionsResponse<ChatCompletionsChunk>) =>\n      isToolCallsMessage(response.choices[0].delta),\n    ),\n    // Collect all tool call chunks\n    toArray(),\n    // Process the array to reconstruct the complete tool call message\n    map((responses: Array<ChatCompletionsResponse<ChatCompletionsChunk>>) => {\n      const toolCallChunks = responses.map(\n        (r) => r.choices[0].delta as ToolCallsMessage,\n      );\n      return recoverToolCallMessage(toolCallChunks);\n    }),\n  );\n}\n\n/**\n * Recovers a complete tool call message from individual chunks.\n *\n * @param toolCallMessages - Array of tool call message chunks\n * @returns A complete tool call message with all chunks combined\n */\nexport function recoverToolCallMessage(\n  toolCallMessages: ToolCallsMessage[],\n): ToolCallsMessage {\n  const recoveredToolCallMessage: ToolCallsMessage = {\n    role: \"assistant\",\n    tool_calls: [],\n  };\n\n  for (const msg of toolCallMessages) {\n    for (const tc of msg.tool_calls) {\n      if (tc.index! >= recoveredToolCallMessage.tool_calls.length) {\n        recoveredToolCallMessage.tool_calls.push({\n          ...tc,\n          function: { ...tc.function, arguments: tc.function.arguments ?? \"\" },\n        });\n      } else {\n        recoveredToolCallMessage.tool_calls[tc.index!].function.arguments +=\n          tc.function.arguments ?? \"\";\n      }\n    }\n  }\n\n  // Ensure final arguments are never empty\n  for (const tc of recoveredToolCallMessage.tool_calls) {\n    if (!tc.function.arguments) {\n      tc.function.arguments = \"{}\";\n    }\n  }\n\n  return recoveredToolCallMessage;\n}\n","/**\n * @deprecated This module is deprecated and will be removed in a future version.\n * Please use the vendor-neutral `llm.ts` module instead.\n *\n * All exports from this file are re-exported from `llm.ts` for backward compatibility.\n *\n * BREAKING CHANGE in v0.13.0: The health check response format has changed from\n * { details: { openAI: { configured: true, ok: true } } }\n * to\n * { details: { llmProvider: { configured: true, ok: true } } }\n *\n * This module now handles both formats for backward compatibility, but will be removed in a future version.\n */\n\nimport { getBackendSrv } from \"@grafana/runtime\";\nimport { LLM_PLUGIN_ROUTE } from \"./constants\";\n\n// Re-export everything from llm.ts except enabled\nexport * from \"./llm\";\n\n// Override enabled function to handle both old and new formats\nexport const enabled = async (): Promise<boolean> => {\n  try {\n    const settings = await getBackendSrv().get(`${LLM_PLUGIN_ROUTE}/settings`);\n    if (!settings.enabled) {\n      return false;\n    }\n\n    const health = await getBackendSrv().get(`${LLM_PLUGIN_ROUTE}/health`);\n    const details = health.details;\n\n    // Handle both new and old formats\n    if (details.llmProvider) {\n      return details.llmProvider.configured && details.llmProvider.ok;\n    }\n    if (details.openAI) {\n      return details.openAI.configured && details.openAI.ok;\n    }\n    return false;\n  } catch (e) {\n    return false;\n  }\n};\n","import React, { useMemo } from \"react\";\n\nimport {\n  isLiveChannelMessageEvent,\n  LiveChannelAddress,\n  LiveChannelMessageEvent,\n} from \"@grafana/data\";\nimport {\n  config,\n  getBackendSrv,\n  getGrafanaLiveSrv,\n  GrafanaLiveSrv,\n  logDebug,\n} from \"@grafana/runtime\";\nimport { Transport } from \"@modelcontextprotocol/sdk/shared/transport.js\";\nimport { Client } from \"@modelcontextprotocol/sdk/client/index.js\";\nimport { StreamableHTTPClientTransport } from \"@modelcontextprotocol/sdk/client/streamableHttp.js\";\nimport {\n  type JSONRPCMessage,\n  JSONRPCMessageSchema,\n  type Tool as MCPTool,\n} from \"@modelcontextprotocol/sdk/types.js\";\nimport { Observable, filter } from \"rxjs\";\nimport { v4 as uuid } from \"uuid\";\n\nimport {\n  LLM_PLUGIN_ID,\n  LLM_PLUGIN_ROUTE,\n  pluginLiveChannel,\n} from \"./constants\";\nimport { Tool as OpenAITool } from \"./openai\";\n\nconst MCP_GRAFANA_PATH = \"mcp/grafana\";\n\n/**\n * An MCP transport which uses the Grafana LLM plugin's built-in MCP server,\n * over Grafana Live.\n *\n * Use this with a client from `@modelcontextprotocol/sdk`.\n *\n * @deprecated Use a `StreamableHTTPClientTransport` with URL returned by `streamableHTTPURL` instead.\n * @experimental\n */\nexport class GrafanaLiveTransport implements Transport {\n  _grafanaLiveSrv: GrafanaLiveSrv = getGrafanaLiveSrv();\n\n  /**\n   * The Grafana Live channel used by this transport.\n   */\n  _subscribeChannel: LiveChannelAddress;\n\n  /**\n   * The Grafana Live channel used by this transport.\n   */\n  _publishChannel: LiveChannelAddress;\n\n  /**\n   * The Grafana Live stream over which MCP messages are received.\n   */\n  _stream?: Observable<LiveChannelMessageEvent<unknown>>;\n\n  // Methods defined as part of the Transport interface.\n  // These will be attached by the client.\n  onclose?: (() => void) | undefined;\n  onerror?: ((error: Error) => void) | undefined;\n  onmessage?: ((message: JSONRPCMessage) => void) | undefined;\n\n  constructor(path?: string) {\n    if (path === undefined) {\n      // Construct a unique path for this transport.\n      const pathId = uuid();\n      path = `${MCP_GRAFANA_PATH}/${pathId}`;\n    }\n    this._subscribeChannel = pluginLiveChannel(`${path}/subscribe`);\n    this._publishChannel = pluginLiveChannel(`${path}/publish`);\n  }\n\n  async start(): Promise<void> {\n    if (this._stream !== undefined) {\n      throw new Error(\n        \"GrafanaLiveTransport already started! If using Client class, note that connect() calls start() automatically.\",\n      );\n    }\n\n    const stream = this._grafanaLiveSrv\n      .getStream(this._subscribeChannel)\n      .pipe(filter((event) => isLiveChannelMessageEvent(event)));\n    this._stream = stream;\n    stream.subscribe((event) => {\n      let message: JSONRPCMessage;\n      try {\n        message = JSONRPCMessageSchema.parse(event.message);\n      } catch (error) {\n        this.onerror?.(error as Error);\n        return;\n      }\n      this.onmessage?.(message);\n    });\n  }\n\n  async send(message: JSONRPCMessage): Promise<void> {\n    if (this._stream === undefined) {\n      throw new Error(\"not connected\");\n    }\n\n    // The Grafana Live service API for publishing messages sends a message\n    // to Grafana's HTTP API rather than over the live channel, for reasons\n    // that are unclear (but presumably justified in the default case).\n    // This is fine when there is only one Grafana instance, but when there\n    // are multiple (e.g. in a HA setup), the HTTP request will be routed\n    // to a random Grafana instance, while we need it to be routed to the\n    // same instance that the client is connected to (since there is a\n    // long-lived stream over the live channel).\n    //\n    // We can use the `useSocket` argument when trying to publish to the\n    // live channel to force the use of the Websocket instead of the HTTP API.\n    // This will work in both single-instance and HA setups. However, it's only\n    // available in Grafana 11.6.0 and later. We can check for this by checking\n    // if the `publish` method has a third argument, which is the `options`\n    // argument.\n    const hasPublishOptions = this._grafanaLiveSrv.publish?.length >= 3;\n    if (hasPublishOptions) {\n      // TODO: use `LivePublishOptions` from `@grafana/runtime` once\n      // Grafana 11.6.0 is released. We can remove these `@ts-expect-error`\n      // comments once that happens.\n      //@ts-expect-error\n      const options: LivePublishOptions = { useSocket: true };\n      this._grafanaLiveSrv.publish(this._publishChannel, message, options);\n    }\n\n    // If that option isn't available, we can first fall back to trying to\n    // drilling down into the implementation details of the Grafana Live\n    // service and using the Centrifuge API directly to publish the message\n    // to the same stream that the client is connected to.\n    // Realistically this should work in all versions of Grafana older than\n    // 9, which is much further back than this plugin even supports, so should\n    // always work.\n    const centrifugeSubscription = // @ts-expect-error\n      this._grafanaLiveSrv.deps?.centrifugeSrv?.getChannel?.(\n        this._publishChannel,\n      )?.subscription;\n    if (centrifugeSubscription) {\n      return centrifugeSubscription.publish(message);\n    }\n\n    // If the centrifuge subscription is still not available for some reason,\n    // fall back to the official HTTP publish method. This won't work in HA\n    // setups but it's better than nothing.\n    console.warn(\n      \"Websocket subscription not available, falling back to HTTP publish. \" +\n        \"This may fail in HA setups. If you see this, please create an issue at \" +\n        \"https://github.com/grafana/grafana-llm-app/issues/new.\",\n    );\n    await this._grafanaLiveSrv.publish(this._publishChannel, message);\n  }\n\n  async close(): Promise<void> {\n    this._stream = undefined;\n  }\n}\n\n/**\n * A result object containing a client instance and whether MCP is enabled.\n */\ninterface ClientResult {\n  /* Whether MCP is enabled for the current Grafana instance. */\n  enabled: boolean;\n  /* The client instance. */\n  client: Client | null;\n  /* Error that occurred during client creation, if any. */\n  error?: Error;\n}\n\n// Create a map to store client instances. These will be keyed by the appName and appVersion.\n// This effectively means:\n// - each app will have a single client instance that is reused across the application.\n// - since clients are stored outside of the MCPClientProvider component, they will be\n//   cleaned up when the component unmounts.\n// - this also allows users to wrap the MCPClientProvider in Suspense, which will\n//   automatically suspend the component until the client is ready.\nconst clientMap = new Map<string, ClientResult>();\n\n// Context holding a client instance if MCP is enabled.\nconst MCPClientContext = React.createContext<ClientResult | null>(null);\n\n// Create a key for the client map.\nfunction clientKey(appName: string, appVersion: string) {\n  return `${appName}-${appVersion}`;\n}\n\n// A resource type, used with `createClientResource` to fetch the client or\n// throw a promise if it's not yet ready.\ntype ClientResource = {\n  read: () => ClientResult;\n};\n\ntype LLMPluginSettings = {\n  enabled: boolean;\n  jsonData: {\n    mcp?: {\n      enabled?: boolean;\n      disabled?: boolean;\n    };\n  };\n};\n\n/**\n * Check if the Grafana LLM app is installed and the MCP server is enabled for the current Grafana instance.\n *\n * @returns Whether MCP is enabled for the current Grafana instance.\n */\nexport async function enabled(): Promise<boolean> {\n  try {\n    const settings: LLMPluginSettings = await getBackendSrv().get(\n      `${LLM_PLUGIN_ROUTE}/settings`,\n      undefined,\n      undefined,\n      {\n        showSuccessAlert: false,\n        showErrorAlert: false,\n      },\n    );\n    if (!settings.enabled) {\n      return false;\n    }\n    // If the `enabled` property is present, it's an older version of the plugin;\n    // use this field.\n    if (settings.jsonData.mcp?.enabled !== undefined) {\n      return !!settings.jsonData.mcp?.enabled;\n    }\n    // Otherwise use the `disabled` property.\n    return !settings.jsonData.mcp?.disabled;\n  } catch (e) {\n    logDebug(String(e));\n    logDebug(\n      \"Failed to check if LLM provider is enabled. This is expected if the Grafana LLM plugin is not installed, and the above error can be ignored.\",\n    );\n    return false;\n  }\n}\n\n/**\n * Get the URL to use if manually creating a StreamableHTTPClientTransport.\n *\n * This can be used if you don't want to use the `mcp.MCPClientProvider` component, or if you\n * want to host the MCP server on your own app plugin.\n *\n * @param appId the ID of the Grafana app plugin to use. The plugin must be exposing the\n *              MCP server's streamable HTTP API as a resource handler.\n * @param mcpPath the path to the MCP server's streamable HTTP API, with leading slash.\n *              Defaults to `/mcp/grafana`.\n * @returns A URL to use as the `url` argument of `StreamableHTTPClientTransport`.\n */\nexport function streamableHTTPURL(\n  appId: string = LLM_PLUGIN_ID,\n  mcpPath = MCP_GRAFANA_PATH,\n): URL {\n  let grafanaUrl = config.appUrl || \"http://localhost:3000/\";\n  if (!grafanaUrl.endsWith(\"/\")) {\n    grafanaUrl = `${grafanaUrl}/`;\n  }\n  if (!mcpPath.startsWith(\"/\")) {\n    mcpPath = `/${mcpPath}`;\n  }\n  return new URL(`${grafanaUrl}api/plugins/${appId}/resources${mcpPath}`);\n}\n\ntype ClientResourceOptions = Required<Omit<MCPClientProviderProps, \"children\">>;\n\n// Create a resource that works with Suspense.\nfunction createClientResource({\n  appName,\n  appVersion,\n  mcpAppName,\n  mcpAppPath,\n}: ClientResourceOptions): ClientResource {\n  let status: \"pending\" | \"success\" | \"error\" = \"pending\";\n  let result: ClientResult | null = null;\n  let error: Error | null = null;\n\n  const key = clientKey(appName, appVersion);\n  const promise = (async () => {\n    if (clientMap.has(key)) {\n      result = clientMap.get(key)!;\n      if (result.error) {\n        status = \"error\";\n        error = result.error;\n        throw result.error;\n      }\n      status = \"success\";\n      return result;\n    }\n\n    try {\n      const isEnabled = await enabled();\n      if (!isEnabled) {\n        status = \"success\";\n        result = { client: null, enabled: isEnabled };\n        clientMap.set(key, result);\n        return result;\n      }\n      const client = new Client({\n        name: appName,\n        version: appVersion,\n      });\n      const transport = new StreamableHTTPClientTransport(\n        streamableHTTPURL(mcpAppName, mcpAppPath),\n        {\n          reconnectionOptions: {\n            maxRetries: 5,\n            initialReconnectionDelay: 1000,\n            maxReconnectionDelay: 5000,\n            reconnectionDelayGrowFactor: 1.5,\n          },\n        },\n      );\n      await client.connect(transport);\n      result = { client, enabled: isEnabled };\n      clientMap.set(key, result);\n      status = \"success\";\n      return result;\n    } catch (e) {\n      status = \"error\";\n      error = e as Error;\n      result = { client: null, enabled: false, error };\n      clientMap.set(key, result);\n      throw e;\n    }\n  })();\n\n  return {\n    read() {\n      if (status === \"pending\") {\n        throw promise;\n      } else if (status === \"error\") {\n        throw error;\n      } else if (status === \"success\" && result) {\n        return result;\n      }\n      throw new Error(\"Unexpected resource state\");\n    },\n  };\n}\n\ninterface MCPClientProviderProps {\n  /**\n   * The name of the application using the MCP server.\n   *\n   * This will be used as the `name` argument of the `Client` constructor,\n   * and also to cache MCP clients to avoid recreating them multiple times,\n   * when using the `mcp.MCPClientProvider` component.\n   */\n  appName: string;\n  /**\n   * The version of the application using the MCP server.\n   *\n   * This will be used as the `version` argument of the `Client` constructor,\n   * and also to cache MCP clients to avoid recreating them multiple times,\n   * when using the `mcp.MCPClientProvider` component.\n   */\n  appVersion: string;\n  /**\n   * The Grafana app plugin to use for the MCP server.\n   *\n   * Defaults to `grafana-llm-app`, meaning the MCP server embedded in the Grafana LLM plugin\n   * will be used.\n   *\n   * If you want to use a different app plugin, you can set this to the ID of the plugin.\n   * You will need to ensure that the plugin is exposing the MCP server's streamable HTTP API\n   * as a resource handler.\n   */\n  mcpAppName?: string;\n  /**\n   * The path to the MCP server's streamable HTTP API, with leading slash.\n   *\n   * Defaults to `/mcp/grafana`.\n   */\n  mcpAppPath?: string;\n  children: React.ReactNode;\n}\n\n/**\n * MCPClientProvider is a React context provider that creates an MCP client\n * and manages its lifecycle.\n *\n * It should be used to wrap the entire application in a single provider.\n * This ensures that the client is created once and reused across the application.\n *\n * It also supports Suspense, which will suspend the component until the client\n * is ready. This allows you to use the client in components that are not yet\n * ready, such as those that are loading data.\n *\n * Example usage:\n * ```tsx\n * <Suspense fallback={<LoadingPlaceholder />}>\n *   <ErrorBoundary>\n *     {({ error }) => {\n *       if (error) {\n *         return <div>Something went wrong: {error.message}</div>;\n *       }\n *       return (\n *         <MCPClientProvider appName=\"MyApp\" appVersion=\"1.0.0\">\n *           <YourComponent />\n *         </MCPClientProvider>\n *       );\n *     }}\n *   </ErrorBoundary>\n * </Suspense>\n * ```\n *\n * @experimental\n */\nexport function MCPClientProvider({\n  appName,\n  appVersion,\n  mcpAppName = LLM_PLUGIN_ID,\n  mcpAppPath = MCP_GRAFANA_PATH,\n  children,\n}: MCPClientProviderProps) {\n  const resource = useMemo(\n    () =>\n      createClientResource({\n        appName,\n        appVersion,\n        mcpAppName,\n        mcpAppPath,\n      }),\n    [appName, appVersion, mcpAppName, mcpAppPath],\n  );\n\n  // This will either return the client or throw a promise/error.\n  // If it throws a promise, Suspense will suspend the component until it resolves.\n  // If it throws an error, it should be caught by an ErrorBoundary.\n  const result = resource.read();\n\n  // Cleanup when the component unmounts.\n  React.useEffect(() => {\n    return () => {\n      if (result?.client) {\n        result.client.close();\n      }\n      clientMap.delete(clientKey(appName, appVersion));\n    };\n  }, [result, appName, appVersion]);\n\n  return (\n    <MCPClientContext.Provider value={result}>\n      {children}\n    </MCPClientContext.Provider>\n  );\n}\n\n/**\n * Convenience hook to use an MCP client from a component.\n *\n * This hook should be used within an `MCPClientProvider`.\n *\n * @experimental\n */\nexport function useMCPClient(): ClientResult {\n  const client = React.useContext(MCPClientContext);\n  if (client === null) {\n    throw new Error(\"MCP is not enabled in this Grafana instance.\");\n  }\n  return client;\n}\n\n/**\n * Re-export of the Client class from the MCP SDK.\n *\n * @experimental\n */\nexport { Client, StreamableHTTPClientTransport };\n\n/**\n * Convert an array of MCP tools to an array of OpenAI tools.\n *\n * This is useful when you want to use the MCP client with the LLM plugin's\n * `chatCompletions` or `streamChatCompletions` functions.\n *\n * @experimental\n */\nexport function convertToolsToOpenAI(tools: MCPTool[]): OpenAITool[] {\n  return tools.map(convertToolToOpenAI);\n}\n\nfunction convertToolToOpenAI(tool: MCPTool): OpenAITool {\n  return {\n    type: \"function\",\n    function: {\n      name: tool.name,\n      description: tool.description,\n      parameters:\n        tool.inputSchema.properties !== undefined\n          ? tool.inputSchema\n          : undefined,\n    },\n  };\n}\n","/**\n * Vector search API.\n *\n * This module can be used to interact with the vector database configured\n * in the Grafana LLM app plugin. That plugin must be installed, enabled and configured\n * in order for these functions to work.\n *\n * The {@link enabled} function can be used to check if the plugin is enabled and configured.\n */\n\nimport { getBackendSrv, logDebug } from \"@grafana/runtime\";\nimport { LLM_PLUGIN_ROUTE, setLLMPluginVersion } from \"./constants\";\nimport { HealthCheckResponse, VectorHealthDetails } from \"./types\";\n\ninterface SearchResultPayload extends Record<string, any> {}\n\n/**\n * A request to search for resources in the vector database.\n **/\nexport interface SearchRequest {\n  /**\n   * The name of the collection to search in.\n   **/\n  collection: string;\n\n  /** The query to search for. */\n  query: string;\n\n  /**\n   * Limit the number of results returned to the top `topK` results.\n   *\n   * Defaults to 10.\n   **/\n  topK?: number;\n\n  /** Metadata filters to apply to the vector search. */\n  /* example: filter: { metric_type: { $eq: 'histogram' } } */\n  filter?: Record<string, any>;\n}\n\n/**\n * The results of a vector search.\n *\n * Results will be ordered by score, descending.\n */\nexport interface SearchResult<T extends SearchResultPayload> {\n  /**\n   * The payload of the result.\n   *\n   * The type of this payload depends on the collection that was searched in.\n   * Grafana core types will be added to the same module as this type as they\n   * are implemented.\n   **/\n  payload: T;\n\n  /**\n   * The score of the result.\n   *\n   * This is a number between 0 and 1, where 1 is the best possible match.\n   */\n  score: number;\n}\n\ninterface SearchResultResponse<T extends SearchResultPayload> {\n  results: Array<SearchResult<T>>;\n}\n\n/**\n * Search for resources in the configured vector database.\n */\nexport async function search<T extends SearchResultPayload>(\n  request: SearchRequest,\n): Promise<Array<SearchResult<T>>> {\n  const response = await getBackendSrv().post<SearchResultResponse<T>>(\n    \"/api/plugins/grafana-llm-app/resources/vector/search\",\n    request,\n    {\n      headers: { \"Content-Type\": \"application/json\" },\n    },\n  );\n  return response.results;\n}\n\nlet loggedWarning = false;\n\n/** Check if the vector API is enabled and configured via the LLM plugin. */\nexport const health = async (): Promise<VectorHealthDetails> => {\n  // First check if the plugin is enabled.\n  try {\n    const settings = await getBackendSrv().get(\n      `${LLM_PLUGIN_ROUTE}/settings`,\n      undefined,\n      undefined,\n      {\n        showSuccessAlert: false,\n        showErrorAlert: false,\n      },\n    );\n    if (!settings.enabled) {\n      return {\n        enabled: false,\n        ok: false,\n        error: \"The Grafana LLM plugin is not enabled.\",\n      };\n    }\n  } catch (e) {\n    logDebug(String(e));\n    logDebug(\n      \"Failed to check if the vector service is enabled. This is expected if the Grafana LLM plugin is not installed, and the above error can be ignored.\",\n    );\n    loggedWarning = true;\n    return {\n      enabled: false,\n      ok: false,\n      error: \"The Grafana LLM plugin is not installed.\",\n    };\n  }\n\n  // Run a health check to see if the vector service is configured on the plugin.\n  let response: HealthCheckResponse;\n  try {\n    response = await getBackendSrv().get(\n      `${LLM_PLUGIN_ROUTE}/health`,\n      undefined,\n      undefined,\n      {\n        showSuccessAlert: false,\n        showErrorAlert: false,\n      },\n    );\n  } catch (e) {\n    // We shouldn't really get here if we managed to get the plugin's settings above,\n    // but catch this just in case.\n    if (!loggedWarning) {\n      logDebug(String(e));\n      logDebug(\n        \"Failed to check if vector service is enabled. This is expected if the Grafana LLM plugin is not installed, and the above error can be ignored.\",\n      );\n      loggedWarning = true;\n    }\n    return {\n      enabled: false,\n      ok: false,\n      error: \"The Grafana LLM plugin is not installed.\",\n    };\n  }\n\n  const { details } = response;\n  // Update the version if it's present on the response.\n  if (details?.version !== undefined) {\n    setLLMPluginVersion(details.version);\n  }\n  if (details?.vector === undefined) {\n    return {\n      enabled: false,\n      ok: false,\n      error: \"The Grafana LLM plugin is outdated; please update it.\",\n    };\n  }\n  return typeof details.vector === \"boolean\"\n    ? { enabled: details.vector, ok: details.vector }\n    : details.vector;\n};\n\nexport const enabled = async (): Promise<boolean> => {\n  const healthDetails = await health();\n  return healthDetails.enabled && healthDetails.ok;\n};\n"],"names":["data","LiveChannelScope","SemVer","logWarning","Model","pipe","filter","map","scan","getBackendSrv","uuidv4","getGrafanaLiveSrv","isLiveChannelMessageEvent","tap","takeWhile","loggedWarning","health","logDebug","enabled","StreamStatus","useState","useCallback","useAsync","useEffect","toArray","uuid","JSONRPCMessageSchema","config","Client","StreamableHTTPClientTransport","useMemo"],"mappings":"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"}