@lobehub/chat 1.51.2 → 1.51.3

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 (59) hide show
  1. package/CHANGELOG.md +41 -0
  2. package/Dockerfile +1 -1
  3. package/Dockerfile.database +1 -1
  4. package/changelog/v1.json +12 -0
  5. package/docs/usage/providers/wenxin.mdx +16 -13
  6. package/docs/usage/providers/wenxin.zh-CN.mdx +11 -8
  7. package/package.json +1 -2
  8. package/src/app/(main)/settings/llm/ProviderList/providers.tsx +2 -4
  9. package/src/config/aiModels/wenxin.ts +125 -19
  10. package/src/config/llm.ts +3 -5
  11. package/src/config/modelProviders/wenxin.ts +100 -23
  12. package/src/const/auth.ts +0 -3
  13. package/src/features/Conversation/Error/APIKeyForm/index.tsx +0 -3
  14. package/src/features/Conversation/components/ChatItem/utils.test.ts +284 -0
  15. package/src/features/Conversation/components/ChatItem/utils.ts +39 -8
  16. package/src/features/Conversation/components/MarkdownElements/LobeArtifact/rehypePlugin.test.ts +125 -0
  17. package/src/features/DevPanel/CacheViewer/DataTable/index.tsx +33 -0
  18. package/src/features/DevPanel/CacheViewer/cacheProvider.tsx +64 -0
  19. package/src/features/DevPanel/CacheViewer/getCacheEntries.ts +52 -0
  20. package/src/features/DevPanel/CacheViewer/index.tsx +25 -0
  21. package/src/features/DevPanel/CacheViewer/schema.ts +49 -0
  22. package/src/features/DevPanel/FeatureFlagViewer/Form.tsx +93 -0
  23. package/src/features/DevPanel/FeatureFlagViewer/index.tsx +11 -0
  24. package/src/features/DevPanel/MetadataViewer/Ld.tsx +25 -0
  25. package/src/features/DevPanel/MetadataViewer/MetaData.tsx +30 -0
  26. package/src/features/DevPanel/MetadataViewer/Og.tsx +75 -0
  27. package/src/features/DevPanel/MetadataViewer/index.tsx +80 -0
  28. package/src/features/DevPanel/MetadataViewer/useHead.ts +16 -0
  29. package/src/features/DevPanel/PostgresViewer/DataTable/index.tsx +39 -49
  30. package/src/features/DevPanel/PostgresViewer/{TableColumns.tsx → SchemaSidebar/Columns.tsx} +6 -4
  31. package/src/features/DevPanel/PostgresViewer/{Schema.tsx → SchemaSidebar/index.tsx} +49 -55
  32. package/src/features/DevPanel/PostgresViewer/index.tsx +4 -2
  33. package/src/features/DevPanel/features/FloatPanel.tsx +218 -0
  34. package/src/features/DevPanel/features/Header.tsx +50 -0
  35. package/src/features/DevPanel/features/Table/TableCell.tsx +73 -0
  36. package/src/features/DevPanel/features/Table/TooltipContent.tsx +39 -0
  37. package/src/features/DevPanel/{PostgresViewer/DataTable/Table.tsx → features/Table/index.tsx} +12 -14
  38. package/src/features/DevPanel/index.tsx +29 -5
  39. package/src/libs/agent-runtime/AgentRuntime.test.ts +0 -1
  40. package/src/libs/agent-runtime/AgentRuntime.ts +7 -0
  41. package/src/libs/agent-runtime/wenxin/index.ts +10 -107
  42. package/src/locales/default/modelProvider.ts +0 -20
  43. package/src/server/modules/AgentRuntime/index.test.ts +0 -21
  44. package/src/services/_auth.ts +0 -14
  45. package/src/store/chat/slices/portal/selectors.test.ts +169 -3
  46. package/src/store/chat/slices/portal/selectors.ts +6 -1
  47. package/src/store/user/slices/modelList/selectors/keyVaults.ts +0 -2
  48. package/src/types/aiProvider.ts +0 -1
  49. package/src/types/user/settings/keyVaults.ts +1 -6
  50. package/src/app/(backend)/webapi/chat/wenxin/route.test.ts +0 -27
  51. package/src/app/(backend)/webapi/chat/wenxin/route.ts +0 -30
  52. package/src/app/(main)/settings/llm/ProviderList/Wenxin/index.tsx +0 -44
  53. package/src/app/(main)/settings/provider/(detail)/wenxin/page.tsx +0 -61
  54. package/src/features/Conversation/Error/APIKeyForm/Wenxin.tsx +0 -49
  55. package/src/features/DevPanel/FloatPanel.tsx +0 -136
  56. package/src/features/DevPanel/PostgresViewer/DataTable/TableCell.tsx +0 -34
  57. package/src/libs/agent-runtime/utils/streams/wenxin.test.ts +0 -153
  58. package/src/libs/agent-runtime/utils/streams/wenxin.ts +0 -38
  59. package/src/libs/agent-runtime/wenxin/type.ts +0 -84
@@ -9,7 +9,8 @@ const BaiduWenxin: ModelProviderCard = {
9
9
  '百度自研的旗舰级大规模⼤语⾔模型,覆盖海量中英文语料,具有强大的通用能力,可满足绝大部分对话问答、创作生成、插件应用场景要求;支持自动对接百度搜索插件,保障问答信息时效。',
10
10
  displayName: 'ERNIE 3.5 8K',
11
11
  enabled: true,
12
- id: 'ERNIE-3.5-8K',
12
+ functionCall: true,
13
+ id: 'ernie-3.5-8k',
13
14
  pricing: {
14
15
  currency: 'CNY',
15
16
  input: 0.8,
@@ -21,7 +22,8 @@ const BaiduWenxin: ModelProviderCard = {
21
22
  description:
22
23
  '百度自研的旗舰级大规模⼤语⾔模型,覆盖海量中英文语料,具有强大的通用能力,可满足绝大部分对话问答、创作生成、插件应用场景要求;支持自动对接百度搜索插件,保障问答信息时效。',
23
24
  displayName: 'ERNIE 3.5 8K Preview',
24
- id: 'ERNIE-3.5-8K-Preview',
25
+ functionCall: true,
26
+ id: 'ernie-3.5-8k-preview',
25
27
  pricing: {
26
28
  currency: 'CNY',
27
29
  input: 0.8,
@@ -34,7 +36,8 @@ const BaiduWenxin: ModelProviderCard = {
34
36
  '百度自研的旗舰级大规模⼤语⾔模型,覆盖海量中英文语料,具有强大的通用能力,可满足绝大部分对话问答、创作生成、插件应用场景要求;支持自动对接百度搜索插件,保障问答信息时效。',
35
37
  displayName: 'ERNIE 3.5 128K',
36
38
  enabled: true,
37
- id: 'ERNIE-3.5-128K',
39
+ functionCall: true,
40
+ id: 'ernie-3.5-128k',
38
41
  pricing: {
39
42
  currency: 'CNY',
40
43
  input: 0.8,
@@ -47,7 +50,8 @@ const BaiduWenxin: ModelProviderCard = {
47
50
  '百度自研的旗舰级超大规模⼤语⾔模型,相较ERNIE 3.5实现了模型能力全面升级,广泛适用于各领域复杂任务场景;支持自动对接百度搜索插件,保障问答信息时效。',
48
51
  displayName: 'ERNIE 4.0 8K',
49
52
  enabled: true,
50
- id: 'ERNIE-4.0-8K-Latest',
53
+ functionCall: true,
54
+ id: 'ernie-4.0-8k-latest',
51
55
  pricing: {
52
56
  currency: 'CNY',
53
57
  input: 30,
@@ -59,7 +63,8 @@ const BaiduWenxin: ModelProviderCard = {
59
63
  description:
60
64
  '百度自研的旗舰级超大规模⼤语⾔模型,相较ERNIE 3.5实现了模型能力全面升级,广泛适用于各领域复杂任务场景;支持自动对接百度搜索插件,保障问答信息时效。',
61
65
  displayName: 'ERNIE 4.0 8K Preview',
62
- id: 'ERNIE-4.0-8K-Preview',
66
+ functionCall: true,
67
+ id: 'ernie-4.0-8k-preview',
63
68
  pricing: {
64
69
  currency: 'CNY',
65
70
  input: 30,
@@ -72,7 +77,8 @@ const BaiduWenxin: ModelProviderCard = {
72
77
  '百度自研的旗舰级超大规模⼤语⾔模型,综合效果表现出色,广泛适用于各领域复杂任务场景;支持自动对接百度搜索插件,保障问答信息时效。相较于ERNIE 4.0在性能表现上更优秀',
73
78
  displayName: 'ERNIE 4.0 Turbo 8K',
74
79
  enabled: true,
75
- id: 'ERNIE-4.0-Turbo-8K-Latest',
80
+ functionCall: true,
81
+ id: 'ernie-4.0-turbo-8k-latest',
76
82
  pricing: {
77
83
  currency: 'CNY',
78
84
  input: 20,
@@ -85,7 +91,8 @@ const BaiduWenxin: ModelProviderCard = {
85
91
  '百度自研的旗舰级超大规模⼤语⾔模型,综合效果表现出色,广泛适用于各领域复杂任务场景;支持自动对接百度搜索插件,保障问答信息时效。相较于ERNIE 4.0在性能表现上更优秀',
86
92
  displayName: 'ERNIE 4.0 Turbo 128K',
87
93
  enabled: true,
88
- id: 'ERNIE-4.0-Turbo-128K',
94
+ functionCall: true,
95
+ id: 'ernie-4.0-turbo-128k',
89
96
  pricing: {
90
97
  currency: 'CNY',
91
98
  input: 20,
@@ -97,20 +104,33 @@ const BaiduWenxin: ModelProviderCard = {
97
104
  description:
98
105
  '百度自研的旗舰级超大规模⼤语⾔模型,综合效果表现出色,广泛适用于各领域复杂任务场景;支持自动对接百度搜索插件,保障问答信息时效。相较于ERNIE 4.0在性能表现上更优秀',
99
106
  displayName: 'ERNIE 4.0 Turbo 8K Preview',
100
- id: 'ERNIE-4.0-Turbo-8K-Preview',
107
+ functionCall: true,
108
+ id: 'ernie-4.0-turbo-8k-preview',
101
109
  pricing: {
102
110
  currency: 'CNY',
103
111
  input: 20,
104
112
  output: 60,
105
113
  },
106
114
  },
115
+ {
116
+ contextWindowTokens: 8192,
117
+ description:
118
+ 'ERNIE Lite是百度自研的轻量级大语言模型,兼顾优异的模型效果与推理性能,适合低算力AI加速卡推理使用。',
119
+ displayName: 'ERNIE Lite 8K',
120
+ id: 'ernie-lite-8k',
121
+ pricing: {
122
+ currency: 'CNY',
123
+ input: 0,
124
+ output: 0,
125
+ },
126
+ },
107
127
  {
108
128
  contextWindowTokens: 128_000,
109
129
  description:
110
130
  '百度自研的轻量级大语言模型,兼顾优异的模型效果与推理性能,效果比ERNIE Lite更优,适合低算力AI加速卡推理使用。',
111
131
  displayName: 'ERNIE Lite Pro 128K',
112
- enabled: true,
113
- id: 'ERNIE-Lite-Pro-128K',
132
+ functionCall: true,
133
+ id: 'ernie-lite-pro-128k',
114
134
  pricing: {
115
135
  currency: 'CNY',
116
136
  input: 0.2,
@@ -118,16 +138,15 @@ const BaiduWenxin: ModelProviderCard = {
118
138
  },
119
139
  },
120
140
  {
121
- contextWindowTokens: 128_000,
141
+ contextWindowTokens: 8192,
122
142
  description:
123
- '百度2024年最新发布的自研高性能大语言模型,通用能力优异,效果比ERNIE Speed更优,适合作为基座模型进行精调,更好地处理特定场景问题,同时具备极佳的推理性能。',
124
- displayName: 'ERNIE Speed Pro 128K',
125
- enabled: true,
126
- id: 'ERNIE-Speed-Pro-128K',
143
+ 'ERNIE Tiny是百度自研的超高性能大语言模型,部署与精调成本在文心系列模型中最低。',
144
+ displayName: 'ERNIE Tiny 8K',
145
+ id: 'ernie-tiny-8k',
127
146
  pricing: {
128
147
  currency: 'CNY',
129
- input: 0.3,
130
- output: 0.6,
148
+ input: 0,
149
+ output: 0,
131
150
  },
132
151
  },
133
152
  {
@@ -135,36 +154,94 @@ const BaiduWenxin: ModelProviderCard = {
135
154
  description:
136
155
  '百度2024年最新发布的自研高性能大语言模型,通用能力优异,适合作为基座模型进行精调,更好地处理特定场景问题,同时具备极佳的推理性能。',
137
156
  displayName: 'ERNIE Speed 128K',
138
- id: 'ERNIE-Speed-128K',
157
+ id: 'ernie-speed-128k',
139
158
  pricing: {
140
159
  currency: 'CNY',
141
160
  input: 0,
142
161
  output: 0,
143
162
  },
144
163
  },
164
+ {
165
+ contextWindowTokens: 128_000,
166
+ description:
167
+ '百度2024年最新发布的自研高性能大语言模型,通用能力优异,效果比ERNIE Speed更优,适合作为基座模型进行精调,更好地处理特定场景问题,同时具备极佳的推理性能。',
168
+ displayName: 'ERNIE Speed Pro 128K',
169
+ id: 'ernie-speed-pro-128k',
170
+ pricing: {
171
+ currency: 'CNY',
172
+ input: 0.3,
173
+ output: 0.6,
174
+ },
175
+ },
145
176
  {
146
177
  contextWindowTokens: 8192,
147
178
  description:
148
179
  '百度自研的垂直场景大语言模型,适合游戏NPC、客服对话、对话角色扮演等应用场景,人设风格更为鲜明、一致,指令遵循能力更强,推理性能更优。',
149
180
  displayName: 'ERNIE Character 8K',
150
- id: 'ERNIE-Character-8K',
181
+ id: 'ernie-char-8k',
151
182
  pricing: {
152
183
  currency: 'CNY',
153
184
  input: 4,
154
185
  output: 8,
155
186
  },
156
187
  },
188
+ {
189
+ contextWindowTokens: 8192,
190
+ description:
191
+ '百度自研的垂直场景大语言模型,适合游戏NPC、客服对话、对话角色扮演等应用场景,人设风格更为鲜明、一致,指令遵循能力更强,推理性能更优。',
192
+ displayName: 'ERNIE Character Fiction 8K',
193
+ id: 'ernie-char-fiction-8k',
194
+ pricing: {
195
+ currency: 'CNY',
196
+ input: 4,
197
+ output: 8,
198
+ },
199
+ },
200
+ {
201
+ contextWindowTokens: 8192,
202
+ description:
203
+ '百度自研通用大语言模型,在小说续写能力上有明显优势,也可用在短剧、电影等场景。',
204
+ displayName: 'ERNIE Novel 8K',
205
+ id: 'ernie-novel-8k',
206
+ pricing: {
207
+ currency: 'CNY',
208
+ input: 40,
209
+ output: 120,
210
+ },
211
+ },
212
+ {
213
+ contextWindowTokens: 65_536,
214
+ description:
215
+ 'DeepSeek-V3 为杭州深度求索人工智能基础技术研究有限公司自研的 MoE 模型,其多项评测成绩突出,在主流榜单中位列开源模型榜首。V3 相比 V2.5 模型生成速度实现 3 倍提升,为用户带来更加迅速流畅的使用体验。',
216
+ displayName: 'DeepSeek V3',
217
+ id: 'deepseek-v3',
218
+ pricing: {
219
+ currency: 'CNY',
220
+ input: 0.8,
221
+ output: 1.6,
222
+ },
223
+ },
224
+ {
225
+ contextWindowTokens: 65_536,
226
+ description:
227
+ 'DeepSeek-R1 在后训练阶段大规模使用了强化学习技术,在仅有极少标注数据的情况下,极大提升了模型推理能力。在数学、代码、自然语言推理等任务上,性能比肩 OpenAI o1 正式版。',
228
+ displayName: 'DeepSeek R1',
229
+ id: 'deepseek-r1',
230
+ pricing: {
231
+ currency: 'CNY',
232
+ input: 2,
233
+ output: 8,
234
+ },
235
+ },
157
236
  ],
158
- checkModel: 'ERNIE-Speed-128K',
237
+ checkModel: 'ernie-speed-128k',
159
238
  description:
160
239
  '企业级一站式大模型与AI原生应用开发及服务平台,提供最全面易用的生成式人工智能模型开发、应用开发全流程工具链',
161
- disableBrowserRequest: true,
162
240
  id: 'wenxin',
163
241
  modelsUrl: 'https://cloud.baidu.com/doc/WENXINWORKSHOP/s/Nlks5zkzu#%E5%AF%B9%E8%AF%9Dchat',
164
242
  name: 'Wenxin',
165
243
  settings: {
166
- disableBrowserRequest: true,
167
- sdkType: 'wenxin',
244
+ sdkType: 'openai',
168
245
  smoothing: {
169
246
  speed: 2,
170
247
  text: true,
package/src/const/auth.ts CHANGED
@@ -39,9 +39,6 @@ export interface JWTPayload {
39
39
 
40
40
  cloudflareBaseURLOrAccountID?: string;
41
41
 
42
- wenxinAccessKey?: string;
43
- wenxinSecretKey?: string;
44
-
45
42
  /**
46
43
  * user id
47
44
  * in client db mode it's a uuid
@@ -10,7 +10,6 @@ import { GlobalLLMProviderKey } from '@/types/user/settings';
10
10
 
11
11
  import BedrockForm from './Bedrock';
12
12
  import ProviderApiKeyForm from './ProviderApiKeyForm';
13
- import WenxinForm from './Wenxin';
14
13
 
15
14
  interface APIKeyFormProps {
16
15
  id: string;
@@ -66,8 +65,6 @@ const APIKeyForm = memo<APIKeyFormProps>(({ id, provider }) => {
66
65
  <Center gap={16} style={{ maxWidth: 300 }}>
67
66
  {provider === ModelProvider.Bedrock ? (
68
67
  <BedrockForm />
69
- ) : provider === ModelProvider.Wenxin ? (
70
- <WenxinForm />
71
68
  ) : (
72
69
  <ProviderApiKeyForm
73
70
  apiKeyPlaceholder={apiKeyPlaceholder}
@@ -147,4 +147,288 @@ describe('processWithArtifact', () => {
147
147
 
148
148
  expect(output).toEqual(`<lobeThinking>这个词汇涉及了`);
149
149
  });
150
+
151
+ it('should handle no empty line between lobeThinking and lobeArtifact', () => {
152
+ const input = `<lobeThinking>这是一个思考过程。</lobeThinking>
153
+ <lobeArtifact identifier="test" type="image/svg+xml" title="测试">
154
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
155
+ <rect width="100" height="100" fill="blue"/>
156
+ </svg>
157
+ </lobeArtifact>`;
158
+
159
+ const output = processWithArtifact(input);
160
+
161
+ expect(output).toEqual(`<lobeThinking>这是一个思考过程。</lobeThinking>
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 remove fenced code block between lobeArtifact and HTML content', () => {
167
+ const input = `<lobeArtifact identifier="web-calculator" type="text/html" title="简单的 Web 计算器">
168
+ \`\`\`html
169
+ <!DOCTYPE html>
170
+ <html lang="zh">
171
+ <head>
172
+ <title>计算器</title>
173
+ </head>
174
+ <body>
175
+ <div>计算器</div>
176
+ </body>
177
+ </html>
178
+ \`\`\`
179
+ </lobeArtifact>`;
180
+
181
+ const output = processWithArtifact(input);
182
+
183
+ expect(output).toEqual(
184
+ `<lobeArtifact identifier="web-calculator" type="text/html" title="简单的 Web 计算器"><!DOCTYPE html><html lang="zh"><head> <title>计算器</title></head><body> <div>计算器</div></body></html></lobeArtifact>`,
185
+ );
186
+ });
187
+
188
+ it('should remove fenced code block between lobeArtifact and HTML content without doctype', () => {
189
+ const input = `<lobeArtifact identifier="web-calculator" type="text/html" title="简单的 Web 计算器">
190
+ \`\`\`html
191
+ <html lang="zh">
192
+ <head>
193
+ <title>计算器</title>
194
+ </head>
195
+ <body>
196
+ <div>计算器</div>
197
+ </body>
198
+ </html>
199
+ \`\`\`
200
+ </lobeArtifact>`;
201
+
202
+ const output = processWithArtifact(input);
203
+
204
+ expect(output).toEqual(
205
+ `<lobeArtifact identifier="web-calculator" type="text/html" title="简单的 Web 计算器"><html lang="zh"><head> <title>计算器</title></head><body> <div>计算器</div></body></html></lobeArtifact>`,
206
+ );
207
+ });
208
+
209
+ it('should remove outer fenced code block wrapping lobeThinking and lobeArtifact', () => {
210
+ const input =
211
+ '```tool_code\n<lobeThinking>这是一个思考过程。</lobeThinking>\n\n<lobeArtifact identifier="test" type="text/html" title="测试">\n<div>测试内容</div>\n</lobeArtifact>\n```';
212
+
213
+ const output = processWithArtifact(input);
214
+
215
+ expect(output).toEqual(
216
+ '<lobeThinking>这是一个思考过程。</lobeThinking>\n\n<lobeArtifact identifier="test" type="text/html" title="测试"><div>测试内容</div></lobeArtifact>',
217
+ );
218
+ });
219
+
220
+ it('should handle both outer code block and inner HTML code block', () => {
221
+ const input =
222
+ '```tool_code\n<lobeThinking>这是一个思考过程。</lobeThinking>\n\n<lobeArtifact identifier="test" type="text/html" title="测试">\n```html\n<!DOCTYPE html>\n<html>\n<body>\n<div>测试内容</div>\n</body>\n</html>\n```\n</lobeArtifact>\n```';
223
+
224
+ const output = processWithArtifact(input);
225
+
226
+ expect(output).toEqual(
227
+ '<lobeThinking>这是一个思考过程。</lobeThinking>\n\n<lobeArtifact identifier="test" type="text/html" title="测试"><!DOCTYPE html><html><body><div>测试内容</div></body></html></lobeArtifact>',
228
+ );
229
+ });
230
+
231
+ it('should handle complete conversation with text and tags', () => {
232
+ const input = `Sure, I can help you with that! Here is a basic calculator built using HTML, CSS, and JavaScript.
233
+
234
+ <lobeThinking>A web calculator is a substantial piece of code and a good candidate for an artifact. It's self-contained, and it's likely that the user will want to modify it. This is a new request, so I will create a new artifact.</lobeThinking>
235
+
236
+ <lobeArtifact identifier="web-calculator" type="text/html" title="Web Calculator">
237
+ \`\`\`html
238
+ <!DOCTYPE html>
239
+ <html lang="en">
240
+ <head>
241
+ <meta charset="UTF-8">
242
+ <title>Simple Calculator</title>
243
+ </head>
244
+ <body>
245
+ <div>Calculator</div>
246
+ </body>
247
+ </html>
248
+ \`\`\`
249
+ </lobeArtifact>
250
+
251
+ This code provides a basic calculator that can perform addition, subtraction, multiplication, and division.`;
252
+
253
+ const output = processWithArtifact(input);
254
+
255
+ expect(output)
256
+ .toEqual(`Sure, I can help you with that! Here is a basic calculator built using HTML, CSS, and JavaScript.
257
+
258
+ <lobeThinking>A web calculator is a substantial piece of code and a good candidate for an artifact. It's self-contained, and it's likely that the user will want to modify it. This is a new request, so I will create a new artifact.</lobeThinking>
259
+
260
+ <lobeArtifact identifier="web-calculator" type="text/html" title="Web Calculator"><!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>Simple Calculator</title></head><body> <div>Calculator</div></body></html></lobeArtifact>
261
+
262
+ This code provides a basic calculator that can perform addition, subtraction, multiplication, and division.`);
263
+ });
264
+ });
265
+
266
+ describe('outer code block removal', () => {
267
+ it('should remove outer html code block', () => {
268
+ const input = `\`\`\`html
269
+ <lobeThinking>Test thinking</lobeThinking>
270
+ <lobeArtifact identifier="test" type="text/html" title="Test">
271
+ <!DOCTYPE html>
272
+ <html>
273
+ <body>Test</body>
274
+ </html>
275
+ </lobeArtifact>
276
+ \`\`\``;
277
+
278
+ const output = processWithArtifact(input);
279
+
280
+ expect(output).toEqual(`<lobeThinking>Test thinking</lobeThinking>
281
+
282
+ <lobeArtifact identifier="test" type="text/html" title="Test"><!DOCTYPE html><html><body>Test</body></html></lobeArtifact>`);
283
+ });
284
+
285
+ it('should remove outer tool_code code block', () => {
286
+ const input = `\`\`\`tool_code
287
+ <lobeThinking>Test thinking</lobeThinking>
288
+ <lobeArtifact identifier="test" type="text/html" title="Test">
289
+ <!DOCTYPE html>
290
+ <html>
291
+ <body>Test</body>
292
+ </html>
293
+ </lobeArtifact>
294
+ \`\`\``;
295
+
296
+ const output = processWithArtifact(input);
297
+
298
+ expect(output).toEqual(`<lobeThinking>Test thinking</lobeThinking>
299
+
300
+ <lobeArtifact identifier="test" type="text/html" title="Test"><!DOCTYPE html><html><body>Test</body></html></lobeArtifact>`);
301
+ });
302
+
303
+ it('should handle input without outer code block', () => {
304
+ const input = `<lobeThinking>Test thinking</lobeThinking>
305
+ <lobeArtifact identifier="test" type="text/html" title="Test">
306
+ <!DOCTYPE html>
307
+ <html>
308
+ <body>Test</body>
309
+ </html>
310
+ </lobeArtifact>`;
311
+
312
+ const output = processWithArtifact(input);
313
+
314
+ expect(output).toEqual(`<lobeThinking>Test thinking</lobeThinking>
315
+
316
+ <lobeArtifact identifier="test" type="text/html" title="Test"><!DOCTYPE html><html><body>Test</body></html></lobeArtifact>`);
317
+ });
318
+
319
+ it('should handle code block with content before and after', () => {
320
+ const input = `Some text before
321
+
322
+ \`\`\`html
323
+ <lobeThinking>Test thinking</lobeThinking>
324
+
325
+ <lobeArtifact identifier="test" type="text/html" title="Test">
326
+ <!DOCTYPE html>
327
+ <html>
328
+ <body>Test</body>
329
+ </html>
330
+ </lobeArtifact>
331
+ \`\`\`
332
+
333
+ Some text after`;
334
+
335
+ const output = processWithArtifact(input);
336
+
337
+ expect(output).toEqual(`Some text before
338
+
339
+ <lobeThinking>Test thinking</lobeThinking>
340
+
341
+ <lobeArtifact identifier="test" type="text/html" title="Test"><!DOCTYPE html><html><body>Test</body></html></lobeArtifact>
342
+
343
+ Some text after`);
344
+ });
345
+
346
+ it('should handle code block with only lobeArtifact tag', () => {
347
+ const input = `\`\`\`html
348
+ <lobeArtifact identifier="test" type="text/html" title="Test">
349
+ <!DOCTYPE html>
350
+ <html>
351
+ <body>Test</body>
352
+ </html>
353
+ </lobeArtifact>
354
+ \`\`\``;
355
+
356
+ const output = processWithArtifact(input);
357
+
358
+ expect(output).toEqual(
359
+ `<lobeArtifact identifier="test" type="text/html" title="Test"><!DOCTYPE html><html><body>Test</body></html></lobeArtifact>`,
360
+ );
361
+ });
362
+
363
+ it('should handle code block with surrounding text and both lobeThinking and lobeArtifact', () => {
364
+ const input = `---
365
+
366
+ \`\`\`tool_code
367
+ <lobeThinking>The user reported a \`SyntaxError\` in the browser console, indicating a problem with the JavaScript code in the calculator artifact. The error message "Identifier 'display' has already been declared" suggests a variable naming conflict. I need to review the JavaScript code and correct the issue. This is an update to the existing "calculator-web-artifact" artifact.</lobeThinking>
368
+ <lobeArtifact identifier="calculator-web-artifact" type="text/html" title="Simple Calculator">
369
+ <!DOCTYPE html>
370
+ <html lang="en">
371
+ ...
372
+ </html>
373
+ </lobeArtifact>
374
+ \`\`\`
375
+ I've updated the calculator artifact. The issue was a naming conflict with the \`display\` variable. I've renamed the input element's ID to \`calc-display\` and the JavaScript variable to \`displayElement\` to avoid the conflict. The calculator should now function correctly.
376
+
377
+ ---`;
378
+
379
+ const output = processWithArtifact(input);
380
+
381
+ expect(output).toEqual(`---
382
+
383
+ <lobeThinking>The user reported a \`SyntaxError\` in the browser console, indicating a problem with the JavaScript code in the calculator artifact. The error message "Identifier 'display' has already been declared" suggests a variable naming conflict. I need to review the JavaScript code and correct the issue. This is an update to the existing "calculator-web-artifact" artifact.</lobeThinking>
384
+
385
+ <lobeArtifact identifier="calculator-web-artifact" type="text/html" title="Simple Calculator"><!DOCTYPE html><html lang="en">...</html></lobeArtifact>
386
+
387
+ I've updated the calculator artifact. The issue was a naming conflict with the \`display\` variable. I've renamed the input element's ID to \`calc-display\` and the JavaScript variable to \`displayElement\` to avoid the conflict. The calculator should now function correctly.
388
+
389
+ ---`);
390
+ });
391
+
392
+ it('should handle code block before lobeThinking and lobeArtifact', () => {
393
+ const input = `
394
+ Okay, I'll create a temperature converter with the logic wrapped in an IIFE and event listeners attached in Javascript.
395
+
396
+ \`\`\`html
397
+ <!DOCTYPE html>
398
+ <html lang="en">
399
+ ...
400
+ </html>
401
+ \`\`\`
402
+
403
+ <lobeThinking>This is a good candidate for an artifact. It's a self-contained HTML document with embedded JavaScript that provides a functional temperature converter. It's more than a simple code snippet and can be reused or modified. This is a new request, so I'll create a new artifact with the identifier "temperature-converter".</lobeThinking>
404
+
405
+ <lobeArtifact identifier="temperature-converter" type="text/html" title="Temperature Converter">
406
+ \`\`\`html
407
+ <!DOCTYPE html>
408
+ <html lang="en">
409
+ ...
410
+ </html>
411
+ \`\`\`
412
+ </lobeArtifact>
413
+ 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.
414
+ `;
415
+
416
+ const output = processWithArtifact(input);
417
+
418
+ expect(output)
419
+ .toEqual(`Okay, I'll create a temperature converter with the logic wrapped in an IIFE and event listeners attached in Javascript.
420
+
421
+ \`\`\`html
422
+ <!DOCTYPE html>
423
+ <html lang="en">
424
+ ...
425
+ </html>
426
+ \`\`\`
427
+
428
+ <lobeThinking>This is a good candidate for an artifact. It's a self-contained HTML document with embedded JavaScript that provides a functional temperature converter. It's more than a simple code snippet and can be reused or modified. This is a new request, so I'll create a new artifact with the identifier "temperature-converter".</lobeThinking>
429
+
430
+ <lobeArtifact identifier="temperature-converter" type="text/html" title="Temperature Converter"><!DOCTYPE html><html lang="en">...</html></lobeArtifact>
431
+
432
+ 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
+ });
150
434
  });
@@ -4,24 +4,55 @@ import { ARTIFACT_TAG_REGEX, ARTIFACT_THINKING_TAG_REGEX } from '@/const/plugin'
4
4
  * Replace all line breaks in the matched `lobeArtifact` tag with an empty string
5
5
  */
6
6
  export const processWithArtifact = (input: string = '') => {
7
- let output = input;
8
- const thinkMatch = ARTIFACT_THINKING_TAG_REGEX.exec(input);
7
+ // First remove outer fenced code block if it exists
8
+ let output = input.replace(
9
+ /^([\S\s]*?)\s*```[^\n]*\n((?:<lobeThinking>[\S\s]*?<\/lobeThinking>\s*\n\s*)?<lobeArtifact[\S\s]*?<\/lobeArtifact>\s*)\n```\s*([\S\s]*?)$/,
10
+ (_, before = '', content, after = '') => {
11
+ return [before.trim(), content.trim(), after.trim()].filter(Boolean).join('\n\n');
12
+ },
13
+ );
14
+
15
+ const thinkMatch = ARTIFACT_THINKING_TAG_REGEX.exec(output);
9
16
 
10
17
  // If the input contains the `lobeThinking` tag, replace all line breaks with an empty string
11
- if (thinkMatch)
12
- output = input.replace(ARTIFACT_THINKING_TAG_REGEX, (match) =>
18
+ if (thinkMatch) {
19
+ output = output.replace(ARTIFACT_THINKING_TAG_REGEX, (match) =>
13
20
  match.replaceAll(/\r?\n|\r/g, ''),
14
21
  );
22
+ }
15
23
 
16
- const match = ARTIFACT_TAG_REGEX.exec(input);
24
+ // Add empty line between lobeThinking and lobeArtifact if they are adjacent
25
+ output = output.replace(/(<\/lobeThinking>)\r?\n(<lobeArtifact)/, '$1\n\n$2');
26
+
27
+ // Remove fenced code block between lobeArtifact and HTML content
28
+ output = output.replace(
29
+ /(<lobeArtifact[^>]*>)\s*```[^\n]*\n([\S\s]*?)(```\n)?(<\/lobeArtifact>)/,
30
+ (_, start, content, __, end) => {
31
+ if (content.trim().startsWith('<!DOCTYPE html') || content.trim().startsWith('<html')) {
32
+ return start + content.trim() + end;
33
+ }
34
+ return start + content + (__ || '') + end;
35
+ },
36
+ );
37
+
38
+ // Keep existing code blocks that are not part of lobeArtifact
39
+ output = output.replace(
40
+ /^([\S\s]*?)(<lobeThinking>[\S\s]*?<\/lobeThinking>\s*\n\s*<lobeArtifact[\S\s]*?<\/lobeArtifact>)([\S\s]*?)$/,
41
+ (_, before, content, after) => {
42
+ return [before.trim(), content.trim(), after.trim()].filter(Boolean).join('\n\n');
43
+ },
44
+ );
45
+
46
+ const match = ARTIFACT_TAG_REGEX.exec(output);
17
47
  // If the input contains the `lobeArtifact` tag, replace all line breaks with an empty string
18
- if (match)
19
- return output.replace(ARTIFACT_TAG_REGEX, (match) => match.replaceAll(/\r?\n|\r/g, ''));
48
+ if (match) {
49
+ output = output.replace(ARTIFACT_TAG_REGEX, (match) => match.replaceAll(/\r?\n|\r/g, ''));
50
+ }
20
51
 
21
52
  // if not match, check if it's start with <lobeArtifact but not closed
22
53
  const regex = /<lobeArtifact\b(?:(?!\/?>)[\S\s])*$/;
23
54
  if (regex.test(output)) {
24
- return output.replace(regex, '<lobeArtifact>');
55
+ output = output.replace(regex, '<lobeArtifact>');
25
56
  }
26
57
 
27
58
  return output;