import { css } from '@emotion/css'; import { useState, useCallback } from 'react'; import { GrafanaTheme2 } from '@grafana/data'; import { Alert, Button, Icon, Input, Stack, Text, TextLink, useStyles2 } from '@grafana/ui'; import { Trans } from 'app/core/internationalization'; import { STOP_GENERATION_TEXT } from './GenAIButton'; import { GenerationHistoryCarousel } from './GenerationHistoryCarousel'; import { QuickFeedback } from './QuickFeedback'; import { StreamStatus, useOpenAIStream } from './hooks'; import { AutoGenerateItem, EventTrackingSrc, reportAutoGenerateInteraction } from './tracking'; import { getFeedbackMessage, Message, DEFAULT_OAI_MODEL, QuickFeedbackType, sanitizeReply } from './utils'; export interface GenAIHistoryProps { history: string[]; messages: Message[]; onApplySuggestion: (suggestion: string) => void; updateHistory: (historyEntry: string) => void; eventTrackingSrc: EventTrackingSrc; } const temperature = 0.5; export const GenAIHistory = ({ eventTrackingSrc, history, messages, onApplySuggestion, updateHistory, }: GenAIHistoryProps) => { const styles = useStyles2(getStyles); const [currentIndex, setCurrentIndex] = useState(1); const [customFeedback, setCustomPrompt] = useState(''); const onResponse = useCallback( (response: string) => { updateHistory(sanitizeReply(response)); }, [updateHistory] ); const { setMessages, stopGeneration, reply, streamStatus, error } = useOpenAIStream({ model: DEFAULT_OAI_MODEL, temperature, onResponse, }); const reportInteraction = (item: AutoGenerateItem, otherMetadata?: object) => reportAutoGenerateInteraction(eventTrackingSrc, item, otherMetadata); const onSubmitCustomFeedback = (text: string) => { onGenerateWithFeedback(text); reportInteraction(AutoGenerateItem.customFeedback, { customFeedback: text }); }; const onStopGeneration = () => { stopGeneration(); reply && onResponse(reply); }; const onApply = () => { onApplySuggestion(history[currentIndex - 1]); }; const onNavigate = (index: number) => { setCurrentIndex(index); reportInteraction(index > currentIndex ? AutoGenerateItem.backHistoryItem : AutoGenerateItem.forwardHistoryItem); }; const onGenerateWithFeedback = (suggestion: string) => { setMessages((messages) => [...messages, ...getFeedbackMessage(history[currentIndex - 1], suggestion)]); if (suggestion in QuickFeedbackType) { reportInteraction(AutoGenerateItem.quickFeedback, { quickFeedbackItem: suggestion }); } }; const onKeyDownCustomFeedbackInput = (e: React.KeyboardEvent) => e.key === 'Enter' && onSubmitCustomFeedback(customFeedback); const onChangeCustomFeedback = (e: React.FormEvent) => setCustomPrompt(e.currentTarget.value); const onClickSubmitCustomFeedback = () => onSubmitCustomFeedback(customFeedback); const onClickDocs = () => reportInteraction(AutoGenerateItem.linkToDocs); const isStreamGenerating = streamStatus === StreamStatus.GENERATING; const showError = error && !isStreamGenerating; return (
{showError && (

Sorry, I was unable to complete your request. Please try again.

)}
Send } value={customFeedback} onChange={onChangeCustomFeedback} onKeyDown={onKeyDownCustomFeedbackInput} />
{isStreamGenerating ? ( ) : ( )}
This content is AI-generated using the{' '} Grafana LLM plugin
); }; const getStyles = (theme: GrafanaTheme2) => ({ container: css({ display: 'flex', flexDirection: 'column', width: 520, maxHeight: 350, // This is the space the footer height paddingBottom: 25, }), applySuggestion: css({ paddingTop: theme.spacing(2), }), actions: css({ display: 'flex', flexDirection: 'row', flexWrap: 'wrap', }), footer: css({ // Absolute positioned since Toggletip doesn't support footer position: 'absolute', bottom: 0, left: 0, width: '100%', display: 'flex', flexDirection: 'row', margin: 0, padding: theme.spacing(1), paddingLeft: theme.spacing(2), alignItems: 'center', gap: theme.spacing(1), borderTop: `1px solid ${theme.colors.border.weak}`, marginTop: theme.spacing(2), }), infoColor: css({ color: theme.colors.info.main, }), actionButtons: css({ display: 'flex', flexDirection: 'row', justifyContent: 'space-between', padding: '24px 0 8px 0', }), });