@lobehub/lobehub 2.0.0-next.66 → 2.0.0-next.68
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.
- package/CHANGELOG.md +50 -0
- package/changelog/v1.json +18 -0
- package/package.json +4 -9
- package/packages/model-runtime/src/core/streams/ollama.test.ts +67 -0
- package/packages/model-runtime/src/core/streams/ollama.ts +5 -5
- package/src/app/[variants]/(main)/chat/components/conversation/features/ChatList/ChatItem/index.tsx +1 -0
- package/src/app/[variants]/(main)/chat/components/conversation/features/ChatMinimap/index.tsx +21 -28
- package/src/features/ChatItem/style.ts +4 -0
- package/src/features/Conversation/Messages/Assistant/Actions/index.tsx +3 -3
- package/src/features/Conversation/Messages/Assistant/index.tsx +329 -230
- package/src/features/Conversation/Messages/Group/Actions/WithContentId.tsx +3 -3
- package/src/features/Conversation/Messages/Group/GroupItem.tsx +3 -5
- package/src/features/Conversation/Messages/Group/index.tsx +80 -13
- package/src/features/Conversation/Messages/User/Actions/ActionsBar.tsx +3 -3
- package/src/features/Conversation/Messages/index.tsx +24 -8
- package/src/features/Conversation/components/VirtualizedList/VirtuosoContext.ts +13 -13
- package/src/features/Conversation/components/VirtualizedList/index.tsx +92 -58
- package/src/features/Conversation/components/WideScreenContainer/index.tsx +10 -6
- package/src/features/Conversation/hooks/useDoubleClickEdit.ts +3 -3
- package/src/store/chat/slices/aiChat/actions/conversationLifecycle.ts +9 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,56 @@
|
|
|
2
2
|
|
|
3
3
|
# Changelog
|
|
4
4
|
|
|
5
|
+
## [Version 2.0.0-next.68](https://github.com/lobehub/lobe-chat/compare/v2.0.0-next.67...v2.0.0-next.68)
|
|
6
|
+
|
|
7
|
+
<sup>Released on **2025-11-16**</sup>
|
|
8
|
+
|
|
9
|
+
#### 🐛 Bug Fixes
|
|
10
|
+
|
|
11
|
+
- **misc**: The tool to fail execution on ollama when a message contains b….
|
|
12
|
+
|
|
13
|
+
<br/>
|
|
14
|
+
|
|
15
|
+
<details>
|
|
16
|
+
<summary><kbd>Improvements and Fixes</kbd></summary>
|
|
17
|
+
|
|
18
|
+
#### What's fixed
|
|
19
|
+
|
|
20
|
+
- **misc**: The tool to fail execution on ollama when a message contains b…, closes [#10259](https://github.com/lobehub/lobe-chat/issues/10259) ([1ad8080](https://github.com/lobehub/lobe-chat/commit/1ad8080))
|
|
21
|
+
|
|
22
|
+
</details>
|
|
23
|
+
|
|
24
|
+
<div align="right">
|
|
25
|
+
|
|
26
|
+
[](#readme-top)
|
|
27
|
+
|
|
28
|
+
</div>
|
|
29
|
+
|
|
30
|
+
## [Version 2.0.0-next.67](https://github.com/lobehub/lobe-chat/compare/v2.0.0-next.66...v2.0.0-next.67)
|
|
31
|
+
|
|
32
|
+
<sup>Released on **2025-11-16**</sup>
|
|
33
|
+
|
|
34
|
+
#### ♻ Code Refactoring
|
|
35
|
+
|
|
36
|
+
- **misc**: Refactor to virtua.
|
|
37
|
+
|
|
38
|
+
<br/>
|
|
39
|
+
|
|
40
|
+
<details>
|
|
41
|
+
<summary><kbd>Improvements and Fixes</kbd></summary>
|
|
42
|
+
|
|
43
|
+
#### Code refactoring
|
|
44
|
+
|
|
45
|
+
- **misc**: Refactor to virtua, closes [#10151](https://github.com/lobehub/lobe-chat/issues/10151) ([9ffb689](https://github.com/lobehub/lobe-chat/commit/9ffb689))
|
|
46
|
+
|
|
47
|
+
</details>
|
|
48
|
+
|
|
49
|
+
<div align="right">
|
|
50
|
+
|
|
51
|
+
[](#readme-top)
|
|
52
|
+
|
|
53
|
+
</div>
|
|
54
|
+
|
|
5
55
|
## [Version 2.0.0-next.66](https://github.com/lobehub/lobe-chat/compare/v2.0.0-next.65...v2.0.0-next.66)
|
|
6
56
|
|
|
7
57
|
<sup>Released on **2025-11-16**</sup>
|
package/changelog/v1.json
CHANGED
|
@@ -1,4 +1,22 @@
|
|
|
1
1
|
[
|
|
2
|
+
{
|
|
3
|
+
"children": {
|
|
4
|
+
"fixes": [
|
|
5
|
+
"The tool to fail execution on ollama when a message contains b…."
|
|
6
|
+
]
|
|
7
|
+
},
|
|
8
|
+
"date": "2025-11-16",
|
|
9
|
+
"version": "2.0.0-next.68"
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
"children": {
|
|
13
|
+
"improvements": [
|
|
14
|
+
"Refactor to virtua."
|
|
15
|
+
]
|
|
16
|
+
},
|
|
17
|
+
"date": "2025-11-16",
|
|
18
|
+
"version": "2.0.0-next.67"
|
|
19
|
+
},
|
|
2
20
|
{
|
|
3
21
|
"children": {
|
|
4
22
|
"features": [
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lobehub/lobehub",
|
|
3
|
-
"version": "2.0.0-next.
|
|
3
|
+
"version": "2.0.0-next.68",
|
|
4
4
|
"description": "LobeHub - an open-source,comprehensive AI Agent framework that supports speech synthesis, multimodal, and extensible Function Call plugin system. Supports one-click free deployment of your private ChatGPT/LLM web application.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"framework",
|
|
@@ -122,9 +122,6 @@
|
|
|
122
122
|
"eslint --fix"
|
|
123
123
|
]
|
|
124
124
|
},
|
|
125
|
-
"overrides": {
|
|
126
|
-
"eta": "4.0.1"
|
|
127
|
-
},
|
|
128
125
|
"dependencies": {
|
|
129
126
|
"@ant-design/icons": "^5.6.1",
|
|
130
127
|
"@ant-design/pro-components": "^2.8.10",
|
|
@@ -265,7 +262,7 @@
|
|
|
265
262
|
"react-fast-marquee": "^1.6.5",
|
|
266
263
|
"react-hotkeys-hook": "^5.2.1",
|
|
267
264
|
"react-i18next": "^15.7.4",
|
|
268
|
-
"react-layout-kit": "^2.0.
|
|
265
|
+
"react-layout-kit": "^2.0.1",
|
|
269
266
|
"react-lazy-load": "^4.0.1",
|
|
270
267
|
"react-pdf": "^9.2.1",
|
|
271
268
|
"react-responsive": "^10.0.1",
|
|
@@ -295,6 +292,7 @@
|
|
|
295
292
|
"url-join": "^5.0.0",
|
|
296
293
|
"use-merge-value": "^1.2.0",
|
|
297
294
|
"uuid": "^11.1.0",
|
|
295
|
+
"virtua": "^0.47.0",
|
|
298
296
|
"word-extractor": "^1.0.4",
|
|
299
297
|
"ws": "^8.18.3",
|
|
300
298
|
"yaml": "^2.8.1",
|
|
@@ -397,9 +395,6 @@
|
|
|
397
395
|
"pnpm": {
|
|
398
396
|
"onlyBuiltDependencies": [
|
|
399
397
|
"@vercel/speed-insights"
|
|
400
|
-
]
|
|
401
|
-
"overrides": {
|
|
402
|
-
"eta": "4.0.1"
|
|
403
|
-
}
|
|
398
|
+
]
|
|
404
399
|
}
|
|
405
400
|
}
|
|
@@ -235,6 +235,73 @@ describe('OllamaStream', () => {
|
|
|
235
235
|
expect(onToolCall).toHaveBeenCalledTimes(1);
|
|
236
236
|
expect(onCompletionMock).toHaveBeenCalledTimes(1);
|
|
237
237
|
});
|
|
238
|
+
|
|
239
|
+
it('tools use with a done', async () => {
|
|
240
|
+
vi.spyOn(uuidModule, 'nanoid').mockReturnValueOnce('1').mockReturnValueOnce('abcd1234');
|
|
241
|
+
|
|
242
|
+
const mockOllamaStream = new ReadableStream<ChatResponse>({
|
|
243
|
+
start(controller) {
|
|
244
|
+
controller.enqueue({
|
|
245
|
+
model: 'qwen2.5',
|
|
246
|
+
created_at: new Date('2024-12-01T03:34:55.166692Z'),
|
|
247
|
+
message: {
|
|
248
|
+
role: 'assistant',
|
|
249
|
+
content: '',
|
|
250
|
+
tool_calls: [
|
|
251
|
+
{
|
|
252
|
+
function: {
|
|
253
|
+
name: 'realtime-weather____fetchCurrentWeather',
|
|
254
|
+
arguments: { city: '杭州' },
|
|
255
|
+
},
|
|
256
|
+
},
|
|
257
|
+
],
|
|
258
|
+
},
|
|
259
|
+
done_reason: 'stop',
|
|
260
|
+
done: true,
|
|
261
|
+
total_duration: 1122415333,
|
|
262
|
+
load_duration: 26178333,
|
|
263
|
+
prompt_eval_count: 221,
|
|
264
|
+
prompt_eval_duration: 507000000,
|
|
265
|
+
eval_count: 26,
|
|
266
|
+
eval_duration: 583000000,
|
|
267
|
+
} as unknown as ChatResponse);
|
|
268
|
+
|
|
269
|
+
controller.close();
|
|
270
|
+
},
|
|
271
|
+
});
|
|
272
|
+
const onStartMock = vi.fn();
|
|
273
|
+
const onTextMock = vi.fn();
|
|
274
|
+
const onToolCall = vi.fn();
|
|
275
|
+
const onCompletionMock = vi.fn();
|
|
276
|
+
|
|
277
|
+
const protocolStream = OllamaStream(mockOllamaStream, {
|
|
278
|
+
onStart: onStartMock,
|
|
279
|
+
onText: onTextMock,
|
|
280
|
+
onCompletion: onCompletionMock,
|
|
281
|
+
onToolsCalling: onToolCall,
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
const decoder = new TextDecoder();
|
|
285
|
+
const chunks = [];
|
|
286
|
+
|
|
287
|
+
// @ts-ignore
|
|
288
|
+
for await (const chunk of protocolStream) {
|
|
289
|
+
chunks.push(decoder.decode(chunk, { stream: true }));
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
expect(chunks).toEqual(
|
|
293
|
+
[
|
|
294
|
+
'id: chat_1',
|
|
295
|
+
'event: tool_calls',
|
|
296
|
+
`data: [{"function":{"arguments":"{\\"city\\":\\"杭州\\"}","name":"realtime-weather____fetchCurrentWeather"},"id":"realtime-weather____fetchCurrentWeather_0_abcd1234","index":0,"type":"function"}]\n`,
|
|
297
|
+
].map((i) => `${i}\n`),
|
|
298
|
+
);
|
|
299
|
+
|
|
300
|
+
expect(onTextMock).toHaveBeenCalledTimes(0);
|
|
301
|
+
expect(onStartMock).toHaveBeenCalledTimes(1);
|
|
302
|
+
expect(onToolCall).toHaveBeenCalledTimes(1);
|
|
303
|
+
expect(onCompletionMock).toHaveBeenCalledTimes(1);
|
|
304
|
+
});
|
|
238
305
|
});
|
|
239
306
|
|
|
240
307
|
it('should handle empty stream', async () => {
|
|
@@ -11,11 +11,6 @@ import {
|
|
|
11
11
|
} from './protocol';
|
|
12
12
|
|
|
13
13
|
const transformOllamaStream = (chunk: ChatResponse, stack: StreamContext): StreamProtocolChunk => {
|
|
14
|
-
// maybe need another structure to add support for multiple choices
|
|
15
|
-
if (chunk.done && !chunk.message.content) {
|
|
16
|
-
return { data: 'finished', id: stack.id, type: 'stop' };
|
|
17
|
-
}
|
|
18
|
-
|
|
19
14
|
if (chunk.message.thinking) {
|
|
20
15
|
return { data: chunk.message.thinking, id: stack.id, type: 'reasoning' };
|
|
21
16
|
}
|
|
@@ -36,6 +31,11 @@ const transformOllamaStream = (chunk: ChatResponse, stack: StreamContext): Strea
|
|
|
36
31
|
};
|
|
37
32
|
}
|
|
38
33
|
|
|
34
|
+
// maybe need another structure to add support for multiple choices
|
|
35
|
+
if (chunk.done && !chunk.message.content) {
|
|
36
|
+
return { data: 'finished', id: stack.id, type: 'stop' };
|
|
37
|
+
}
|
|
38
|
+
|
|
39
39
|
// 判断是否有 <think> 或 </think> 标签,更新 thinkingInContent 状态
|
|
40
40
|
if (chunk.message.content.includes('<think>')) {
|
|
41
41
|
stack.thinkingInContent = true;
|
package/src/app/[variants]/(main)/chat/components/conversation/features/ChatMinimap/index.tsx
CHANGED
|
@@ -4,6 +4,7 @@ import { Icon } from '@lobehub/ui';
|
|
|
4
4
|
import { Popover, Tooltip } from 'antd';
|
|
5
5
|
import { createStyles, useTheme } from 'antd-style';
|
|
6
6
|
import debug from 'debug';
|
|
7
|
+
import isEqual from 'fast-deep-equal';
|
|
7
8
|
import { ChevronDown, ChevronUp } from 'lucide-react';
|
|
8
9
|
import { markdownToTxt } from 'markdown-to-txt';
|
|
9
10
|
import { memo, useCallback, useMemo, useState, useSyncExternalStore } from 'react';
|
|
@@ -11,13 +12,13 @@ import { useTranslation } from 'react-i18next';
|
|
|
11
12
|
import { Flexbox } from 'react-layout-kit';
|
|
12
13
|
|
|
13
14
|
import {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
getVirtuaActiveIndex,
|
|
16
|
+
getVirtuaGlobalRef,
|
|
17
|
+
subscribeVirtuaActiveIndex,
|
|
18
|
+
subscribeVirtuaGlobalRef,
|
|
18
19
|
} from '@/features/Conversation/components/VirtualizedList/VirtuosoContext';
|
|
19
20
|
import { useChatStore } from '@/store/chat';
|
|
20
|
-
import {
|
|
21
|
+
import { displayMessageSelectors } from '@/store/chat/selectors';
|
|
21
22
|
|
|
22
23
|
const log = debug('lobe-react:chat-minimap');
|
|
23
24
|
|
|
@@ -194,21 +195,17 @@ interface MinimapIndicator {
|
|
|
194
195
|
width: number;
|
|
195
196
|
}
|
|
196
197
|
|
|
197
|
-
const ChatMinimap = () => {
|
|
198
|
+
const ChatMinimap = memo(() => {
|
|
198
199
|
const { t } = useTranslation('chat');
|
|
199
200
|
const { styles, cx } = useStyles();
|
|
200
201
|
const [isHovered, setIsHovered] = useState(false);
|
|
201
|
-
const
|
|
202
|
-
subscribeVirtuosoGlobalRef,
|
|
203
|
-
getVirtuosoGlobalRef,
|
|
204
|
-
() => null,
|
|
205
|
-
);
|
|
202
|
+
const virtuaRef = useSyncExternalStore(subscribeVirtuaGlobalRef, getVirtuaGlobalRef, () => null);
|
|
206
203
|
const activeIndex = useSyncExternalStore(
|
|
207
|
-
|
|
208
|
-
|
|
204
|
+
subscribeVirtuaActiveIndex,
|
|
205
|
+
getVirtuaActiveIndex,
|
|
209
206
|
() => null,
|
|
210
207
|
);
|
|
211
|
-
const messages = useChatStore(
|
|
208
|
+
const messages = useChatStore(displayMessageSelectors.mainAIChats, isEqual);
|
|
212
209
|
|
|
213
210
|
const theme = useTheme();
|
|
214
211
|
|
|
@@ -247,20 +244,19 @@ const ChatMinimap = () => {
|
|
|
247
244
|
|
|
248
245
|
const handleJump = useCallback(
|
|
249
246
|
(virtIndex: number) => {
|
|
250
|
-
|
|
247
|
+
virtuaRef?.current?.scrollToIndex(virtIndex, {
|
|
251
248
|
align: 'start',
|
|
252
|
-
behavior: 'smooth',
|
|
253
|
-
index: virtIndex,
|
|
254
249
|
// The current index detection will be off by 1, so we need to add 1 here
|
|
255
250
|
offset: 6,
|
|
251
|
+
smooth: true,
|
|
256
252
|
});
|
|
257
253
|
},
|
|
258
|
-
[
|
|
254
|
+
[virtuaRef],
|
|
259
255
|
);
|
|
260
256
|
|
|
261
257
|
const handleStep = useCallback(
|
|
262
258
|
(direction: 'prev' | 'next') => {
|
|
263
|
-
const ref =
|
|
259
|
+
const ref = virtuaRef?.current;
|
|
264
260
|
if (!ref || indicators.length === 0) return;
|
|
265
261
|
|
|
266
262
|
let targetPosition: number;
|
|
@@ -303,14 +299,13 @@ const ChatMinimap = () => {
|
|
|
303
299
|
|
|
304
300
|
if (!targetIndicator) return;
|
|
305
301
|
|
|
306
|
-
ref.scrollToIndex({
|
|
302
|
+
ref.scrollToIndex(targetIndicator.virtuosoIndex, {
|
|
307
303
|
align: 'start',
|
|
308
|
-
behavior: 'smooth',
|
|
309
|
-
index: targetIndicator.virtuosoIndex,
|
|
310
304
|
offset: 6,
|
|
305
|
+
smooth: true,
|
|
311
306
|
});
|
|
312
307
|
},
|
|
313
|
-
[activeIndex, activeIndicatorPosition, indicators,
|
|
308
|
+
[activeIndex, activeIndicatorPosition, indicators, virtuaRef],
|
|
314
309
|
);
|
|
315
310
|
|
|
316
311
|
if (indicators.length <= MIN_MESSAGES) return null;
|
|
@@ -353,9 +348,7 @@ const ChatMinimap = () => {
|
|
|
353
348
|
aria-label={t('minimap.jumpToMessage', { index: position + 1 })}
|
|
354
349
|
className={styles.indicator}
|
|
355
350
|
onClick={() => handleJump(virtuosoIndex)}
|
|
356
|
-
style={{
|
|
357
|
-
width,
|
|
358
|
-
}}
|
|
351
|
+
style={{ width }}
|
|
359
352
|
type={'button'}
|
|
360
353
|
>
|
|
361
354
|
<div
|
|
@@ -382,6 +375,6 @@ const ChatMinimap = () => {
|
|
|
382
375
|
</Flexbox>
|
|
383
376
|
</Flexbox>
|
|
384
377
|
);
|
|
385
|
-
};
|
|
378
|
+
});
|
|
386
379
|
|
|
387
|
-
export default
|
|
380
|
+
export default ChatMinimap;
|
|
@@ -6,7 +6,7 @@ import { memo, use, useCallback, useContext, useMemo, useState } from 'react';
|
|
|
6
6
|
import { useTranslation } from 'react-i18next';
|
|
7
7
|
|
|
8
8
|
import ShareMessageModal from '@/features/Conversation/components/ShareMessageModal';
|
|
9
|
-
import {
|
|
9
|
+
import { VirtuaContext } from '@/features/Conversation/components/VirtualizedList/VirtuosoContext';
|
|
10
10
|
import { useChatStore } from '@/store/chat';
|
|
11
11
|
import { messageStateSelectors, threadSelectors } from '@/store/chat/selectors';
|
|
12
12
|
import { useSessionStore } from '@/store/session';
|
|
@@ -90,7 +90,7 @@ export const AssistantActionsBar = memo<AssistantActionsProps>(({ id, data, inde
|
|
|
90
90
|
s.toggleMessageCollapsed,
|
|
91
91
|
]);
|
|
92
92
|
const { message } = App.useApp();
|
|
93
|
-
const
|
|
93
|
+
const virtuaRef = use(VirtuaContext);
|
|
94
94
|
|
|
95
95
|
const onActionClick = useCallback(
|
|
96
96
|
async (action: ActionIconGroupEvent) => {
|
|
@@ -98,7 +98,7 @@ export const AssistantActionsBar = memo<AssistantActionsProps>(({ id, data, inde
|
|
|
98
98
|
case 'edit': {
|
|
99
99
|
toggleMessageEditing(id, true);
|
|
100
100
|
|
|
101
|
-
|
|
101
|
+
virtuaRef?.current?.scrollToIndex(index, { align: 'start' });
|
|
102
102
|
}
|
|
103
103
|
}
|
|
104
104
|
if (!data) return;
|