@lobehub/lobehub 2.1.3 → 2.1.4

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 (129) hide show
  1. package/.env.example +0 -3
  2. package/.env.example.development +0 -3
  3. package/CHANGELOG.md +34 -0
  4. package/changelog/v2.json +9 -0
  5. package/docker-compose/deploy/.env.example +3 -1
  6. package/docker-compose/deploy/.env.zh-CN.example +4 -1
  7. package/docker-compose/local/.env.example +0 -1
  8. package/docker-compose/local/.env.zh-CN.example +0 -1
  9. package/docker-compose/local/grafana/.env.example +0 -1
  10. package/docker-compose/local/grafana/.env.zh-CN.example +0 -1
  11. package/docker-compose/local/logto/docker-compose.yml +0 -1
  12. package/docker-compose/local/zitadel/.env.example +1 -2
  13. package/docker-compose/local/zitadel/.env.zh-CN.example +1 -2
  14. package/docker-compose/production/grafana/.env.example +0 -1
  15. package/docker-compose/production/grafana/.env.zh-CN.example +0 -1
  16. package/docker-compose/production/logto/.env.example +0 -2
  17. package/docker-compose/production/logto/.env.zh-CN.example +0 -2
  18. package/docker-compose/production/zitadel/.env.example +0 -2
  19. package/docker-compose/production/zitadel/.env.zh-CN.example +0 -2
  20. package/docker-compose/setup.sh +16 -2
  21. package/docs/development/basic/folder-structure.mdx +23 -14
  22. package/docs/development/basic/folder-structure.zh-CN.mdx +23 -14
  23. package/docs/development/basic/work-with-server-side-database.mdx +0 -1
  24. package/docs/development/basic/work-with-server-side-database.zh-CN.mdx +0 -1
  25. package/docs/development/start.mdx +19 -12
  26. package/docs/development/start.zh-CN.mdx +19 -12
  27. package/docs/self-hosting/advanced/s3/cloudflare-r2.mdx +0 -5
  28. package/docs/self-hosting/advanced/s3/cloudflare-r2.zh-CN.mdx +0 -5
  29. package/docs/self-hosting/advanced/s3/rustfs.mdx +0 -2
  30. package/docs/self-hosting/advanced/s3/rustfs.zh-CN.mdx +0 -2
  31. package/docs/self-hosting/advanced/s3/tencent-cloud.mdx +0 -1
  32. package/docs/self-hosting/advanced/s3/tencent-cloud.zh-CN.mdx +0 -2
  33. package/docs/self-hosting/advanced/s3.mdx +0 -9
  34. package/docs/self-hosting/advanced/s3.zh-CN.mdx +0 -8
  35. package/docs/self-hosting/environment-variables/s3.mdx +0 -7
  36. package/docs/self-hosting/environment-variables/s3.zh-CN.mdx +0 -7
  37. package/docs/self-hosting/platform/docker-compose.mdx +0 -1
  38. package/docs/self-hosting/platform/docker-compose.zh-CN.mdx +0 -1
  39. package/docs/self-hosting/platform/docker.mdx +5 -3
  40. package/docs/self-hosting/platform/docker.zh-CN.mdx +5 -4
  41. package/docs/self-hosting/platform/dokploy.mdx +0 -2
  42. package/docs/self-hosting/platform/dokploy.zh-CN.mdx +0 -2
  43. package/docs/self-hosting/platform/vercel.mdx +0 -7
  44. package/docs/self-hosting/platform/vercel.zh-CN.mdx +0 -7
  45. package/e2e/src/steps/home/sidebarAgent.steps.ts +56 -24
  46. package/locales/ar/authError.json +1 -0
  47. package/locales/ar/models.json +25 -22
  48. package/locales/ar/providers.json +0 -1
  49. package/locales/ar/setting.json +16 -0
  50. package/locales/bg-BG/authError.json +1 -0
  51. package/locales/bg-BG/models.json +18 -21
  52. package/locales/bg-BG/providers.json +0 -1
  53. package/locales/bg-BG/setting.json +16 -0
  54. package/locales/de-DE/authError.json +1 -0
  55. package/locales/de-DE/models.json +20 -20
  56. package/locales/de-DE/providers.json +0 -1
  57. package/locales/de-DE/setting.json +16 -0
  58. package/locales/en-US/models.json +22 -22
  59. package/locales/en-US/providers.json +0 -1
  60. package/locales/es-ES/authError.json +1 -0
  61. package/locales/es-ES/models.json +84 -20
  62. package/locales/es-ES/providers.json +0 -1
  63. package/locales/es-ES/setting.json +16 -0
  64. package/locales/fa-IR/authError.json +1 -0
  65. package/locales/fa-IR/models.json +43 -20
  66. package/locales/fa-IR/providers.json +0 -1
  67. package/locales/fa-IR/setting.json +16 -0
  68. package/locales/fr-FR/authError.json +1 -0
  69. package/locales/fr-FR/models.json +19 -21
  70. package/locales/fr-FR/providers.json +0 -1
  71. package/locales/fr-FR/setting.json +16 -0
  72. package/locales/it-IT/authError.json +1 -0
  73. package/locales/it-IT/models.json +17 -19
  74. package/locales/it-IT/providers.json +0 -1
  75. package/locales/it-IT/setting.json +16 -0
  76. package/locales/ja-JP/authError.json +1 -0
  77. package/locales/ja-JP/models.json +43 -22
  78. package/locales/ja-JP/providers.json +0 -1
  79. package/locales/ja-JP/setting.json +16 -0
  80. package/locales/ko-KR/authError.json +1 -0
  81. package/locales/ko-KR/models.json +41 -20
  82. package/locales/ko-KR/providers.json +0 -1
  83. package/locales/ko-KR/setting.json +16 -0
  84. package/locales/nl-NL/authError.json +1 -0
  85. package/locales/nl-NL/models.json +48 -20
  86. package/locales/nl-NL/providers.json +0 -1
  87. package/locales/nl-NL/setting.json +16 -0
  88. package/locales/pl-PL/authError.json +1 -0
  89. package/locales/pl-PL/models.json +19 -22
  90. package/locales/pl-PL/providers.json +0 -1
  91. package/locales/pl-PL/setting.json +16 -0
  92. package/locales/pt-BR/authError.json +1 -0
  93. package/locales/pt-BR/models.json +21 -21
  94. package/locales/pt-BR/providers.json +0 -1
  95. package/locales/pt-BR/setting.json +16 -0
  96. package/locales/ru-RU/authError.json +1 -0
  97. package/locales/ru-RU/models.json +23 -20
  98. package/locales/ru-RU/providers.json +0 -1
  99. package/locales/ru-RU/setting.json +16 -0
  100. package/locales/tr-TR/authError.json +1 -0
  101. package/locales/tr-TR/models.json +37 -20
  102. package/locales/tr-TR/providers.json +0 -1
  103. package/locales/tr-TR/setting.json +16 -0
  104. package/locales/vi-VN/authError.json +1 -0
  105. package/locales/vi-VN/models.json +15 -19
  106. package/locales/vi-VN/providers.json +0 -1
  107. package/locales/vi-VN/setting.json +16 -0
  108. package/locales/zh-CN/models.json +20 -20
  109. package/locales/zh-CN/providers.json +0 -1
  110. package/locales/zh-TW/authError.json +1 -0
  111. package/locales/zh-TW/models.json +20 -20
  112. package/locales/zh-TW/providers.json +0 -1
  113. package/locales/zh-TW/setting.json +16 -0
  114. package/package.json +1 -1
  115. package/packages/model-bank/src/aiModels/google.ts +0 -19
  116. package/packages/model-bank/src/aiModels/moonshot.ts +56 -5
  117. package/packages/model-bank/src/aiModels/ollamacloud.ts +14 -0
  118. package/packages/model-bank/src/aiModels/openrouter.ts +0 -14
  119. package/packages/model-bank/src/aiModels/qwen.ts +105 -4
  120. package/packages/model-bank/src/aiModels/siliconcloud.ts +39 -0
  121. package/packages/model-bank/src/aiModels/wenxin.ts +0 -99
  122. package/packages/model-runtime/src/core/contextBuilders/openai.test.ts +24 -0
  123. package/packages/model-runtime/src/core/contextBuilders/openai.ts +22 -5
  124. package/packages/model-runtime/src/core/openaiCompatibleFactory/index.ts +10 -3
  125. package/packages/model-runtime/src/core/streams/google/google-ai.test.ts +54 -13
  126. package/packages/model-runtime/src/core/streams/google/index.ts +1 -4
  127. package/packages/model-runtime/src/providers/moonshot/index.ts +24 -2
  128. package/packages/model-runtime/src/providers/qwen/index.ts +16 -15
  129. package/src/server/routers/lambda/__tests__/integration/aiAgent/execAgent.integration.test.ts +3 -2
@@ -73,6 +73,7 @@ export interface OpenAICompatibleFactoryOptions<T extends Record<string, any> =
73
73
  baseURL?: string;
74
74
  chatCompletion?: {
75
75
  excludeUsage?: boolean;
76
+ forceImageBase64?: boolean;
76
77
  handleError?: (
77
78
  error: any,
78
79
  options: ConstructorOptions<T>,
@@ -387,7 +388,9 @@ export const createOpenAICompatibleRuntime = <T extends Record<string, any> = an
387
388
  this.baseURL = targetBaseURL;
388
389
  }
389
390
 
390
- const messages = await convertOpenAIMessages(postPayload.messages);
391
+ const messages = await convertOpenAIMessages(postPayload.messages, {
392
+ forceImageBase64: chatCompletion?.forceImageBase64,
393
+ });
391
394
 
392
395
  let response: Stream<OpenAI.Chat.Completions.ChatCompletionChunk>;
393
396
 
@@ -877,7 +880,9 @@ export const createOpenAICompatibleRuntime = <T extends Record<string, any> = an
877
880
  delete res.frequency_penalty;
878
881
  delete res.presence_penalty;
879
882
 
880
- const input = await convertOpenAIResponseInputs(messages as any);
883
+ const input = await convertOpenAIResponseInputs(messages as any, {
884
+ forceImageBase64: chatCompletion?.forceImageBase64,
885
+ });
881
886
 
882
887
  const isStreaming = payload.stream !== false;
883
888
  log(
@@ -1006,7 +1011,9 @@ export const createOpenAICompatibleRuntime = <T extends Record<string, any> = an
1006
1011
 
1007
1012
  if (shouldUseResponses) {
1008
1013
  log('calling responses.create for tool calling');
1009
- const input = await convertOpenAIResponseInputs(messages as any);
1014
+ const input = await convertOpenAIResponseInputs(messages as any, {
1015
+ forceImageBase64: chatCompletion?.forceImageBase64,
1016
+ });
1010
1017
 
1011
1018
  const res = await this.client.responses.create(
1012
1019
  {
@@ -251,16 +251,16 @@ describe('GoogleGenerativeAIStream', () => {
251
251
  expect(chunks).toEqual(
252
252
  [
253
253
  'id: chat_1',
254
- 'event: content_part',
255
- 'data: {"content":"234","partType":"text"}\n',
254
+ 'event: text',
255
+ 'data: "234"\n',
256
256
 
257
257
  'id: chat_1',
258
258
  'event: text',
259
259
  'data: ""\n',
260
260
 
261
261
  'id: chat_1',
262
- 'event: content_part',
263
- `data: {"content":"567890\\n","partType":"text"}\n`,
262
+ 'event: text',
263
+ `data: "567890\\n"\n`,
264
264
  // stop
265
265
  'id: chat_1',
266
266
  'event: stop',
@@ -384,12 +384,12 @@ describe('GoogleGenerativeAIStream', () => {
384
384
  `data: {"content":"**Finalizing Interpretation**\\n\\n","inReasoning":true,"partType":"text"}\n`,
385
385
 
386
386
  'id: chat_1',
387
- 'event: content_part',
388
- `data: {"content":"简单来说,","partType":"text"}\n`,
387
+ 'event: text',
388
+ 'data: "简单来说,"\n',
389
389
 
390
390
  'id: chat_1',
391
- 'event: content_part',
392
- `data: {"content":"文本内容。","partType":"text"}\n`,
391
+ 'event: text',
392
+ 'data: "文本内容。"\n',
393
393
  // stop
394
394
  'id: chat_1',
395
395
  'event: stop',
@@ -471,12 +471,12 @@ describe('GoogleGenerativeAIStream', () => {
471
471
  expect(chunks).toEqual(
472
472
  [
473
473
  'id: chat_1',
474
- 'event: content_part',
475
- 'data: {"content":"234","partType":"text"}\n',
474
+ 'event: text',
475
+ 'data: "234"\n',
476
476
 
477
477
  'id: chat_1',
478
- 'event: content_part',
479
- `data: {"content":"567890\\n","partType":"text"}\n`,
478
+ 'event: text',
479
+ 'data: "567890\\n"\n',
480
480
  // stop
481
481
  'id: chat_1',
482
482
  'event: stop',
@@ -1103,7 +1103,7 @@ describe('GoogleGenerativeAIStream', () => {
1103
1103
  content: {
1104
1104
  parts: [
1105
1105
  {
1106
- text: '**Planning the Solution**\n\nI\'m solidifying my plan...',
1106
+ text: "**Planning the Solution**\n\nI'm solidifying my plan...",
1107
1107
  thought: true,
1108
1108
  },
1109
1109
  ],
@@ -1901,5 +1901,46 @@ describe('GoogleGenerativeAIStream', () => {
1901
1901
  ].map((i) => i + '\n'),
1902
1902
  );
1903
1903
  });
1904
+
1905
+ it('should NOT use multimodal processing if only thoughtsTokenCount is present in metadata but no thought parts', async () => {
1906
+ vi.spyOn(uuidModule, 'nanoid').mockReturnValueOnce('1');
1907
+
1908
+ const data = [
1909
+ {
1910
+ candidates: [
1911
+ {
1912
+ content: {
1913
+ parts: [{ text: 'Hello world' }],
1914
+ role: 'model',
1915
+ },
1916
+ index: 0,
1917
+ },
1918
+ ],
1919
+ usageMetadata: {
1920
+ promptTokenCount: 10,
1921
+ candidatesTokenCount: 2,
1922
+ totalTokenCount: 17,
1923
+ thoughtsTokenCount: 5,
1924
+ },
1925
+ modelVersion: 'gemini-2.5-flash',
1926
+ },
1927
+ ];
1928
+
1929
+ const mockGoogleStream = new ReadableStream({
1930
+ start(controller) {
1931
+ data.forEach((item) => {
1932
+ controller.enqueue(item);
1933
+ });
1934
+ controller.close();
1935
+ },
1936
+ });
1937
+
1938
+ const protocolStream = GoogleGenerativeAIStream(mockGoogleStream);
1939
+ const chunks = await decodeStreamChunks(protocolStream);
1940
+
1941
+ // Should use 'text' event, not 'content_part'
1942
+ expect(chunks).toContain('event: text\n');
1943
+ expect(chunks).not.toContain('event: content_part\n');
1944
+ });
1904
1945
  });
1905
1946
  });
@@ -120,7 +120,6 @@ const transformGoogleGenerativeAIStream = (
120
120
  const hasReasoningParts = parts.some((p: any) => p.thought === true);
121
121
  const hasImageParts = parts.some((p: any) => p.inlineData);
122
122
  const hasThoughtSignature = parts.some((p: any) => p.thoughtSignature);
123
- const hasThoughtsInMetadata = (usageMetadata as any)?.thoughtsTokenCount > 0;
124
123
 
125
124
  // Check model version to determine if new format should be used
126
125
  const modelVersion = (chunk as any).modelVersion || '';
@@ -144,8 +143,7 @@ const transformGoogleGenerativeAIStream = (
144
143
  // 1. There are reasoning parts in current chunk (thought: true)
145
144
  // 2. There are multiple parts with images (multimodal content)
146
145
  // 3. There are thoughtSignature in parts (reasoning metadata attached to content)
147
- // 4. There is thoughtsTokenCount in metadata (indicates response contains reasoning)
148
- // 5. This is Gemini 3 model with image generation (always use new format for consistency)
146
+ // 4. This is Gemini 3 model with image generation (always use new format for consistency)
149
147
  // BUT NOT for:
150
148
  // - The legacy single-image scenario
151
149
  // - Grounding metadata scenario (uses legacy text + grounding events)
@@ -153,7 +151,6 @@ const transformGoogleGenerativeAIStream = (
153
151
  (hasReasoningParts ||
154
152
  (hasImageParts && parts.length > 1) ||
155
153
  hasThoughtSignature ||
156
- hasThoughtsInMetadata ||
157
154
  isGemini3Model) &&
158
155
  !isSingleImageWithFinish &&
159
156
  !hasGroundingMetadata;
@@ -15,8 +15,9 @@ export interface MoonshotModelCard {
15
15
  export const params = {
16
16
  baseURL: 'https://api.moonshot.cn/v1',
17
17
  chatCompletion: {
18
+ forceImageBase64: true,
18
19
  handlePayload: (payload: ChatStreamPayload) => {
19
- const { enabledSearch, messages, temperature, tools, ...rest } = payload;
20
+ const { enabledSearch, messages, model, temperature, thinking, tools, ...rest } = payload;
20
21
 
21
22
  const filteredMessages = messages.map((message: any) => {
22
23
  let normalizedMessage = message;
@@ -51,12 +52,33 @@ export const params = {
51
52
  ]
52
53
  : tools;
53
54
 
54
- // Resolve parameters with normalization
55
+ const isK25Model = model === 'kimi-k2.5';
56
+
57
+ if (isK25Model) {
58
+ const thinkingParam =
59
+ thinking?.type === 'disabled' ? { type: 'disabled' } : { type: 'enabled' };
60
+ const isThinkingEnabled = thinkingParam.type === 'enabled';
61
+
62
+ return {
63
+ ...rest,
64
+ frequency_penalty: 0,
65
+ messages: filteredMessages,
66
+ model,
67
+ presence_penalty: 0,
68
+ temperature: isThinkingEnabled ? 1 : 0.6,
69
+ thinking: thinkingParam,
70
+ tools: moonshotTools,
71
+ top_p: 0.95,
72
+ } as any;
73
+ }
74
+
75
+ // Resolve parameters with normalization for non-K2.5 models
55
76
  const resolvedParams = resolveParameters({ temperature }, { normalizeTemperature: true });
56
77
 
57
78
  return {
58
79
  ...rest,
59
80
  messages: filteredMessages,
81
+ model,
60
82
  temperature: resolvedParams.temperature,
61
83
  tools: moonshotTools,
62
84
  } as any;
@@ -47,24 +47,25 @@ export const LobeQwenAI = createOpenAICompatibleRuntime({
47
47
  ...rest,
48
48
  ...(model.includes('-thinking')
49
49
  ? {
50
- enable_thinking: true,
50
+ enable_thinking: true,
51
+ thinking_budget:
52
+ thinking?.budget_tokens === 0 ? 0 : thinking?.budget_tokens || undefined,
53
+ }
54
+ : [
55
+ 'qwen3',
56
+ 'qwen-turbo',
57
+ 'qwen-plus',
58
+ 'qwen-flash',
59
+ 'deepseek-v3.1',
60
+ 'deepseek-v3.2',
61
+ 'glm',
62
+ 'kimi-k2.5',
63
+ ].some((keyword) => model.toLowerCase().includes(keyword))
64
+ ? {
65
+ enable_thinking: thinking !== undefined ? thinking.type === 'enabled' : false,
51
66
  thinking_budget:
52
67
  thinking?.budget_tokens === 0 ? 0 : thinking?.budget_tokens || undefined,
53
68
  }
54
- : [
55
- 'qwen3',
56
- 'qwen-turbo',
57
- 'qwen-plus',
58
- 'qwen-flash',
59
- 'deepseek-v3.1',
60
- 'deepseek-v3.2',
61
- 'glm',
62
- ].some((keyword) => model.toLowerCase().includes(keyword))
63
- ? {
64
- enable_thinking: thinking !== undefined ? thinking.type === 'enabled' : false,
65
- thinking_budget:
66
- thinking?.budget_tokens === 0 ? 0 : thinking?.budget_tokens || undefined,
67
- }
68
69
  : {}),
69
70
  frequency_penalty: undefined,
70
71
  model,
@@ -232,14 +232,15 @@ describe('execAgent', () => {
232
232
  })
233
233
  .returning();
234
234
 
235
- const [thread] = await serverDB
235
+ const threadResult = (await serverDB
236
236
  .insert(threads)
237
237
  .values({
238
238
  topicId: topic.id,
239
239
  type: 'standalone',
240
240
  userId,
241
241
  })
242
- .returning();
242
+ .returning()) as { id: string }[];
243
+ const thread = threadResult[0];
243
244
 
244
245
  const caller = aiAgentRouter.createCaller(createTestContext());
245
246