@lobehub/chat 1.136.13 → 1.137.1

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 (190) hide show
  1. package/.cursor/rules/add-setting-env.mdc +175 -0
  2. package/.cursor/rules/db-migrations.mdc +25 -0
  3. package/.env.example +7 -0
  4. package/CHANGELOG.md +50 -0
  5. package/Dockerfile +3 -2
  6. package/Dockerfile.database +15 -3
  7. package/Dockerfile.pglite +3 -2
  8. package/changelog/v1.json +18 -0
  9. package/docs/development/database-schema.dbml +1 -0
  10. package/docs/self-hosting/advanced/feature-flags.mdx +25 -15
  11. package/docs/self-hosting/advanced/feature-flags.zh-CN.mdx +25 -15
  12. package/docs/self-hosting/environment-variables/basic.mdx +12 -0
  13. package/docs/self-hosting/environment-variables/basic.zh-CN.mdx +12 -0
  14. package/locales/ar/setting.json +8 -0
  15. package/locales/bg-BG/setting.json +8 -0
  16. package/locales/de-DE/setting.json +8 -0
  17. package/locales/en-US/setting.json +8 -0
  18. package/locales/es-ES/setting.json +8 -0
  19. package/locales/fa-IR/setting.json +8 -0
  20. package/locales/fr-FR/setting.json +8 -0
  21. package/locales/it-IT/setting.json +8 -0
  22. package/locales/ja-JP/setting.json +8 -0
  23. package/locales/ko-KR/setting.json +8 -0
  24. package/locales/nl-NL/setting.json +8 -0
  25. package/locales/pl-PL/setting.json +8 -0
  26. package/locales/pt-BR/setting.json +8 -0
  27. package/locales/ru-RU/setting.json +8 -0
  28. package/locales/tr-TR/setting.json +8 -0
  29. package/locales/vi-VN/setting.json +8 -0
  30. package/locales/zh-CN/setting.json +8 -0
  31. package/locales/zh-TW/setting.json +8 -0
  32. package/package.json +1 -1
  33. package/packages/agent-runtime/examples/tools-calling.ts +4 -3
  34. package/packages/agent-runtime/src/core/__tests__/runtime.test.ts +559 -29
  35. package/packages/agent-runtime/src/core/runtime.ts +171 -43
  36. package/packages/agent-runtime/src/types/instruction.ts +32 -6
  37. package/packages/agent-runtime/src/types/runtime.ts +2 -2
  38. package/packages/agent-runtime/src/types/state.ts +1 -8
  39. package/packages/agent-runtime/vitest.config.mts +14 -0
  40. package/packages/const/src/settings/image.ts +8 -0
  41. package/packages/const/src/settings/index.ts +3 -0
  42. package/packages/context-engine/src/__tests__/pipeline.test.ts +485 -0
  43. package/packages/context-engine/src/base/__tests__/BaseProcessor.test.ts +381 -0
  44. package/packages/context-engine/src/base/__tests__/BaseProvider.test.ts +392 -0
  45. package/packages/context-engine/src/processors/__tests__/MessageCleanup.test.ts +346 -0
  46. package/packages/context-engine/src/processors/__tests__/ToolCall.test.ts +552 -0
  47. package/packages/database/migrations/0038_add_image_user_settings.sql +1 -0
  48. package/packages/database/migrations/meta/0038_snapshot.json +7580 -0
  49. package/packages/database/migrations/meta/_journal.json +7 -0
  50. package/packages/database/src/core/migrations.json +6 -0
  51. package/packages/database/src/models/user.ts +3 -1
  52. package/packages/database/src/schemas/user.ts +1 -0
  53. package/packages/file-loaders/src/loaders/docx/index.test.ts +0 -1
  54. package/packages/file-loaders/src/loaders/excel/__snapshots__/index.test.ts.snap +30 -0
  55. package/packages/file-loaders/src/loaders/excel/index.test.ts +8 -0
  56. package/packages/file-loaders/src/loaders/pptx/index.test.ts +25 -0
  57. package/packages/file-loaders/src/utils/parser-utils.test.ts +155 -0
  58. package/packages/file-loaders/vitest.config.mts +8 -0
  59. package/packages/model-runtime/CLAUDE.md +5 -0
  60. package/packages/model-runtime/docs/test-coverage.md +706 -0
  61. package/packages/model-runtime/src/core/ModelRuntime.test.ts +231 -0
  62. package/packages/model-runtime/src/core/RouterRuntime/createRuntime.ts +1 -1
  63. package/packages/model-runtime/src/core/openaiCompatibleFactory/createImage.test.ts +799 -0
  64. package/packages/model-runtime/src/core/openaiCompatibleFactory/index.test.ts +188 -4
  65. package/packages/model-runtime/src/core/openaiCompatibleFactory/index.ts +41 -10
  66. package/packages/model-runtime/src/core/streams/openai/__snapshots__/responsesStream.test.ts.snap +439 -0
  67. package/packages/model-runtime/src/core/streams/openai/openai.test.ts +789 -0
  68. package/packages/model-runtime/src/core/streams/openai/responsesStream.test.ts +551 -0
  69. package/packages/model-runtime/src/core/usageConverters/utils/computeChatCost.test.ts +230 -0
  70. package/packages/model-runtime/src/core/usageConverters/utils/computeImageCost.test.ts +334 -37
  71. package/packages/model-runtime/src/providerTestUtils.ts +148 -145
  72. package/packages/model-runtime/src/providers/ai302/index.test.ts +60 -0
  73. package/packages/model-runtime/src/providers/ai302/index.ts +9 -4
  74. package/packages/model-runtime/src/providers/ai360/index.test.ts +1213 -1
  75. package/packages/model-runtime/src/providers/ai360/index.ts +9 -4
  76. package/packages/model-runtime/src/providers/aihubmix/index.test.ts +73 -0
  77. package/packages/model-runtime/src/providers/aihubmix/index.ts +6 -9
  78. package/packages/model-runtime/src/providers/akashchat/index.test.ts +433 -3
  79. package/packages/model-runtime/src/providers/akashchat/index.ts +12 -7
  80. package/packages/model-runtime/src/providers/anthropic/generateObject.test.ts +183 -29
  81. package/packages/model-runtime/src/providers/anthropic/generateObject.ts +40 -24
  82. package/packages/model-runtime/src/providers/azureai/index.test.ts +102 -0
  83. package/packages/model-runtime/src/providers/baichuan/index.test.ts +416 -26
  84. package/packages/model-runtime/src/providers/baichuan/index.ts +23 -20
  85. package/packages/model-runtime/src/providers/bedrock/index.test.ts +420 -2
  86. package/packages/model-runtime/src/providers/cerebras/index.test.ts +465 -0
  87. package/packages/model-runtime/src/providers/cerebras/index.ts +8 -3
  88. package/packages/model-runtime/src/providers/cohere/index.test.ts +1074 -1
  89. package/packages/model-runtime/src/providers/cohere/index.ts +8 -3
  90. package/packages/model-runtime/src/providers/cometapi/index.test.ts +439 -3
  91. package/packages/model-runtime/src/providers/cometapi/index.ts +8 -3
  92. package/packages/model-runtime/src/providers/deepseek/index.test.ts +116 -1
  93. package/packages/model-runtime/src/providers/deepseek/index.ts +8 -3
  94. package/packages/model-runtime/src/providers/fireworksai/index.test.ts +264 -3
  95. package/packages/model-runtime/src/providers/fireworksai/index.ts +8 -3
  96. package/packages/model-runtime/src/providers/giteeai/index.test.ts +325 -3
  97. package/packages/model-runtime/src/providers/giteeai/index.ts +23 -6
  98. package/packages/model-runtime/src/providers/github/index.test.ts +532 -3
  99. package/packages/model-runtime/src/providers/github/index.ts +8 -3
  100. package/packages/model-runtime/src/providers/groq/index.test.ts +344 -31
  101. package/packages/model-runtime/src/providers/groq/index.ts +8 -3
  102. package/packages/model-runtime/src/providers/higress/index.test.ts +142 -0
  103. package/packages/model-runtime/src/providers/higress/index.ts +8 -3
  104. package/packages/model-runtime/src/providers/huggingface/index.test.ts +612 -1
  105. package/packages/model-runtime/src/providers/huggingface/index.ts +9 -4
  106. package/packages/model-runtime/src/providers/hunyuan/index.test.ts +365 -1
  107. package/packages/model-runtime/src/providers/hunyuan/index.ts +9 -3
  108. package/packages/model-runtime/src/providers/infiniai/index.test.ts +71 -0
  109. package/packages/model-runtime/src/providers/internlm/index.test.ts +369 -2
  110. package/packages/model-runtime/src/providers/internlm/index.ts +10 -5
  111. package/packages/model-runtime/src/providers/jina/index.test.ts +164 -3
  112. package/packages/model-runtime/src/providers/jina/index.ts +8 -3
  113. package/packages/model-runtime/src/providers/lmstudio/index.test.ts +182 -3
  114. package/packages/model-runtime/src/providers/lmstudio/index.ts +8 -3
  115. package/packages/model-runtime/src/providers/mistral/index.test.ts +779 -27
  116. package/packages/model-runtime/src/providers/mistral/index.ts +8 -3
  117. package/packages/model-runtime/src/providers/modelscope/index.test.ts +232 -1
  118. package/packages/model-runtime/src/providers/modelscope/index.ts +8 -3
  119. package/packages/model-runtime/src/providers/moonshot/index.test.ts +489 -2
  120. package/packages/model-runtime/src/providers/moonshot/index.ts +8 -3
  121. package/packages/model-runtime/src/providers/nebius/index.test.ts +381 -3
  122. package/packages/model-runtime/src/providers/nebius/index.ts +8 -3
  123. package/packages/model-runtime/src/providers/newapi/index.test.ts +667 -3
  124. package/packages/model-runtime/src/providers/newapi/index.ts +6 -3
  125. package/packages/model-runtime/src/providers/nvidia/index.test.ts +168 -1
  126. package/packages/model-runtime/src/providers/nvidia/index.ts +12 -7
  127. package/packages/model-runtime/src/providers/ollama/index.test.ts +797 -1
  128. package/packages/model-runtime/src/providers/ollama/index.ts +8 -0
  129. package/packages/model-runtime/src/providers/ollamacloud/index.test.ts +411 -0
  130. package/packages/model-runtime/src/providers/ollamacloud/index.ts +8 -3
  131. package/packages/model-runtime/src/providers/openai/index.test.ts +171 -2
  132. package/packages/model-runtime/src/providers/openai/index.ts +8 -3
  133. package/packages/model-runtime/src/providers/openrouter/index.test.ts +1647 -95
  134. package/packages/model-runtime/src/providers/openrouter/index.ts +12 -7
  135. package/packages/model-runtime/src/providers/qiniu/index.test.ts +294 -1
  136. package/packages/model-runtime/src/providers/qiniu/index.ts +8 -3
  137. package/packages/model-runtime/src/providers/search1api/index.test.ts +1131 -11
  138. package/packages/model-runtime/src/providers/search1api/index.ts +10 -4
  139. package/packages/model-runtime/src/providers/sensenova/index.test.ts +1069 -1
  140. package/packages/model-runtime/src/providers/sensenova/index.ts +8 -3
  141. package/packages/model-runtime/src/providers/siliconcloud/index.test.ts +196 -0
  142. package/packages/model-runtime/src/providers/siliconcloud/index.ts +8 -3
  143. package/packages/model-runtime/src/providers/spark/index.test.ts +293 -1
  144. package/packages/model-runtime/src/providers/spark/index.ts +8 -3
  145. package/packages/model-runtime/src/providers/stepfun/index.test.ts +322 -3
  146. package/packages/model-runtime/src/providers/stepfun/index.ts +8 -3
  147. package/packages/model-runtime/src/providers/tencentcloud/index.test.ts +182 -3
  148. package/packages/model-runtime/src/providers/tencentcloud/index.ts +8 -3
  149. package/packages/model-runtime/src/providers/togetherai/index.test.ts +359 -4
  150. package/packages/model-runtime/src/providers/togetherai/index.ts +12 -5
  151. package/packages/model-runtime/src/providers/v0/index.test.ts +341 -0
  152. package/packages/model-runtime/src/providers/v0/index.ts +20 -6
  153. package/packages/model-runtime/src/providers/vercelaigateway/index.test.ts +710 -0
  154. package/packages/model-runtime/src/providers/vercelaigateway/index.ts +19 -13
  155. package/packages/model-runtime/src/providers/vllm/index.test.ts +45 -1
  156. package/packages/model-runtime/src/providers/volcengine/index.test.ts +75 -0
  157. package/packages/model-runtime/src/providers/wenxin/index.test.ts +144 -1
  158. package/packages/model-runtime/src/providers/wenxin/index.ts +8 -3
  159. package/packages/model-runtime/src/providers/xai/index.test.ts +105 -1
  160. package/packages/model-runtime/src/providers/xinference/index.test.ts +70 -1
  161. package/packages/model-runtime/src/providers/zeroone/index.test.ts +327 -3
  162. package/packages/model-runtime/src/providers/zeroone/index.ts +23 -6
  163. package/packages/model-runtime/src/providers/zhipu/index.test.ts +908 -236
  164. package/packages/model-runtime/src/providers/zhipu/index.ts +8 -3
  165. package/packages/model-runtime/src/types/structureOutput.ts +5 -1
  166. package/packages/model-runtime/vitest.config.mts +7 -1
  167. package/packages/types/src/aiChat.ts +20 -2
  168. package/packages/types/src/serverConfig.ts +7 -1
  169. package/packages/types/src/tool/index.ts +1 -0
  170. package/packages/types/src/tool/tool.ts +33 -0
  171. package/packages/types/src/user/settings/image.ts +3 -0
  172. package/packages/types/src/user/settings/index.ts +3 -0
  173. package/src/app/[variants]/(main)/settings/_layout/SettingsContent.tsx +3 -0
  174. package/src/app/[variants]/(main)/settings/hooks/useCategory.tsx +8 -3
  175. package/src/app/[variants]/(main)/settings/image/index.tsx +74 -0
  176. package/src/components/FormInput/FormSliderWithInput.tsx +40 -0
  177. package/src/components/FormInput/index.ts +1 -0
  178. package/src/envs/image.ts +27 -0
  179. package/src/features/Conversation/Messages/Assistant/index.tsx +1 -1
  180. package/src/features/Conversation/Messages/User/index.tsx +2 -2
  181. package/src/hooks/useFetchAiImageConfig.ts +12 -17
  182. package/src/locales/default/setting.ts +8 -0
  183. package/src/server/globalConfig/index.ts +5 -0
  184. package/src/server/routers/lambda/aiChat.ts +2 -0
  185. package/src/store/global/initialState.ts +1 -0
  186. package/src/store/image/slices/generationConfig/action.test.ts +17 -0
  187. package/src/store/image/slices/generationConfig/action.ts +18 -21
  188. package/src/store/image/slices/generationConfig/initialState.ts +3 -2
  189. package/src/store/user/slices/common/action.ts +1 -0
  190. package/src/store/user/slices/settings/selectors/settings.ts +3 -0
@@ -1,11 +1,14 @@
1
1
  // @vitest-environment node
2
- import { InvokeModelWithResponseStreamCommand } from '@aws-sdk/client-bedrock-runtime';
2
+ import {
3
+ InvokeModelCommand,
4
+ InvokeModelWithResponseStreamCommand,
5
+ } from '@aws-sdk/client-bedrock-runtime';
3
6
  import { AgentRuntimeErrorType } from '@lobechat/model-runtime';
4
7
  import { ModelProvider } from 'model-bank';
5
8
  import { Mock, afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
6
9
 
7
10
  import * as debugStreamModule from '../../utils/debugStream';
8
- import { LobeBedrockAI } from './index';
11
+ import { LobeBedrockAI, experimental_buildLlama2Prompt } from './index';
9
12
 
10
13
  const provider = 'bedrock';
11
14
 
@@ -16,6 +19,7 @@ vi.mock('@aws-sdk/client-bedrock-runtime', async (importOriginal) => {
16
19
  const module = await importOriginal();
17
20
  return {
18
21
  ...(module as any),
22
+ InvokeModelCommand: vi.fn(),
19
23
  InvokeModelWithResponseStreamCommand: vi.fn(),
20
24
  };
21
25
  });
@@ -45,6 +49,59 @@ describe('LobeBedrockAI', () => {
45
49
  accessKeySecret: 'test-access-key-secret',
46
50
  });
47
51
  expect(instance).toBeInstanceOf(LobeBedrockAI);
52
+ expect(instance.region).toBe('us-west-2');
53
+ });
54
+
55
+ it('should use default region if not provided', () => {
56
+ const instance = new LobeBedrockAI({
57
+ accessKeyId: 'test-access-key-id',
58
+ accessKeySecret: 'test-access-key-secret',
59
+ });
60
+ expect(instance.region).toBe('us-east-1');
61
+ });
62
+
63
+ it('should correctly initialize with session token', () => {
64
+ const instance = new LobeBedrockAI({
65
+ region: 'us-west-2',
66
+ accessKeyId: 'test-access-key-id',
67
+ accessKeySecret: 'test-access-key-secret',
68
+ sessionToken: 'test-session-token',
69
+ });
70
+ expect(instance).toBeInstanceOf(LobeBedrockAI);
71
+ });
72
+
73
+ it('should throw InvalidBedrockCredentials if accessKeyId is missing', () => {
74
+ expect(() => {
75
+ new LobeBedrockAI({
76
+ accessKeySecret: 'test-access-key-secret',
77
+ });
78
+ }).toThrow(
79
+ expect.objectContaining({
80
+ errorType: AgentRuntimeErrorType.InvalidBedrockCredentials,
81
+ }),
82
+ );
83
+ });
84
+
85
+ it('should throw InvalidBedrockCredentials if accessKeySecret is missing', () => {
86
+ expect(() => {
87
+ new LobeBedrockAI({
88
+ accessKeyId: 'test-access-key-id',
89
+ });
90
+ }).toThrow(
91
+ expect.objectContaining({
92
+ errorType: AgentRuntimeErrorType.InvalidBedrockCredentials,
93
+ }),
94
+ );
95
+ });
96
+
97
+ it('should throw InvalidBedrockCredentials if both credentials are missing', () => {
98
+ expect(() => {
99
+ new LobeBedrockAI({});
100
+ }).toThrow(
101
+ expect.objectContaining({
102
+ errorType: AgentRuntimeErrorType.InvalidBedrockCredentials,
103
+ }),
104
+ );
48
105
  });
49
106
  });
50
107
 
@@ -201,6 +258,83 @@ describe('LobeBedrockAI', () => {
201
258
  expect(result).toBeInstanceOf(Response);
202
259
  });
203
260
 
261
+ it('should handle tools parameter', async () => {
262
+ // Arrange
263
+ const mockStream = new ReadableStream({
264
+ start(controller) {
265
+ controller.enqueue('Hello, world!');
266
+ controller.close();
267
+ },
268
+ });
269
+ const mockResponse = Promise.resolve(mockStream);
270
+ (instance['client'].send as Mock).mockResolvedValue(mockResponse);
271
+
272
+ const tools = [
273
+ {
274
+ function: {
275
+ description: 'Get weather information',
276
+ name: 'get_weather',
277
+ parameters: {
278
+ properties: {
279
+ location: {
280
+ description: 'City name',
281
+ type: 'string',
282
+ },
283
+ },
284
+ required: ['location'],
285
+ type: 'object',
286
+ },
287
+ },
288
+ type: 'function' as const,
289
+ },
290
+ ];
291
+
292
+ // Act
293
+ await instance.chat({
294
+ messages: [{ content: 'Hello', role: 'user' }],
295
+ model: 'anthropic.claude-v2:1',
296
+ temperature: 0.5,
297
+ tools,
298
+ });
299
+
300
+ // Assert
301
+ const callArgs = (InvokeModelWithResponseStreamCommand as any as Mock).mock.calls[0][0];
302
+ const bodyContent = JSON.parse(callArgs.body);
303
+ expect(bodyContent.tools).toBeDefined();
304
+ });
305
+
306
+ it('should use default max_tokens when not provided', async () => {
307
+ // Arrange
308
+ const mockStream = new ReadableStream({
309
+ start(controller) {
310
+ controller.enqueue('Hello, world!');
311
+ controller.close();
312
+ },
313
+ });
314
+ const mockResponse = Promise.resolve(mockStream);
315
+ (instance['client'].send as Mock).mockResolvedValue(mockResponse);
316
+
317
+ // Act
318
+ await instance.chat({
319
+ messages: [{ content: 'Hello', role: 'user' }],
320
+ model: 'anthropic.claude-v2:1',
321
+ temperature: 0,
322
+ });
323
+
324
+ // Assert
325
+ expect(InvokeModelWithResponseStreamCommand).toHaveBeenCalledWith({
326
+ accept: 'application/json',
327
+ body: JSON.stringify({
328
+ anthropic_version: 'bedrock-2023-05-31',
329
+ max_tokens: 4096,
330
+ messages: [{ content: 'Hello', role: 'user' }],
331
+ temperature: 0,
332
+ }),
333
+ contentType: 'application/json',
334
+ modelId: 'anthropic.claude-v2:1',
335
+ });
336
+ });
337
+
204
338
  it('should call Anthropic model without unsupported opions', async () => {
205
339
  // Arrange
206
340
  const mockStream = new ReadableStream({
@@ -457,6 +591,17 @@ describe('LobeBedrockAI', () => {
457
591
  });
458
592
 
459
593
  describe('Llama Model', () => {
594
+ it('should return a Response on successful API call', async () => {
595
+ const result = await instance.chat({
596
+ messages: [{ content: 'Hello', role: 'user' }],
597
+ model: 'meta.llama:1',
598
+ temperature: 0,
599
+ });
600
+
601
+ // Assert
602
+ expect(result).toBeInstanceOf(Response);
603
+ });
604
+
460
605
  it('should call Llama model with valid payload', async () => {
461
606
  // Arrange
462
607
  const mockStream = new ReadableStream({
@@ -538,6 +683,69 @@ describe('LobeBedrockAI', () => {
538
683
  // Clean up
539
684
  delete process.env.DEBUG_BEDROCK_CHAT_COMPLETION;
540
685
  });
686
+
687
+ it('should use default max_tokens (400) when not provided', async () => {
688
+ // Arrange
689
+ const mockStream = new ReadableStream({
690
+ start(controller) {
691
+ controller.enqueue('Hello, world!');
692
+ controller.close();
693
+ },
694
+ });
695
+ const mockResponse = Promise.resolve(mockStream);
696
+ (instance['client'].send as Mock).mockResolvedValue(mockResponse);
697
+
698
+ // Act
699
+ await instance.chat({
700
+ messages: [{ content: 'Hello', role: 'user' }],
701
+ model: 'meta.llama:1',
702
+ temperature: 0,
703
+ });
704
+
705
+ // Assert
706
+ expect(InvokeModelWithResponseStreamCommand).toHaveBeenCalledWith({
707
+ accept: 'application/json',
708
+ body: JSON.stringify({
709
+ max_gen_len: 400,
710
+ prompt: '<s>[INST] Hello [/INST]',
711
+ }),
712
+ contentType: 'application/json',
713
+ modelId: 'meta.llama:1',
714
+ });
715
+ });
716
+
717
+ it('should handle system message in Llama prompt', async () => {
718
+ // Arrange
719
+ const mockStream = new ReadableStream({
720
+ start(controller) {
721
+ controller.enqueue('Hello, world!');
722
+ controller.close();
723
+ },
724
+ });
725
+ const mockResponse = Promise.resolve(mockStream);
726
+ (instance['client'].send as Mock).mockResolvedValue(mockResponse);
727
+
728
+ // Act
729
+ await instance.chat({
730
+ messages: [
731
+ { content: 'You are a helpful assistant', role: 'system' },
732
+ { content: 'Hello', role: 'user' },
733
+ ],
734
+ model: 'meta.llama2-70b-chat-v1',
735
+ temperature: 0,
736
+ });
737
+
738
+ // Assert
739
+ expect(InvokeModelWithResponseStreamCommand).toHaveBeenCalledWith({
740
+ accept: 'application/json',
741
+ body: JSON.stringify({
742
+ max_gen_len: 400,
743
+ prompt: '<s>[INST] <<SYS>>\nYou are a helpful assistant\n<</SYS>>\n\nHello [/INST]',
744
+ }),
745
+ contentType: 'application/json',
746
+ modelId: 'meta.llama2-70b-chat-v1',
747
+ });
748
+ });
541
749
  });
542
750
 
543
751
  it('should call options.callback when provided', async () => {
@@ -558,4 +766,214 @@ describe('LobeBedrockAI', () => {
558
766
  expect(onStart).toHaveBeenCalled();
559
767
  });
560
768
  });
769
+
770
+ describe('embeddings', () => {
771
+ it('should handle single input string', async () => {
772
+ // Arrange
773
+ const mockEmbedding = [0.1, 0.2, 0.3];
774
+ const mockResponse = {
775
+ body: new TextEncoder().encode(JSON.stringify({ embedding: mockEmbedding })),
776
+ };
777
+ (instance['client'].send as Mock).mockResolvedValue(mockResponse);
778
+
779
+ // Act
780
+ const result = await instance.embeddings({
781
+ input: 'test input',
782
+ model: 'amazon.titan-embed-text-v1',
783
+ });
784
+
785
+ // Assert
786
+ expect(result).toHaveLength(1);
787
+ expect(result[0]).toEqual(mockEmbedding);
788
+ expect(InvokeModelCommand).toHaveBeenCalledWith({
789
+ accept: 'application/json',
790
+ body: JSON.stringify({
791
+ inputText: 'test input',
792
+ normalize: true,
793
+ }),
794
+ contentType: 'application/json',
795
+ modelId: 'amazon.titan-embed-text-v1',
796
+ });
797
+ });
798
+
799
+ it('should handle multiple input strings', async () => {
800
+ // Arrange
801
+ const mockEmbedding1 = [0.1, 0.2, 0.3];
802
+ const mockEmbedding2 = [0.4, 0.5, 0.6];
803
+ const mockResponse1 = {
804
+ body: new TextEncoder().encode(JSON.stringify({ embedding: mockEmbedding1 })),
805
+ };
806
+ const mockResponse2 = {
807
+ body: new TextEncoder().encode(JSON.stringify({ embedding: mockEmbedding2 })),
808
+ };
809
+ (instance['client'].send as Mock)
810
+ .mockResolvedValueOnce(mockResponse1)
811
+ .mockResolvedValueOnce(mockResponse2);
812
+
813
+ // Act
814
+ const result = await instance.embeddings({
815
+ input: ['test input 1', 'test input 2'],
816
+ model: 'amazon.titan-embed-text-v1',
817
+ });
818
+
819
+ // Assert
820
+ expect(result).toHaveLength(2);
821
+ expect(result[0]).toEqual(mockEmbedding1);
822
+ expect(result[1]).toEqual(mockEmbedding2);
823
+ expect(InvokeModelCommand).toHaveBeenCalledTimes(2);
824
+ });
825
+
826
+ it('should handle dimensions parameter', async () => {
827
+ // Arrange
828
+ const mockEmbedding = [0.1, 0.2, 0.3];
829
+ const mockResponse = {
830
+ body: new TextEncoder().encode(JSON.stringify({ embedding: mockEmbedding })),
831
+ };
832
+ (instance['client'].send as Mock).mockResolvedValue(mockResponse);
833
+
834
+ // Act
835
+ const result = await instance.embeddings({
836
+ dimensions: 512,
837
+ input: 'test input',
838
+ model: 'amazon.titan-embed-text-v1',
839
+ });
840
+
841
+ // Assert
842
+ expect(result).toHaveLength(1);
843
+ expect(InvokeModelCommand).toHaveBeenCalledWith({
844
+ accept: 'application/json',
845
+ body: JSON.stringify({
846
+ dimensions: 512,
847
+ inputText: 'test input',
848
+ normalize: true,
849
+ }),
850
+ contentType: 'application/json',
851
+ modelId: 'amazon.titan-embed-text-v1',
852
+ });
853
+ });
854
+
855
+ it('should handle abort signal', async () => {
856
+ // Arrange
857
+ const mockEmbedding = [0.1, 0.2, 0.3];
858
+ const mockResponse = {
859
+ body: new TextEncoder().encode(JSON.stringify({ embedding: mockEmbedding })),
860
+ };
861
+ const sendSpy = vi.spyOn(instance['client'], 'send').mockResolvedValue(mockResponse as any);
862
+ const abortController = new AbortController();
863
+
864
+ // Act
865
+ await instance.embeddings(
866
+ {
867
+ input: 'test input',
868
+ model: 'amazon.titan-embed-text-v1',
869
+ },
870
+ { signal: abortController.signal },
871
+ );
872
+
873
+ // Assert
874
+ expect(sendSpy).toHaveBeenCalledWith(expect.any(InvokeModelCommand), {
875
+ abortSignal: abortController.signal,
876
+ });
877
+ });
878
+
879
+ it('should throw AgentRuntimeError on API error', async () => {
880
+ // Arrange
881
+ const errorMessage = 'Embedding API error';
882
+ const errorMetadata = { statusCode: 400 };
883
+ const mockError = new Error(errorMessage);
884
+ (mockError as any).$metadata = errorMetadata;
885
+ (instance['client'].send as Mock).mockRejectedValue(mockError);
886
+
887
+ // Act & Assert
888
+ await expect(
889
+ instance.embeddings({
890
+ input: 'test input',
891
+ model: 'amazon.titan-embed-text-v1',
892
+ }),
893
+ ).rejects.toThrow(
894
+ expect.objectContaining({
895
+ error: {
896
+ body: errorMetadata,
897
+ message: errorMessage,
898
+ type: 'Error',
899
+ },
900
+ errorType: AgentRuntimeErrorType.ProviderBizError,
901
+ provider: ModelProvider.Bedrock,
902
+ region: 'us-west-2',
903
+ }),
904
+ );
905
+ });
906
+ });
907
+
908
+ describe('experimental_buildLlama2Prompt', () => {
909
+ it('should build prompt with user message only', () => {
910
+ const messages = [{ content: 'Hello', role: 'user' }];
911
+ const result = experimental_buildLlama2Prompt(messages);
912
+ expect(result).toBe('<s>[INST] Hello [/INST]');
913
+ });
914
+
915
+ it('should build prompt with system and user messages', () => {
916
+ const messages = [
917
+ { content: 'You are a helpful assistant', role: 'system' },
918
+ { content: 'Hello', role: 'user' },
919
+ ];
920
+ const result = experimental_buildLlama2Prompt(messages);
921
+ expect(result).toBe(
922
+ '<s>[INST] <<SYS>>\nYou are a helpful assistant\n<</SYS>>\n\nHello [/INST]',
923
+ );
924
+ });
925
+
926
+ it('should build prompt with conversation history', () => {
927
+ const messages = [
928
+ { content: 'Hello', role: 'user' },
929
+ { content: 'Hi there!', role: 'assistant' },
930
+ { content: 'How are you?', role: 'user' },
931
+ ];
932
+ const result = experimental_buildLlama2Prompt(messages);
933
+ expect(result).toBe('<s>[INST] Hello [/INST] Hi there!</s><s>[INST] How are you? [/INST]');
934
+ });
935
+
936
+ it('should build prompt with system, user, and assistant messages', () => {
937
+ const messages = [
938
+ { content: 'You are a helpful assistant', role: 'system' },
939
+ { content: 'Hello', role: 'user' },
940
+ { content: 'Hi there!', role: 'assistant' },
941
+ { content: 'How are you?', role: 'user' },
942
+ ];
943
+ const result = experimental_buildLlama2Prompt(messages);
944
+ expect(result).toBe(
945
+ '<s>[INST] <<SYS>>\nYou are a helpful assistant\n<</SYS>>\n\nHello [/INST] Hi there!</s><s>[INST] How are you? [/INST]',
946
+ );
947
+ });
948
+
949
+ it('should trim user messages', () => {
950
+ const messages = [{ content: ' Hello ', role: 'user' }];
951
+ const result = experimental_buildLlama2Prompt(messages);
952
+ expect(result).toBe('<s>[INST] Hello [/INST]');
953
+ });
954
+
955
+ it('should throw error for function messages', () => {
956
+ const messages = [{ content: 'function call', role: 'function' }];
957
+ expect(() => experimental_buildLlama2Prompt(messages)).toThrow(
958
+ 'Llama 2 does not support function calls.',
959
+ );
960
+ });
961
+
962
+ it('should throw error for invalid role', () => {
963
+ const messages = [{ content: 'invalid', role: 'invalid' }];
964
+ expect(() => experimental_buildLlama2Prompt(messages)).toThrow(
965
+ 'Invalid message role: invalid',
966
+ );
967
+ });
968
+
969
+ it('should throw error for system message not at index 0', () => {
970
+ const messages = [
971
+ { content: 'Hello', role: 'user' },
972
+ { content: 'You are a helpful assistant', role: 'system' },
973
+ ];
974
+ expect(() => experimental_buildLlama2Prompt(messages)).toThrow(
975
+ 'Invalid message role: system',
976
+ );
977
+ });
978
+ });
561
979
  });