diff --git a/locales/en/plugin__lightspeed-console-plugin.json b/locales/en/plugin__lightspeed-console-plugin.json index 583d51d0..c7526454 100644 --- a/locales/en/plugin__lightspeed-console-plugin.json +++ b/locales/en/plugin__lightspeed-console-plugin.json @@ -17,6 +17,7 @@ "Configure events attachment": "Configure events attachment", "Configure log attachment": "Configure log attachment", "Confirm chat deletion": "Confirm chat deletion", + "Content": "Content", "Conversation history has been truncated to fit within context window.": "Conversation history has been truncated to fit within context window.", "Copied": "Copied", "Copy conversation": "Copy conversation", @@ -59,6 +60,7 @@ "Large prompt": "Large prompt", "Leave": "Leave", "Logs": "Logs", + "MCP server": "MCP server", "Minimize": "Minimize", "Most recent {{lines}} lines": "Most recent {{lines}} lines", "Most recent {{numEvents}} events": "Most recent {{numEvents}} events", @@ -80,13 +82,16 @@ "Revert to original": "Revert to original", "Save": "Save", "Send a message...": "Send a message...", + "Status": "Status", "Stay": "Stay", + "Structured content": "Structured content", "Submit": "Submit", "The following output was generated when running <2>{{name}} with arguments <5>{{argsFormatted}}.": "The following output was generated when running <2>{{name}} with arguments <5>{{argsFormatted}}.", "The following output was generated when running <2>{{name}} with no arguments.": "The following output was generated when running <2>{{name}} with no arguments.", "The OpenShift Lightspeed service is not yet ready to receive requests. If this message persists, please check the OLSConfig.": "The OpenShift Lightspeed service is not yet ready to receive requests. If this message persists, please check the OLSConfig.", "Tool output": "Tool output", "Total size of attachments exceeds {{max}} characters.": "Total size of attachments exceeds {{max}} characters.", + "UI resource": "UI resource", "Upload from computer": "Upload from computer", "Uploaded file is not valid YAML": "Uploaded file is not valid YAML", "Uploaded file is too large. Max size is {{max}} MB.": "Uploaded file is too large. Max size is {{max}} MB.", diff --git a/src/components/Prompt.tsx b/src/components/Prompt.tsx index 6781d7fe..3e534b5d 100644 --- a/src/components/Prompt.tsx +++ b/src/components/Prompt.tsx @@ -573,8 +573,23 @@ const Prompt: React.FC = ({ scrollIntoView }) => { const { args, id, name: toolName } = json.data; dispatch(chatHistoryUpdateTool(chatEntryID, id, { name: toolName, args })); } else if (json.event === 'tool_result') { - const { content, id, status } = json.data; - dispatch(chatHistoryUpdateTool(chatEntryID, id, { content, status })); + const { + content, + id, + status, + ui_resource_uri: uiResourceUri, + server_name: serverName, + structured_content: structuredContent, + } = json.data; + dispatch( + chatHistoryUpdateTool(chatEntryID, id, { + content, + status, + ...(uiResourceUri && { uiResourceUri }), + ...(serverName && { serverName }), + ...(structuredContent && { structuredContent }), + }), + ); } else if (json.event === 'error') { dispatch( chatHistoryUpdateByID(chatEntryID, { diff --git a/src/components/ResponseToolModal.tsx b/src/components/ResponseToolModal.tsx index 0aa6db71..de8d102c 100644 --- a/src/components/ResponseToolModal.tsx +++ b/src/components/ResponseToolModal.tsx @@ -2,7 +2,19 @@ import { Map as ImmutableMap } from 'immutable'; import * as React from 'react'; import { Trans, useTranslation } from 'react-i18next'; import { useDispatch, useSelector } from 'react-redux'; -import { Alert, CodeBlock, CodeBlockAction, CodeBlockCode, Icon } from '@patternfly/react-core'; +import { + Alert, + CodeBlock, + CodeBlockAction, + CodeBlockCode, + DescriptionList, + DescriptionListDescription, + DescriptionListGroup, + DescriptionListTerm, + Icon, + Label, + Title, +} from '@patternfly/react-core'; import { InfoCircleIcon } from '@patternfly/react-icons'; import { openToolClear } from '../redux-actions'; @@ -32,12 +44,26 @@ const ToolModal: React.FC = () => { if (!tool) { return null; } - const { args, content, name, status } = tool.toJS(); - const argsFormatted = Object.entries(args) + const { args, content, name, serverName, status, structuredContent, uiResourceUri } = + tool.toJS() as { + args: Record; + content: string; + name: string; + serverName?: string; + status: string; + structuredContent?: Record; + uiResourceUri?: string; + }; + + const argsFormatted = Object.entries(args ?? {}) .map(([key, value]) => `${key}=${value}`) .join(', '); + const structuredContentFormatted = structuredContent + ? JSON.stringify(structuredContent, null, 2) + : undefined; + return ( { )}

+ + + + {t('Status')} + + + + + {serverName && ( + + {t('MCP server')} + {serverName} + + )} + {uiResourceUri && ( + + {t('UI resource')} + + {uiResourceUri} + + + )} + + {content ? ( - - - - - - - } - className="ols-plugin__code-block ols-plugin__code-block--attachment" - > - {content} - + <> + + {t('Content')} + + + + + + + + } + className="ols-plugin__code-block ols-plugin__code-block--attachment" + > + {content} + + ) : ( { variant="info" /> )} + + {structuredContentFormatted && ( + <> + + {t('Structured content')} + + + + + + + + } + className="ols-plugin__code-block ols-plugin__code-block--attachment" + > + + {structuredContentFormatted} + + + + )}
); }; diff --git a/src/components/ResponseTools.tsx b/src/components/ResponseTools.tsx index a787af8e..0bf56df5 100644 --- a/src/components/ResponseTools.tsx +++ b/src/components/ResponseTools.tsx @@ -2,7 +2,7 @@ import { Map as ImmutableMap } from 'immutable'; import * as React from 'react'; import { useDispatch, useSelector } from 'react-redux'; import { Label, LabelGroup } from '@patternfly/react-core'; -import { CodeIcon, InfoCircleIcon } from '@patternfly/react-icons'; +import { CodeIcon, ExternalLinkAltIcon, InfoCircleIcon } from '@patternfly/react-icons'; import { openToolSet } from '../redux-actions'; import { State } from '../redux-reducers'; @@ -23,16 +23,25 @@ const ToolLabel: React.FC = ({ entryIndex, toolID }) => { dispatch(openToolSet(entryIndex, toolID)); }, [dispatch, entryIndex, toolID]); - const isError = tool.get('status') === 'error'; + const status = tool.get('status') as string | undefined; + const isError = status === 'error'; + const isTruncated = status === 'truncated'; + const hasUI = !!tool.get('uiResourceUri'); + + const color = isError ? 'red' : isTruncated ? 'yellow' : hasUI ? 'blue' : undefined; + const icon = isError ? ( + + ) : isTruncated ? ( + + ) : hasUI ? ( + + ) : ( + + ); return ( -