@lobehub/chat 1.107.5 → 1.107.6
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 +25 -0
- package/changelog/v1.json +9 -0
- package/package.json +1 -1
- package/src/features/Conversation/Messages/Assistant/Tool/Inspector/Debug.tsx +1 -1
- package/src/features/Conversation/Messages/Assistant/Tool/Inspector/{PluginResultJSON.tsx → PluginResult.tsx} +9 -4
- package/src/features/Conversation/Messages/Assistant/Tool/Render/CustomRender.tsx +1 -1
- package/src/features/Conversation/components/ChatItem/utils.test.ts +66 -0
- package/src/features/Conversation/components/ChatItem/utils.ts +2 -1
- package/src/store/image/slices/createImage/action.test.ts +40 -0
- package/src/store/image/slices/createImage/action.ts +10 -1
- package/src/tools/artifacts/systemRole.ts +1 -1
package/CHANGELOG.md
CHANGED
@@ -2,6 +2,31 @@
|
|
2
2
|
|
3
3
|
# Changelog
|
4
4
|
|
5
|
+
### [Version 1.107.6](https://github.com/lobehub/lobe-chat/compare/v1.107.5...v1.107.6)
|
6
|
+
|
7
|
+
<sup>Released on **2025-08-05**</sup>
|
8
|
+
|
9
|
+
#### 🐛 Bug Fixes
|
10
|
+
|
11
|
+
- **misc**: Break line for Gemini Artifacts.
|
12
|
+
|
13
|
+
<br/>
|
14
|
+
|
15
|
+
<details>
|
16
|
+
<summary><kbd>Improvements and Fixes</kbd></summary>
|
17
|
+
|
18
|
+
#### What's fixed
|
19
|
+
|
20
|
+
- **misc**: Break line for Gemini Artifacts, closes [#8627](https://github.com/lobehub/lobe-chat/issues/8627) ([65609dd](https://github.com/lobehub/lobe-chat/commit/65609dd))
|
21
|
+
|
22
|
+
</details>
|
23
|
+
|
24
|
+
<div align="right">
|
25
|
+
|
26
|
+
[](#readme-top)
|
27
|
+
|
28
|
+
</div>
|
29
|
+
|
5
30
|
### [Version 1.107.5](https://github.com/lobehub/lobe-chat/compare/v1.107.4...v1.107.5)
|
6
31
|
|
7
32
|
<sup>Released on **2025-08-04**</sup>
|
package/changelog/v1.json
CHANGED
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@lobehub/chat",
|
3
|
-
"version": "1.107.
|
3
|
+
"version": "1.107.6",
|
4
4
|
"description": "Lobe Chat - an open-source, high-performance chatbot 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",
|
@@ -3,7 +3,7 @@ import { Tabs } from 'antd';
|
|
3
3
|
import { memo } from 'react';
|
4
4
|
import { useTranslation } from 'react-i18next';
|
5
5
|
|
6
|
-
import PluginResult from './
|
6
|
+
import PluginResult from './PluginResult';
|
7
7
|
import PluginState from './PluginState';
|
8
8
|
|
9
9
|
interface DebugProps {
|
@@ -12,17 +12,22 @@ export interface FunctionMessageProps {
|
|
12
12
|
const PluginResult = memo<FunctionMessageProps>(({ toolCallId, variant }) => {
|
13
13
|
const toolMessage = useChatStore(chatSelectors.getMessageByToolCallId(toolCallId));
|
14
14
|
|
15
|
-
const data = useMemo(() => {
|
15
|
+
const { data, language } = useMemo(() => {
|
16
16
|
try {
|
17
|
-
|
17
|
+
const parsed = JSON.parse(toolMessage?.content || '');
|
18
|
+
// Special case: if the parsed result is a string, it means the original content was a stringified string
|
19
|
+
if (typeof parsed === 'string') {
|
20
|
+
return { data: parsed, language: 'plaintext' }; // Return the parsed string directly, do not re-serialize
|
21
|
+
}
|
22
|
+
return { data: JSON.stringify(parsed, null, 2), language: 'json' };
|
18
23
|
} catch {
|
19
|
-
return toolMessage?.content || '';
|
24
|
+
return { data: toolMessage?.content || '', language: 'plaintext' };
|
20
25
|
}
|
21
26
|
}, [toolMessage?.content]);
|
22
27
|
|
23
28
|
return (
|
24
29
|
<Highlighter
|
25
|
-
language={
|
30
|
+
language={language}
|
26
31
|
style={{ maxHeight: 200, overflow: 'scroll', width: '100%' }}
|
27
32
|
variant={variant}
|
28
33
|
>
|
@@ -6,7 +6,7 @@ import { memo, useCallback, useEffect, useState } from 'react';
|
|
6
6
|
import { useTranslation } from 'react-i18next';
|
7
7
|
import { Flexbox } from 'react-layout-kit';
|
8
8
|
|
9
|
-
import PluginResult from '@/features/Conversation/Messages/Assistant/Tool/Inspector/
|
9
|
+
import PluginResult from '@/features/Conversation/Messages/Assistant/Tool/Inspector/PluginResult';
|
10
10
|
import PluginRender from '@/features/PluginsUI/Render';
|
11
11
|
import { useChatStore } from '@/store/chat';
|
12
12
|
import { chatSelectors } from '@/store/chat/selectors';
|
@@ -160,6 +160,20 @@ describe('processWithArtifact', () => {
|
|
160
160
|
|
161
161
|
expect(output).toEqual(`<lobeThinking>这是一个思考过程。</lobeThinking>
|
162
162
|
|
163
|
+
<lobeArtifact identifier="test" type="image/svg+xml" title="测试"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"> <rect width="100" height="100" fill="blue"/></svg></lobeArtifact>`);
|
164
|
+
});
|
165
|
+
|
166
|
+
it('should handle Gemini case with no line break between lobeThinking and lobeArtifact tags', () => {
|
167
|
+
const input = `<lobeThinking>这是一个思考过程。</lobeThinking><lobeArtifact identifier="test" type="image/svg+xml" title="测试">
|
168
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
|
169
|
+
<rect width="100" height="100" fill="blue"/>
|
170
|
+
</svg>
|
171
|
+
</lobeArtifact>`;
|
172
|
+
|
173
|
+
const output = processWithArtifact(input);
|
174
|
+
|
175
|
+
expect(output).toEqual(`<lobeThinking>这是一个思考过程。</lobeThinking>
|
176
|
+
|
163
177
|
<lobeArtifact identifier="test" type="image/svg+xml" title="测试"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"> <rect width="100" height="100" fill="blue"/></svg></lobeArtifact>`);
|
164
178
|
});
|
165
179
|
|
@@ -431,4 +445,56 @@ This HTML document includes the temperature converter with the requested feature
|
|
431
445
|
|
432
446
|
This HTML document includes the temperature converter with the requested features: the logic is wrapped in an IIFE, and event listeners are attached in JavaScript.`);
|
433
447
|
});
|
448
|
+
|
449
|
+
describe('idempotency tests', () => {
|
450
|
+
it('should not add extra blank lines when running processWithArtifact multiple times', () => {
|
451
|
+
const input = `<lobeThinking>这是一个思考过程。</lobeThinking><lobeArtifact identifier="test" type="image/svg+xml" title="测试">
|
452
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
|
453
|
+
<rect width="100" height="100" fill="blue"/>
|
454
|
+
</svg>
|
455
|
+
</lobeArtifact>`;
|
456
|
+
|
457
|
+
// First run
|
458
|
+
const firstRun = processWithArtifact(input);
|
459
|
+
|
460
|
+
// Second run - should produce the same result
|
461
|
+
const secondRun = processWithArtifact(firstRun);
|
462
|
+
|
463
|
+
// Third run - should still produce the same result
|
464
|
+
const thirdRun = processWithArtifact(secondRun);
|
465
|
+
|
466
|
+
// All runs should produce the same output
|
467
|
+
expect(firstRun).toEqual(secondRun);
|
468
|
+
expect(secondRun).toEqual(thirdRun);
|
469
|
+
|
470
|
+
// Verify the output has exactly two newlines between tags
|
471
|
+
expect(firstRun).toContain('</lobeThinking>\n\n<lobeArtifact');
|
472
|
+
expect(firstRun.match(/(<\/lobeThinking>)\n\n(<lobeArtifact)/)).toBeTruthy();
|
473
|
+
});
|
474
|
+
|
475
|
+
it('should handle already processed content with proper spacing', () => {
|
476
|
+
const alreadyProcessed = `<lobeThinking>这是一个思考过程。</lobeThinking>
|
477
|
+
|
478
|
+
<lobeArtifact identifier="test" type="image/svg+xml" title="测试"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"> <rect width="100" height="100" fill="blue"/></svg></lobeArtifact>`;
|
479
|
+
|
480
|
+
const result = processWithArtifact(alreadyProcessed);
|
481
|
+
|
482
|
+
// Should remain unchanged
|
483
|
+
expect(result).toEqual(alreadyProcessed);
|
484
|
+
});
|
485
|
+
|
486
|
+
it('should not convert spaces between tags into extra blank lines', () => {
|
487
|
+
const inputWithSpaces = `<lobeThinking>这是一个思考过程。</lobeThinking> <lobeArtifact identifier="test" type="image/svg+xml" title="测试">
|
488
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
|
489
|
+
<rect width="100" height="100" fill="blue"/>
|
490
|
+
</svg>
|
491
|
+
</lobeArtifact>`;
|
492
|
+
|
493
|
+
const output = processWithArtifact(inputWithSpaces);
|
494
|
+
|
495
|
+
// Should still have the space and not convert it to newlines
|
496
|
+
expect(output).toContain('</lobeThinking> <lobeArtifact');
|
497
|
+
expect(output).not.toContain('</lobeThinking>\n\n<lobeArtifact');
|
498
|
+
});
|
499
|
+
});
|
434
500
|
});
|
@@ -22,7 +22,8 @@ export const processWithArtifact = (input: string = '') => {
|
|
22
22
|
}
|
23
23
|
|
24
24
|
// Add empty line between lobeThinking and lobeArtifact if they are adjacent
|
25
|
-
|
25
|
+
// Support both cases: with line break (e.g. from other models) and without (e.g. from Gemini)
|
26
|
+
output = output.replace(/(<\/lobeThinking>)(?:\r?\n)?(<lobeArtifact)/, '$1\n\n$2');
|
26
27
|
|
27
28
|
// Remove fenced code block between lobeArtifact and HTML content
|
28
29
|
output = output.replace(
|
@@ -95,6 +95,9 @@ describe('CreateImageAction', () => {
|
|
95
95
|
|
96
96
|
// Verify refresh was called
|
97
97
|
expect(mockRefreshGenerationBatches).toHaveBeenCalled();
|
98
|
+
|
99
|
+
// Verify prompt is cleared after successful image creation
|
100
|
+
expect(result.current.parameters?.prompt).toBe('');
|
98
101
|
});
|
99
102
|
|
100
103
|
it('should create new topic when no active topic exists', async () => {
|
@@ -134,6 +137,9 @@ describe('CreateImageAction', () => {
|
|
134
137
|
imageNum: 4,
|
135
138
|
params: { prompt: 'test prompt', width: 1024, height: 1024 },
|
136
139
|
});
|
140
|
+
|
141
|
+
// Verify prompt is cleared after successful image creation
|
142
|
+
expect(result.current.parameters?.prompt).toBe('');
|
137
143
|
});
|
138
144
|
|
139
145
|
it('should throw error when parameters is not initialized', async () => {
|
@@ -193,6 +199,9 @@ describe('CreateImageAction', () => {
|
|
193
199
|
|
194
200
|
// The service should have been called before the error
|
195
201
|
expect(mockImageService.createImage).toHaveBeenCalled();
|
202
|
+
|
203
|
+
// Verify prompt is NOT cleared when error occurs
|
204
|
+
expect(result.current.parameters?.prompt).toBe('test prompt');
|
196
205
|
});
|
197
206
|
|
198
207
|
it('should handle service error with new topic', async () => {
|
@@ -223,6 +232,37 @@ describe('CreateImageAction', () => {
|
|
223
232
|
// Verify topic was created before the error
|
224
233
|
expect(mockCreateGenerationTopic).toHaveBeenCalled();
|
225
234
|
expect(mockSwitchGenerationTopic).toHaveBeenCalled();
|
235
|
+
|
236
|
+
// Verify prompt is NOT cleared when error occurs
|
237
|
+
expect(result.current.parameters?.prompt).toBe('test prompt');
|
238
|
+
});
|
239
|
+
|
240
|
+
it('should clear prompt input after successful image creation', async () => {
|
241
|
+
const mockRefreshGenerationBatches = vi.fn().mockResolvedValue(undefined);
|
242
|
+
const { result } = renderHook(() => useImageStore());
|
243
|
+
|
244
|
+
// Set initial prompt value
|
245
|
+
act(() => {
|
246
|
+
useImageStore.setState({
|
247
|
+
parameters: { prompt: 'detailed landscape artwork', width: 1024, height: 1024 },
|
248
|
+
refreshGenerationBatches: mockRefreshGenerationBatches,
|
249
|
+
});
|
250
|
+
});
|
251
|
+
|
252
|
+
// Verify initial prompt is set
|
253
|
+
expect(result.current.parameters?.prompt).toBe('detailed landscape artwork');
|
254
|
+
|
255
|
+
// Create image
|
256
|
+
await act(async () => {
|
257
|
+
await result.current.createImage();
|
258
|
+
});
|
259
|
+
|
260
|
+
// Verify prompt is cleared
|
261
|
+
expect(result.current.parameters?.prompt).toBe('');
|
262
|
+
|
263
|
+
// Verify other parameters remain unchanged
|
264
|
+
expect(result.current.parameters?.width).toBe(1024);
|
265
|
+
expect(result.current.parameters?.height).toBe(1024);
|
226
266
|
});
|
227
267
|
});
|
228
268
|
|
@@ -85,8 +85,17 @@ export const createCreateImageSlice: StateCreator<
|
|
85
85
|
if (!isNewTopic) {
|
86
86
|
await get().refreshGenerationBatches();
|
87
87
|
}
|
88
|
+
|
89
|
+
// 7. Clear the prompt input after successful image creation
|
90
|
+
set(
|
91
|
+
(state) => ({
|
92
|
+
parameters: { ...state.parameters, prompt: '' },
|
93
|
+
}),
|
94
|
+
false,
|
95
|
+
'createImage/clearPrompt',
|
96
|
+
);
|
88
97
|
} finally {
|
89
|
-
//
|
98
|
+
// 8. Reset all creating states
|
90
99
|
if (isNewTopic) {
|
91
100
|
set(
|
92
101
|
{ isCreating: false, isCreatingWithNewTopic: false },
|
@@ -27,7 +27,7 @@ The assistant can create and reference artifacts during conversations. Artifacts
|
|
27
27
|
<artifact_instructions>
|
28
28
|
When collaborating with the user on creating content that falls into compatible categories, the assistant should follow these steps:
|
29
29
|
|
30
|
-
1. Immediately before invoking an artifact, think for one sentence in <lobeThinking> tags about how it evaluates against the criteria for a good and bad artifact. Consider if the content would work just fine without an artifact. If it's artifact-worthy, in another sentence determine if it's a new artifact or an update to an existing one (most common). For updates, reuse the prior identifier.
|
30
|
+
1. Immediately before invoking an artifact, think for one sentence in <lobeThinking> tags about how it evaluates against the criteria for a good and bad artifact. Consider if the content would work just fine without an artifact. If it's artifact-worthy, in another sentence determine if it's a new artifact or an update to an existing one (most common). For updates, reuse the prior identifier. IMPORTANT: Always ensure there is a line break between the closing </lobeThinking> tag and the opening <lobeArtifact> tag.
|
31
31
|
2. Wrap the content in opening and closing \`<lobeArtifact>\` tags.
|
32
32
|
3. Assign an identifier to the \`identifier\` attribute of the opening \`<lobeArtifact>\` tag. For updates, reuse the prior identifier. For new artifacts, the identifier should be descriptive and relevant to the content, using kebab-case (e.g., "example-code-snippet"). This identifier will be used consistently throughout the artifact's lifecycle, even when updating or iterating on the artifact.
|
33
33
|
4. Include a \`title\` attribute in the \`<lobeArtifact>\` tag to provide a brief title or description of the content.
|