@orchestrator-ui/orchestrator-ui-components 7.5.1 → 7.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/.turbo/turbo-build.log +7 -7
  2. package/.turbo/turbo-lint.log +1 -1
  3. package/.turbo/turbo-test.log +7 -7
  4. package/CHANGELOG.md +26 -0
  5. package/dist/index.d.ts +542 -53
  6. package/dist/index.js +1297 -1101
  7. package/dist/index.js.map +1 -1
  8. package/jest.config.cjs +4 -1
  9. package/package.json +6 -6
  10. package/src/components/WfoAgent/ExportButton/ExportButton.tsx +5 -11
  11. package/src/components/WfoAgent/WfoAgent/WfoAgent.tsx +79 -31
  12. package/src/components/WfoAgent/WfoAgentChart/WfoAgentLineChart.tsx +2 -2
  13. package/src/components/WfoAgent/WfoAgentChart/WfoAgentPieChart.tsx +2 -2
  14. package/src/components/WfoAgent/WfoAgentTable/WfoAgentTable.tsx +9 -9
  15. package/src/components/WfoAgent/WfoAgentVisualization/WfoAgentVisualization.tsx +2 -2
  16. package/src/components/WfoAgent/WfoPlanProgress/WfoPlanProgress.tsx +107 -0
  17. package/src/components/WfoAgent/WfoPlanProgress/index.ts +1 -0
  18. package/src/components/WfoAgent/WfoPlanProgress/styles.ts +62 -0
  19. package/src/components/WfoAgent/WfoQueryArtifact/WfoQueryArtifact.tsx +40 -0
  20. package/src/components/WfoAgent/WfoQueryArtifact/index.ts +1 -0
  21. package/src/components/WfoAgent/index.ts +2 -0
  22. package/src/components/WfoBadges/WfoEngineStatusBadge/WfoEngineStatusBadge.tsx +3 -1
  23. package/src/components/WfoBadges/WfoVersionIncompatibleBadge/WfoVersionIncompatibleBadge.tsx +7 -6
  24. package/src/components/WfoBadges/WfoWebsocketStatusBadge/WfoWebsocketStatusBadge.tsx +9 -3
  25. package/src/components/WfoKeyValueTable/WfoValueCell.tsx +1 -1
  26. package/src/components/WfoPydanticForm/fields/WfoInteger.tsx +22 -3
  27. package/src/components/WfoSubscription/WfoSubscriptionActions/WfoSubscriptionActions.tsx +3 -2
  28. package/src/components/WfoSubscription/WfoSubscriptionGeneralSections/WfoSubscriptionDetailSection.tsx +34 -3
  29. package/src/components/WfoSubscriptionsList/WfoSubscriptionsList.tsx +9 -9
  30. package/src/components/WfoTable/WfoAdvancedTable/WfoAdvancedTable.tsx +1 -1
  31. package/src/components/WfoTable/WfoFirstPartUUID/WfoFirstPartUUID.tsx +1 -1
  32. package/src/components/WfoTitleWithWebsocketBadge/WfoTitleWithWebsocketBadge.tsx +3 -2
  33. package/src/components/WfoWorkflowSteps/WfoStep/WfoStep.tsx +57 -40
  34. package/src/components/WfoWorkflowSteps/WfoStep/useStepDetailOverride.ts +9 -0
  35. package/src/components/WfoWorkflowSteps/WfoWorkflowStepList/WfoStepListHeader.tsx +0 -1
  36. package/src/configuration/constants.ts +3 -0
  37. package/src/configuration/version.ts +1 -1
  38. package/src/hooks/useAgentPlanEvents.ts +188 -0
  39. package/src/messages/en-GB.json +7 -0
  40. package/src/messages/nl-NL.json +2 -0
  41. package/src/pages/metadata/WfoScheduleTaskFormPage.tsx +153 -35
  42. package/src/rtk/endpoints/agentQueryResults.ts +19 -0
  43. package/src/rtk/endpoints/forms.ts +1 -1
  44. package/src/rtk/endpoints/index.ts +1 -0
  45. package/src/rtk/endpoints/metadata/scheduledTasks.ts +9 -17
  46. package/src/rtk/endpoints/streamMessages.ts +10 -23
  47. package/src/rtk/slices/orchestratorComponentOverride.ts +5 -1
  48. package/src/types/search.ts +19 -4
  49. package/src/utils/compareVersions.spec.ts +5 -0
  50. package/src/utils/compareVersions.ts +55 -23
  51. package/src/components/WfoAgent/ToolProgress/DiscoverFilterPathsDisplay.tsx +0 -99
  52. package/src/components/WfoAgent/ToolProgress/RunSearchDisplay.tsx +0 -34
  53. package/src/components/WfoAgent/ToolProgress/SetFilterTreeDisplay.styles.ts +0 -62
  54. package/src/components/WfoAgent/ToolProgress/SetFilterTreeDisplay.tsx +0 -107
  55. package/src/components/WfoAgent/ToolProgress/StartNewSearchDisplay.tsx +0 -60
  56. package/src/components/WfoAgent/ToolProgress/ToolProgress.tsx +0 -98
  57. package/src/components/WfoAgent/ToolProgress/index.ts +0 -1
  58. package/src/components/WfoAgent/ToolProgress/styles.ts +0 -52
package/jest.config.cjs CHANGED
@@ -11,7 +11,10 @@ const createJestConfig = nextJest({
11
11
  const customJestConfig = {
12
12
  ...base,
13
13
  displayName: 'Wfo-UI Tests',
14
- moduleNameMapper: { '^uuid$': 'uuid' },
14
+ moduleNameMapper: {
15
+ '^uuid$': 'uuid',
16
+ '^@copilotkit/react-core/v2$': '<rootDir>/__mocks__/@copilotkit/react-core.js',
17
+ },
15
18
  };
16
19
 
17
20
  module.exports = createJestConfig(customJestConfig);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@orchestrator-ui/orchestrator-ui-components",
3
- "version": "7.5.1",
3
+ "version": "7.7.0",
4
4
  "license": "Apache-2.0",
5
5
  "description": "Library of UI Components used to display the workflow orchestrator frontend",
6
6
  "author": {
@@ -34,10 +34,10 @@
34
34
  "dev": "tsup --watch"
35
35
  },
36
36
  "dependencies": {
37
- "@ag-ui/client": "0.0.42",
38
- "@copilotkit/react-core": "1.10.6",
39
- "@copilotkit/react-ui": "1.10.6",
40
- "@copilotkit/runtime": "1.10.6",
37
+ "@ag-ui/client": "^0.0.47",
38
+ "@copilotkit/react-core": "1.54.0",
39
+ "@copilotkit/react-ui": "1.54.0",
40
+ "@copilotkit/runtime": "1.54.0",
41
41
  "@elastic/charts": "^64.1.0",
42
42
  "@emotion/css": "^11.11.2",
43
43
  "@emotion/react": "^11.11.4",
@@ -54,7 +54,7 @@
54
54
  "next-query-params": "^5.0.0",
55
55
  "object-hash": "^3.0.0",
56
56
  "prism-themes": "^1.9.0",
57
- "pydantic-forms": "^1.0.4",
57
+ "pydantic-forms": "^1.0.7",
58
58
  "react-diff-view": "^3.2.0",
59
59
  "react-draggable": "^4.4.6",
60
60
  "react-redux": "^9.1.2",
@@ -6,20 +6,14 @@ import { EuiIcon, EuiLoadingSpinner } from '@elastic/eui';
6
6
 
7
7
  import { useShowToastMessage, useWithOrchestratorTheme } from '@/hooks';
8
8
  import { useLazyGetAgentExportQuery } from '@/rtk/endpoints/agentExport';
9
- import { GraphQLPageInfo } from '@/types';
9
+ import { ExportArtifact, GraphQLPageInfo } from '@/types';
10
10
  import { getCsvFileNameWithDate } from '@/utils';
11
11
  import { csvDownloadHandler } from '@/utils/csvDownload';
12
12
 
13
13
  import { getExportButtonStyles } from './styles';
14
14
 
15
- export type ExportData = {
16
- action: string;
17
- download_url: string;
18
- message: string;
19
- };
20
-
21
15
  export type ExportButtonProps = {
22
- exportData: ExportData;
16
+ artifact: ExportArtifact;
23
17
  };
24
18
 
25
19
  type ExportApiResponse = {
@@ -27,7 +21,7 @@ type ExportApiResponse = {
27
21
  pageInfo?: GraphQLPageInfo;
28
22
  };
29
23
 
30
- export function ExportButton({ exportData }: ExportButtonProps) {
24
+ export function ExportButton({ artifact }: ExportButtonProps) {
31
25
  const { showToastMessage } = useShowToastMessage();
32
26
  const tError = useTranslations('errors');
33
27
  const [triggerExport, { isFetching }] = useLazyGetAgentExportQuery();
@@ -45,7 +39,7 @@ export function ExportButton({ exportData }: ExportButtonProps) {
45
39
  const filename = getCsvFileNameWithDate('export');
46
40
 
47
41
  const onDownloadClick = async () => {
48
- const data = await triggerExport(exportData.download_url).unwrap();
42
+ const data = await triggerExport(artifact.download_url).unwrap();
49
43
 
50
44
  const keyOrder = data.page.length > 0 ? Object.keys(data.page[0]) : [];
51
45
 
@@ -74,7 +68,7 @@ export function ExportButton({ exportData }: ExportButtonProps) {
74
68
  return (
75
69
  <div css={containerStyle}>
76
70
  <div css={buttonWrapperStyle}>
77
- {exportData.message && <div css={titleStyle}>{exportData.message}</div>}
71
+ {artifact.description && <div css={titleStyle}>{artifact.description}</div>}
78
72
  <div css={fileRowStyle} onClick={onDownloadClick}>
79
73
  <div css={fileInfoStyle}>
80
74
  <EuiIcon type="document" size="m" />
@@ -1,61 +1,108 @@
1
- import React from 'react';
1
+ import React, { useCallback, useRef } from 'react';
2
2
 
3
3
  import { useTranslations } from 'next-intl';
4
4
 
5
- import { CatchAllActionRenderProps, useCopilotAction, useRenderToolCall } from '@copilotkit/react-core';
5
+ import { useRenderToolCall } from '@copilotkit/react-core';
6
6
  import { CopilotChat } from '@copilotkit/react-ui';
7
+ import type { RenderMessageProps } from '@copilotkit/react-ui';
7
8
 
8
9
  import { WfoAvailabilityCheck } from '@/components/WfoAvailabilityCheck';
9
10
  import { getPageTemplateStyles } from '@/components/WfoPageTemplate/WfoPageTemplate/styles';
10
11
  import { useWithOrchestratorTheme } from '@/hooks';
12
+ import { type PlanExecutionState, useAgentPlanEvents } from '@/hooks/useAgentPlanEvents';
11
13
  import { useAgentAvailability } from '@/hooks/useBackendAvailability';
12
- import { AggregationResultsData } from '@/types';
14
+ import { ExportArtifact, QueryArtifact } from '@/types';
13
15
 
14
- import { ExportButton, ExportData } from '../ExportButton';
15
- import { ToolProgress } from '../ToolProgress';
16
- import { WfoAgentVisualization } from '../WfoAgentVisualization';
16
+ import { ExportButton } from '../ExportButton';
17
+ import { WfoPlanProgress } from '../WfoPlanProgress';
18
+ import { WfoQueryArtifact } from '../WfoQueryArtifact';
17
19
 
18
20
  export function WfoAgent() {
19
21
  const tPage = useTranslations('agent.page');
20
22
 
21
23
  const { NAVIGATION_HEIGHT } = useWithOrchestratorTheme(getPageTemplateStyles);
22
24
  const agentAvailability = useAgentAvailability();
25
+ const planProgress = useAgentPlanEvents();
23
26
 
24
- useRenderToolCall({
25
- name: 'run_search',
26
- render: ({ result }) => {
27
- if (!result) {
28
- return <div></div>;
27
+ // Use a ref so the RenderMessage callback stays stable (no blink)
28
+ // while always reading the latest plan progress
29
+ const planProgressRef = useRef<PlanExecutionState>(planProgress);
30
+ planProgressRef.current = planProgress;
31
+
32
+ const RenderMessage = useCallback(
33
+ ({
34
+ message,
35
+ messages,
36
+ inProgress,
37
+ index,
38
+ isCurrentMessage,
39
+ AssistantMessage,
40
+ UserMessage,
41
+ ImageRenderer,
42
+ onRegenerate,
43
+ ...rest
44
+ }: RenderMessageProps) => {
45
+ if (message.role === 'user') {
46
+ return UserMessage ?
47
+ <UserMessage key={index} rawData={message} message={message} ImageRenderer={ImageRenderer!} />
48
+ : null;
29
49
  }
30
- return <WfoAgentVisualization aggregationData={result as AggregationResultsData} />;
31
- },
32
- });
33
50
 
34
- useRenderToolCall({
35
- name: 'run_aggregation',
36
- render: ({ result }) => {
37
- if (!result) {
38
- return <div></div>;
51
+ if (message.role === 'assistant') {
52
+ const progress = planProgressRef.current;
53
+
54
+ // Show plan progress on the first assistant message
55
+ // after the last user message (the active response)
56
+ const lastUserIndex = [...messages].reverse().findIndex((msg) => msg.role === 'user');
57
+ const firstAssistantAfterUser = lastUserIndex >= 0 ? messages.length - lastUserIndex : -1;
58
+ const showPlanProgress = index === firstAssistantAfterUser && (progress.planning || progress.steps.length > 0);
59
+
60
+ return (
61
+ <>
62
+ {showPlanProgress && <WfoPlanProgress executionState={progress} />}
63
+ {AssistantMessage && (
64
+ <AssistantMessage
65
+ key={index}
66
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- generativeUI is an internal CopilotKit property not in public types
67
+ subComponent={(message as any).generativeUI?.()}
68
+ rawData={message}
69
+ message={message}
70
+ messages={messages}
71
+ isLoading={inProgress && isCurrentMessage && !message.content}
72
+ isGenerating={inProgress && isCurrentMessage && !!message.content}
73
+ isCurrentMessage={isCurrentMessage}
74
+ onRegenerate={() => onRegenerate?.(message.id)}
75
+ feedback={rest.messageFeedback?.[message.id] || null}
76
+ ImageRenderer={ImageRenderer!}
77
+ {...rest}
78
+ />
79
+ )}
80
+ </>
81
+ );
39
82
  }
40
- return <WfoAgentVisualization aggregationData={result as AggregationResultsData} />;
83
+
84
+ return null;
41
85
  },
42
- });
86
+ [],
87
+ );
88
+
89
+ const renderQueryResult = ({ result }: { result?: unknown }) => {
90
+ if (!result) {
91
+ return <></>;
92
+ }
93
+ return <WfoQueryArtifact artifact={result as QueryArtifact} />;
94
+ };
95
+
96
+ useRenderToolCall({ name: 'run_search', render: renderQueryResult });
97
+ useRenderToolCall({ name: 'run_aggregation', render: renderQueryResult });
43
98
 
44
99
  useRenderToolCall({
45
100
  name: 'prepare_export',
46
101
  render: ({ result }) => {
47
102
  if (!result) {
48
- return <div></div>;
103
+ return <></>;
49
104
  }
50
- return <ExportButton exportData={result as ExportData} />;
51
- },
52
- });
53
-
54
- // Automatically render all other tool calls
55
- useCopilotAction({
56
- name: '*',
57
- render: ({ name, status, args, result }: CatchAllActionRenderProps<[]>) => {
58
- return <ToolProgress name={name} status={status} args={args} result={result} />;
105
+ return <ExportButton artifact={result as ExportArtifact} />;
59
106
  },
60
107
  });
61
108
 
@@ -70,6 +117,7 @@ export function WfoAgent() {
70
117
  }
71
118
  `}</style>
72
119
  <CopilotChat
120
+ RenderMessage={RenderMessage}
73
121
  labels={{
74
122
  title: tPage('copilot.title'),
75
123
  initial: tPage('copilot.initial'),
@@ -5,10 +5,10 @@ import '@elastic/charts/dist/theme_only_dark.css';
5
5
  import '@elastic/charts/dist/theme_only_light.css';
6
6
 
7
7
  import { useOrchestratorTheme } from '@/hooks';
8
- import { AggregationResultsData } from '@/types';
8
+ import { QueryResultsData } from '@/types';
9
9
 
10
10
  export type WfoAgentLineChartProps = {
11
- aggregationData: AggregationResultsData;
11
+ aggregationData: QueryResultsData;
12
12
  };
13
13
 
14
14
  export function WfoAgentLineChart({ aggregationData }: WfoAgentLineChartProps) {
@@ -4,13 +4,13 @@ import { Chart, DARK_THEME, LIGHT_THEME, Partition, PartitionLayout, Position, S
4
4
  import '@elastic/charts/dist/theme_only_dark.css';
5
5
  import '@elastic/charts/dist/theme_only_light.css';
6
6
 
7
- import { AggregationResultsData } from '@/types';
7
+ import { QueryResultsData } from '@/types';
8
8
 
9
9
  import { useOrchestratorTheme } from '../../../hooks';
10
10
  import { containerStyle } from './styles';
11
11
 
12
12
  export type WfoAgentPieChartProps = {
13
- aggregationData: AggregationResultsData;
13
+ aggregationData: QueryResultsData;
14
14
  };
15
15
 
16
16
  export function WfoAgentPieChart({ aggregationData }: WfoAgentPieChartProps) {
@@ -2,10 +2,10 @@ import React from 'react';
2
2
 
3
3
  import { EuiBasicTable, EuiBasicTableColumn } from '@elastic/eui';
4
4
 
5
- import { AggregationResult, AggregationResultsData } from '@/types';
5
+ import { QueryResultsData, ResultRow } from '@/types';
6
6
 
7
7
  export type WfoAgentTableProps = {
8
- aggregationData: AggregationResultsData;
8
+ aggregationData: QueryResultsData;
9
9
  };
10
10
 
11
11
  const formatColumnName = (key: string) => key.replace(/_/g, ' ').replace(/\b\w/g, (l) => l.toUpperCase());
@@ -15,23 +15,23 @@ export function WfoAgentTable({ aggregationData }: WfoAgentTableProps) {
15
15
  const [pageIndex, setPageIndex] = React.useState(0);
16
16
  const [pageSize, setPageSize] = React.useState(5);
17
17
 
18
- const columns: EuiBasicTableColumn<AggregationResult>[] = React.useMemo(() => {
18
+ const columns: EuiBasicTableColumn<ResultRow>[] = React.useMemo(() => {
19
19
  if (results.length === 0) return [];
20
20
 
21
21
  const firstResult = results[0];
22
22
  const groupKeys = Object.keys(firstResult.group_values);
23
23
  const aggKeys = Object.keys(firstResult.aggregations);
24
24
 
25
- const groupColumns: EuiBasicTableColumn<AggregationResult>[] = groupKeys.map((key) => ({
26
- field: 'group_values' as keyof AggregationResult,
25
+ const groupColumns: EuiBasicTableColumn<ResultRow>[] = groupKeys.map((key) => ({
26
+ field: 'group_values' as keyof ResultRow,
27
27
  name: formatColumnName(key),
28
- render: (_: unknown, record: AggregationResult) => record.group_values[key],
28
+ render: (_: unknown, record: ResultRow) => record.group_values[key],
29
29
  }));
30
30
 
31
- const aggColumns: EuiBasicTableColumn<AggregationResult>[] = aggKeys.map((key) => ({
32
- field: 'aggregations' as keyof AggregationResult,
31
+ const aggColumns: EuiBasicTableColumn<ResultRow>[] = aggKeys.map((key) => ({
32
+ field: 'aggregations' as keyof ResultRow,
33
33
  name: formatColumnName(key),
34
- render: (_: unknown, record: AggregationResult) => record.aggregations[key],
34
+ render: (_: unknown, record: ResultRow) => record.aggregations[key],
35
35
  }));
36
36
 
37
37
  return [...groupColumns, ...aggColumns];
@@ -4,14 +4,14 @@ import { useTranslations } from 'next-intl';
4
4
 
5
5
  import { EuiText } from '@elastic/eui';
6
6
 
7
- import { AggregationResultsData, VisualizationType } from '@/types';
7
+ import { QueryResultsData, VisualizationType } from '@/types';
8
8
 
9
9
  import { WfoAgentLineChart } from '../WfoAgentChart/WfoAgentLineChart';
10
10
  import { WfoAgentPieChart } from '../WfoAgentChart/WfoAgentPieChart';
11
11
  import { WfoAgentTable } from '../WfoAgentTable';
12
12
 
13
13
  export type WfoAgentVisualizationProps = {
14
- aggregationData: AggregationResultsData;
14
+ aggregationData: QueryResultsData;
15
15
  };
16
16
 
17
17
  export function WfoAgentVisualization({ aggregationData }: WfoAgentVisualizationProps) {
@@ -0,0 +1,107 @@
1
+ import React, { useState } from 'react';
2
+
3
+ import { useTranslations } from 'next-intl';
4
+
5
+ import { EuiLoadingSpinner, EuiPanel, EuiText } from '@elastic/eui';
6
+
7
+ import { useWithOrchestratorTheme } from '@/hooks';
8
+ import type { PlanExecutionState } from '@/hooks/useAgentPlanEvents';
9
+ import { useOrchestratorTheme } from '@/hooks/useOrchestratorTheme';
10
+ import { WfoCheckmarkCircleFill, WfoChevronDown, WfoChevronUp, WfoMinusCircleOutline } from '@/icons';
11
+
12
+ import { getWfoPlanProgressStyles } from './styles';
13
+
14
+ type WfoPlanProgressProps = {
15
+ executionState: PlanExecutionState;
16
+ };
17
+
18
+ export const WfoPlanProgress = ({ executionState }: WfoPlanProgressProps) => {
19
+ const [expandedSteps, setExpandedSteps] = useState<Set<string>>(new Set());
20
+ const t = useTranslations('agent.page.planProgress');
21
+ const { theme } = useOrchestratorTheme();
22
+
23
+ const { containerStyle, headerStyle, rowStyle, reasoningStyle, toolCallsToggleStyle, toolCallsListStyle, iconSize } =
24
+ useWithOrchestratorTheme(getWfoPlanProgressStyles);
25
+
26
+ if (!executionState.planning && executionState.steps.length === 0) {
27
+ return null;
28
+ }
29
+
30
+ const toggleStep = (stepName: string) => {
31
+ setExpandedSteps((prev) => {
32
+ const next = new Set(prev);
33
+ if (next.has(stepName)) {
34
+ next.delete(stepName);
35
+ } else {
36
+ next.add(stepName);
37
+ }
38
+ return next;
39
+ });
40
+ };
41
+
42
+ const completedCount = executionState.steps.filter((s) => s.status === 'completed').length;
43
+ const totalCount = executionState.steps.length;
44
+ const allDone = totalCount > 0 && completedCount === totalCount;
45
+
46
+ const headerLabel =
47
+ executionState.planning ? t('planning')
48
+ : allDone ? t('completed')
49
+ : t('executing', { completed: completedCount, total: totalCount });
50
+
51
+ const StatusIcon = ({ status }: { status: string }) => {
52
+ if (status === 'completed')
53
+ return <WfoCheckmarkCircleFill color={theme.colors.success} width={iconSize} height={iconSize} />;
54
+ if (status === 'pending')
55
+ return <WfoMinusCircleOutline color={theme.colors.textSubdued} width={iconSize} height={iconSize} />;
56
+ return <EuiLoadingSpinner size="s" />;
57
+ };
58
+
59
+ return (
60
+ <EuiPanel hasBorder paddingSize="s" css={containerStyle}>
61
+ <div css={headerStyle}>
62
+ {allDone ?
63
+ <WfoCheckmarkCircleFill color={theme.colors.success} width={iconSize} height={iconSize} />
64
+ : <EuiLoadingSpinner size="s" />}
65
+ <span>{headerLabel}</span>
66
+ </div>
67
+ {executionState.steps.map((step) => {
68
+ const isExpanded = expandedSteps.has(step.step_name);
69
+ const hasToolCalls = step.tool_calls.length > 0;
70
+
71
+ return (
72
+ <div key={step.step_name}>
73
+ <div css={rowStyle}>
74
+ <StatusIcon status={step.status} />
75
+ <EuiText size="s" color={step.status === 'pending' ? 'subdued' : undefined}>
76
+ {step.step_name}
77
+ </EuiText>
78
+ {hasToolCalls && (
79
+ <span css={toolCallsToggleStyle} onClick={() => toggleStep(step.step_name)}>
80
+ {step.tool_calls.length}
81
+ {isExpanded ?
82
+ <WfoChevronUp width={iconSize} height={iconSize} />
83
+ : <WfoChevronDown width={iconSize} height={iconSize} />}
84
+ </span>
85
+ )}
86
+ </div>
87
+ {step.reasoning && (
88
+ <EuiText size="xs" color="subdued" css={reasoningStyle}>
89
+ {step.reasoning}
90
+ </EuiText>
91
+ )}
92
+ {isExpanded && hasToolCalls && (
93
+ <div css={toolCallsListStyle}>
94
+ {step.tool_calls.map((tc) => (
95
+ <EuiText size="xs" key={tc.id} css={rowStyle}>
96
+ <StatusIcon status={tc.status === 'complete' ? 'completed' : 'active'} />
97
+ <span>{tc.name}</span>
98
+ </EuiText>
99
+ ))}
100
+ </div>
101
+ )}
102
+ </div>
103
+ );
104
+ })}
105
+ </EuiPanel>
106
+ );
107
+ };
@@ -0,0 +1 @@
1
+ export { WfoPlanProgress } from './WfoPlanProgress';
@@ -0,0 +1,62 @@
1
+ import { css } from '@emotion/react';
2
+
3
+ import { WfoThemeHelpers } from '@/hooks';
4
+
5
+ export const getWfoPlanProgressStyles = ({ theme }: WfoThemeHelpers) => {
6
+ const iconSize = 14;
7
+
8
+ const containerStyle = css({
9
+ maxWidth: '50%',
10
+ marginRight: 'auto',
11
+ marginBottom: theme.size.s,
12
+ fontSize: theme.size.m,
13
+ });
14
+
15
+ const headerStyle = css({
16
+ display: 'flex',
17
+ alignItems: 'center',
18
+ gap: theme.size.s,
19
+ paddingBottom: theme.size.s,
20
+ marginBottom: theme.size.xs,
21
+ borderBottom: `${theme.border.width.thin} solid ${theme.colors.borderBaseSubdued}`,
22
+ fontWeight: theme.font.weight.semiBold,
23
+ });
24
+
25
+ const rowStyle = css({
26
+ display: 'flex',
27
+ alignItems: 'center',
28
+ gap: theme.size.s,
29
+ padding: `${theme.size.xxs} 0`,
30
+ });
31
+
32
+ const reasoningStyle = css({
33
+ marginLeft: `calc(${iconSize}px + ${theme.size.s})`,
34
+ });
35
+
36
+ const toolCallsToggleStyle = css({
37
+ display: 'inline-flex',
38
+ alignItems: 'center',
39
+ gap: theme.size.xxs,
40
+ cursor: 'pointer',
41
+ fontSize: theme.size.s,
42
+ color: theme.colors.textSubdued,
43
+ marginLeft: 'auto',
44
+ '&:hover': {
45
+ color: theme.colors.textParagraph,
46
+ },
47
+ });
48
+
49
+ const toolCallsListStyle = css({
50
+ paddingLeft: `calc(${iconSize}px + ${theme.size.s})`,
51
+ });
52
+
53
+ return {
54
+ containerStyle,
55
+ headerStyle,
56
+ rowStyle,
57
+ reasoningStyle,
58
+ toolCallsToggleStyle,
59
+ toolCallsListStyle,
60
+ iconSize,
61
+ };
62
+ };
@@ -0,0 +1,40 @@
1
+ import React from 'react';
2
+
3
+ import { useTranslations } from 'next-intl';
4
+
5
+ import { EuiText } from '@elastic/eui';
6
+
7
+ import { WfoLoading } from '@/components';
8
+ import { useGetAgentQueryResultsQuery } from '@/rtk/endpoints/agentQueryResults';
9
+ import { QueryArtifact } from '@/types';
10
+
11
+ import { WfoAgentVisualization } from '../WfoAgentVisualization';
12
+
13
+ export type WfoQueryArtifactProps = {
14
+ artifact: QueryArtifact;
15
+ };
16
+
17
+ export function WfoQueryArtifact({ artifact }: WfoQueryArtifactProps) {
18
+ const t = useTranslations('agent.page.visualization');
19
+ const { data, isLoading, isError } = useGetAgentQueryResultsQuery(artifact.query_id);
20
+
21
+ if (isLoading) {
22
+ return <WfoLoading />;
23
+ }
24
+
25
+ if (isError || !data) {
26
+ return (
27
+ <EuiText size="s" color="danger">
28
+ <p>{t('noDataAvailable')}</p>
29
+ </EuiText>
30
+ );
31
+ }
32
+
33
+ // Use the visualization_type from the artifact (set by the LLM) rather than the response
34
+ const aggregationData = {
35
+ ...data,
36
+ visualization_type: artifact.visualization_type,
37
+ };
38
+
39
+ return <WfoAgentVisualization aggregationData={aggregationData} />;
40
+ }
@@ -0,0 +1 @@
1
+ export * from './WfoQueryArtifact';
@@ -3,3 +3,5 @@ export * from './WfoAgentTable';
3
3
  export * from './WfoAgentChart/WfoAgentLineChart';
4
4
  export * from './WfoAgentChart/WfoAgentPieChart';
5
5
  export * from './WfoAgentVisualization';
6
+ export * from './WfoPlanProgress';
7
+ export * from './WfoQueryArtifact';
@@ -13,10 +13,12 @@ export const WfoEngineStatusBadge = () => {
13
13
  const { data } = useGetEngineStatusQuery();
14
14
  const { engineStatus } = data || {};
15
15
  const { useWebSockets } = useGetOrchestratorConfig();
16
+
17
+ const { orchestratorWebsocketUrl } = useGetOrchestratorConfig();
16
18
  const [websocketTrigger, { isUninitialized }] = useLazyStreamMessagesQuery();
17
19
 
18
20
  if (useWebSockets && isUninitialized) {
19
- websocketTrigger();
21
+ websocketTrigger(orchestratorWebsocketUrl);
20
22
  }
21
23
 
22
24
  const engineStatusText: string = engineStatus ? `Engine is ${engineStatus}` : 'Engine status is unavailable';
@@ -23,11 +23,12 @@ export const WfoVersionIncompatibleBadge: FC<WfoVersionIncompatibleBadgeProps> =
23
23
  const t = useTranslations('main');
24
24
  const { theme } = useOrchestratorTheme();
25
25
  const mappedVersions: MappedVersion[] = versionCompatibility;
26
- const minimumOrchestratorCoreVersion = getOrchestratorCoreVersionIfNotCompatible(
27
- orchestratorUiVersion,
28
- orchestratorCoreVersion,
29
- mappedVersions,
30
- );
26
+ const isReady = orchestratorCoreVersion && orchestratorUiVersion;
27
+
28
+ const minimumOrchestratorCoreVersion =
29
+ isReady ?
30
+ getOrchestratorCoreVersionIfNotCompatible(orchestratorUiVersion, orchestratorCoreVersion, mappedVersions)
31
+ : null;
31
32
 
32
33
  return (
33
34
  <EuiToolTip
@@ -52,7 +53,7 @@ export const WfoVersionIncompatibleBadge: FC<WfoVersionIncompatibleBadgeProps> =
52
53
  textColor={theme.colors.textGhost}
53
54
  css={{
54
55
  marginLeft: theme.size.s,
55
- visibility: minimumOrchestratorCoreVersion ? 'visible' : 'hidden',
56
+ display: minimumOrchestratorCoreVersion ? 'block' : 'none',
56
57
  }}
57
58
  >
58
59
  {t('incompatibleVersion')}
@@ -6,7 +6,7 @@ import { useTranslations } from 'next-intl';
6
6
  import { EuiToolTip } from '@elastic/eui';
7
7
 
8
8
  import { WfoHeaderBadge } from '@/components';
9
- import { useWithOrchestratorTheme } from '@/hooks';
9
+ import { useGetOrchestratorConfig, useWithOrchestratorTheme } from '@/hooks';
10
10
  import { useOrchestratorTheme } from '@/hooks/useOrchestratorTheme';
11
11
  import { WfoBoltFill, WfoBoltSlashFill } from '@/icons';
12
12
  import { orchestratorApi } from '@/rtk';
@@ -15,16 +15,22 @@ import { useStreamMessagesQuery } from '@/rtk/endpoints/streamMessages';
15
15
  import { getStyles } from './styles';
16
16
 
17
17
  interface WfoWebsocketStatusBadgeProps {
18
+ wsUrl?: string;
18
19
  hideWhenConnected?: boolean;
19
20
  }
20
21
 
21
- export const WfoWebsocketStatusBadge: FC<WfoWebsocketStatusBadgeProps> = ({ hideWhenConnected = false }) => {
22
+ export const WfoWebsocketStatusBadge: FC<WfoWebsocketStatusBadgeProps> = ({
23
+ hideWhenConnected = false,
24
+ wsUrl = undefined,
25
+ }) => {
22
26
  const dispatch = useDispatch();
23
27
  const { connectedStyle, disconnectedStyle } = useWithOrchestratorTheme(getStyles);
24
28
 
25
29
  const t = useTranslations('main');
26
30
  const { theme } = useOrchestratorTheme();
27
- const { data: websocketConnected } = useStreamMessagesQuery();
31
+
32
+ const { orchestratorWebsocketUrl } = useGetOrchestratorConfig();
33
+ const { data: websocketConnected } = useStreamMessagesQuery(wsUrl || orchestratorWebsocketUrl);
28
34
 
29
35
  const showBadge = !(websocketConnected && hideWhenConnected);
30
36
 
@@ -45,7 +45,7 @@ export const WfoValueCell: FC<WfoValueCellProps> = ({ value, textToCopy, rowNumb
45
45
  <WfoClipboardCopy
46
46
  width={clipboardIconSize}
47
47
  height={clipboardIconSize}
48
- color={theme.colors.backgroundBaseDisabled}
48
+ color={theme.colors.backgroundBaseAccent}
49
49
  />
50
50
  </div>
51
51
  )}