@lobehub/lobehub 2.0.10 → 2.0.12

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 (101) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/Dockerfile +44 -52
  3. package/changelog/v2.json +18 -0
  4. package/locales/ar/chat.json +4 -0
  5. package/locales/ar/models.json +65 -0
  6. package/locales/bg-BG/chat.json +4 -0
  7. package/locales/bg-BG/models.json +10 -0
  8. package/locales/de-DE/chat.json +4 -0
  9. package/locales/de-DE/models.json +41 -0
  10. package/locales/en-US/chat.json +4 -0
  11. package/locales/es-ES/chat.json +4 -0
  12. package/locales/es-ES/models.json +50 -0
  13. package/locales/fa-IR/chat.json +4 -0
  14. package/locales/fa-IR/models.json +39 -0
  15. package/locales/fr-FR/chat.json +4 -0
  16. package/locales/fr-FR/models.json +9 -0
  17. package/locales/it-IT/chat.json +4 -0
  18. package/locales/it-IT/models.json +62 -0
  19. package/locales/ja-JP/chat.json +4 -0
  20. package/locales/ja-JP/models.json +40 -0
  21. package/locales/ko-KR/chat.json +4 -0
  22. package/locales/ko-KR/models.json +31 -0
  23. package/locales/nl-NL/chat.json +4 -0
  24. package/locales/nl-NL/models.json +52 -0
  25. package/locales/pl-PL/chat.json +4 -0
  26. package/locales/pl-PL/models.json +43 -0
  27. package/locales/pt-BR/chat.json +4 -0
  28. package/locales/pt-BR/models.json +92 -0
  29. package/locales/ru-RU/chat.json +4 -0
  30. package/locales/ru-RU/models.json +34 -0
  31. package/locales/tr-TR/chat.json +4 -0
  32. package/locales/tr-TR/models.json +55 -0
  33. package/locales/vi-VN/chat.json +4 -0
  34. package/locales/vi-VN/models.json +31 -0
  35. package/locales/zh-CN/chat.json +4 -0
  36. package/locales/zh-TW/chat.json +4 -0
  37. package/package.json +1 -1
  38. package/packages/agent-runtime/src/groupOrchestration/GroupOrchestrationSupervisor.ts +18 -1
  39. package/packages/agent-runtime/src/groupOrchestration/__tests__/GroupOrchestrationSupervisor.test.ts +76 -5
  40. package/packages/agent-runtime/src/groupOrchestration/types.ts +3 -3
  41. package/packages/builtin-tool-group-management/src/client/Intervention/ExecuteTask.tsx +11 -11
  42. package/packages/builtin-tool-group-management/src/client/Intervention/ExecuteTasks.tsx +78 -79
  43. package/packages/builtin-tool-group-management/src/client/Render/ExecuteTask/index.tsx +3 -3
  44. package/packages/builtin-tool-group-management/src/client/Render/ExecuteTasks/index.tsx +61 -63
  45. package/packages/builtin-tool-group-management/src/client/Streaming/ExecuteTask/index.tsx +3 -3
  46. package/packages/builtin-tool-group-management/src/executor.test.ts +7 -9
  47. package/packages/builtin-tool-group-management/src/executor.ts +3 -3
  48. package/packages/builtin-tool-group-management/src/manifest.ts +49 -50
  49. package/packages/builtin-tool-group-management/src/systemRole.ts +153 -5
  50. package/packages/builtin-tool-group-management/src/types.ts +3 -2
  51. package/packages/builtin-tool-gtd/src/systemRole.ts +4 -4
  52. package/packages/context-engine/src/processors/TasksFlatten.ts +7 -5
  53. package/packages/context-engine/src/processors/__tests__/TasksFlatten.test.ts +164 -0
  54. package/packages/conversation-flow/src/__tests__/fixtures/inputs/agentGroup/index.ts +4 -0
  55. package/packages/conversation-flow/src/__tests__/fixtures/inputs/agentGroup/supervisor-after-multi-tasks.json +91 -0
  56. package/packages/conversation-flow/src/__tests__/fixtures/inputs/agentGroup/supervisor-content-only.json +74 -0
  57. package/packages/conversation-flow/src/__tests__/parse.test.ts +37 -0
  58. package/packages/conversation-flow/src/transformation/FlatListBuilder.ts +70 -4
  59. package/packages/conversation-flow/src/transformation/__tests__/FlatListBuilder.test.ts +147 -0
  60. package/packages/database/src/repositories/agentGroup/index.ts +4 -0
  61. package/packages/types/src/message/ui/chat.ts +2 -0
  62. package/packages/types/src/tool/builtin.ts +5 -5
  63. package/src/app/[variants]/(main)/community/(detail)/group_agent/features/Sidebar/ActionButton/ForkGroupAndChat.tsx +2 -1
  64. package/src/app/[variants]/(main)/group/profile/features/Header/GroupPublishButton/index.tsx +2 -2
  65. package/src/app/[variants]/(main)/group/profile/features/Header/GroupPublishButton/useMarketGroupPublish.ts +11 -12
  66. package/src/features/Conversation/ChatItem/components/Title.tsx +1 -1
  67. package/src/features/Conversation/ChatList/index.tsx +0 -1
  68. package/src/features/Conversation/Messages/GroupTasks/TaskItem/ClientTaskItem.tsx +183 -0
  69. package/src/features/Conversation/Messages/GroupTasks/TaskItem/ServerTaskItem.tsx +94 -0
  70. package/src/features/Conversation/Messages/GroupTasks/TaskItem/TaskTitle.tsx +177 -0
  71. package/src/features/Conversation/Messages/GroupTasks/TaskItem/index.tsx +26 -0
  72. package/src/features/Conversation/Messages/GroupTasks/TaskItem/useClientTaskStats.ts +93 -0
  73. package/src/features/Conversation/Messages/GroupTasks/index.tsx +151 -0
  74. package/src/features/Conversation/Messages/Supervisor/index.tsx +7 -1
  75. package/src/features/Conversation/Messages/Task/ClientTaskDetail/index.tsx +72 -91
  76. package/src/features/Conversation/Messages/Task/TaskDetailPanel/StatusContent.tsx +46 -17
  77. package/src/features/Conversation/Messages/Tasks/TaskItem/ClientTaskItem.tsx +9 -24
  78. package/src/features/Conversation/Messages/Tasks/TaskItem/ServerTaskItem.tsx +18 -38
  79. package/src/features/Conversation/Messages/Tasks/shared/ErrorState.tsx +45 -2
  80. package/src/features/Conversation/Messages/Tasks/shared/InitializingState.tsx +16 -1
  81. package/src/features/Conversation/Messages/Tasks/shared/TaskContent.tsx +68 -0
  82. package/src/features/Conversation/Messages/Tasks/shared/TaskMessages.tsx +383 -0
  83. package/src/features/Conversation/Messages/Tasks/shared/index.ts +4 -0
  84. package/src/features/Conversation/Messages/Tasks/shared/useTaskPolling.ts +48 -0
  85. package/src/features/Conversation/Messages/index.tsx +5 -0
  86. package/src/locales/default/chat.ts +4 -0
  87. package/src/server/modules/AgentRuntime/RuntimeExecutors.ts +4 -0
  88. package/src/server/modules/AgentRuntime/__tests__/RuntimeExecutors.test.ts +106 -1
  89. package/src/server/routers/lambda/agentGroup.ts +2 -0
  90. package/src/server/routers/lambda/market/agent.ts +17 -45
  91. package/src/server/routers/lambda/market/agentGroup.ts +13 -25
  92. package/src/server/services/aiAgent/__tests__/execAgent.threadId.test.ts +2 -2
  93. package/src/server/utils/truncateToolResult.ts +1 -4
  94. package/src/store/chat/agents/GroupOrchestration/__tests__/batch-exec-async-tasks.test.ts +15 -15
  95. package/src/store/chat/agents/GroupOrchestration/createGroupOrchestrationExecutors.ts +22 -15
  96. package/src/store/chat/agents/__tests__/createAgentExecutors/exec-tasks.test.ts +21 -10
  97. package/src/store/chat/agents/createAgentExecutors.ts +2 -0
  98. package/src/store/chat/slices/aiAgent/actions/groupOrchestration.ts +10 -7
  99. package/src/features/Conversation/Messages/Task/ClientTaskDetail/CompletedState.tsx +0 -108
  100. package/src/features/Conversation/Messages/Task/ClientTaskDetail/InstructionAccordion.tsx +0 -63
  101. package/src/features/Conversation/Messages/Task/ClientTaskDetail/ProcessingState.tsx +0 -123
@@ -66,7 +66,8 @@ describe('exec_tasks executor', () => {
66
66
  expect(result.nextContext).toBeDefined();
67
67
  expect((result.nextContext as AgentRuntimeContext).phase).toBe('tasks_batch_result');
68
68
 
69
- const payload = (result.nextContext as AgentRuntimeContext).payload as TasksBatchResultPayload;
69
+ const payload = (result.nextContext as AgentRuntimeContext)
70
+ .payload as TasksBatchResultPayload;
70
71
  expect(payload.results).toHaveLength(1);
71
72
  expect(payload.results[0].success).toBe(true);
72
73
  expect(payload.results[0].threadId).toBe('thread_1');
@@ -131,7 +132,8 @@ describe('exec_tasks executor', () => {
131
132
 
132
133
  // Then
133
134
  expect(result.nextContext).toBeDefined();
134
- const payload = (result.nextContext as AgentRuntimeContext).payload as TasksBatchResultPayload;
135
+ const payload = (result.nextContext as AgentRuntimeContext)
136
+ .payload as TasksBatchResultPayload;
135
137
  expect(payload.results).toHaveLength(3);
136
138
  expect(payload.results.every((r) => r.success)).toBe(true);
137
139
  });
@@ -170,7 +172,8 @@ describe('exec_tasks executor', () => {
170
172
 
171
173
  // Then
172
174
  expect(result.nextContext).toBeDefined();
173
- const payload = (result.nextContext as AgentRuntimeContext).payload as TasksBatchResultPayload;
175
+ const payload = (result.nextContext as AgentRuntimeContext)
176
+ .payload as TasksBatchResultPayload;
174
177
  expect(payload.results).toHaveLength(1);
175
178
  expect(payload.results[0].success).toBe(false);
176
179
  expect(payload.results[0].error).toBe('No valid context available');
@@ -196,7 +199,8 @@ describe('exec_tasks executor', () => {
196
199
  });
197
200
 
198
201
  // Then
199
- const payload = (result.nextContext as AgentRuntimeContext).payload as TasksBatchResultPayload;
202
+ const payload = (result.nextContext as AgentRuntimeContext)
203
+ .payload as TasksBatchResultPayload;
200
204
  expect(payload.results[0].success).toBe(false);
201
205
  expect(payload.results[0].error).toBe('Failed to create task message');
202
206
  });
@@ -228,7 +232,8 @@ describe('exec_tasks executor', () => {
228
232
  });
229
233
 
230
234
  // Then
231
- const payload = (result.nextContext as AgentRuntimeContext).payload as TasksBatchResultPayload;
235
+ const payload = (result.nextContext as AgentRuntimeContext)
236
+ .payload as TasksBatchResultPayload;
232
237
  expect(payload.results[0].success).toBe(false);
233
238
  expect(payload.results[0].error).toBe('API error');
234
239
  expect(mockStore.optimisticUpdateMessageContent).toHaveBeenCalledWith(
@@ -270,7 +275,8 @@ describe('exec_tasks executor', () => {
270
275
  });
271
276
 
272
277
  // Then
273
- const payload = (result.nextContext as AgentRuntimeContext).payload as TasksBatchResultPayload;
278
+ const payload = (result.nextContext as AgentRuntimeContext)
279
+ .payload as TasksBatchResultPayload;
274
280
  expect(payload.results[0].success).toBe(false);
275
281
  expect(payload.results[0].error).toBe('Execution error');
276
282
  });
@@ -318,7 +324,8 @@ describe('exec_tasks executor', () => {
318
324
  },
319
325
  { operationId: 'test-op' },
320
326
  );
321
- const payload = (result.nextContext as AgentRuntimeContext).payload as TasksBatchResultPayload;
327
+ const payload = (result.nextContext as AgentRuntimeContext)
328
+ .payload as TasksBatchResultPayload;
322
329
  expect(payload.results[0].success).toBe(true);
323
330
  });
324
331
 
@@ -353,7 +360,8 @@ describe('exec_tasks executor', () => {
353
360
  });
354
361
 
355
362
  // Then
356
- const payload = (result.nextContext as AgentRuntimeContext).payload as TasksBatchResultPayload;
363
+ const payload = (result.nextContext as AgentRuntimeContext)
364
+ .payload as TasksBatchResultPayload;
357
365
  expect(payload.results[0].success).toBe(false);
358
366
  expect(payload.results[0].error).toBe('Task was cancelled');
359
367
  expect(mockStore.optimisticUpdateMessageContent).toHaveBeenCalledWith(
@@ -402,7 +410,8 @@ describe('exec_tasks executor', () => {
402
410
  });
403
411
 
404
412
  // Then
405
- const payload = (result.nextContext as AgentRuntimeContext).payload as TasksBatchResultPayload;
413
+ const payload = (result.nextContext as AgentRuntimeContext)
414
+ .payload as TasksBatchResultPayload;
406
415
  expect(payload.results[0].success).toBe(false);
407
416
  expect(payload.results[0].error).toBe('Operation cancelled');
408
417
  // getSubAgentTaskStatus should not be called since operation was cancelled before poll
@@ -532,6 +541,7 @@ describe('exec_tasks executor', () => {
532
541
  {
533
542
  agentId: 'agent_1',
534
543
  content: '',
544
+ createdAt: expect.any(Number),
535
545
  metadata: { instruction: 'Do something important' },
536
546
  parentId: 'msg_parent',
537
547
  role: 'task',
@@ -588,7 +598,8 @@ describe('exec_tasks executor', () => {
588
598
  });
589
599
 
590
600
  // Then
591
- const payload = (result.nextContext as AgentRuntimeContext).payload as TasksBatchResultPayload;
601
+ const payload = (result.nextContext as AgentRuntimeContext)
602
+ .payload as TasksBatchResultPayload;
592
603
  expect(payload.results).toHaveLength(2);
593
604
  expect(payload.results[0].success).toBe(true);
594
605
  expect(payload.results[1].success).toBe(false);
@@ -1349,6 +1349,7 @@ export const createAgentExecutors = (context: {
1349
1349
  {
1350
1350
  agentId,
1351
1351
  content: '',
1352
+ createdAt: Date.now() + taskIndex,
1352
1353
  metadata: { instruction: task.instruction },
1353
1354
  parentId: parentMessageId,
1354
1355
  role: 'task',
@@ -2035,6 +2036,7 @@ export const createAgentExecutors = (context: {
2035
2036
  {
2036
2037
  agentId,
2037
2038
  content: '',
2039
+ createdAt: Date.now() + taskIndex,
2038
2040
  metadata: { instruction: task.instruction, taskTitle: task.description },
2039
2041
  parentId: parentMessageId,
2040
2042
  role: 'task',
@@ -216,17 +216,17 @@ export const groupOrchestrationSlice: StateCreator<
216
216
  const {
217
217
  supervisorAgentId,
218
218
  agentId,
219
- task,
219
+ instruction,
220
220
  timeout,
221
221
  toolMessageId,
222
222
  skipCallSupervisor,
223
223
  runInClient,
224
224
  } = params;
225
225
  log(
226
- '[triggerExecuteTask] Starting orchestration with execute_task: supervisorAgentId=%s, agentId=%s, task=%s, timeout=%s, toolMessageId=%s, skipCallSupervisor=%s, runInClient=%s',
226
+ '[triggerExecuteTask] Starting orchestration with execute_task: supervisorAgentId=%s, agentId=%s, instruction=%s, timeout=%s, toolMessageId=%s, skipCallSupervisor=%s, runInClient=%s',
227
227
  supervisorAgentId,
228
228
  agentId,
229
- task,
229
+ instruction,
230
230
  timeout,
231
231
  toolMessageId,
232
232
  skipCallSupervisor,
@@ -248,7 +248,7 @@ export const groupOrchestrationSlice: StateCreator<
248
248
  type: 'supervisor_decided',
249
249
  payload: {
250
250
  decision: 'execute_task',
251
- params: { agentId, runInClient, task, timeout, toolMessageId },
251
+ params: { agentId, instruction, runInClient, timeout, toolMessageId },
252
252
  skipCallSupervisor: skipCallSupervisor ?? false,
253
253
  },
254
254
  },
@@ -446,12 +446,15 @@ export const groupOrchestrationSlice: StateCreator<
446
446
  revalidateOnReconnect: false,
447
447
  refreshInterval: POLLING_INTERVAL,
448
448
  onSuccess: (data) => {
449
- if (data?.taskDetail && messageId) {
450
- // Update taskDetail
449
+ if (data && messageId) {
450
+ // Update taskDetail and tasks (intermediate messages)
451
451
  get().internal_dispatchMessage({
452
452
  id: messageId,
453
453
  type: 'updateMessage',
454
- value: { taskDetail: data.taskDetail },
454
+ value: {
455
+ taskDetail: data.taskDetail,
456
+ tasks: data.messages,
457
+ },
455
458
  });
456
459
 
457
460
  // Update content when task is completed or failed
@@ -1,108 +0,0 @@
1
- 'use client';
2
-
3
- import { type AssistantContentBlock } from '@lobechat/types';
4
- import { Accordion, AccordionItem, Block, Flexbox, Icon, Text } from '@lobehub/ui';
5
- import { cssVar } from 'antd-style';
6
- import { Workflow } from 'lucide-react';
7
- import { memo, useMemo } from 'react';
8
- import { useTranslation } from 'react-i18next';
9
-
10
- import ContentBlock from '../../AssistantGroup/components/ContentBlock';
11
- import { formatDuration } from '../../Tasks/shared/utils';
12
- import Usage from '../../components/Extras/Usage';
13
-
14
- interface CompletedStateProps {
15
- assistantId: string;
16
- blocks: AssistantContentBlock[];
17
- duration?: number;
18
- model?: string;
19
- provider?: string;
20
- totalCost?: number;
21
- totalTokens?: number;
22
- totalToolCalls?: number;
23
- }
24
-
25
- const CompletedState = memo<CompletedStateProps>(
26
- ({ blocks, assistantId, duration, totalToolCalls, model, provider, totalTokens, totalCost }) => {
27
- const { t } = useTranslation('chat');
28
-
29
- // Split blocks: intermediate steps (all but last) and final result (last)
30
- const { intermediateBlocks, finalBlock } = useMemo(() => {
31
- if (blocks.length === 0) return { finalBlock: null, intermediateBlocks: [] };
32
- if (blocks.length === 1) return { finalBlock: blocks[0], intermediateBlocks: [] };
33
-
34
- return {
35
- finalBlock: blocks.at(-1)!,
36
- intermediateBlocks: blocks.slice(0, -1),
37
- };
38
- }, [blocks]);
39
-
40
- if (!finalBlock) return null;
41
-
42
- const title = (
43
- <Flexbox align="center" gap={8} horizontal>
44
- <Block
45
- align="center"
46
- flex="none"
47
- gap={4}
48
- height={24}
49
- horizontal
50
- justify="center"
51
- style={{ fontSize: 12 }}
52
- variant="outlined"
53
- width={24}
54
- >
55
- <Icon color={cssVar.colorTextSecondary} icon={Workflow} />
56
- </Block>
57
- <Flexbox align="center" gap={4} horizontal>
58
- <Text as="span" type="secondary" weight={500}>
59
- {totalToolCalls}
60
- </Text>
61
- <Text as="span" type="secondary">
62
- {t('task.metrics.toolCallsShort')}
63
- </Text>
64
- {/* Duration display */}
65
- {duration && (
66
- <Text as="span" type="secondary">
67
- {t('task.metrics.duration', { duration: formatDuration(duration) })}
68
- </Text>
69
- )}
70
- </Flexbox>
71
- </Flexbox>
72
- );
73
-
74
- return (
75
- <Flexbox gap={8}>
76
- {/* Intermediate steps - collapsed by default */}
77
- {intermediateBlocks.length > 0 && (
78
- <Accordion defaultExpandedKeys={[]} gap={8}>
79
- <AccordionItem itemKey="intermediate" paddingBlock={4} paddingInline={4} title={title}>
80
- <Flexbox gap={8} paddingInline={4} style={{ marginTop: 8 }}>
81
- {intermediateBlocks.map((block) => (
82
- <ContentBlock
83
- {...block}
84
- assistantId={assistantId}
85
- disableEditing
86
- key={block.id}
87
- />
88
- ))}
89
- </Flexbox>
90
- </AccordionItem>
91
- </Accordion>
92
- )}
93
-
94
- {/* Final result - always visible */}
95
- <ContentBlock {...finalBlock} assistantId={assistantId} disableEditing />
96
-
97
- {/* Usage display */}
98
- {model && provider && (
99
- <Usage model={model} provider={provider} usage={{ cost: totalCost, totalTokens }} />
100
- )}
101
- </Flexbox>
102
- );
103
- },
104
- );
105
-
106
- CompletedState.displayName = 'ClientCompletedState';
107
-
108
- export default CompletedState;
@@ -1,63 +0,0 @@
1
- import { Accordion, AccordionItem, Block, Flexbox, Icon, Markdown, Text } from '@lobehub/ui';
2
- import { cssVar } from 'antd-style';
3
- import { ScrollText } from 'lucide-react';
4
- import { memo, useEffect, useState } from 'react';
5
- import { useTranslation } from 'react-i18next';
6
-
7
- interface InstructionAccordionProps {
8
- childrenCount: number;
9
- instruction: string;
10
- }
11
-
12
- const InstructionAccordion = memo<InstructionAccordionProps>(({ instruction, childrenCount }) => {
13
- const { t } = useTranslation('chat');
14
-
15
- // Auto-collapse instruction when children count exceeds threshold
16
- const [expandedKeys, setExpandedKeys] = useState<string[]>(['instruction']);
17
-
18
- useEffect(() => {
19
- if (childrenCount > 1) {
20
- setExpandedKeys([]);
21
- }
22
- }, [childrenCount > 1]);
23
-
24
- return (
25
- <Accordion
26
- expandedKeys={expandedKeys}
27
- gap={8}
28
- onExpandedChange={(keys) => setExpandedKeys(keys as string[])}
29
- >
30
- <AccordionItem
31
- itemKey="instruction"
32
- paddingBlock={4}
33
- paddingInline={4}
34
- title={
35
- <Flexbox align="center" gap={8} horizontal>
36
- <Block
37
- align="center"
38
- flex="none"
39
- gap={4}
40
- height={24}
41
- horizontal
42
- justify="center"
43
- style={{ fontSize: 12 }}
44
- variant="outlined"
45
- width={24}
46
- >
47
- <Icon color={cssVar.colorTextSecondary} icon={ScrollText} />
48
- </Block>
49
- <Text as="span" type="secondary">
50
- {t('task.instruction')}
51
- </Text>
52
- </Flexbox>
53
- }
54
- >
55
- <Block padding={12} style={{ marginBlock: 8, maxHeight: 300, overflow: 'auto' }} variant={'outlined'}>
56
- <Markdown variant={'chat'}>{instruction}</Markdown>
57
- </Block>
58
- </AccordionItem>
59
- </Accordion>
60
- );
61
- });
62
-
63
- export default InstructionAccordion;
@@ -1,123 +0,0 @@
1
- 'use client';
2
-
3
- import { type AssistantContentBlock } from '@lobechat/types';
4
- import { Block, Flexbox, ScrollShadow, Text } from '@lobehub/ui';
5
- import { createStaticStyles } from 'antd-style';
6
- import { type RefObject, memo, useEffect, useMemo, useState } from 'react';
7
- import { useTranslation } from 'react-i18next';
8
-
9
- import NeuralNetworkLoading from '@/components/NeuralNetworkLoading';
10
- import AnimatedNumber from '@/features/Conversation/Messages/components/Extras/Usage/UsageDetail/AnimatedNumber';
11
- import { useAutoScroll } from '@/hooks/useAutoScroll';
12
-
13
- import ContentBlock from '../../AssistantGroup/components/ContentBlock';
14
- import { accumulateUsage, formatElapsedTime } from '../../Tasks/shared/utils';
15
- import Usage from '../../components/Extras/Usage';
16
-
17
- const styles = createStaticStyles(({ css }) => ({
18
- contentScroll: css`
19
- max-height: min(50vh, 300px);
20
- `,
21
- }));
22
-
23
- interface ProcessingStateProps {
24
- assistantId: string;
25
- blocks: AssistantContentBlock[];
26
- model?: string;
27
- provider?: string;
28
- startTime?: number;
29
- }
30
-
31
- const ProcessingState = memo<ProcessingStateProps>(
32
- ({ blocks, assistantId, startTime, model, provider }) => {
33
- const { t } = useTranslation('chat');
34
- const [elapsedTime, setElapsedTime] = useState(0);
35
- const { ref, handleScroll } = useAutoScroll<HTMLDivElement>({
36
- deps: [blocks],
37
- enabled: true,
38
- });
39
-
40
- const totalToolCalls = useMemo(
41
- () => blocks.reduce((sum, block) => sum + (block.tools?.length || 0), 0),
42
- [blocks],
43
- );
44
-
45
- // Accumulate usage from all blocks
46
- const accumulatedUsage = useMemo(() => accumulateUsage(blocks), [blocks]);
47
-
48
- // Calculate initial elapsed time
49
- useEffect(() => {
50
- if (startTime) {
51
- setElapsedTime(Math.max(0, Date.now() - startTime));
52
- }
53
- }, [startTime]);
54
-
55
- // Timer for updating elapsed time every second
56
- useEffect(() => {
57
- if (!startTime) return;
58
-
59
- const timer = setInterval(() => {
60
- setElapsedTime(Math.max(0, Date.now() - startTime));
61
- }, 1000);
62
-
63
- return () => clearInterval(timer);
64
- }, [startTime]);
65
-
66
- return (
67
- <Flexbox gap={8}>
68
- <Flexbox align="center" gap={8} horizontal paddingInline={4}>
69
- <Block
70
- align="center"
71
- flex="none"
72
- gap={4}
73
- height={24}
74
- horizontal
75
- justify="center"
76
- style={{ fontSize: 12 }}
77
- variant="outlined"
78
- width={24}
79
- >
80
- <NeuralNetworkLoading size={16} />
81
- </Block>
82
- <Flexbox align="center" gap={4} horizontal>
83
- <Text as="span" type="secondary" weight={500}>
84
- <AnimatedNumber
85
- duration={500}
86
- formatter={(v) => Math.round(v).toString()}
87
- value={totalToolCalls}
88
- />
89
- </Text>
90
- <Text as="span" type="secondary">
91
- {t('task.metrics.toolCallsShort')}
92
- </Text>
93
- {startTime && (
94
- <Text as="span" type="secondary">
95
- ({formatElapsedTime(elapsedTime)})
96
- </Text>
97
- )}
98
- </Flexbox>
99
- </Flexbox>
100
- <ScrollShadow
101
- className={styles.contentScroll}
102
- offset={12}
103
- onScroll={handleScroll}
104
- ref={ref as RefObject<HTMLDivElement>}
105
- size={8}
106
- >
107
- <Flexbox gap={8}>
108
- {blocks.map((block) => (
109
- <ContentBlock {...block} assistantId={assistantId} disableEditing key={block.id} />
110
- ))}
111
- </Flexbox>
112
- </ScrollShadow>
113
-
114
- {/* Usage display */}
115
- {model && provider && <Usage model={model} provider={provider} usage={accumulatedUsage} />}
116
- </Flexbox>
117
- );
118
- },
119
- );
120
-
121
- ProcessingState.displayName = 'ClientProcessingState';
122
-
123
- export default ProcessingState;