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 c5b1d0d05d1..c7ec80a9662 100644 --- a/docs/sources/setup-grafana/configure-grafana/feature-toggles/index.md +++ b/docs/sources/setup-grafana/configure-grafana/feature-toggles/index.md @@ -76,6 +76,7 @@ Most [generally available](https://grafana.com/docs/release-life-cycle/#general- | `alertingMigrationUI` | Enables the alerting migration UI, to migrate data source-managed rules to Grafana-managed rules | Yes | | `alertingImportYAMLUI` | Enables a UI feature for importing rules from a Prometheus file to Grafana-managed rules | Yes | | `unifiedNavbars` | Enables unified navbars | | +| `grafanaAssistantInProfilesDrilldown` | Enables integration with Grafana Assistant in Profiles Drilldown | Yes | | `tabularNumbers` | Use fixed-width numbers globally in the UI | | ## Public preview feature toggles diff --git a/packages/grafana-data/src/types/featureToggles.gen.ts b/packages/grafana-data/src/types/featureToggles.gen.ts index a8d6dd05efc..cc2d2b8b63b 100644 --- a/packages/grafana-data/src/types/featureToggles.gen.ts +++ b/packages/grafana-data/src/types/featureToggles.gen.ts @@ -950,6 +950,11 @@ export interface FeatureToggles { */ metricsFromProfiles?: boolean; /** + * Enables integration with Grafana Assistant in Profiles Drilldown + * @default true + */ + grafanaAssistantInProfilesDrilldown?: boolean; + /** * Enables using PGX instead of libpq for PostgreSQL datasource */ postgresDSUsePGX?: boolean; diff --git a/packages/grafana-flamegraph/package.json b/packages/grafana-flamegraph/package.json index 06914eb8915..fb5b0fa7939 100644 --- a/packages/grafana-flamegraph/package.json +++ b/packages/grafana-flamegraph/package.json @@ -83,6 +83,7 @@ "typescript": "5.9.2" }, "peerDependencies": { + "@grafana/assistant": "^0.0.12", "react": "^18.0.0", "react-dom": "^18.0.0" } diff --git a/packages/grafana-flamegraph/src/AnalyzeFlameGraphButton.tsx b/packages/grafana-flamegraph/src/AnalyzeFlameGraphButton.tsx new file mode 100644 index 00000000000..005e1d9b21a --- /dev/null +++ b/packages/grafana-flamegraph/src/AnalyzeFlameGraphButton.tsx @@ -0,0 +1,34 @@ +import { ChatContextItem, useAssistant } from '@grafana/assistant'; +import { Button } from '@grafana/ui'; + +type Props = { + assistantContext: ChatContextItem[]; + className?: string; +}; + +export function AnalyzeFlameGraphButton(props: Props) { + const { assistantContext, className } = props; + const [isAvailable, openAssistant] = useAssistant(); + + if (!isAvailable || !openAssistant || assistantContext.length === 0) { + return null; + } + + return ( + + ); +} diff --git a/packages/grafana-flamegraph/src/FlameGraphContainer.test.tsx b/packages/grafana-flamegraph/src/FlameGraphContainer.test.tsx index 5be3bb3c3db..85ce8dd3470 100644 --- a/packages/grafana-flamegraph/src/FlameGraphContainer.test.tsx +++ b/packages/grafana-flamegraph/src/FlameGraphContainer.test.tsx @@ -9,6 +9,11 @@ import { data } from './FlameGraph/testData/dataNestedSet'; import FlameGraphContainer, { labelSearch } from './FlameGraphContainer'; import { MIN_WIDTH_TO_SHOW_BOTH_TOPTABLE_AND_FLAMEGRAPH } from './constants'; +jest.mock('@grafana/assistant', () => ({ + useAssistant: jest.fn(() => [false, null]), // [isAvailable, openAssistant] + createContext: jest.fn(), +})); + jest.mock('react-use', () => ({ ...jest.requireActual('react-use'), useMeasure: () => { diff --git a/packages/grafana-flamegraph/src/FlameGraphContainer.tsx b/packages/grafana-flamegraph/src/FlameGraphContainer.tsx index 706702142db..da09ea26739 100644 --- a/packages/grafana-flamegraph/src/FlameGraphContainer.tsx +++ b/packages/grafana-flamegraph/src/FlameGraphContainer.tsx @@ -14,6 +14,7 @@ import FlameGraphHeader from './FlameGraphHeader'; import FlameGraphTopTableContainer from './TopTable/FlameGraphTopTableContainer'; import { MIN_WIDTH_TO_SHOW_BOTH_TOPTABLE_AND_FLAMEGRAPH } from './constants'; import { ClickedItemData, ColorScheme, ColorSchemeDiff, SelectedView, TextAlign } from './types'; +import { getAssistantContextFromDataFrame } from './utils'; const ufuzzy = new uFuzzy(); @@ -77,6 +78,14 @@ export type Props = { * Whether or not to keep any focused item when the profile data changes. */ keepFocusOnDataChange?: boolean; + + /** + * If true, the assistant button will be shown in the header if available. + * This is needed mainly for Profiles Drilldown where in some cases we need to hide the button to show alternative + * option to use AI. + * @default true + */ + showAnalyzeWithAssistant?: boolean; }; const FlameGraphContainer = ({ @@ -93,6 +102,7 @@ const FlameGraphContainer = ({ disableCollapsing, keepFocusOnDataChange, getExtraContextMenuButtons, + showAnalyzeWithAssistant = true, }: Props) => { const [focusedItemData, setFocusedItemData] = useState(); @@ -293,6 +303,7 @@ const FlameGraphContainer = ({ isDiffMode={dataContainer.isDiffFlamegraph()} setCollapsedMap={setCollapsedMap} collapsedMap={collapsedMap} + assistantContext={data && showAnalyzeWithAssistant ? getAssistantContextFromDataFrame(data) : undefined} /> )} diff --git a/packages/grafana-flamegraph/src/FlameGraphHeader.test.tsx b/packages/grafana-flamegraph/src/FlameGraphHeader.test.tsx index 546450dad2b..0ca621d9f64 100644 --- a/packages/grafana-flamegraph/src/FlameGraphHeader.test.tsx +++ b/packages/grafana-flamegraph/src/FlameGraphHeader.test.tsx @@ -7,6 +7,11 @@ import { CollapsedMap } from './FlameGraph/dataTransform'; import FlameGraphHeader from './FlameGraphHeader'; import { ColorScheme, SelectedView } from './types'; +jest.mock('@grafana/assistant', () => ({ + useAssistant: jest.fn(() => [false, null]), // [isAvailable, openAssistant] + createContext: jest.fn(), +})); + describe('FlameGraphHeader', () => { function setup(props: Partial> = {}) { const setSearch = jest.fn(); diff --git a/packages/grafana-flamegraph/src/FlameGraphHeader.tsx b/packages/grafana-flamegraph/src/FlameGraphHeader.tsx index 91541f216b7..e51dd78b051 100644 --- a/packages/grafana-flamegraph/src/FlameGraphHeader.tsx +++ b/packages/grafana-flamegraph/src/FlameGraphHeader.tsx @@ -3,9 +3,11 @@ import { useEffect, useState } from 'react'; import * as React from 'react'; import { useDebounce, usePrevious } from 'react-use'; +import { ChatContextItem } from '@grafana/assistant'; import { GrafanaTheme2, SelectableValue } from '@grafana/data'; import { Button, ButtonGroup, Dropdown, Input, Menu, RadioButtonGroup, useStyles2 } from '@grafana/ui'; +import { AnalyzeFlameGraphButton } from './AnalyzeFlameGraphButton'; import { byPackageGradient, byValueGradient, diffColorBlindGradient, diffDefaultGradient } from './FlameGraph/colors'; import { CollapsedMap } from './FlameGraph/dataTransform'; import { MIN_WIDTH_TO_SHOW_BOTH_TOPTABLE_AND_FLAMEGRAPH } from './constants'; @@ -30,6 +32,8 @@ type Props = { collapsedMap: CollapsedMap; extraHeaderElements?: React.ReactNode; + + assistantContext?: ChatContextItem[]; }; const FlameGraphHeader = ({ @@ -50,6 +54,7 @@ const FlameGraphHeader = ({ isDiffMode, setCollapsedMap, collapsedMap, + assistantContext, }: Props) => { const styles = useStyles2(getStyles); const [localSearch, setLocalSearch] = useSearchInput(search, setSearch); @@ -84,6 +89,9 @@ const FlameGraphHeader = ({
+ {assistantContext && ( + + )} {showResetButton && (