From 6eca5c09dfb3e45a84bbb7e9a25ad3e4f8d79c26 Mon Sep 17 00:00:00 2001 From: Edward Qian Date: Fri, 21 Feb 2025 05:33:12 -0500 Subject: [PATCH] Prometheus: Remove query assistant and related components (#100669) * remove query assistant related components * remove export statement * remove grafana/llm from prometheus packages * remove extra package * revert unintended change * incorrect handling of managedPluginsInstall merge deletion * update yarn.lock * linting fix * linting fix --- .../feature-toggles/index.md | 1 - .../various-suite/prometheus-editor.spec.ts | 13 - e2e/various-suite/prometheus-editor.spec.ts | 13 - .../src/types/featureToggles.gen.ts | 1 - packages/grafana-prometheus/package.json | 1 - packages/grafana-prometheus/src/index.ts | 1 - .../components/PromQueryBuilder.test.tsx | 24 +- .../components/PromQueryBuilder.tsx | 40 +- .../components/promQail/PromQail.test.tsx | 148 ----- .../components/promQail/PromQail.tsx | 616 ------------------ .../promQail/QueryAssistantButton.test.tsx | 51 -- .../promQail/QueryAssistantButton.tsx | 86 --- .../promQail/QuerySuggestionContainer.tsx | 102 --- .../promQail/QuerySuggestionItem.tsx | 322 --------- .../querybuilder/components/promQail/index.ts | 1 - .../components/promQail/prompts.ts | 115 ---- .../promQail/resources/AI_Logo_bw.svg | 4 - .../promQail/resources/AI_Logo_color.svg | 11 - .../components/promQail/state/helpers.test.ts | 73 --- .../components/promQail/state/helpers.ts | 415 ------------ .../components/promQail/state/state.ts | 44 -- .../components/promQail/state/templates.ts | 342 ---------- .../querybuilder/components/promQail/types.ts | 18 - pkg/services/featuremgmt/registry.go | 7 - pkg/services/featuremgmt/toggles-gitlog.csv | 1 - pkg/services/featuremgmt/toggles_gen.csv | 1 - pkg/services/featuremgmt/toggles_gen.go | 4 - pkg/services/featuremgmt/toggles_gen.json | 16 - yarn.lock | 1 - 29 files changed, 2 insertions(+), 2470 deletions(-) delete mode 100644 packages/grafana-prometheus/src/querybuilder/components/promQail/PromQail.test.tsx delete mode 100644 packages/grafana-prometheus/src/querybuilder/components/promQail/PromQail.tsx delete mode 100644 packages/grafana-prometheus/src/querybuilder/components/promQail/QueryAssistantButton.test.tsx delete mode 100644 packages/grafana-prometheus/src/querybuilder/components/promQail/QueryAssistantButton.tsx delete mode 100644 packages/grafana-prometheus/src/querybuilder/components/promQail/QuerySuggestionContainer.tsx delete mode 100644 packages/grafana-prometheus/src/querybuilder/components/promQail/QuerySuggestionItem.tsx delete mode 100644 packages/grafana-prometheus/src/querybuilder/components/promQail/index.ts delete mode 100644 packages/grafana-prometheus/src/querybuilder/components/promQail/prompts.ts delete mode 100644 packages/grafana-prometheus/src/querybuilder/components/promQail/resources/AI_Logo_bw.svg delete mode 100644 packages/grafana-prometheus/src/querybuilder/components/promQail/resources/AI_Logo_color.svg delete mode 100644 packages/grafana-prometheus/src/querybuilder/components/promQail/state/helpers.test.ts delete mode 100644 packages/grafana-prometheus/src/querybuilder/components/promQail/state/helpers.ts delete mode 100644 packages/grafana-prometheus/src/querybuilder/components/promQail/state/state.ts delete mode 100644 packages/grafana-prometheus/src/querybuilder/components/promQail/state/templates.ts delete mode 100644 packages/grafana-prometheus/src/querybuilder/components/promQail/types.ts diff --git a/docs/sources/setup-grafana/configure-grafana/feature-toggles/index.md b/docs/sources/setup-grafana/configure-grafana/feature-toggles/index.md index 716f2ace6f9..c1fcf849447 100644 --- a/docs/sources/setup-grafana/configure-grafana/feature-toggles/index.md +++ b/docs/sources/setup-grafana/configure-grafana/feature-toggles/index.md @@ -174,7 +174,6 @@ Experimental features might be changed or removed without prior notice. | `queryServiceRewrite` | Rewrite requests targeting /ds/query to the query service | | `queryServiceFromUI` | Routes requests to the new query service | | `cachingOptimizeSerializationMemoryUsage` | If enabled, the caching backend gradually serializes query responses for the cache, comparing against the configured `[caching]max_value_mb` value as it goes. This can can help prevent Grafana from running out of memory while attempting to cache very large query responses. | -| `prometheusPromQAIL` | Prometheus and AI/ML to assist users in creating a query | | `prometheusCodeModeMetricNamesSearch` | Enables search for metric names in Code Mode, to improve performance when working with an enormous number of metric names | | `alertmanagerRemoteSecondary` | Enable Grafana to sync configuration and state with a remote Alertmanager. | | `alertmanagerRemotePrimary` | Enable Grafana to have a remote Alertmanager instance as the primary Alertmanager. | diff --git a/e2e/old-arch/various-suite/prometheus-editor.spec.ts b/e2e/old-arch/various-suite/prometheus-editor.spec.ts index d04d502a706..64993979a9a 100644 --- a/e2e/old-arch/various-suite/prometheus-editor.spec.ts +++ b/e2e/old-arch/various-suite/prometheus-editor.spec.ts @@ -159,19 +159,6 @@ describe('Prometheus query editor', () => { e2e.components.DataSource.Prometheus.queryEditor.builder.metricsExplorer().should('exist'); }); - - // NEED TO COMPLETE QUEY ADVISOR WORK OR FIGURE OUT HOW TO ENABLE EXPERIMENTAL FEATURE TOGGLES - // it('should have a query advisor when enabled with feature toggle', () => { - // cy.window().then((win) => { - // win.localStorage.setItem('grafana.featureToggles', 'prometheusPromQAIL=0'); - - // navigateToEditor('Builder', 'prometheusBuilder'); - - // getResources(); - - // e2e.components.DataSource.Prometheus.queryEditor.builder.queryAdvisor().should('exist'); - // }); - // }); }); }); diff --git a/e2e/various-suite/prometheus-editor.spec.ts b/e2e/various-suite/prometheus-editor.spec.ts index 09ae51e4f7f..e1746edf6b7 100644 --- a/e2e/various-suite/prometheus-editor.spec.ts +++ b/e2e/various-suite/prometheus-editor.spec.ts @@ -159,19 +159,6 @@ describe.skip('Prometheus query editor', () => { e2e.components.DataSource.Prometheus.queryEditor.builder.metricsExplorer().should('exist'); }); - - // NEED TO COMPLETE QUEY ADVISOR WORK OR FIGURE OUT HOW TO ENABLE EXPERIMENTAL FEATURE TOGGLES - // it('should have a query advisor when enabled with feature toggle', () => { - // cy.window().then((win) => { - // win.localStorage.setItem('grafana.featureToggles', 'prometheusPromQAIL=0'); - - // navigateToEditor('Builder', 'prometheusBuilder'); - - // getResources(); - - // e2e.components.DataSource.Prometheus.queryEditor.builder.queryAdvisor().should('exist'); - // }); - // }); }); }); diff --git a/packages/grafana-data/src/types/featureToggles.gen.ts b/packages/grafana-data/src/types/featureToggles.gen.ts index 2b2f4befd4f..84091bf6c14 100644 --- a/packages/grafana-data/src/types/featureToggles.gen.ts +++ b/packages/grafana-data/src/types/featureToggles.gen.ts @@ -118,7 +118,6 @@ export interface FeatureToggles { recoveryThreshold?: boolean; lokiStructuredMetadata?: boolean; cachingOptimizeSerializationMemoryUsage?: boolean; - prometheusPromQAIL?: boolean; prometheusCodeModeMetricNamesSearch?: boolean; addFieldFromCalculationStatFunctions?: boolean; alertmanagerRemoteSecondary?: boolean; diff --git a/packages/grafana-prometheus/package.json b/packages/grafana-prometheus/package.json index 17a88423cb2..4232a385393 100644 --- a/packages/grafana-prometheus/package.json +++ b/packages/grafana-prometheus/package.json @@ -40,7 +40,6 @@ "@floating-ui/react": "0.27.3", "@grafana/data": "11.6.0-pre", "@grafana/e2e-selectors": "11.6.0-pre", - "@grafana/llm": "0.12.0", "@grafana/plugin-ui": "0.10.1", "@grafana/runtime": "11.6.0-pre", "@grafana/schema": "11.6.0-pre", diff --git a/packages/grafana-prometheus/src/index.ts b/packages/grafana-prometheus/src/index.ts index 2e7bc495150..80fa0e5130d 100644 --- a/packages/grafana-prometheus/src/index.ts +++ b/packages/grafana-prometheus/src/index.ts @@ -55,7 +55,6 @@ export { PromQueryEditorSelector } from './querybuilder/components/PromQueryEdit export { PromQueryLegendEditor } from './querybuilder/components/PromQueryLegendEditor'; export { QueryPreview } from './querybuilder/components/QueryPreview'; export { MetricsModal } from './querybuilder/components/metrics-modal/MetricsModal'; -export { PromQail } from './querybuilder/components/promQail/PromQail'; // SRC/ // Main export diff --git a/packages/grafana-prometheus/src/querybuilder/components/PromQueryBuilder.test.tsx b/packages/grafana-prometheus/src/querybuilder/components/PromQueryBuilder.test.tsx index db74ff98655..8e58f495963 100644 --- a/packages/grafana-prometheus/src/querybuilder/components/PromQueryBuilder.test.tsx +++ b/packages/grafana-prometheus/src/querybuilder/components/PromQueryBuilder.test.tsx @@ -11,7 +11,7 @@ import { QueryHint, TimeRange, } from '@grafana/data'; -import { config, TemplateSrv } from '@grafana/runtime'; +import { TemplateSrv } from '@grafana/runtime'; import { PrometheusDatasource } from '../../datasource'; import PromQlLanguageProvider from '../../language_provider'; @@ -108,28 +108,6 @@ describe('PromQueryBuilder', () => { await waitFor(() => expect(datasource.getVariables).toBeCalled()); }); - it('checks if the LLM plugin is enabled when the `prometheusPromQAIL` feature is enabled', async () => { - jest.replaceProperty(config, 'featureToggles', { - prometheusPromQAIL: true, - }); - const mockIsLLMPluginEnabled = jest.fn(); - mockIsLLMPluginEnabled.mockResolvedValue(true); - jest.spyOn(require('./promQail/state/helpers'), 'isLLMPluginEnabled').mockImplementation(mockIsLLMPluginEnabled); - setup(); - await waitFor(() => expect(mockIsLLMPluginEnabled).toHaveBeenCalledTimes(1)); - }); - - it('does not check if the LLM plugin is enabled when the `prometheusPromQAIL` feature is disabled', async () => { - jest.replaceProperty(config, 'featureToggles', { - prometheusPromQAIL: false, - }); - const mockIsLLMPluginEnabled = jest.fn(); - mockIsLLMPluginEnabled.mockResolvedValue(true); - jest.spyOn(require('./promQail/state/helpers'), 'isLLMPluginEnabled').mockImplementation(mockIsLLMPluginEnabled); - setup(); - await waitFor(() => expect(mockIsLLMPluginEnabled).toHaveBeenCalledTimes(0)); - }); - // it('tries to load labels when metric selected', async () => { const { languageProvider } = setup(); diff --git a/packages/grafana-prometheus/src/querybuilder/components/PromQueryBuilder.tsx b/packages/grafana-prometheus/src/querybuilder/components/PromQueryBuilder.tsx index d0812e1b0f0..11c79f5932c 100644 --- a/packages/grafana-prometheus/src/querybuilder/components/PromQueryBuilder.tsx +++ b/packages/grafana-prometheus/src/querybuilder/components/PromQueryBuilder.tsx @@ -1,12 +1,10 @@ // Core Grafana history https://github.com/grafana/grafana/blob/v11.0.0-preview/public/app/plugins/datasource/prometheus/querybuilder/components/PromQueryBuilder.tsx import { css } from '@emotion/css'; -import { memo, useEffect, useState } from 'react'; +import { memo, useState } from 'react'; import { DataSourceApi, PanelData } from '@grafana/data'; import { selectors } from '@grafana/e2e-selectors'; import { EditorRow } from '@grafana/plugin-ui'; -import { config } from '@grafana/runtime'; -import { Drawer } from '@grafana/ui'; import { PrometheusDatasource } from '../../datasource'; import promqlGrammar from '../../promql'; @@ -24,9 +22,6 @@ import { PromVisualQuery } from '../types'; import { MetricsLabelsSection } from './MetricsLabelsSection'; import { NestedQueryList } from './NestedQueryList'; import { EXPLAIN_LABEL_FILTER_CONTENT } from './PromQueryBuilderExplained'; -import { PromQail } from './promQail/PromQail'; -import { QueryAssistantButton } from './promQail/QueryAssistantButton'; -import { isLLMPluginEnabled } from './promQail/state/helpers'; export interface PromQueryBuilderProps { query: PromVisualQuery; @@ -40,37 +35,13 @@ export interface PromQueryBuilderProps { export const PromQueryBuilder = memo((props) => { const { datasource, query, onChange, onRunQuery, data, showExplain } = props; const [highlightedOp, setHighlightedOp] = useState(); - const [showDrawer, setShowDrawer] = useState(false); - const [llmAppEnabled, updateLlmAppEnabled] = useState(false); - const { prometheusPromQAIL } = config.featureToggles; // AI/ML + Prometheus const lang = { grammar: promqlGrammar, name: 'promql' }; const initHints = datasource.getInitHints(); - useEffect(() => { - async function checkLlms() { - const check = await isLLMPluginEnabled(); - updateLlmAppEnabled(check); - } - - if (prometheusPromQAIL) { - checkLlms(); - } - }, [prometheusPromQAIL]); - return ( <> - {prometheusPromQAIL && showDrawer && ( - setShowDrawer(false)}> - setShowDrawer(false)} - onChange={onChange} - datasource={datasource} - /> - - )} @@ -108,15 +79,6 @@ export const PromQueryBuilder = memo((props) => { onRunQuery={onRunQuery} highlightedOp={highlightedOp} /> - {prometheusPromQAIL && ( -
- -
- )}
datasource={datasource} diff --git a/packages/grafana-prometheus/src/querybuilder/components/promQail/PromQail.test.tsx b/packages/grafana-prometheus/src/querybuilder/components/promQail/PromQail.test.tsx deleted file mode 100644 index 2f95975f5c4..00000000000 --- a/packages/grafana-prometheus/src/querybuilder/components/promQail/PromQail.test.tsx +++ /dev/null @@ -1,148 +0,0 @@ -// Core Grafana history https://github.com/grafana/grafana/blob/v11.0.0-preview/public/app/plugins/datasource/prometheus/querybuilder/components/promQail/PromQail.test.tsx -import { render, screen, waitFor } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; - -import { DataSourceInstanceSettings, DataSourcePluginMeta } from '@grafana/data'; - -import { PrometheusDatasource } from '../../../datasource'; -import PromQlLanguageProvider from '../../../language_provider'; -import { EmptyLanguageProviderMock } from '../../../language_provider.mock'; -import { PromOptions } from '../../../types'; -import { PromVisualQuery } from '../../types'; - -import { PromQail, queryAssistanttestIds } from './PromQail'; - -// don't care about interaction tracking in our unit tests -jest.mock('@grafana/runtime', () => ({ - ...jest.requireActual('@grafana/runtime'), - reportInteraction: jest.fn(), -})); - -window.HTMLElement.prototype.scrollIntoView = jest.fn(); - -describe('PromQail', () => { - it('renders the drawer', async () => { - setup(defaultQuery); - await waitFor(() => { - expect(screen.getByText('Query advisor')).toBeInTheDocument(); - }); - }); - - it('shows an option to not show security warning', async () => { - setup(defaultQuery); - await waitFor(() => { - expect(screen.getByText("Don't show this message again")).toBeInTheDocument(); - }); - }); - - it('shows selected metric and asks for a prompt', async () => { - setup(defaultQuery); - - await clickSecurityButton(); - - await waitFor(() => { - expect(screen.getByText('random_metric')).toBeInTheDocument(); - expect(screen.getByText('Do you know what you want to query?')).toBeInTheDocument(); - }); - }); - - it('displays a prompt when the user knows what they want to query', async () => { - setup(defaultQuery); - - await clickSecurityButton(); - - await waitFor(() => { - expect(screen.getByText('random_metric')).toBeInTheDocument(); - expect(screen.getByText('Do you know what you want to query?')).toBeInTheDocument(); - }); - - const aiPrompt = screen.getByTestId(queryAssistanttestIds.clickForAi); - - await userEvent.click(aiPrompt); - - await waitFor(() => { - expect(screen.getByText('What kind of data do you want to see with your metric?')).toBeInTheDocument(); - }); - }); - - it('does not display a prompt when choosing historical', async () => { - setup(defaultQuery); - - await clickSecurityButton(); - - await waitFor(() => { - expect(screen.getByText('random_metric')).toBeInTheDocument(); - expect(screen.getByText('Do you know what you want to query?')).toBeInTheDocument(); - }); - - const historicalPrompt = screen.getByTestId(queryAssistanttestIds.clickForHistorical); - - await userEvent.click(historicalPrompt); - - await waitFor(() => { - expect(screen.queryByText('What kind of data do you want to see with your metric?')).toBeNull(); - }); - }); -}); - -const defaultQuery: PromVisualQuery = { - metric: 'random_metric', - labels: [], - operations: [], -}; - -function createDatasource(withLabels?: boolean) { - const languageProvider = new EmptyLanguageProviderMock() as unknown as PromQlLanguageProvider; - - languageProvider.metricsMetadata = { - 'all-metrics': { - type: 'all-metrics-type', - help: 'all-metrics-help', - }, - a: { - type: 'counter', - help: 'a-metric-help', - }, - a_bucket: { - type: 'counter', - help: 'for functions', - }, - }; - - const datasource = new PrometheusDatasource( - { - url: '', - jsonData: {}, - meta: {} as DataSourcePluginMeta, - } as DataSourceInstanceSettings, - undefined, - languageProvider - ); - return datasource; -} - -function createProps(query: PromVisualQuery, datasource: PrometheusDatasource) { - return { - datasource, - onChange: jest.fn(), - closeDrawer: jest.fn(), - query: query, - }; -} - -function setup(query: PromVisualQuery) { - const withLabels: boolean = query.labels.length > 0; - const datasource = createDatasource(withLabels); - const props = createProps(query, datasource); - - // render the drawer only - const { container } = render(); - - return container; -} - -async function clickSecurityButton() { - const securityInfoButton = screen.getByTestId(queryAssistanttestIds.securityInfoButton); - - await userEvent.click(securityInfoButton); -} diff --git a/packages/grafana-prometheus/src/querybuilder/components/promQail/PromQail.tsx b/packages/grafana-prometheus/src/querybuilder/components/promQail/PromQail.tsx deleted file mode 100644 index 4993cb51127..00000000000 --- a/packages/grafana-prometheus/src/querybuilder/components/promQail/PromQail.tsx +++ /dev/null @@ -1,616 +0,0 @@ -// Core Grafana history https://github.com/grafana/grafana/blob/v11.0.0-preview/public/app/plugins/datasource/prometheus/querybuilder/components/promQail/PromQail.tsx -import { css, cx } from '@emotion/css'; -import { PayloadAction, createSlice } from '@reduxjs/toolkit'; -import { useEffect, useReducer, useRef, useState } from 'react'; - -import { GrafanaTheme2, store } from '@grafana/data'; -import { reportInteraction } from '@grafana/runtime'; -import { Alert, Button, Checkbox, Input, Spinner, useTheme2 } from '@grafana/ui'; - -import { PrometheusDatasource } from '../../../datasource'; -import { PromVisualQuery } from '../../types'; - -import { QuerySuggestionContainer } from './QuerySuggestionContainer'; -// @ts-ignore until we can get these added for icons -import AI_Logo_color from './resources/AI_Logo_color.svg'; -import { promQailExplain, promQailSuggest } from './state/helpers'; -import { createInteraction, initialState } from './state/state'; -import { Interaction, SuggestionType } from './types'; - -export type PromQailProps = { - query: PromVisualQuery; - closeDrawer: () => void; - onChange: (query: PromVisualQuery) => void; - datasource: PrometheusDatasource; -}; - -const SKIP_STARTING_MESSAGE = 'SKIP_STARTING_MESSAGE'; - -export const PromQail = (props: PromQailProps) => { - const { query, closeDrawer, onChange, datasource } = props; - const skipStartingMessage = store.getBool(SKIP_STARTING_MESSAGE, false); - - const [state, dispatch] = useReducer(stateSlice.reducer, initialState(query, !skipStartingMessage)); - - const [labelNames, setLabelNames] = useState([]); - - const suggestions = state.interactions.reduce((acc, int) => acc + int.suggestions.length, 0); - - const responsesEndRef = useRef(null); - - const scrollToBottom = () => { - if (responsesEndRef) { - // @ts-ignore for React.MutableRefObject - responsesEndRef?.current?.scrollIntoView({ behavior: 'smooth' }); - } - }; - - useEffect(() => { - // only scroll when an interaction has been added or the suggestions have been updated - scrollToBottom(); - }, [state.interactions.length, suggestions]); - - useEffect(() => { - const fetchLabels = async () => { - let labelsIndex: Record = await datasource.languageProvider.fetchLabelsWithMatch(query.metric); - setLabelNames(Object.keys(labelsIndex)); - }; - fetchLabels(); - }, [query, datasource]); - - const theme = useTheme2(); - const styles = getStyles(theme); - - return ( -
- {/* Query Advisor */} - {/* header */} -
-

Query advisor

-
- {/* Starting message */} -
-
- AI logo color Assistant -
- {state.showStartingMessage ? ( - <> -
-
    -
  1. - Query Advisor suggests queries based on a metric and requests you type in. -
  2. -
  3. - Query Advisor sends Prometheus metrics, labels and metadata to the LLM provider you've configured. - Be sure to align its usage with your company's internal policies. -
  4. -
  5. - An AI-suggested query may not fully answer your question. Always take a moment to understand a query - before you use it. -
  6. -
-
- - Query Advisor is currently in Private Preview. Feedback is appreciated and can be provided on explanations - and suggestions. - - - {/* don't show this message again, store in localstorage */} -
- { - const val = store.getBool(SKIP_STARTING_MESSAGE, false); - store.set(SKIP_STARTING_MESSAGE, !val); - dispatch(indicateCheckbox(!val)); - }} - label="Don't show this message again" - /> -
-
-
- - -
-
- - ) : ( -
- {/* MAKE THIS TABLE RESPONSIVE */} - {/* FIT SUPER LONG METRICS AND LABELS IN HERE */} -
Here is the metric you have selected:
-
-
- - - - - - - - {state.query.labels.map((label, idx) => { - const text = idx === 0 ? 'labels' : ''; - return ( - - - - - - ); - })} - -
metric{state.query.metric} - -
{text}{`${label.label}${label.op}${label.value}`}
-
-
- - {/* Ask if you know what you want to query? */} - {!state.askForQueryHelp && state.interactions.length === 0 && ( - <> -
Do you know what you want to query?
-
-
- - -
-
- - )} - - {state.interactions.map((interaction: Interaction, idx: number) => { - return ( -
- {interaction.suggestionType === SuggestionType.AI ? ( - <> -
What kind of data do you want to see with your metric?
-
-
You do not need to enter in a metric or a label again in the prompt.
-
Example: I want to monitor request latency, not errors.
-
-
- 0} - onChange={(e) => { - const prompt = e.currentTarget.value; - - const payload = { - idx: idx, - interaction: { ...interaction, prompt }, - }; - - dispatch(updateInteraction(payload)); - }} - /> -
- {interaction.suggestions.length === 0 ? ( - interaction.isLoading ? ( - <> -
- Waiting for OpenAI -
- - ) : ( - <> -
-
- - - -
-
- - ) - ) : ( - // LIST OF SUGGESTED QUERIES FROM AI - { - const isLoading = false; - const suggestionType = SuggestionType.AI; - dispatch(addInteraction({ suggestionType, isLoading })); - }} - queryExplain={(suggIdx: number) => - interaction.suggestions[suggIdx].explanation === '' - ? promQailExplain(dispatch, idx, query, interaction, suggIdx, datasource) - : interaction.suggestions[suggIdx].explanation - } - onChange={onChange} - prompt={interaction.prompt ?? ''} - /> - )} - - ) : // HISTORICAL SUGGESTIONS - interaction.isLoading ? ( - <> -
- Waiting for OpenAI -
- - ) : ( - // LIST OF SUGGESTED QUERIES FROM HISTORICAL DATA - { - const isLoading = false; - const suggestionType = SuggestionType.AI; - dispatch(addInteraction({ suggestionType, isLoading })); - }} - queryExplain={(suggIdx: number) => - interaction.suggestions[suggIdx].explanation === '' - ? promQailExplain(dispatch, idx, query, interaction, suggIdx, datasource) - : interaction.suggestions[suggIdx].explanation - } - onChange={onChange} - prompt={interaction.prompt ?? ''} - /> - )} -
- ); - })} -
- )} -
-
-
- ); -}; - -export const getStyles = (theme: GrafanaTheme2) => { - return { - sectionPadding: css({ - padding: '20px', - }), - header: css({ - display: 'flex', - - button: { - marginLeft: 'auto', - }, - }), - iconSection: css({ - padding: '0 0 10px 0', - color: `${theme.colors.text.secondary}`, - - img: { - paddingRight: '4px', - }, - }), - rightButtonsWrapper: css({ - display: 'flex', - }), - rightButtons: css({ - marginLeft: 'auto', - }), - leftButton: css({ - marginRight: '10px', - }), - dataList: css({ - padding: '0px 28px 0px 28px', - }), - textPadding: css({ - paddingBottom: '12px', - }), - containerPadding: css({ - padding: '28px', - }), - infoContainer: css({ - border: `${theme.colors.border.strong}`, - padding: '16px', - backgroundColor: `${theme.colors.background.secondary}`, - borderRadius: `8px`, - borderBottomLeftRadius: 0, - }), - infoContainerWrapper: css({ - paddingBottom: '24px', - }), - metricTable: css({ - width: '100%', - }), - metricTableName: css({ - width: '15%', - }), - metricTableValue: css({ - fontFamily: `${theme.typography.fontFamilyMonospace}`, - fontSize: `${theme.typography.bodySmall.fontSize}`, - overflow: 'scroll', - textWrap: 'nowrap', - maxWidth: '150px', - width: '60%', - maskImage: `linear-gradient(to right, rgba(0, 0, 0, 1) 90%, rgba(0, 0, 0, 0))`, - }), - metricTableButton: css({ - float: 'right', - }), - queryQuestion: css({ - textAlign: 'end', - padding: '8px 0', - }), - secondaryText: css({ - color: `${theme.colors.text.secondary}`, - }), - loadingMessageContainer: css({ - border: `${theme.colors.border.strong}`, - padding: `16px`, - backgroundColor: `${theme.colors.background.secondary}`, - marginBottom: `20px`, - borderRadius: `8px`, - color: `${theme.colors.text.secondary}`, - fontStyle: 'italic', - }), - floatRight: css({ - float: 'right', - }), - codeText: css({ - fontFamily: `${theme.typography.fontFamilyMonospace}`, - fontSize: `${theme.typography.bodySmall.fontSize}`, - }), - bodySmall: css({ - fontSize: `${theme.typography.bodySmall.fontSize}`, - }), - explainPadding: css({ - paddingLeft: '26px', - }), - bottomMargin: css({ - marginBottom: '20px', - }), - topPadding: css({ - paddingTop: '22px', - }), - doc: css({ - textDecoration: 'underline', - }), - afterButtons: css({ - display: 'flex', - justifyContent: 'flex-end', - }), - feedbackStyle: css({ - margin: 0, - textAlign: 'right', - paddingTop: '22px', - paddingBottom: '22px', - }), - nextInteractionHeight: css({ - height: '88px', - }), - center: css({ - display: 'flex', - alignItems: 'center', - justifyContent: 'center', - }), - inputPadding: css({ - paddingBottom: '24px', - }), - querySuggestion: css({ - display: 'flex', - flexWrap: 'nowrap', - }), - longCode: css({ - width: '90%', - textWrap: 'nowrap', - overflow: 'scroll', - maskImage: `linear-gradient(to right, rgba(0, 0, 0, 1) 90%, rgba(0, 0, 0, 0))`, - - div: { - display: 'inline-block', - }, - }), - useButton: css({ - marginLeft: 'auto', - }), - suggestionFeedback: css({ - textAlign: 'left', - }), - feedbackQuestion: css({ - display: 'flex', - padding: '8px 0px', - h6: { marginBottom: 0 }, - i: { - marginTop: '1px', - }, - }), - explationTextInput: css({ - paddingLeft: '24px', - }), - submitFeedback: css({ - padding: '16px 0', - }), - noMargin: css({ - margin: 0, - }), - enableButtonTooltip: css({ - padding: 8, - }), - enableButtonTooltipText: css({ - color: `${theme.colors.text.secondary}`, - ul: { - marginLeft: 16, - }, - }), - link: css({ - color: `${theme.colors.text.link} !important`, - }), - }; -}; - -export const queryAssistanttestIds = { - promQail: 'prom-qail', - securityInfoButton: 'security-info-button', - clickForHistorical: 'click-for-historical', - clickForAi: 'click-for-ai', - submitPrompt: 'submit-prompt', - refinePrompt: 'refine-prompt', -}; - -const stateSlice = createSlice({ - name: 'metrics-modal-state', - initialState: initialState(), - reducers: { - showExplainer: (state, action: PayloadAction) => { - state.showExplainer = action.payload; - }, - showStartingMessage: (state, action: PayloadAction) => { - state.showStartingMessage = action.payload; - }, - indicateCheckbox: (state, action: PayloadAction) => { - state.indicateCheckbox = action.payload; - }, - askForQueryHelp: (state, action: PayloadAction) => { - state.askForQueryHelp = action.payload; - }, - /* - * start working on a collection of interactions - * { - * askForhelp y n - * prompt question - * queries querySuggestions - * } - * - */ - addInteraction: (state, action: PayloadAction<{ suggestionType: SuggestionType; isLoading: boolean }>) => { - // AI or Historical? - const interaction = createInteraction(action.payload.suggestionType, action.payload.isLoading); - const interactions = state.interactions; - state.interactions = interactions.concat([interaction]); - }, - updateInteraction: (state, action: PayloadAction<{ idx: number; interaction: Interaction }>) => { - // update the interaction by index - // will most likely be the last interaction but we might update previous by giving them cues of helpful or not - const index = action.payload.idx; - const updInteraction = action.payload.interaction; - - state.interactions = state.interactions.map((interaction: Interaction, idx: number) => { - if (idx === index) { - return updInteraction; - } - - return interaction; - }); - }, - }, -}); - -// actions to update the state -export const { showStartingMessage, indicateCheckbox, addInteraction, updateInteraction } = stateSlice.actions; diff --git a/packages/grafana-prometheus/src/querybuilder/components/promQail/QueryAssistantButton.test.tsx b/packages/grafana-prometheus/src/querybuilder/components/promQail/QueryAssistantButton.test.tsx deleted file mode 100644 index 8d1bf6971d4..00000000000 --- a/packages/grafana-prometheus/src/querybuilder/components/promQail/QueryAssistantButton.test.tsx +++ /dev/null @@ -1,51 +0,0 @@ -// Core Grafana history https://github.com/grafana/grafana/blob/v11.0.0-preview/public/app/plugins/datasource/prometheus/querybuilder/components/promQail/QueryAssistantButton.test.tsx -import { fireEvent, render, screen, waitFor } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; - -import { QueryAssistantButton } from './QueryAssistantButton'; - -const setShowDrawer = jest.fn(() => {}); - -describe('QueryAssistantButton', () => { - it('renders the button', async () => { - const props = createProps(true, 'metric', setShowDrawer); - render(); - expect(screen.getByText('Get query suggestions')).toBeInTheDocument(); - }); - - it('shows the LLM app disabled message when LLM app is not set up with vector DB', async () => { - const props = createProps(false, 'metric', setShowDrawer); - render(); - const button = screen.getByText('Get query suggestions'); - await userEvent.hover(button); - await waitFor(() => { - expect(screen.getByText('Install and enable the LLM plugin')).toBeInTheDocument(); - }); - }); - - it('shows the message to select a metric when LLM is enabled and no metric is selected', async () => { - const props = createProps(true, '', setShowDrawer); - render(); - const button = screen.getByText('Get query suggestions'); - await userEvent.hover(button); - await waitFor(() => { - expect(screen.getByText('First, select a metric.')).toBeInTheDocument(); - }); - }); - - it('calls setShowDrawer when button is clicked', async () => { - const props = createProps(true, 'metric', setShowDrawer); - render(); - const button = screen.getByText('Get query suggestions'); - fireEvent.click(button); - expect(setShowDrawer).toHaveBeenCalled(); - }); -}); - -function createProps(llmAppEnabled: boolean, metric: string, setShowDrawer: () => void) { - return { - llmAppEnabled, - metric, - setShowDrawer, - }; -} diff --git a/packages/grafana-prometheus/src/querybuilder/components/promQail/QueryAssistantButton.tsx b/packages/grafana-prometheus/src/querybuilder/components/promQail/QueryAssistantButton.tsx deleted file mode 100644 index 2324f60e402..00000000000 --- a/packages/grafana-prometheus/src/querybuilder/components/promQail/QueryAssistantButton.tsx +++ /dev/null @@ -1,86 +0,0 @@ -// Core Grafana history https://github.com/grafana/grafana/blob/v11.0.0-preview/public/app/plugins/datasource/prometheus/querybuilder/components/promQail/QueryAssistantButton.tsx -import { selectors } from '@grafana/e2e-selectors'; -import { reportInteraction } from '@grafana/runtime'; -import { Button, Tooltip, useTheme2 } from '@grafana/ui'; - -import { getStyles } from './PromQail'; -import AI_Logo_color from './resources/AI_Logo_color.svg'; - -export type Props = { - llmAppEnabled: boolean; - metric: string; - setShowDrawer: (show: boolean) => void; -}; - -export function QueryAssistantButton(props: Props) { - const { llmAppEnabled, metric, setShowDrawer } = props; - - const llmAppDisabled = !llmAppEnabled; - const noMetricSelected = !metric; - - const theme = useTheme2(); - const styles = getStyles(theme); - - const button = () => { - return ( - - ); - }; - - const selectMetricMessage = ( - - {button()} - - ); - - const llmAppMessage = ( - -
Query Advisor is disabled
-
To enable Query Advisor you must:
-
- -
-
- } - > - {button()} - - ); - - if (llmAppDisabled) { - return llmAppMessage; - } else if (noMetricSelected) { - return selectMetricMessage; - } else { - return button(); - } -} diff --git a/packages/grafana-prometheus/src/querybuilder/components/promQail/QuerySuggestionContainer.tsx b/packages/grafana-prometheus/src/querybuilder/components/promQail/QuerySuggestionContainer.tsx deleted file mode 100644 index ac51393edac..00000000000 --- a/packages/grafana-prometheus/src/querybuilder/components/promQail/QuerySuggestionContainer.tsx +++ /dev/null @@ -1,102 +0,0 @@ -// Core Grafana history https://github.com/grafana/grafana/blob/v11.0.0-preview/public/app/plugins/datasource/prometheus/querybuilder/components/promQail/QuerySuggestionContainer.tsx -import { cx } from '@emotion/css'; -import { useState } from 'react'; - -import { Button, useTheme2 } from '@grafana/ui'; - -import { PromVisualQuery } from '../../types'; - -import { getStyles, queryAssistanttestIds } from './PromQail'; -import { QuerySuggestionItem } from './QuerySuggestionItem'; -import { QuerySuggestion, SuggestionType } from './types'; - -export type Props = { - querySuggestions: QuerySuggestion[]; - suggestionType: SuggestionType; - closeDrawer: () => void; - nextInteraction: () => void; - queryExplain: (idx: number) => void; - onChange: (query: PromVisualQuery) => void; - prompt: string; -}; - -export function QuerySuggestionContainer(props: Props) { - const { suggestionType, querySuggestions, closeDrawer, nextInteraction, queryExplain, onChange, prompt } = props; - - const [hasNextInteraction, updateHasNextInteraction] = useState(false); - - const theme = useTheme2(); - const styles = getStyles(theme); - - let text, secondaryText, refineText; - - if (suggestionType === SuggestionType.Historical) { - text = `Here are ${querySuggestions.length} query suggestions:`; - refineText = 'I want to write a prompt'; - } else if (suggestionType === SuggestionType.AI) { - text = text = 'Here is your query suggestion:'; - secondaryText = - 'This query is based off of natural language descriptions of the most commonly used PromQL queries.'; - refineText = 'Refine prompt'; - } - - return ( - <> - {suggestionType === SuggestionType.Historical ? ( -
{text}
- ) : ( - <> -
{text}
-
{secondaryText}
- - )} - -
-
- {querySuggestions.map((qs: QuerySuggestion, idx: number) => { - return ( - { - return acc + '$$' + qs.query; - }, '')} - prompt={prompt ?? ''} - /> - ); - })} -
-
- {!hasNextInteraction && ( -
-
- -
-
- -
-
- )} - - ); -} diff --git a/packages/grafana-prometheus/src/querybuilder/components/promQail/QuerySuggestionItem.tsx b/packages/grafana-prometheus/src/querybuilder/components/promQail/QuerySuggestionItem.tsx deleted file mode 100644 index a173c8c4efb..00000000000 --- a/packages/grafana-prometheus/src/querybuilder/components/promQail/QuerySuggestionItem.tsx +++ /dev/null @@ -1,322 +0,0 @@ -// Core Grafana history https://github.com/grafana/grafana/blob/v11.0.0-preview/public/app/plugins/datasource/prometheus/querybuilder/components/promQail/QuerySuggestionItem.tsx -import { cx } from '@emotion/css'; -import { FormEvent, useState } from 'react'; - -import { SelectableValue } from '@grafana/data'; -import { reportInteraction } from '@grafana/runtime'; -import { Button, RadioButtonList, Spinner, TextArea, Toggletip, useTheme2 } from '@grafana/ui'; - -import { buildVisualQueryFromString } from '../../parsing'; -import { PromVisualQuery } from '../../types'; - -import { getStyles } from './PromQail'; -import { QuerySuggestion } from './types'; - -export type Props = { - querySuggestion: QuerySuggestion; - order: number; - queryExplain: (idx: number) => void; - historical: boolean; - onChange: (query: PromVisualQuery) => void; - closeDrawer: () => void; - last: boolean; - prompt: string; - allSuggestions: string | undefined; -}; - -const suggestionOptions: SelectableValue[] = [ - { label: 'Yes', value: 'yes' }, - { label: 'No', value: 'no' }, -]; -const explationOptions: SelectableValue[] = [ - { label: 'Too vague', value: 'too vague' }, - { label: 'Too technical', value: 'too technical' }, - { label: 'Inaccurate', value: 'inaccurate' }, - { label: 'Other', value: 'other' }, -]; - -export function QuerySuggestionItem(props: Props) { - const { querySuggestion, order, queryExplain, historical, onChange, closeDrawer, last, allSuggestions, prompt } = - props; - const [showExp, updShowExp] = useState(false); - - const [gaveExplanationFeedback, updateGaveExplanationFeedback] = useState(false); - const [gaveSuggestionFeedback, updateGaveSuggestionFeedback] = useState(false); - - const [suggestionFeedback, setSuggestionFeedback] = useState({ - radioInput: '', - text: '', - }); - - const [explanationFeedback, setExplanationFeedback] = useState({ - radioInput: '', - text: '', - }); - - const theme = useTheme2(); - const styles = getStyles(theme); - - const { query, explanation } = querySuggestion; - - const feedbackToggleTip = (type: string) => { - const updateRadioFeedback = (value: string) => { - if (type === 'explanation') { - setExplanationFeedback({ - ...explanationFeedback, - radioInput: value, - }); - } else { - setSuggestionFeedback({ - ...suggestionFeedback, - radioInput: value, - }); - } - }; - - const updateTextFeedback = (e: FormEvent) => { - if (type === 'explanation') { - setExplanationFeedback({ - ...explanationFeedback, - text: e.currentTarget.value, - }); - } else { - setSuggestionFeedback({ - ...suggestionFeedback, - text: e.currentTarget.value, - }); - } - }; - - const disabledButton = () => - type === 'explanation' ? !explanationFeedback.radioInput : !suggestionFeedback.radioInput; - - const questionOne = - type === 'explanation' ? 'Why was the explanation not helpful?' : 'Were the query suggestions helpful?'; - - return ( -
-
-
-
{questionOne}
- (Required) -
- -
-
- {type !== 'explanation' && ( -
-
How can we improve the query suggestions?
-
- )} -