grafana/public/app/features/dashboard/components/GenAI/GenAIButton.tsx

97 lines
2.5 KiB
TypeScript

import { css } from '@emotion/css';
import React, { useEffect } from 'react';
import { GrafanaTheme2 } from '@grafana/data';
import { Button, Spinner, useStyles2, Tooltip } from '@grafana/ui';
import { useOpenAIStream } from './hooks';
import { OPEN_AI_MODEL, Message } from './utils';
export interface GenAIButtonProps {
// Button label text
text?: string;
// Button label text when loading
loadingText?: string;
// Button click handler
onClick?: (e: React.MouseEvent<HTMLButtonElement>) => void;
// Messages to send to the LLM plugin
messages: Message[];
// Callback function that the LLM plugin streams responses to
onGenerate: (response: string) => void;
// Temperature for the LLM plugin. Default is 1.
// Closer to 0 means more conservative, closer to 1 means more creative.
temperature?: number;
}
export const GenAIButton = ({
text = 'Auto-generate',
loadingText = 'Generating',
onClick: onClickProp,
messages,
onGenerate,
temperature = 1,
}: GenAIButtonProps) => {
const styles = useStyles2(getStyles);
const { setMessages, reply, isGenerating, value, error } = useOpenAIStream(OPEN_AI_MODEL, temperature);
useEffect(() => {
// Todo: Consider other options for `"` sanitation
if (isGenerating && reply) {
onGenerate(reply.replace(/^"|"$/g, ''));
}
}, [isGenerating, reply, onGenerate]);
// The button is disabled if the plugin is not installed or enabled
if (!value?.enabled) {
return null;
}
const onClick = (e: React.MouseEvent<HTMLButtonElement>) => {
onClickProp?.(e);
setMessages(messages);
};
const getIcon = () => {
if (error || !value?.enabled) {
return 'exclamation-circle';
}
if (isGenerating) {
return undefined;
}
return 'ai';
};
const getText = () => {
if (error) {
return 'Retry';
}
return !isGenerating ? text : loadingText;
};
return (
<div className={styles.wrapper}>
{isGenerating && <Spinner size={14} />}
<Tooltip show={error ? undefined : false} interactive content={`OpenAI error: ${error?.message}`}>
<Button
icon={getIcon()}
onClick={onClick}
fill="text"
size="sm"
disabled={isGenerating || (!value?.enabled && !error)}
variant={error ? 'destructive' : 'primary'}
>
{getText()}
</Button>
</Tooltip>
</div>
);
};
const getStyles = (theme: GrafanaTheme2) => ({
wrapper: css({
display: 'flex',
}),
});