@lobehub/chat 1.124.1 → 1.124.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 (119) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/changelog/v1.json +18 -0
  3. package/package.json +1 -1
  4. package/packages/const/package.json +3 -1
  5. package/packages/const/src/analytics.ts +1 -1
  6. package/packages/const/src/desktop.ts +3 -2
  7. package/packages/const/src/discover.ts +3 -2
  8. package/packages/const/src/guide.ts +2 -2
  9. package/packages/const/src/hotkeys.ts +1 -1
  10. package/packages/const/src/index.ts +2 -0
  11. package/packages/const/src/settings/common.ts +1 -1
  12. package/packages/const/src/settings/genUserLLMConfig.test.ts +1 -2
  13. package/packages/const/src/settings/genUserLLMConfig.ts +1 -2
  14. package/packages/const/src/settings/hotkey.ts +3 -2
  15. package/packages/const/src/settings/index.ts +1 -3
  16. package/packages/const/src/settings/knowledge.ts +1 -1
  17. package/packages/const/src/settings/llm.ts +2 -4
  18. package/packages/const/src/settings/systemAgent.ts +1 -5
  19. package/packages/const/src/settings/tts.ts +1 -1
  20. package/packages/const/src/trace.ts +1 -1
  21. package/packages/const/src/url.ts +2 -2
  22. package/packages/const/src/user.ts +1 -2
  23. package/packages/database/src/client/db.test.ts +19 -13
  24. package/packages/electron-server-ipc/src/ipcClient.test.ts +783 -1
  25. package/packages/file-loaders/src/loadFile.test.ts +61 -0
  26. package/packages/file-loaders/src/utils/isTextReadableFile.test.ts +43 -0
  27. package/packages/file-loaders/src/utils/parser-utils.test.ts +155 -0
  28. package/packages/model-runtime/package.json +2 -1
  29. package/packages/model-runtime/src/ai21/index.test.ts +2 -2
  30. package/packages/model-runtime/src/ai360/index.test.ts +2 -2
  31. package/packages/model-runtime/src/akashchat/index.test.ts +19 -0
  32. package/packages/model-runtime/src/anthropic/index.test.ts +1 -2
  33. package/packages/model-runtime/src/baichuan/index.test.ts +1 -2
  34. package/packages/model-runtime/src/bedrock/index.test.ts +1 -2
  35. package/packages/model-runtime/src/bfl/createImage.test.ts +1 -2
  36. package/packages/model-runtime/src/bfl/index.test.ts +1 -2
  37. package/packages/model-runtime/src/cloudflare/index.test.ts +1 -2
  38. package/packages/model-runtime/src/cohere/index.test.ts +19 -0
  39. package/packages/model-runtime/src/deepseek/index.test.ts +2 -2
  40. package/packages/model-runtime/src/fireworksai/index.test.ts +2 -2
  41. package/packages/model-runtime/src/giteeai/index.test.ts +2 -2
  42. package/packages/model-runtime/src/github/index.test.ts +2 -2
  43. package/packages/model-runtime/src/google/createImage.test.ts +1 -2
  44. package/packages/model-runtime/src/google/index.test.ts +1 -1
  45. package/packages/model-runtime/src/groq/index.test.ts +2 -3
  46. package/packages/model-runtime/src/higress/index.ts +2 -3
  47. package/packages/model-runtime/src/huggingface/index.test.ts +40 -0
  48. package/packages/model-runtime/src/hunyuan/index.test.ts +2 -3
  49. package/packages/model-runtime/src/internlm/index.test.ts +2 -2
  50. package/packages/model-runtime/src/jina/index.test.ts +19 -0
  51. package/packages/model-runtime/src/lmstudio/index.test.ts +2 -2
  52. package/packages/model-runtime/src/minimax/index.test.ts +19 -0
  53. package/packages/model-runtime/src/mistral/index.test.ts +2 -3
  54. package/packages/model-runtime/src/modelscope/index.test.ts +19 -0
  55. package/packages/model-runtime/src/moonshot/index.test.ts +1 -2
  56. package/packages/model-runtime/src/nebius/index.test.ts +19 -0
  57. package/packages/model-runtime/src/novita/index.test.ts +3 -4
  58. package/packages/model-runtime/src/nvidia/index.test.ts +19 -0
  59. package/packages/model-runtime/src/openrouter/index.test.ts +2 -3
  60. package/packages/model-runtime/src/perplexity/index.test.ts +2 -3
  61. package/packages/model-runtime/src/ppio/index.test.ts +3 -4
  62. package/packages/model-runtime/src/qwen/index.test.ts +2 -2
  63. package/packages/model-runtime/src/sambanova/index.test.ts +19 -0
  64. package/packages/model-runtime/src/search1api/index.test.ts +19 -0
  65. package/packages/model-runtime/src/sensenova/index.test.ts +2 -2
  66. package/packages/model-runtime/src/spark/index.test.ts +2 -2
  67. package/packages/model-runtime/src/stepfun/index.test.ts +2 -2
  68. package/packages/model-runtime/src/taichu/index.test.ts +4 -5
  69. package/packages/model-runtime/src/tencentcloud/index.test.ts +1 -1
  70. package/packages/model-runtime/src/togetherai/index.test.ts +1 -2
  71. package/packages/model-runtime/src/upstage/index.test.ts +1 -2
  72. package/packages/model-runtime/src/utils/openaiCompatibleFactory/index.test.ts +9 -7
  73. package/packages/model-runtime/src/utils/streams/anthropic.ts +2 -2
  74. package/packages/model-runtime/src/utils/streams/openai/openai.ts +20 -13
  75. package/packages/model-runtime/src/utils/streams/openai/responsesStream.test.ts +1 -2
  76. package/packages/model-runtime/src/utils/streams/openai/responsesStream.ts +2 -2
  77. package/packages/model-runtime/src/utils/streams/protocol.ts +2 -2
  78. package/packages/model-runtime/src/wenxin/index.test.ts +2 -3
  79. package/packages/model-runtime/src/xai/index.test.ts +2 -2
  80. package/packages/model-runtime/src/zeroone/index.test.ts +1 -2
  81. package/packages/model-runtime/src/zhipu/index.test.ts +2 -3
  82. package/packages/model-runtime/vitest.config.mts +0 -7
  83. package/packages/types/src/discover/index.ts +0 -8
  84. package/packages/types/src/index.ts +4 -0
  85. package/packages/types/src/message/base.ts +1 -1
  86. package/packages/types/src/openai/chat.ts +2 -3
  87. package/packages/types/src/tool/index.ts +1 -0
  88. package/packages/types/src/tool/tool.ts +1 -1
  89. package/packages/types/src/user/settings/index.ts +1 -2
  90. package/packages/utils/package.json +2 -1
  91. package/packages/utils/src/_deprecated/parseModels.test.ts +1 -1
  92. package/packages/utils/src/_deprecated/parseModels.ts +1 -1
  93. package/packages/utils/src/client/topic.test.ts +1 -2
  94. package/packages/utils/src/client/topic.ts +1 -2
  95. package/packages/utils/src/electron/desktopRemoteRPCFetch.ts +1 -1
  96. package/packages/utils/src/fetch/fetchSSE.ts +7 -8
  97. package/packages/utils/src/fetch/parseError.ts +1 -3
  98. package/packages/utils/src/format.test.ts +1 -2
  99. package/packages/utils/src/index.ts +1 -0
  100. package/packages/utils/src/toolManifest.ts +1 -2
  101. package/packages/utils/src/trace.ts +1 -1
  102. package/packages/utils/vitest.config.mts +1 -2
  103. package/packages/web-crawler/src/__tests__/urlRules.test.ts +275 -0
  104. package/packages/web-crawler/src/crawImpl/__tests__/exa.test.ts +269 -0
  105. package/packages/web-crawler/src/crawImpl/__tests__/firecrawl.test.ts +284 -0
  106. package/packages/web-crawler/src/crawImpl/__tests__/naive.test.ts +234 -0
  107. package/packages/web-crawler/src/crawImpl/__tests__/tavily.test.ts +359 -0
  108. package/packages/web-crawler/src/utils/__tests__/errorType.test.ts +217 -0
  109. package/packages/web-crawler/vitest.config.mts +3 -0
  110. package/src/app/(backend)/webapi/models/[provider]/pull/route.ts +0 -2
  111. package/src/app/(backend)/webapi/models/[provider]/route.ts +0 -2
  112. package/src/app/(backend)/webapi/text-to-image/[provider]/route.ts +0 -2
  113. package/src/app/(backend)/webapi/trace/route.ts +0 -2
  114. package/src/components/Thinking/index.tsx +2 -3
  115. package/src/features/ChatInput/StoreUpdater.tsx +2 -0
  116. package/src/libs/traces/index.ts +1 -1
  117. package/src/server/modules/ModelRuntime/trace.ts +1 -2
  118. package/packages/const/src/settings/sync.ts +0 -5
  119. package/packages/model-runtime/src/openrouter/__snapshots__/index.test.ts.snap +0 -113
@@ -0,0 +1,61 @@
1
+ // @vitest-environment node
2
+ import * as fsPromises from 'node:fs/promises';
3
+ import path from 'node:path';
4
+ import { describe, expect, it, vi } from 'vitest';
5
+
6
+ import { loadFile } from './loadFile';
7
+
8
+ const fixtures = path.join(__dirname, '../test/fixtures');
9
+ const fp = (name: string) => path.join(fixtures, name);
10
+
11
+ describe('loadFile', () => {
12
+ it('loads text-like files via TextLoader and aggregates', async () => {
13
+ const file = fp('test.txt');
14
+ const doc = await loadFile(file);
15
+ expect(doc.fileType).toBe('txt');
16
+ expect(doc.filename).toBe('test.txt');
17
+ expect(doc.source).toBe(file);
18
+ expect(doc.content).toContain('This is line 1.');
19
+ expect(doc.pages?.[0].metadata.lineNumberStart).toBe(1);
20
+ expect(doc.totalCharCount).toBeGreaterThan(0);
21
+ expect(doc.totalLineCount).toBeGreaterThan(0);
22
+ });
23
+
24
+ it('loads pdf files via PdfLoader and aggregates', async () => {
25
+ const file = fp('test.pdf');
26
+ const doc = await loadFile(file);
27
+ expect(doc.fileType).toBe('pdf');
28
+ expect(doc.filename).toBe('test.pdf');
29
+ expect(doc.source).toBe(file);
30
+ expect(doc.content).toContain('123');
31
+ expect(doc.pages && doc.pages.length).toBeGreaterThan(0);
32
+ });
33
+
34
+ it('returns error page when fs.stat fails', async () => {
35
+ const doc = await loadFile('/not/exists.xyz');
36
+ expect(doc.pages).toHaveLength(1);
37
+ expect(doc.pages?.[0].metadata.error).toContain('Failed to access file stats:');
38
+ expect(doc.metadata.error).toContain('Failed to access file stats:');
39
+ });
40
+
41
+ it('falls back to TextLoader for unsupported type and warns', async () => {
42
+ const warn = vi.spyOn(console, 'warn').mockImplementation(() => {});
43
+ const doc = await loadFile(fp('test.epub')); // epub is unsupported in current mapping
44
+ expect(warn).toHaveBeenCalled();
45
+ expect(doc.content.length).toBeGreaterThanOrEqual(0);
46
+ warn.mockRestore();
47
+ });
48
+
49
+ it('allows overriding metadata via second parameter', async () => {
50
+ const file = fp('test.txt');
51
+ const override = {
52
+ source: 's3://bucket/key',
53
+ filename: 'override.txt',
54
+ fileType: 'custom',
55
+ };
56
+ const doc = await loadFile(file, override);
57
+ expect(doc.source).toBe('s3://bucket/key');
58
+ expect(doc.filename).toBe('override.txt');
59
+ expect(doc.fileType).toBe('custom');
60
+ });
61
+ });
@@ -0,0 +1,43 @@
1
+ import { describe, expect, it } from 'vitest';
2
+
3
+ import { isTextReadableFile } from './isTextReadableFile';
4
+
5
+ describe('isTextReadableFile', () => {
6
+ it('should return true for common text-readable types', () => {
7
+ const positives = [
8
+ 'txt',
9
+ 'md',
10
+ 'mdx',
11
+ 'json',
12
+ 'yaml',
13
+ 'yml',
14
+ 'csv',
15
+ 'html',
16
+ 'css',
17
+ 'js',
18
+ 'ts',
19
+ 'py',
20
+ 'log',
21
+ 'sql',
22
+ 'patch',
23
+ 'diff',
24
+ ];
25
+
26
+ for (const ext of positives) {
27
+ expect(isTextReadableFile(ext)).toBe(true);
28
+ }
29
+ });
30
+
31
+ it('should be case-insensitive', () => {
32
+ expect(isTextReadableFile('Md')).toBe(true);
33
+ expect(isTextReadableFile('JSON')).toBe(true);
34
+ expect(isTextReadableFile('YML')).toBe(true);
35
+ });
36
+
37
+ it('should return false for non text-readable or binary types', () => {
38
+ const negatives = ['pdf', 'docx', 'xlsx', 'pptx', 'png', 'jpg', 'gif'];
39
+ for (const ext of negatives) {
40
+ expect(isTextReadableFile(ext)).toBe(false);
41
+ }
42
+ });
43
+ });
@@ -0,0 +1,155 @@
1
+ // @vitest-environment node
2
+ import { beforeEach, describe, expect, it, vi } from 'vitest';
3
+
4
+ import { type ExtractedFile, extractFiles, parseString } from './parser-utils';
5
+
6
+ describe('parser-utils', () => {
7
+ describe('parseString', () => {
8
+ it('should parse valid XML string into XMLDocument', () => {
9
+ const xml = '<root><item id="1">hello</item></root>';
10
+ const doc = parseString(xml);
11
+
12
+ // The parsed document should contain the root and item node
13
+ const root = (doc as any).getElementsByTagName('root')[0];
14
+ expect(root).toBeDefined();
15
+ const item = (doc as any).getElementsByTagName('item')[0];
16
+ expect(item).toBeDefined();
17
+ expect(item.getAttribute('id')).toBe('1');
18
+ expect(item.textContent).toBe('hello');
19
+ });
20
+ });
21
+
22
+ describe('extractFiles', () => {
23
+ beforeEach(() => {
24
+ vi.resetModules();
25
+ });
26
+
27
+ it('should reject on invalid input type', async () => {
28
+ // @ts-expect-error intentional wrong type
29
+ await expect(extractFiles(123, () => true)).rejects.toThrow(
30
+ '[OfficeParser]: Invalid input type',
31
+ );
32
+ });
33
+
34
+ it('should read entries via yauzl.fromBuffer and filter matches', async () => {
35
+ // Arrange: build a fake zipfile object with two file entries and one directory
36
+ const entryHandlers: Record<string, (cb: () => void) => void> = {};
37
+ const listeners: Record<string, Function[]> = { entry: [], end: [], error: [] };
38
+ let idx = 0;
39
+ const sequence = [
40
+ { fileName: 'folder/' },
41
+ { fileName: 'keep.txt' },
42
+ { fileName: 'skip.bin' },
43
+ ];
44
+
45
+ const emit = (name: string, payload?: any) =>
46
+ (listeners[name] || []).forEach((fn) => fn(payload));
47
+
48
+ const fakeZipfile = {
49
+ readEntry: vi.fn().mockImplementation(() => {
50
+ queueMicrotask(() => {
51
+ if (idx < sequence.length) {
52
+ const entry = sequence[idx++];
53
+ emit('entry', entry as any);
54
+ } else {
55
+ emit('end');
56
+ }
57
+ });
58
+ }),
59
+ openReadStream: vi.fn((entry: any, cb: (err: any, stream?: any) => void) => {
60
+ if (entry.fileName === 'keep.txt') {
61
+ // Provide a minimal readable stream compatible with concat-stream
62
+ const chunks: any[] = [];
63
+ const stream = {
64
+ pipe(destination: any) {
65
+ // emulate piping to concat-stream writable
66
+ if (typeof destination.end === 'function') {
67
+ destination.end(Buffer.from('hello world'));
68
+ }
69
+ return destination;
70
+ },
71
+ on: vi.fn(),
72
+ };
73
+ cb(null, stream);
74
+ } else if (entry.fileName === 'skip.bin') {
75
+ // This entry should be skipped by filter, so not invoked
76
+ cb(null, undefined as any);
77
+ }
78
+ }),
79
+ on: vi.fn((evt: string, handler: Function) => {
80
+ listeners[evt] = listeners[evt] || [];
81
+ listeners[evt].push(handler);
82
+ }),
83
+ close: vi.fn(),
84
+ } as any;
85
+
86
+ // Mock yauzl.fromBuffer to pass back our fake zipfile
87
+ vi.doMock('yauzl', () => ({
88
+ default: {
89
+ fromBuffer: (_buf: Buffer, _opts: any, cb: (err: any, zf?: any) => void) =>
90
+ cb(null, fakeZipfile),
91
+ open: vi.fn(),
92
+ },
93
+ }));
94
+
95
+ // Re-import module to use mocked yauzl
96
+ const { extractFiles: mockedExtractFiles } = await import('./parser-utils');
97
+
98
+ const files: ExtractedFile[] = await mockedExtractFiles(Buffer.from('zip'), (name) =>
99
+ name.endsWith('.txt'),
100
+ );
101
+ expect(files).toEqual([{ path: 'keep.txt', content: 'hello world' }]);
102
+ });
103
+
104
+ it('should open zip by file path when input is string', async () => {
105
+ const listeners: Record<string, Function[]> = { entry: [], end: [], error: [] };
106
+ let idx = 0;
107
+ const entries = [{ fileName: 'keep.txt' }];
108
+ const emit2 = (name: string, payload?: any) =>
109
+ (listeners[name] || []).forEach((fn) => fn(payload));
110
+
111
+ const fakeZipfile = {
112
+ readEntry: vi.fn().mockImplementation(() => {
113
+ queueMicrotask(() => {
114
+ if (idx < entries.length) {
115
+ const entry = entries[idx++];
116
+ emit2('entry', entry as any);
117
+ } else {
118
+ emit2('end');
119
+ }
120
+ });
121
+ }),
122
+ openReadStream: vi.fn((entry: any, cb: (err: any, stream?: any) => void) => {
123
+ const stream = {
124
+ pipe(destination: any) {
125
+ if (typeof destination.end === 'function') {
126
+ destination.end(Buffer.from('A'));
127
+ }
128
+ return destination;
129
+ },
130
+ on: vi.fn(),
131
+ };
132
+ cb(null, stream);
133
+ }),
134
+ on: vi.fn((evt: string, handler: Function) => {
135
+ listeners[evt] = listeners[evt] || [];
136
+ listeners[evt].push(handler);
137
+ }),
138
+ close: vi.fn(),
139
+ } as any;
140
+
141
+ vi.doMock('yauzl', () => ({
142
+ default: {
143
+ fromBuffer: vi.fn(),
144
+ open: (_path: string, _opts: any, cb: (err: any, zf?: any) => void) =>
145
+ cb(null, fakeZipfile),
146
+ },
147
+ }));
148
+
149
+ const { extractFiles: mockedExtractFiles } = await import('./parser-utils');
150
+
151
+ const files = await mockedExtractFiles('/tmp/file.zip', (name) => name === 'keep.txt');
152
+ expect(files).toEqual([{ path: 'keep.txt', content: 'A' }]);
153
+ });
154
+ });
155
+ });
@@ -8,7 +8,8 @@
8
8
  },
9
9
  "scripts": {
10
10
  "test": "vitest",
11
- "test:coverage": "vitest --coverage"
11
+ "test:coverage": "vitest --coverage",
12
+ "test:update": "vitest -u"
12
13
  },
13
14
  "dependencies": {
14
15
  "@aws-sdk/client-bedrock-runtime": "^3.862.0",
@@ -1,7 +1,7 @@
1
1
  // @vitest-environment node
2
- import { ModelProvider } from '@/libs/model-runtime';
3
- import { testProvider } from '@/libs/model-runtime/providerTestUtils';
2
+ import { ModelProvider } from '@lobechat/model-runtime';
4
3
 
4
+ import { testProvider } from '../providerTestUtils';
5
5
  import { LobeAi21AI } from './index';
6
6
 
7
7
  testProvider({
@@ -1,7 +1,7 @@
1
1
  // @vitest-environment node
2
- import { ModelProvider } from '@/libs/model-runtime';
3
- import { testProvider } from '@/libs/model-runtime/providerTestUtils';
2
+ import { ModelProvider } from '@lobechat/model-runtime';
4
3
 
4
+ import { testProvider } from '../providerTestUtils';
5
5
  import { LobeAi360AI } from './index';
6
6
 
7
7
  testProvider({
@@ -0,0 +1,19 @@
1
+ // @vitest-environment node
2
+ import { ModelProvider } from '@lobechat/model-runtime';
3
+
4
+ import { testProvider } from '../providerTestUtils';
5
+ import { LobeAkashChatAI } from './index';
6
+
7
+ const provider = ModelProvider.AkashChat;
8
+ const defaultBaseURL = 'https://chatapi.akash.network/api/v1';
9
+
10
+ testProvider({
11
+ Runtime: LobeAkashChatAI,
12
+ provider,
13
+ defaultBaseURL,
14
+ chatDebugEnv: 'DEBUG_AKASH_CHAT_COMPLETION',
15
+ chatModel: 'llama-3.1-8b-instruct',
16
+ test: {
17
+ skipAPICall: true,
18
+ },
19
+ });
@@ -1,8 +1,7 @@
1
1
  // @vitest-environment node
2
+ import { ChatCompletionTool, ChatStreamPayload } from '@lobechat/model-runtime';
2
3
  import { Mock, afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
3
4
 
4
- import { ChatCompletionTool, ChatStreamPayload } from '@/libs/model-runtime';
5
-
6
5
  import * as anthropicHelpers from '../utils/anthropicHelpers';
7
6
  import * as debugStreamModule from '../utils/debugStream';
8
7
  import { LobeAnthropicAI } from './index';
@@ -1,8 +1,7 @@
1
1
  // @vitest-environment node
2
+ import { LobeOpenAICompatibleRuntime, ModelProvider } from '@lobechat/model-runtime';
2
3
  import { Mock, afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
3
4
 
4
- import { LobeOpenAICompatibleRuntime, ModelProvider } from '@/libs/model-runtime';
5
-
6
5
  import { testProvider } from '../providerTestUtils';
7
6
  import { LobeBaichuanAI } from './index';
8
7
 
@@ -1,9 +1,8 @@
1
1
  // @vitest-environment node
2
2
  import { InvokeModelWithResponseStreamCommand } from '@aws-sdk/client-bedrock-runtime';
3
+ import { AgentRuntimeErrorType, ModelProvider } from '@lobechat/model-runtime';
3
4
  import { Mock, afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
4
5
 
5
- import { AgentRuntimeErrorType, ModelProvider } from '@/libs/model-runtime';
6
-
7
6
  import * as debugStreamModule from '../utils/debugStream';
8
7
  import { LobeBedrockAI } from './index';
9
8
 
@@ -1,8 +1,7 @@
1
1
  // @vitest-environment node
2
2
  import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
3
3
 
4
- import { CreateImagePayload } from '@/libs/model-runtime/types/image';
5
-
4
+ import { CreateImagePayload } from '../types/image';
6
5
  import { createBflImage } from './createImage';
7
6
  import { BflStatusResponse } from './types';
8
7
 
@@ -1,8 +1,7 @@
1
1
  // @vitest-environment node
2
2
  import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
3
3
 
4
- import { CreateImagePayload } from '@/libs/model-runtime/types/image';
5
-
4
+ import { CreateImagePayload } from '../types/image';
6
5
  import { LobeBflAI } from './index';
7
6
 
8
7
  // Mock the createBflImage function
@@ -1,8 +1,7 @@
1
1
  // @vitest-environment node
2
+ import { ChatCompletionTool } from '@lobechat/model-runtime';
2
3
  import { Mock, afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
3
4
 
4
- import { ChatCompletionTool } from '@/libs/model-runtime';
5
-
6
5
  import * as debugStreamModule from '../utils/debugStream';
7
6
  import { LobeCloudflareAI } from './index';
8
7
 
@@ -0,0 +1,19 @@
1
+ // @vitest-environment node
2
+ import { ModelProvider } from '@lobechat/model-runtime';
3
+
4
+ import { testProvider } from '../providerTestUtils';
5
+ import { LobeCohereAI } from './index';
6
+
7
+ const provider = ModelProvider.Cohere;
8
+ const defaultBaseURL = 'https://api.cohere.ai/compatibility/v1';
9
+
10
+ testProvider({
11
+ Runtime: LobeCohereAI,
12
+ provider,
13
+ defaultBaseURL,
14
+ chatDebugEnv: 'DEBUG_COHERE_CHAT_COMPLETION',
15
+ chatModel: 'command-r7b',
16
+ test: {
17
+ skipAPICall: true,
18
+ },
19
+ });
@@ -1,7 +1,7 @@
1
1
  // @vitest-environment node
2
- import { ModelProvider } from '@/libs/model-runtime';
3
- import { testProvider } from '@/libs/model-runtime/providerTestUtils';
2
+ import { ModelProvider } from '@lobechat/model-runtime';
4
3
 
4
+ import { testProvider } from '../providerTestUtils';
5
5
  import { LobeDeepSeekAI } from './index';
6
6
 
7
7
  const provider = ModelProvider.DeepSeek;
@@ -1,7 +1,7 @@
1
1
  // @vitest-environment node
2
- import { ModelProvider } from '@/libs/model-runtime';
3
- import { testProvider } from '@/libs/model-runtime/providerTestUtils';
2
+ import { ModelProvider } from '@lobechat/model-runtime';
4
3
 
4
+ import { testProvider } from '../providerTestUtils';
5
5
  import { LobeFireworksAI } from './index';
6
6
 
7
7
  const provider = ModelProvider.FireworksAI;
@@ -1,7 +1,7 @@
1
1
  // @vitest-environment node
2
- import { ModelProvider } from '@/libs/model-runtime';
3
- import { testProvider } from '@/libs/model-runtime/providerTestUtils';
2
+ import { ModelProvider } from '@lobechat/model-runtime';
4
3
 
4
+ import { testProvider } from '../providerTestUtils';
5
5
  import { LobeGiteeAI } from './index';
6
6
 
7
7
  testProvider({
@@ -1,7 +1,7 @@
1
1
  // @vitest-environment node
2
- import { ModelProvider } from '@/libs/model-runtime';
3
- import { testProvider } from '@/libs/model-runtime/providerTestUtils';
2
+ import { ModelProvider } from '@lobechat/model-runtime';
4
3
 
4
+ import { testProvider } from '../providerTestUtils';
5
5
  import { LobeGithubAI } from './index';
6
6
 
7
7
  testProvider({
@@ -2,8 +2,7 @@
2
2
  import { GoogleGenAI } from '@google/genai';
3
3
  import { beforeEach, describe, expect, it, vi } from 'vitest';
4
4
 
5
- import { CreateImagePayload } from '@/libs/model-runtime/types/image';
6
-
5
+ import { CreateImagePayload } from '../types/image';
7
6
  import * as imageToBase64Module from '../utils/imageToBase64';
8
7
  import { createGoogleImage } from './createImage';
9
8
 
@@ -1,9 +1,9 @@
1
1
  // @vitest-environment edge-runtime
2
2
  import { GenerateContentResponse, Tool } from '@google/genai';
3
+ import { OpenAIChatMessage } from '@lobechat/model-runtime';
3
4
  import OpenAI from 'openai';
4
5
  import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
5
6
 
6
- import { OpenAIChatMessage } from '@/libs/model-runtime';
7
7
  import { ChatStreamPayload } from '@/types/openai/chat';
8
8
 
9
9
  import * as debugStreamModule from '../utils/debugStream';
@@ -1,9 +1,8 @@
1
1
  // @vitest-environment node
2
+ import { LobeOpenAICompatibleRuntime } from '@lobechat/model-runtime';
2
3
  import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
3
4
 
4
- import { LobeOpenAICompatibleRuntime } from '@/libs/model-runtime';
5
- import { testProvider } from '@/libs/model-runtime/providerTestUtils';
6
-
5
+ import { testProvider } from '../providerTestUtils';
7
6
  import { LobeGroq } from './index';
8
7
 
9
8
  testProvider({
@@ -1,6 +1,5 @@
1
- import { uniqueId } from 'lodash-es';
2
-
3
- import type { ChatModelCard } from '@/types/llm';
1
+ import type { ChatModelCard } from '@lobechat/types';
2
+ import uniqueId from 'lodash-es/uniqueId';
4
3
 
5
4
  import { ModelProvider } from '../types';
6
5
  import { createOpenAICompatibleRuntime } from '../utils/openaiCompatibleFactory';
@@ -0,0 +1,40 @@
1
+ // @vitest-environment node
2
+ import { ModelProvider } from '@lobechat/model-runtime';
3
+ import { beforeEach, describe, expect, it, vi } from 'vitest';
4
+
5
+ import { LobeHuggingFaceAI } from './index';
6
+
7
+ describe('LobeHuggingFaceAI', () => {
8
+ let instance: any;
9
+
10
+ beforeEach(() => {
11
+ instance = new LobeHuggingFaceAI({ apiKey: 'test' });
12
+
13
+ const mockAsyncIterable = {
14
+ async *[Symbol.asyncIterator]() {
15
+ yield { choices: [] } as any;
16
+ },
17
+ } as any;
18
+
19
+ // mock custom client's chatCompletionStream
20
+ instance['client'] = {
21
+ chatCompletionStream: vi.fn().mockReturnValue(mockAsyncIterable),
22
+ } as any;
23
+ });
24
+
25
+ it('should initialize and return StreamingTextResponse on chat', async () => {
26
+ const res = await instance.chat({
27
+ messages: [{ role: 'user', content: 'hello' }],
28
+ model: 'meta-llama/Meta-Llama-3.1-8B-Instruct',
29
+ temperature: 0,
30
+ stream: true,
31
+ });
32
+
33
+ expect(res).toBeInstanceOf(Response);
34
+ });
35
+
36
+ it('should set provider id properly', async () => {
37
+ // provider id 用于错误封装等,这里验证暴露 id 一致
38
+ expect(ModelProvider.HuggingFace).toBe('huggingface');
39
+ });
40
+ });
@@ -1,9 +1,8 @@
1
1
  // @vitest-environment node
2
+ import { LobeOpenAICompatibleRuntime, ModelProvider } from '@lobechat/model-runtime';
2
3
  import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
3
4
 
4
- import { LobeOpenAICompatibleRuntime, ModelProvider } from '@/libs/model-runtime';
5
- import { testProvider } from '@/libs/model-runtime/providerTestUtils';
6
-
5
+ import { testProvider } from '../providerTestUtils';
7
6
  import { LobeHunyuanAI } from './index';
8
7
 
9
8
  testProvider({
@@ -1,7 +1,7 @@
1
1
  // @vitest-environment node
2
- import { ModelProvider } from '@/libs/model-runtime';
3
- import { testProvider } from '@/libs/model-runtime/providerTestUtils';
2
+ import { ModelProvider } from '@lobechat/model-runtime';
4
3
 
4
+ import { testProvider } from '../providerTestUtils';
5
5
  import { LobeInternLMAI } from './index';
6
6
 
7
7
  testProvider({
@@ -0,0 +1,19 @@
1
+ // @vitest-environment node
2
+ import { ModelProvider } from '@lobechat/model-runtime';
3
+
4
+ import { testProvider } from '../providerTestUtils';
5
+ import { LobeJinaAI } from './index';
6
+
7
+ const provider = ModelProvider.Jina;
8
+ const defaultBaseURL = 'https://deepsearch.jina.ai/v1';
9
+
10
+ testProvider({
11
+ Runtime: LobeJinaAI,
12
+ provider,
13
+ defaultBaseURL,
14
+ chatDebugEnv: 'DEBUG_JINA_CHAT_COMPLETION',
15
+ chatModel: 'jina-embeddings-v3',
16
+ test: {
17
+ skipAPICall: true,
18
+ },
19
+ });
@@ -1,7 +1,7 @@
1
1
  // @vitest-environment node
2
- import { ModelProvider } from '@/libs/model-runtime';
3
- import { testProvider } from '@/libs/model-runtime/providerTestUtils';
2
+ import { ModelProvider } from '@lobechat/model-runtime';
4
3
 
4
+ import { testProvider } from '../providerTestUtils';
5
5
  import { LobeLMStudioAI } from './index';
6
6
 
7
7
  const provider = ModelProvider.LMStudio;
@@ -0,0 +1,19 @@
1
+ // @vitest-environment node
2
+ import { ModelProvider } from '@lobechat/model-runtime';
3
+
4
+ import { testProvider } from '../providerTestUtils';
5
+ import { LobeMinimaxAI } from './index';
6
+
7
+ const provider = ModelProvider.Minimax;
8
+ const defaultBaseURL = 'https://api.minimax.chat/v1';
9
+
10
+ testProvider({
11
+ Runtime: LobeMinimaxAI,
12
+ provider,
13
+ defaultBaseURL,
14
+ chatDebugEnv: 'DEBUG_MINIMAX_CHAT_COMPLETION',
15
+ chatModel: 'abab6.5s-chat',
16
+ test: {
17
+ skipAPICall: true,
18
+ },
19
+ });
@@ -1,9 +1,8 @@
1
1
  // @vitest-environment node
2
+ import { LobeOpenAICompatibleRuntime } from '@lobechat/model-runtime';
2
3
  import { Mock, afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
3
4
 
4
- import { LobeOpenAICompatibleRuntime } from '@/libs/model-runtime';
5
- import { testProvider } from '@/libs/model-runtime/providerTestUtils';
6
-
5
+ import { testProvider } from '../providerTestUtils';
7
6
  import { LobeMistralAI } from './index';
8
7
 
9
8
  testProvider({
@@ -0,0 +1,19 @@
1
+ // @vitest-environment node
2
+ import { ModelProvider } from '@lobechat/model-runtime';
3
+
4
+ import { testProvider } from '../providerTestUtils';
5
+ import { LobeModelScopeAI } from './index';
6
+
7
+ const provider = ModelProvider.ModelScope;
8
+ const defaultBaseURL = 'https://api-inference.modelscope.cn/v1';
9
+
10
+ testProvider({
11
+ Runtime: LobeModelScopeAI,
12
+ provider,
13
+ defaultBaseURL,
14
+ chatDebugEnv: 'DEBUG_MODELSCOPE_CHAT_COMPLETION',
15
+ chatModel: 'qwen2-7b-instruct',
16
+ test: {
17
+ skipAPICall: true,
18
+ },
19
+ });
@@ -1,6 +1,5 @@
1
1
  // @vitest-environment node
2
- import { testProvider } from '@/libs/model-runtime/providerTestUtils';
3
-
2
+ import { testProvider } from '../providerTestUtils';
4
3
  import { LobeMoonshotAI } from './index';
5
4
 
6
5
  const provider = 'moonshot';