Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add message action bar and refactor the original code #253

Closed
wants to merge 14 commits into from
4 changes: 3 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,6 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
- Add experimental feature to support text to visualization ([#218](https://github.com/opensearch-project/dashboards-assistant/pull/218))
- Be compatible with ML configuration index mapping change ([#239](https://github.com/opensearch-project/dashboards-assistant/pull/239))
- Support context aware alert analysis by reusing incontext insight component([#215](https://github.com/opensearch-project/dashboards-assistant/pull/215))
- Use smaller and compressed variants of buttons and form components ([#250](https://github.com/opensearch-project/dashboards-assistant/pull/250))
- Use smaller and compressed variants of buttons and form components ([#250](https://github.com/opensearch-project/dashboards-assistant/pull/250))
- Add the message action bar module and refactor the original code([#253](https://github.com/opensearch-project/dashboards-assistant/pull/253))

4 changes: 3 additions & 1 deletion opensearch_dashboards.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@
],
"optionalPlugins": [
"dataSource",
"dataSourceManagement"
"dataSourceManagement",
"usageCollection",
"telemetry"
],
"requiredBundles": [],
"configPath": [
Expand Down
144 changes: 100 additions & 44 deletions public/components/incontext_insight/generate_popover_body.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,23 +14,47 @@ import {
EuiSpacer,
EuiText,
} from '@elastic/eui';
import { IMessage, Interaction, IOutput } from 'common/types/chat_saved_object_attributes';
import { MessageActions } from '../../tabs/chat/messages/message_action';
import { IncontextInsight as IncontextInsightInput } from '../../types';
import { getConfigSchema, getIncontextInsightRegistry, getNotifications } from '../../services';
import { HttpSetup } from '../../../../../src/core/public';
import { DataSourceService } from '../../services/data_source_service';
import { ASSISTANT_API } from '../../../common/constants/llm';
import { getAssistantRole } from '../../utils/constants';
import { UsageCollectionSetup } from '../../../../../src/plugins/usage_collection/public';

export const GeneratePopoverBody: React.FC<{
incontextInsight: IncontextInsightInput;
httpSetup?: HttpSetup;
dataSourceService?: DataSourceService;
usageCollection?: UsageCollectionSetup;
closePopover: () => void;
}> = ({ incontextInsight, httpSetup, closePopover }) => {
}> = ({ incontextInsight, httpSetup, dataSourceService, usageCollection, closePopover }) => {
const [isLoading, setIsLoading] = useState(false);
const [summary, setSummary] = useState('');
const [conversationId, setConversationId] = useState('');
const [interaction, setInteraction] = useState<Interaction | null>(null);
const [message, setMessage] = useState<IMessage | null>(null);

const toasts = getNotifications().toasts;
const registry = getIncontextInsightRegistry();

// eslint-disable-next-line @typescript-eslint/no-explicit-any
const handleSummaryResponse = (response: any) => {
const interactionLength = response.interactions.length;
if (interactionLength > 0) {
setConversationId(response.interactions[interactionLength - 1].conversation_id);
setInteraction(response.interactions[interactionLength - 1]);
}

const messageLength = response.messages.length;
if (messageLength > 0 && response.messages[messageLength - 1].type === 'output') {
setSummary(response.messages[messageLength - 1].content);
setMessage(response.messages[messageLength - 1]);
}
};

const onChatContinuation = () => {
registry?.continueInChat(incontextInsight, conversationId);
closePopover();
Expand All @@ -52,8 +76,8 @@ export const GeneratePopoverBody: React.FC<{
incontextInsightType = incontextInsight.key;
}

await httpSetup
?.post(ASSISTANT_API.SEND_MESSAGE, {
try {
const response = await httpSetup?.post(ASSISTANT_API.SEND_MESSAGE, {
body: JSON.stringify({
messages: [],
input: {
Expand All @@ -64,63 +88,95 @@ export const GeneratePopoverBody: React.FC<{
promptPrefix: getAssistantRole(incontextInsightType),
},
}),
})
.then((response) => {
const interactionLength = response.interactions.length;
if (interactionLength > 0) {
setConversationId(response.interactions[interactionLength - 1].conversation_id);
}

const messageLength = response.messages.length;
if (messageLength > 0 && response.messages[messageLength - 1].type === 'output') {
setSummary(response.messages[messageLength - 1].content);
}
})
.catch((error) => {
toasts.addDanger(
i18n.translate('assistantDashboards.incontextInsight.generateSummaryError', {
defaultMessage: 'Generate summary error',
})
);
})
.finally(() => {
setIsLoading(false);
});
handleSummaryResponse(response);
} catch (error) {
toasts.addDanger(
i18n.translate('assistantDashboards.incontextInsight.generateSummaryError', {
defaultMessage: 'Generate summary error',
})
);
} finally {
setIsLoading(false);
}
};

return summarize();
};

const onRegenerateSummary = async (interactionId: string) => {
setIsLoading(true);
setSummary('');
if (conversationId) {
try {
const response = await httpSetup?.put(`${ASSISTANT_API.REGENERATE}`, {
body: JSON.stringify({
conversationId,
interactionId,
}),
query: dataSourceService?.getDataSourceQuery(),
});
handleSummaryResponse(response);
} catch (error) {
toasts.addDanger(
i18n.translate('assistantDashboards.incontextInsight.generateSummaryError', {
defaultMessage: 'Regenerate summary error',
})
);
}
}
setIsLoading(false);
};

return summary ? (
<>
<EuiPanel paddingSize="s" hasBorder hasShadow={false} color="plain">
<EuiText size="s">{summary}</EuiText>
</EuiPanel>
<EuiSpacer size={'xs'} />
{getConfigSchema().chat.enabled && (
{
<EuiPanel
hasShadow={false}
style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}
paddingSize="s"
hasBorder={false}
element="div"
onClick={() => onChatContinuation()}
grow={false}
paddingSize="none"
style={{ width: '120px', float: 'right' }}
hasShadow={false}
color="plain"
>
<EuiFlexGroup gutterSize="none" style={{ marginTop: 5 }}>
<EuiFlexItem grow={false}>
<EuiIcon type={'chatRight'} style={{ marginRight: 5 }} />
</EuiFlexItem>
<EuiFlexItem>
<EuiText size="xs">
{i18n.translate('assistantDashboards.incontextInsight.continueInChat', {
defaultMessage: 'Continue in chat',
})}
</EuiText>
</EuiFlexItem>
</EuiFlexGroup>
<MessageActions
contentToCopy={summary}
showRegenerate
onRegenerate={async () => onRegenerateSummary(interaction?.interaction_id || '')}
interaction={interaction}
showFeedback
showTraceIcon={false}
usageCollection={usageCollection}
/>
{getConfigSchema().chat.enabled && (
<EuiPanel
hasShadow={false}
hasBorder={false}
element="div"
onClick={() => onChatContinuation()}
grow={false}
paddingSize="none"
style={{ width: '120px', float: 'right' }}
>
<EuiFlexGroup gutterSize="none" style={{ marginTop: 5 }}>
<EuiFlexItem grow={false}>
<EuiIcon type={'chatRight'} style={{ marginRight: 5 }} />
</EuiFlexItem>
<EuiFlexItem>
<EuiText size="xs">
{i18n.translate('assistantDashboards.incontextInsight.continueInChat', {
defaultMessage: 'Continue in chat',
})}
</EuiText>
</EuiFlexItem>
</EuiFlexGroup>
</EuiPanel>
)}
</EuiPanel>
)}
}
</>
) : (
<EuiButton
Expand Down
13 changes: 12 additions & 1 deletion public/components/incontext_insight/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,24 @@ import { getIncontextInsightRegistry, getNotifications } from '../../services';
// TODO: Replace with getChrome().logos.Chat.url
import chatIcon from '../../assets/chat.svg';
import { HttpSetup } from '../../../../../src/core/public';
import { DataSourceService } from '../../services/data_source_service';
import { GeneratePopoverBody } from './generate_popover_body';
import { UsageCollectionSetup } from '../../../../../src/plugins/usage_collection/public/plugin';

export interface IncontextInsightProps {
children?: React.ReactNode;
httpSetup?: HttpSetup;
dataSourceService?: DataSourceService;
usageCollection?: UsageCollectionSetup;
}

// TODO: add saved objects / config to store seed suggestions
export const IncontextInsight = ({ children, httpSetup }: IncontextInsightProps) => {
export const IncontextInsight = ({
children,
httpSetup,
dataSourceService,
usageCollection,
}: IncontextInsightProps) => {
const anchor = useRef<HTMLDivElement>(null);
const [isVisible, setIsVisible] = useState(false);

Expand Down Expand Up @@ -278,6 +287,8 @@ export const IncontextInsight = ({ children, httpSetup }: IncontextInsightProps)
<GeneratePopoverBody
incontextInsight={input}
httpSetup={httpSetup}
dataSourceService={dataSourceService}
usageCollection={usageCollection}
closePopover={closePopover}
/>
);
Expand Down
Loading