@lobehub/chat 1.68.8 → 1.68.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +50 -0
- package/changelog/v1.json +18 -0
- package/docs/usage/providers/ppio.mdx +5 -5
- package/docs/usage/providers/ppio.zh-CN.mdx +7 -7
- package/locales/ar/chat.json +5 -1
- package/locales/ar/models.json +6 -9
- package/locales/bg-BG/chat.json +5 -1
- package/locales/bg-BG/models.json +6 -9
- package/locales/de-DE/chat.json +5 -1
- package/locales/de-DE/models.json +6 -9
- package/locales/en-US/chat.json +5 -1
- package/locales/en-US/models.json +6 -9
- package/locales/es-ES/chat.json +5 -1
- package/locales/es-ES/models.json +6 -9
- package/locales/fa-IR/chat.json +5 -1
- package/locales/fa-IR/models.json +6 -9
- package/locales/fr-FR/chat.json +5 -1
- package/locales/fr-FR/models.json +6 -9
- package/locales/it-IT/chat.json +5 -1
- package/locales/it-IT/models.json +6 -9
- package/locales/ja-JP/chat.json +5 -1
- package/locales/ja-JP/models.json +6 -9
- package/locales/ko-KR/chat.json +5 -1
- package/locales/ko-KR/models.json +6 -9
- package/locales/nl-NL/chat.json +5 -1
- package/locales/nl-NL/models.json +6 -9
- package/locales/pl-PL/chat.json +5 -1
- package/locales/pl-PL/models.json +6 -9
- package/locales/pt-BR/chat.json +5 -1
- package/locales/pt-BR/models.json +6 -9
- package/locales/ru-RU/chat.json +5 -1
- package/locales/ru-RU/models.json +6 -9
- package/locales/tr-TR/chat.json +5 -1
- package/locales/tr-TR/models.json +6 -9
- package/locales/vi-VN/chat.json +5 -1
- package/locales/vi-VN/models.json +6 -9
- package/locales/zh-CN/chat.json +5 -1
- package/locales/zh-CN/models.json +6 -9
- package/locales/zh-TW/chat.json +5 -1
- package/locales/zh-TW/models.json +6 -9
- package/package.json +3 -1
- package/src/config/aiModels/perplexity.ts +36 -20
- package/src/config/modelProviders/ppio.ts +1 -1
- package/src/database/client/migrations.json +8 -3
- package/src/features/Conversation/Extras/Usage/UsageDetail/ModelCard.tsx +27 -9
- package/src/features/Conversation/Extras/Usage/UsageDetail/index.tsx +77 -35
- package/src/features/Conversation/Extras/Usage/UsageDetail/tokens.test.ts +253 -0
- package/src/features/Conversation/Extras/Usage/UsageDetail/tokens.ts +65 -46
- package/src/libs/agent-runtime/baichuan/index.test.ts +58 -1
- package/src/libs/agent-runtime/groq/index.test.ts +36 -284
- package/src/libs/agent-runtime/mistral/index.test.ts +39 -300
- package/src/libs/agent-runtime/perplexity/index.test.ts +12 -10
- package/src/libs/agent-runtime/providerTestUtils.ts +58 -0
- package/src/libs/agent-runtime/togetherai/index.test.ts +7 -295
- package/src/libs/agent-runtime/utils/openaiCompatibleFactory/index.test.ts +3 -0
- package/src/libs/agent-runtime/utils/openaiCompatibleFactory/index.ts +5 -2
- package/src/libs/agent-runtime/utils/streams/anthropic.test.ts +89 -5
- package/src/libs/agent-runtime/utils/streams/anthropic.ts +25 -8
- package/src/libs/agent-runtime/utils/streams/openai.test.ts +188 -84
- package/src/libs/agent-runtime/utils/streams/openai.ts +8 -17
- package/src/libs/agent-runtime/utils/usageConverter.test.ts +249 -0
- package/src/libs/agent-runtime/utils/usageConverter.ts +50 -0
- package/src/libs/agent-runtime/zeroone/index.test.ts +7 -294
- package/src/libs/langchain/loaders/epub/__tests__/__snapshots__/index.test.ts.snap +238 -0
- package/src/libs/langchain/loaders/epub/__tests__/demo.epub +0 -0
- package/src/libs/langchain/loaders/epub/__tests__/index.test.ts +24 -0
- package/src/libs/langchain/loaders/epub/index.ts +21 -0
- package/src/libs/langchain/loaders/index.ts +9 -0
- package/src/libs/langchain/types.ts +2 -1
- package/src/locales/default/chat.ts +4 -0
- package/src/server/utils/tempFileManager.ts +70 -0
- package/src/types/message/base.ts +14 -4
- package/src/utils/filter.test.ts +0 -122
- package/src/utils/filter.ts +0 -29
@@ -0,0 +1,70 @@
|
|
1
|
+
import { mkdtempSync, rmSync , writeFileSync, existsSync } from 'node:fs';
|
2
|
+
import { tmpdir } from 'node:os';
|
3
|
+
import { join } from 'node:path';
|
4
|
+
import { v4 as uuidv4 } from 'uuid';
|
5
|
+
|
6
|
+
/**
|
7
|
+
* 安全存储临时文件工具类
|
8
|
+
*/
|
9
|
+
export class TempFileManager {
|
10
|
+
private readonly tempDir: string;
|
11
|
+
private filePaths: Set<string> = new Set();
|
12
|
+
|
13
|
+
constructor() {
|
14
|
+
// 创建唯一临时目录 (跨平台安全)
|
15
|
+
this.tempDir = mkdtempSync(join(tmpdir(), 'epub-'));
|
16
|
+
// 注册退出清理钩子
|
17
|
+
this.registerCleanupHook();
|
18
|
+
}
|
19
|
+
|
20
|
+
/**
|
21
|
+
* 将 Uint8Array 写入临时文件
|
22
|
+
* @param data 文件数据
|
23
|
+
* @param ext 文件扩展名 (默认 .epub)
|
24
|
+
* @returns 临时文件绝对路径
|
25
|
+
*/
|
26
|
+
async writeTempFile(data: Uint8Array, ext = '.epub'): Promise<string> {
|
27
|
+
const filePath = join(this.tempDir, `${uuidv4()}${ext}`);
|
28
|
+
|
29
|
+
try {
|
30
|
+
writeFileSync(filePath, data);
|
31
|
+
this.filePaths.add(filePath);
|
32
|
+
return filePath;
|
33
|
+
} catch (error) {
|
34
|
+
this.cleanup(); // 写入失败时立即清理
|
35
|
+
throw new Error(`Failed to write temp file: ${(error as Error).message}`);
|
36
|
+
}
|
37
|
+
}
|
38
|
+
|
39
|
+
/**
|
40
|
+
* 安全清理临时资源
|
41
|
+
*/
|
42
|
+
cleanup(): void {
|
43
|
+
if (existsSync(this.tempDir)) {
|
44
|
+
// 递归删除目录及内容
|
45
|
+
rmSync(this.tempDir, { force: true, recursive: true });
|
46
|
+
this.filePaths.clear();
|
47
|
+
}
|
48
|
+
}
|
49
|
+
|
50
|
+
/**
|
51
|
+
* 注册进程退出/异常时的自动清理
|
52
|
+
*/
|
53
|
+
private registerCleanupHook(): void {
|
54
|
+
// 正常退出
|
55
|
+
process.on('exit', () => this.cleanup());
|
56
|
+
// 异常退出
|
57
|
+
process.on('uncaughtException', (err) => {
|
58
|
+
console.error('Uncaught exception, cleaning temp files:', err);
|
59
|
+
this.cleanup();
|
60
|
+
process.exit(1);
|
61
|
+
});
|
62
|
+
// 信号终止
|
63
|
+
['SIGINT', 'SIGTERM'].forEach((signal) => {
|
64
|
+
process.on(signal, () => {
|
65
|
+
this.cleanup();
|
66
|
+
process.exit(0);
|
67
|
+
});
|
68
|
+
});
|
69
|
+
}
|
70
|
+
}
|
@@ -15,14 +15,24 @@ export interface ModelReasoning {
|
|
15
15
|
|
16
16
|
export interface ModelTokensUsage {
|
17
17
|
acceptedPredictionTokens?: number;
|
18
|
-
cachedTokens?: number;
|
19
18
|
inputAudioTokens?: number;
|
20
19
|
inputCacheMissTokens?: number;
|
21
|
-
|
20
|
+
inputCachedTokens?: number;
|
21
|
+
/**
|
22
|
+
* currently only pplx has citation_tokens
|
23
|
+
*/
|
24
|
+
inputCitationTokens?: number;
|
25
|
+
/**
|
26
|
+
* user prompt input
|
27
|
+
*/
|
28
|
+
inputTextTokens?: number;
|
29
|
+
inputWriteCacheTokens?: number;
|
22
30
|
outputAudioTokens?: number;
|
23
|
-
|
24
|
-
|
31
|
+
outputReasoningTokens?: number;
|
32
|
+
outputTextTokens?: number;
|
25
33
|
rejectedPredictionTokens?: number;
|
34
|
+
totalInputTokens?: number;
|
35
|
+
totalOutputTokens?: number;
|
26
36
|
totalTokens?: number;
|
27
37
|
}
|
28
38
|
|
package/src/utils/filter.test.ts
DELETED
@@ -1,122 +0,0 @@
|
|
1
|
-
import { test } from 'vitest';
|
2
|
-
|
3
|
-
test('placeholder', () => {});
|
4
|
-
// describe('filterWithKeywords', () => {
|
5
|
-
// const data: Record<string, BaseDataModel> = {
|
6
|
-
// 1: {
|
7
|
-
// id: '1',
|
8
|
-
// meta: {
|
9
|
-
// title: 'hello world',
|
10
|
-
// description: 'test case',
|
11
|
-
// tag: ['a', 'b'],
|
12
|
-
// },
|
13
|
-
// },
|
14
|
-
// 2: {
|
15
|
-
// id: '2',
|
16
|
-
// meta: {
|
17
|
-
// title: 'goodbye',
|
18
|
-
// description: 'hello world',
|
19
|
-
// tag: ['c', 'd'],
|
20
|
-
// },
|
21
|
-
// },
|
22
|
-
// };
|
23
|
-
//
|
24
|
-
// it('should return an empty object if map is empty', () => {
|
25
|
-
// const result = filterWithKeywords({}, 'hello');
|
26
|
-
// expect(result).toEqual({});
|
27
|
-
// });
|
28
|
-
//
|
29
|
-
// it('should return the original map if keywords is empty', () => {
|
30
|
-
// const result = filterWithKeywords(data, '');
|
31
|
-
// expect(result).toEqual(data);
|
32
|
-
// });
|
33
|
-
//
|
34
|
-
// it('should return a filtered map if keywords is not empty', () => {
|
35
|
-
// const result = filterWithKeywords(data, 'world');
|
36
|
-
// expect(result).toEqual({
|
37
|
-
// 1: {
|
38
|
-
// id: '1',
|
39
|
-
// meta: {
|
40
|
-
// title: 'hello world',
|
41
|
-
// description: 'test case',
|
42
|
-
// tag: ['a', 'b'],
|
43
|
-
// },
|
44
|
-
// },
|
45
|
-
// 2: {
|
46
|
-
// id: '2',
|
47
|
-
// meta: {
|
48
|
-
// title: 'goodbye',
|
49
|
-
// description: 'hello world',
|
50
|
-
// tag: ['c', 'd'],
|
51
|
-
// },
|
52
|
-
// },
|
53
|
-
// });
|
54
|
-
// });
|
55
|
-
//
|
56
|
-
// it('should only consider title, description and tag properties if extraSearchStr is not provided', () => {
|
57
|
-
// const result = filterWithKeywords(data, 'test');
|
58
|
-
// expect(result).toEqual({
|
59
|
-
// 1: {
|
60
|
-
// id: '1',
|
61
|
-
// meta: {
|
62
|
-
// title: 'hello world',
|
63
|
-
// description: 'test case',
|
64
|
-
// tag: ['a', 'b'],
|
65
|
-
// },
|
66
|
-
// },
|
67
|
-
// });
|
68
|
-
// });
|
69
|
-
//
|
70
|
-
// it('should consider extraSearchStr in addition to title, description and tag properties if provided', () => {
|
71
|
-
// const extraSearchStr = (item: BaseDataModel) => {
|
72
|
-
// return item.meta.avatar || '';
|
73
|
-
// };
|
74
|
-
// const data: Record<string, BaseDataModel> = {
|
75
|
-
// a: {
|
76
|
-
// id: 'a',
|
77
|
-
// meta: {
|
78
|
-
// title: 'hello world',
|
79
|
-
// description: 'test case',
|
80
|
-
// tag: ['a', 'b'],
|
81
|
-
// avatar: 'xxx',
|
82
|
-
// },
|
83
|
-
// },
|
84
|
-
// b: {
|
85
|
-
// id: 'b',
|
86
|
-
// meta: {
|
87
|
-
// title: 'goodbye',
|
88
|
-
// description: 'hello world',
|
89
|
-
// tag: ['c', 'd'],
|
90
|
-
// avatar: 'yyy',
|
91
|
-
// },
|
92
|
-
// },
|
93
|
-
// };
|
94
|
-
//
|
95
|
-
// const result = filterWithKeywords(data, 'yyy', extraSearchStr);
|
96
|
-
// expect(result).toEqual({
|
97
|
-
// b: {
|
98
|
-
// id: 'b',
|
99
|
-
// meta: {
|
100
|
-
// title: 'goodbye',
|
101
|
-
// description: 'hello world',
|
102
|
-
// tag: ['c', 'd'],
|
103
|
-
// avatar: 'yyy',
|
104
|
-
// },
|
105
|
-
// },
|
106
|
-
// });
|
107
|
-
// });
|
108
|
-
//
|
109
|
-
// it('should ensure that each filtered object has at least one property that includes the keyword or extraSearchStr', () => {
|
110
|
-
// const result = filterWithKeywords(data, 't');
|
111
|
-
// expect(result).toEqual({
|
112
|
-
// 1: {
|
113
|
-
// id: '1',
|
114
|
-
// meta: {
|
115
|
-
// title: 'hello world',
|
116
|
-
// description: 'test case',
|
117
|
-
// tag: ['a', 'b'],
|
118
|
-
// },
|
119
|
-
// },
|
120
|
-
// });
|
121
|
-
// });
|
122
|
-
// });
|
package/src/utils/filter.ts
DELETED
@@ -1,29 +0,0 @@
|
|
1
|
-
import { BaseDataModel } from '@/types/meta';
|
2
|
-
|
3
|
-
export const filterWithKeywords = <T extends BaseDataModel>(
|
4
|
-
map: Record<string, T>,
|
5
|
-
keywords: string,
|
6
|
-
extraSearchStr?: (item: T) => string | string[],
|
7
|
-
) => {
|
8
|
-
if (!keywords) return map;
|
9
|
-
|
10
|
-
return Object.fromEntries(
|
11
|
-
Object.entries(map).filter(([, item]) => {
|
12
|
-
const meta = item.meta;
|
13
|
-
|
14
|
-
const keyList = [meta.title, meta.description, meta.tags?.join('')].filter(
|
15
|
-
Boolean,
|
16
|
-
) as string[];
|
17
|
-
|
18
|
-
const defaultSearchKey = keyList.join('');
|
19
|
-
|
20
|
-
let extraSearchKey: string = '';
|
21
|
-
if (extraSearchStr) {
|
22
|
-
const searchStr = extraSearchStr(item);
|
23
|
-
extraSearchKey = Array.isArray(searchStr) ? searchStr.join('') : searchStr;
|
24
|
-
}
|
25
|
-
|
26
|
-
return `${defaultSearchKey}${extraSearchKey}`.toLowerCase().includes(keywords.toLowerCase());
|
27
|
-
}),
|
28
|
-
);
|
29
|
-
};
|