@lobehub/chat 1.79.2 → 1.79.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.
- package/.env.desktop +7 -0
- package/.github/scripts/pr-release-body.js +3 -0
- package/CHANGELOG.md +50 -0
- package/changelog/v1.json +18 -0
- package/package.json +15 -7
- package/packages/electron-client-ipc/src/events/file.ts +5 -0
- package/packages/electron-client-ipc/src/events/index.ts +19 -1
- package/packages/electron-client-ipc/src/events/shortcut.ts +4 -0
- package/packages/electron-client-ipc/src/events/system.ts +5 -0
- package/packages/electron-client-ipc/src/events/update.ts +20 -0
- package/packages/electron-client-ipc/src/events/windows.ts +9 -0
- package/packages/electron-client-ipc/src/index.ts +1 -0
- package/packages/electron-client-ipc/src/types/file.ts +14 -0
- package/packages/electron-client-ipc/src/types/index.ts +4 -0
- package/packages/electron-client-ipc/src/types/route.ts +46 -0
- package/packages/electron-client-ipc/src/types/shortcut.ts +11 -0
- package/packages/electron-client-ipc/src/types/update.ts +23 -0
- package/packages/electron-client-ipc/src/useWatchBroadcast.ts +37 -0
- package/packages/electron-server-ipc/src/events/database.ts +4 -0
- package/packages/electron-server-ipc/src/events/file.ts +6 -0
- package/packages/electron-server-ipc/src/events/index.ts +29 -0
- package/packages/electron-server-ipc/src/events/storagePath.ts +4 -0
- package/packages/electron-server-ipc/src/index.ts +1 -0
- package/packages/electron-server-ipc/src/ipcClient.test.ts +3 -4
- package/packages/electron-server-ipc/src/ipcClient.ts +52 -24
- package/packages/electron-server-ipc/src/ipcServer.ts +6 -14
- package/packages/electron-server-ipc/src/types/file.ts +4 -0
- package/packages/electron-server-ipc/src/types/index.ts +14 -1
- package/scripts/electronWorkflow/buildElectron.ts +52 -0
- package/scripts/electronWorkflow/moveNextStandalone.ts +69 -0
- package/scripts/electronWorkflow/setDesktopVersion.ts +96 -0
- package/scripts/prebuild.mts +77 -0
- package/src/config/aiModels/xai.ts +79 -18
- package/src/libs/agent-runtime/azureai/index.ts +3 -1
- package/src/prompts/files/file.ts +6 -4
- package/src/prompts/files/image.ts +6 -3
- package/src/prompts/files/index.test.ts +50 -0
- package/src/prompts/files/index.ts +4 -2
- package/src/services/chat.ts +4 -2
- package/src/store/global/actions/general.ts +13 -6
- package/packages/electron-server-ipc/src/types/event.ts +0 -18
@@ -4,16 +4,8 @@ import os from 'node:os';
|
|
4
4
|
import path from 'node:path';
|
5
5
|
|
6
6
|
import { SOCK_FILE, SOCK_INFO_FILE, WINDOW_PIPE_FILE } from './const';
|
7
|
-
import {
|
8
|
-
|
9
|
-
export type IPCEventMethod = (
|
10
|
-
params: any,
|
11
|
-
context: { id: string; method: string; socket: net.Socket },
|
12
|
-
) => Promise<any>;
|
13
|
-
|
14
|
-
export type ElectronIPCEventHandler = {
|
15
|
-
[key in IElectronIPCMethods]: IPCEventMethod;
|
16
|
-
};
|
7
|
+
import { ServerDispatchEventKey } from './events';
|
8
|
+
import { ElectronIPCEventHandler } from './types';
|
17
9
|
|
18
10
|
export class ElectronIPCServer {
|
19
11
|
private server: net.Server;
|
@@ -45,7 +37,7 @@ export class ElectronIPCServer {
|
|
45
37
|
});
|
46
38
|
|
47
39
|
this.server.listen(this.socketPath, () => {
|
48
|
-
console.log(`Electron IPC server listening on ${this.socketPath}`);
|
40
|
+
console.log(`[ElectronIPCServer] Electron IPC server listening on ${this.socketPath}`);
|
49
41
|
|
50
42
|
// 将套接字路径写入临时文件,供 Next.js 服务端读取
|
51
43
|
const tempDir = os.tmpdir();
|
@@ -86,7 +78,7 @@ export class ElectronIPCServer {
|
|
86
78
|
const { id, method, params } = request;
|
87
79
|
|
88
80
|
// 根据请求方法执行相应的操作
|
89
|
-
const eventHandler = this.eventHandler[method as
|
81
|
+
const eventHandler = this.eventHandler[method as ServerDispatchEventKey];
|
90
82
|
if (!eventHandler) return;
|
91
83
|
|
92
84
|
try {
|
@@ -100,12 +92,12 @@ export class ElectronIPCServer {
|
|
100
92
|
|
101
93
|
// 发送结果
|
102
94
|
private sendResult(socket: net.Socket, id: string, result: any): void {
|
103
|
-
socket.write(JSON.stringify({ id, result }));
|
95
|
+
socket.write(JSON.stringify({ id, result }) + '\n');
|
104
96
|
}
|
105
97
|
|
106
98
|
// 发送错误
|
107
99
|
private sendError(socket: net.Socket, id: string, error: string): void {
|
108
|
-
socket.write(JSON.stringify({ error, id }));
|
100
|
+
socket.write(JSON.stringify({ error, id }) + '\n');
|
109
101
|
}
|
110
102
|
|
111
103
|
// 关闭服务器
|
@@ -1 +1,14 @@
|
|
1
|
-
|
1
|
+
import net from 'node:net';
|
2
|
+
|
3
|
+
import { ServerDispatchEventKey } from '../events';
|
4
|
+
|
5
|
+
export type IPCEventMethod = (
|
6
|
+
params: any,
|
7
|
+
context: { id: string; method: string; socket: net.Socket },
|
8
|
+
) => Promise<any>;
|
9
|
+
|
10
|
+
export type ElectronIPCEventHandler = {
|
11
|
+
[key in ServerDispatchEventKey]: IPCEventMethod;
|
12
|
+
};
|
13
|
+
|
14
|
+
export * from './file';
|
@@ -0,0 +1,52 @@
|
|
1
|
+
/* eslint-disable unicorn/no-process-exit */
|
2
|
+
import { execSync } from 'node:child_process';
|
3
|
+
import os from 'node:os';
|
4
|
+
|
5
|
+
/**
|
6
|
+
* Build desktop application based on current operating system platform
|
7
|
+
*/
|
8
|
+
const buildElectron = () => {
|
9
|
+
const platform = os.platform();
|
10
|
+
const startTime = Date.now();
|
11
|
+
|
12
|
+
console.log(`🔨 Starting to build desktop app for ${platform} platform...`);
|
13
|
+
|
14
|
+
try {
|
15
|
+
let buildCommand = '';
|
16
|
+
|
17
|
+
// Determine build command based on platform
|
18
|
+
switch (platform) {
|
19
|
+
case 'darwin': {
|
20
|
+
buildCommand = 'npm run build:mac --prefix=./apps/desktop';
|
21
|
+
console.log('📦 Building macOS desktop application...');
|
22
|
+
break;
|
23
|
+
}
|
24
|
+
case 'win32': {
|
25
|
+
buildCommand = 'npm run build:win --prefix=./apps/desktop';
|
26
|
+
console.log('📦 Building Windows desktop application...');
|
27
|
+
break;
|
28
|
+
}
|
29
|
+
case 'linux': {
|
30
|
+
buildCommand = 'npm run build:linux --prefix=./apps/desktop';
|
31
|
+
console.log('📦 Building Linux desktop application...');
|
32
|
+
break;
|
33
|
+
}
|
34
|
+
default: {
|
35
|
+
throw new Error(`Unsupported platform: ${platform}`);
|
36
|
+
}
|
37
|
+
}
|
38
|
+
|
39
|
+
// Execute build command
|
40
|
+
execSync(buildCommand, { stdio: 'inherit' });
|
41
|
+
|
42
|
+
const endTime = Date.now();
|
43
|
+
const buildTime = ((endTime - startTime) / 1000).toFixed(2);
|
44
|
+
console.log(`✅ Desktop application build completed! (${buildTime}s)`);
|
45
|
+
} catch (error) {
|
46
|
+
console.error('❌ Build failed:', error);
|
47
|
+
process.exit(1);
|
48
|
+
}
|
49
|
+
};
|
50
|
+
|
51
|
+
// Execute build
|
52
|
+
buildElectron();
|
@@ -0,0 +1,69 @@
|
|
1
|
+
/* eslint-disable unicorn/no-process-exit */
|
2
|
+
import fs from 'fs-extra';
|
3
|
+
import { execSync } from 'node:child_process';
|
4
|
+
import os from 'node:os';
|
5
|
+
import path from 'node:path';
|
6
|
+
|
7
|
+
const rootDir = path.resolve(__dirname, '../..');
|
8
|
+
|
9
|
+
// 定义源目录和目标目录
|
10
|
+
const sourceDir: string = path.join(rootDir, '.next/standalone');
|
11
|
+
const targetDir: string = path.join(rootDir, 'apps/desktop/dist/next');
|
12
|
+
|
13
|
+
// 向 sourceDir 写入 .env 文件
|
14
|
+
const env = fs.readFileSync(path.join(rootDir, '.env.desktop'), 'utf8');
|
15
|
+
|
16
|
+
fs.writeFileSync(path.join(sourceDir, '.env'), env, 'utf8');
|
17
|
+
console.log(`⚓️ Inject .env successful`);
|
18
|
+
|
19
|
+
// 确保目标目录的父目录存在
|
20
|
+
fs.ensureDirSync(path.dirname(targetDir));
|
21
|
+
|
22
|
+
// 如果目标目录已存在,先删除它
|
23
|
+
if (fs.existsSync(targetDir)) {
|
24
|
+
console.log(`🗑️ Target directory ${targetDir} already exists, deleting...`);
|
25
|
+
try {
|
26
|
+
fs.removeSync(targetDir);
|
27
|
+
console.log(`✅ Old target directory removed successfully`);
|
28
|
+
} catch (error) {
|
29
|
+
console.warn(`⚠️ Failed to delete target directory: ${error}`);
|
30
|
+
console.log('🔄 Trying to delete using system command...');
|
31
|
+
try {
|
32
|
+
if (os.platform() === 'win32') {
|
33
|
+
execSync(`rmdir /S /Q "${targetDir}"`, { stdio: 'inherit' });
|
34
|
+
} else {
|
35
|
+
execSync(`rm -rf "${targetDir}"`, { stdio: 'inherit' });
|
36
|
+
}
|
37
|
+
console.log('✅ Successfully deleted old target directory');
|
38
|
+
} catch (cmdError) {
|
39
|
+
console.error(`❌ Unable to delete target directory, might need manual cleanup: ${cmdError}`);
|
40
|
+
}
|
41
|
+
}
|
42
|
+
}
|
43
|
+
|
44
|
+
console.log(`🚚 Moving ${sourceDir} to ${targetDir}...`);
|
45
|
+
|
46
|
+
try {
|
47
|
+
// 使用 fs-extra 的 move 方法
|
48
|
+
fs.moveSync(sourceDir, targetDir, { overwrite: true });
|
49
|
+
console.log(`✅ Directory moved successfully!`);
|
50
|
+
} catch (error) {
|
51
|
+
console.error('❌ fs-extra move failed:', error);
|
52
|
+
console.log('🔄 Trying to move using system command...');
|
53
|
+
|
54
|
+
try {
|
55
|
+
// 使用系统命令进行移动
|
56
|
+
if (os.platform() === 'win32') {
|
57
|
+
execSync(`move "${sourceDir}" "${targetDir}"`, { stdio: 'inherit' });
|
58
|
+
} else {
|
59
|
+
execSync(`mv "${sourceDir}" "${targetDir}"`, { stdio: 'inherit' });
|
60
|
+
}
|
61
|
+
console.log('✅ System command move completed successfully!');
|
62
|
+
} catch (mvError) {
|
63
|
+
console.error('❌ Failed to move directory:', mvError);
|
64
|
+
console.log('💡 Try running manually: sudo mv ' + sourceDir + ' ' + targetDir);
|
65
|
+
process.exit(1);
|
66
|
+
}
|
67
|
+
}
|
68
|
+
|
69
|
+
console.log(`🎉 Move completed!`);
|
@@ -0,0 +1,96 @@
|
|
1
|
+
/* eslint-disable unicorn/no-process-exit */
|
2
|
+
import fs from 'fs-extra';
|
3
|
+
import path from 'node:path';
|
4
|
+
|
5
|
+
// 获取脚本的命令行参数
|
6
|
+
const version = process.argv[2];
|
7
|
+
const isPr = process.argv[3] === 'true';
|
8
|
+
|
9
|
+
if (!version) {
|
10
|
+
console.error('Missing version parameter, usage: bun run setDesktopVersion.ts <version> [isPr]');
|
11
|
+
process.exit(1);
|
12
|
+
}
|
13
|
+
|
14
|
+
// 获取根目录
|
15
|
+
const rootDir = path.resolve(__dirname, '../..');
|
16
|
+
|
17
|
+
// 桌面应用 package.json 的路径
|
18
|
+
const desktopPackageJsonPath = path.join(rootDir, 'apps/desktop/package.json');
|
19
|
+
|
20
|
+
// 更新应用图标
|
21
|
+
function updateAppIcon() {
|
22
|
+
try {
|
23
|
+
const buildDir = path.join(rootDir, 'apps/desktop/build');
|
24
|
+
|
25
|
+
// 定义需要处理的图标映射,考虑到大小写敏感性
|
26
|
+
const iconMappings = [
|
27
|
+
// { ext: '.ico', nightly: 'icon-nightly.ico', normal: 'icon.ico' },
|
28
|
+
{ ext: '.png', nightly: 'icon-nightly.png', normal: 'icon.png' },
|
29
|
+
{ ext: '.icns', nightly: 'Icon-nightly.icns', normal: 'Icon.icns' },
|
30
|
+
];
|
31
|
+
|
32
|
+
// 处理每种图标格式
|
33
|
+
for (const mapping of iconMappings) {
|
34
|
+
const sourceFile = path.join(buildDir, mapping.nightly);
|
35
|
+
const targetFile = path.join(buildDir, mapping.normal);
|
36
|
+
|
37
|
+
// 检查源文件是否存在
|
38
|
+
if (fs.existsSync(sourceFile)) {
|
39
|
+
// 只有当源文件和目标文件不同,才进行复制
|
40
|
+
if (sourceFile !== targetFile) {
|
41
|
+
fs.copyFileSync(sourceFile, targetFile);
|
42
|
+
console.log(`Updated app icon: ${targetFile}`);
|
43
|
+
}
|
44
|
+
} else {
|
45
|
+
console.warn(`Warning: Source icon not found: ${sourceFile}`);
|
46
|
+
}
|
47
|
+
}
|
48
|
+
} catch (error) {
|
49
|
+
console.error('Error updating icons:', error);
|
50
|
+
// 继续处理,不终止程序
|
51
|
+
}
|
52
|
+
}
|
53
|
+
|
54
|
+
function updateVersion() {
|
55
|
+
try {
|
56
|
+
// 确保文件存在
|
57
|
+
if (!fs.existsSync(desktopPackageJsonPath)) {
|
58
|
+
console.error(`Error: File not found ${desktopPackageJsonPath}`);
|
59
|
+
process.exit(1);
|
60
|
+
}
|
61
|
+
|
62
|
+
// 读取 package.json 文件
|
63
|
+
const packageJson = fs.readJSONSync(desktopPackageJsonPath);
|
64
|
+
|
65
|
+
// 更新版本号
|
66
|
+
packageJson.version = version;
|
67
|
+
packageJson.productName = 'LobeHub';
|
68
|
+
packageJson.name = 'lobehub-desktop';
|
69
|
+
|
70
|
+
// 如果是 PR 构建,设置为 Nightly 版本
|
71
|
+
if (isPr) {
|
72
|
+
// 修改包名,添加 -nightly 后缀
|
73
|
+
if (!packageJson.name.endsWith('-nightly')) {
|
74
|
+
packageJson.name = `${packageJson.name}-nightly`;
|
75
|
+
}
|
76
|
+
|
77
|
+
// 修改产品名称为 LobeHub Nightly
|
78
|
+
packageJson.productName = 'LobeHub-Nightly';
|
79
|
+
|
80
|
+
console.log('🌙 Setting as Nightly version with modified package name and productName');
|
81
|
+
|
82
|
+
// 使用 nightly 图标替换常规图标
|
83
|
+
updateAppIcon();
|
84
|
+
}
|
85
|
+
|
86
|
+
// 写回文件
|
87
|
+
fs.writeJsonSync(desktopPackageJsonPath, packageJson, { spaces: 2 });
|
88
|
+
|
89
|
+
console.log(`Desktop app version updated to: ${version}, isPr: ${isPr}`);
|
90
|
+
} catch (error) {
|
91
|
+
console.error('Error updating version:', error);
|
92
|
+
process.exit(1);
|
93
|
+
}
|
94
|
+
}
|
95
|
+
|
96
|
+
updateVersion();
|
@@ -0,0 +1,77 @@
|
|
1
|
+
import * as dotenv from 'dotenv';
|
2
|
+
import { existsSync } from 'node:fs';
|
3
|
+
import { rm } from 'node:fs/promises';
|
4
|
+
import path from 'node:path';
|
5
|
+
|
6
|
+
const isDesktop = process.env.NEXT_PUBLIC_IS_DESKTOP_APP === '1';
|
7
|
+
|
8
|
+
dotenv.config();
|
9
|
+
// 创建需要排除的特性映射
|
10
|
+
/* eslint-disable sort-keys-fix/sort-keys-fix */
|
11
|
+
const partialBuildPages = [
|
12
|
+
// no need for desktop
|
13
|
+
{
|
14
|
+
name: 'changelog',
|
15
|
+
disabled: isDesktop,
|
16
|
+
paths: ['src/app/[variants]/@modal/(.)changelog', 'src/app/[variants]/(main)/changelog'],
|
17
|
+
},
|
18
|
+
{
|
19
|
+
name: 'auth',
|
20
|
+
disabled: isDesktop,
|
21
|
+
paths: ['src/app/[variants]/(auth)'],
|
22
|
+
},
|
23
|
+
{
|
24
|
+
name: 'mobile',
|
25
|
+
disabled: isDesktop,
|
26
|
+
paths: ['src/app/[variants]/(main)/(mobile)'],
|
27
|
+
},
|
28
|
+
{
|
29
|
+
name: 'api-webhooks',
|
30
|
+
disabled: isDesktop,
|
31
|
+
paths: ['src/app/(backend)/api/webhooks'],
|
32
|
+
},
|
33
|
+
|
34
|
+
// no need for web
|
35
|
+
{
|
36
|
+
name: 'desktop-devtools',
|
37
|
+
disabled: !isDesktop,
|
38
|
+
paths: ['src/app/desktop'],
|
39
|
+
},
|
40
|
+
{
|
41
|
+
name: 'desktop-trpc',
|
42
|
+
disabled: !isDesktop,
|
43
|
+
paths: ['src/app/(backend)/trpc/desktop'],
|
44
|
+
},
|
45
|
+
];
|
46
|
+
/* eslint-enable */
|
47
|
+
|
48
|
+
/**
|
49
|
+
* 删除指定的目录
|
50
|
+
*/
|
51
|
+
const removeDirectories = async () => {
|
52
|
+
// 遍历 partialBuildPages 数组
|
53
|
+
for (const page of partialBuildPages) {
|
54
|
+
// 检查是否需要禁用该功能
|
55
|
+
if (page.disabled) {
|
56
|
+
for (const dirPath of page.paths) {
|
57
|
+
const fullPath = path.resolve(process.cwd(), dirPath);
|
58
|
+
|
59
|
+
// 检查目录是否存在
|
60
|
+
if (existsSync(fullPath)) {
|
61
|
+
try {
|
62
|
+
// 递归删除目录
|
63
|
+
await rm(fullPath, { force: true, recursive: true });
|
64
|
+
console.log(`♻️ Removed ${dirPath} successfully`);
|
65
|
+
} catch (error) {
|
66
|
+
console.error(`Failed to remove directory ${dirPath}:`, error);
|
67
|
+
}
|
68
|
+
}
|
69
|
+
}
|
70
|
+
}
|
71
|
+
}
|
72
|
+
};
|
73
|
+
|
74
|
+
// 执行删除操作
|
75
|
+
console.log('Starting prebuild cleanup...');
|
76
|
+
await removeDirectories();
|
77
|
+
console.log('Prebuild cleanup completed.');
|
@@ -1,51 +1,68 @@
|
|
1
1
|
import { AIChatModelCard } from '@/types/aiModel';
|
2
|
-
|
2
|
+
// https://docs.x.ai/docs/models
|
3
3
|
const xaiChatModels: AIChatModelCard[] = [
|
4
4
|
{
|
5
5
|
abilities: {
|
6
6
|
functionCall: true,
|
7
7
|
},
|
8
8
|
contextWindowTokens: 131_072,
|
9
|
-
description: '
|
10
|
-
displayName: 'Grok Beta',
|
9
|
+
description: '旗舰级模型,擅长数据提取、编程和文本摘要等企业级应用,拥有金融、医疗、法律和科学等领域的深厚知识。',
|
10
|
+
displayName: 'Grok 3 Beta',
|
11
11
|
enabled: true,
|
12
|
-
id: 'grok-beta',
|
12
|
+
id: 'grok-3-beta',
|
13
13
|
pricing: {
|
14
|
-
input:
|
14
|
+
input: 3,
|
15
15
|
output: 15,
|
16
16
|
},
|
17
|
+
releasedAt: '2025-04-03',
|
17
18
|
type: 'chat',
|
18
19
|
},
|
19
20
|
{
|
20
21
|
abilities: {
|
21
22
|
functionCall: true,
|
22
|
-
vision: true,
|
23
23
|
},
|
24
|
-
contextWindowTokens:
|
25
|
-
description: '
|
26
|
-
displayName: 'Grok
|
27
|
-
|
28
|
-
id: 'grok-vision-beta',
|
24
|
+
contextWindowTokens: 131_072,
|
25
|
+
description: '旗舰级模型,擅长数据提取、编程和文本摘要等企业级应用,拥有金融、医疗、法律和科学等领域的深厚知识。',
|
26
|
+
displayName: 'Grok 3 Beta (Fast mode)',
|
27
|
+
id: 'grok-3-fast-beta',
|
29
28
|
pricing: {
|
30
29
|
input: 5,
|
31
|
-
output:
|
30
|
+
output: 25,
|
32
31
|
},
|
32
|
+
releasedAt: '2025-04-03',
|
33
33
|
type: 'chat',
|
34
34
|
},
|
35
35
|
{
|
36
36
|
abilities: {
|
37
37
|
functionCall: true,
|
38
|
+
reasoning: true,
|
38
39
|
},
|
39
40
|
contextWindowTokens: 131_072,
|
40
|
-
description: '
|
41
|
-
displayName: 'Grok
|
41
|
+
description: '轻量级模型,回话前会先思考。运行快速、智能,适用于不需要深层领域知识的逻辑任务,并能获取原始的思维轨迹。',
|
42
|
+
displayName: 'Grok 3 Mini Beta',
|
42
43
|
enabled: true,
|
43
|
-
id: 'grok-
|
44
|
+
id: 'grok-3-mini-beta',
|
44
45
|
pricing: {
|
45
|
-
input:
|
46
|
-
output:
|
46
|
+
input: 0.3,
|
47
|
+
output: 0.5,
|
47
48
|
},
|
48
|
-
releasedAt: '
|
49
|
+
releasedAt: '2025-04-03',
|
50
|
+
type: 'chat',
|
51
|
+
},
|
52
|
+
{
|
53
|
+
abilities: {
|
54
|
+
functionCall: true,
|
55
|
+
reasoning: true,
|
56
|
+
},
|
57
|
+
contextWindowTokens: 131_072,
|
58
|
+
description: '轻量级模型,回话前会先思考。运行快速、智能,适用于不需要深层领域知识的逻辑任务,并能获取原始的思维轨迹。',
|
59
|
+
displayName: 'Grok 3 Mini Beta (Fast mode)',
|
60
|
+
id: 'grok-3-mini-fast-beta',
|
61
|
+
pricing: {
|
62
|
+
input: 0.6,
|
63
|
+
output: 4,
|
64
|
+
},
|
65
|
+
releasedAt: '2025-04-03',
|
49
66
|
type: 'chat',
|
50
67
|
},
|
51
68
|
{
|
@@ -65,6 +82,50 @@ const xaiChatModels: AIChatModelCard[] = [
|
|
65
82
|
releasedAt: '2024-12-12',
|
66
83
|
type: 'chat',
|
67
84
|
},
|
85
|
+
{
|
86
|
+
abilities: {
|
87
|
+
functionCall: true,
|
88
|
+
},
|
89
|
+
contextWindowTokens: 131_072,
|
90
|
+
description: '该模型在准确性、指令遵循和多语言能力方面有所改进。',
|
91
|
+
displayName: 'Grok 2 1212',
|
92
|
+
id: 'grok-2-1212', // legacy
|
93
|
+
pricing: {
|
94
|
+
input: 2,
|
95
|
+
output: 10,
|
96
|
+
},
|
97
|
+
releasedAt: '2024-12-12',
|
98
|
+
type: 'chat',
|
99
|
+
},
|
100
|
+
{
|
101
|
+
abilities: {
|
102
|
+
functionCall: true,
|
103
|
+
},
|
104
|
+
contextWindowTokens: 131_072,
|
105
|
+
description: '拥有与 Grok 2 相当的性能,但具有更高的效率、速度和功能。',
|
106
|
+
displayName: 'Grok Beta',
|
107
|
+
id: 'grok-beta', // legacy
|
108
|
+
pricing: {
|
109
|
+
input: 5,
|
110
|
+
output: 15,
|
111
|
+
},
|
112
|
+
type: 'chat',
|
113
|
+
},
|
114
|
+
{
|
115
|
+
abilities: {
|
116
|
+
functionCall: true,
|
117
|
+
vision: true,
|
118
|
+
},
|
119
|
+
contextWindowTokens: 8192,
|
120
|
+
description: '最新的图像理解模型,可以处理各种各样的视觉信息,包括文档、图表、截图和照片等。',
|
121
|
+
displayName: 'Grok Vision Beta',
|
122
|
+
id: 'grok-vision-beta', // legacy
|
123
|
+
pricing: {
|
124
|
+
input: 5,
|
125
|
+
output: 15,
|
126
|
+
},
|
127
|
+
type: 'chat',
|
128
|
+
},
|
68
129
|
];
|
69
130
|
|
70
131
|
export const allModels = [...xaiChatModels];
|
@@ -34,7 +34,7 @@ export class LobeAzureAI implements LobeRuntimeAI {
|
|
34
34
|
baseURL: string;
|
35
35
|
|
36
36
|
async chat(payload: ChatStreamPayload, options?: ChatCompetitionOptions) {
|
37
|
-
const { messages, model, ...params } = payload;
|
37
|
+
const { messages, model, temperature, top_p, ...params } = payload;
|
38
38
|
// o1 series models on Azure OpenAI does not support streaming currently
|
39
39
|
const enableStreaming = model.includes('o1') ? false : (params.stream ?? true);
|
40
40
|
|
@@ -56,7 +56,9 @@ export class LobeAzureAI implements LobeRuntimeAI {
|
|
56
56
|
model,
|
57
57
|
...params,
|
58
58
|
stream: enableStreaming,
|
59
|
+
temperature: model.includes('o3') ? undefined : temperature,
|
59
60
|
tool_choice: params.tools ? 'auto' : undefined,
|
61
|
+
top_p: model.includes('o3') ? undefined : top_p,
|
60
62
|
},
|
61
63
|
});
|
62
64
|
|
@@ -1,14 +1,16 @@
|
|
1
1
|
import { ChatFileItem } from '@/types/message';
|
2
2
|
|
3
|
-
const filePrompt = (item: ChatFileItem) =>
|
4
|
-
|
3
|
+
const filePrompt = (item: ChatFileItem, addUrl: boolean) =>
|
4
|
+
addUrl
|
5
|
+
? `<file id="${item.id}" name="${item.name}" type="${item.fileType}" size="${item.size}" url="${item.url}"></file>`
|
6
|
+
: `<file id="${item.id}" name="${item.name}" type="${item.fileType}" size="${item.size}"></file>`;
|
5
7
|
|
6
|
-
export const filePrompts = (fileList: ChatFileItem[]) => {
|
8
|
+
export const filePrompts = (fileList: ChatFileItem[], addUrl: boolean) => {
|
7
9
|
if (fileList.length === 0) return '';
|
8
10
|
|
9
11
|
const prompt = `<files>
|
10
12
|
<files_docstring>here are user upload files you can refer to</files_docstring>
|
11
|
-
${fileList.map((item) => filePrompt(item)).join('\n')}
|
13
|
+
${fileList.map((item) => filePrompt(item, addUrl)).join('\n')}
|
12
14
|
</files>`;
|
13
15
|
|
14
16
|
return prompt.trim();
|
@@ -1,13 +1,16 @@
|
|
1
1
|
import { ChatImageItem } from '@/types/message';
|
2
2
|
|
3
|
-
const imagePrompt = (item: ChatImageItem) =>
|
3
|
+
const imagePrompt = (item: ChatImageItem, attachUrl: boolean) =>
|
4
|
+
attachUrl
|
5
|
+
? `<image name="${item.alt}" url="${item.url}"></image>`
|
6
|
+
: `<image name="${item.alt}"></image>`;
|
4
7
|
|
5
|
-
export const imagesPrompts = (imageList: ChatImageItem[]) => {
|
8
|
+
export const imagesPrompts = (imageList: ChatImageItem[], attachUrl: boolean) => {
|
6
9
|
if (imageList.length === 0) return '';
|
7
10
|
|
8
11
|
const prompt = `<images>
|
9
12
|
<images_docstring>here are user upload images you can refer to</images_docstring>
|
10
|
-
${imageList.map((item) => imagePrompt(item)).join('\n')}
|
13
|
+
${imageList.map((item) => imagePrompt(item, attachUrl)).join('\n')}
|
11
14
|
</images>`;
|
12
15
|
|
13
16
|
return prompt.trim();
|
@@ -135,4 +135,54 @@ describe('filesPrompts', () => {
|
|
135
135
|
expect(result).toMatch(/<image.*?>.*<image.*?>/s); // Check for multiple image tags
|
136
136
|
expect(result).toMatch(/<file.*?>.*<file.*?>/s); // Check for multiple file tags
|
137
137
|
});
|
138
|
+
|
139
|
+
it('should handle without url', () => {
|
140
|
+
const images: ChatImageItem[] = [
|
141
|
+
mockImage,
|
142
|
+
{
|
143
|
+
id: 'img-2',
|
144
|
+
alt: 'second image',
|
145
|
+
url: 'https://example.com/image2.jpg',
|
146
|
+
},
|
147
|
+
];
|
148
|
+
|
149
|
+
const files: ChatFileItem[] = [
|
150
|
+
mockFile,
|
151
|
+
{
|
152
|
+
id: 'file-2',
|
153
|
+
name: 'document.docx',
|
154
|
+
fileType: 'application/docx',
|
155
|
+
size: 2048,
|
156
|
+
url: 'https://example.com/document.docx',
|
157
|
+
},
|
158
|
+
];
|
159
|
+
|
160
|
+
const result = filesPrompts({
|
161
|
+
imageList: images,
|
162
|
+
fileList: files,
|
163
|
+
addUrl: false,
|
164
|
+
});
|
165
|
+
|
166
|
+
expect(result).toMatchInlineSnapshot(`
|
167
|
+
"<!-- SYSTEM CONTEXT (NOT PART OF USER QUERY) -->
|
168
|
+
<context.instruction>following part contains context information injected by the system. Please follow these instructions:
|
169
|
+
|
170
|
+
1. Always prioritize handling user-visible content.
|
171
|
+
2. the context is only required when user's queries rely on it.
|
172
|
+
</context.instruction>
|
173
|
+
<files_info>
|
174
|
+
<images>
|
175
|
+
<images_docstring>here are user upload images you can refer to</images_docstring>
|
176
|
+
<image name="test image"></image>
|
177
|
+
<image name="second image"></image>
|
178
|
+
</images>
|
179
|
+
<files>
|
180
|
+
<files_docstring>here are user upload files you can refer to</files_docstring>
|
181
|
+
<file id="file-1" name="test.pdf" type="application/pdf" size="1024"></file>
|
182
|
+
<file id="file-2" name="document.docx" type="application/docx" size="2048"></file>
|
183
|
+
</files>
|
184
|
+
</files_info>
|
185
|
+
<!-- END SYSTEM CONTEXT -->"
|
186
|
+
`);
|
187
|
+
});
|
138
188
|
});
|
@@ -6,7 +6,9 @@ import { imagesPrompts } from './image';
|
|
6
6
|
export const filesPrompts = ({
|
7
7
|
imageList,
|
8
8
|
fileList,
|
9
|
+
addUrl = true,
|
9
10
|
}: {
|
11
|
+
addUrl?: boolean;
|
10
12
|
fileList?: ChatFileItem[];
|
11
13
|
imageList: ChatImageItem[];
|
12
14
|
}) => {
|
@@ -19,8 +21,8 @@ export const filesPrompts = ({
|
|
19
21
|
2. the context is only required when user's queries rely on it.
|
20
22
|
</context.instruction>
|
21
23
|
<files_info>
|
22
|
-
${imagesPrompts(imageList)}
|
23
|
-
${fileList ? filePrompts(fileList) : ''}
|
24
|
+
${imagesPrompts(imageList, addUrl)}
|
25
|
+
${fileList ? filePrompts(fileList, addUrl) : ''}
|
24
26
|
</files_info>
|
25
27
|
<!-- END SYSTEM CONTEXT -->`;
|
26
28
|
|
package/src/services/chat.ts
CHANGED
@@ -8,7 +8,7 @@ import { INBOX_GUIDE_SYSTEMROLE } from '@/const/guide';
|
|
8
8
|
import { INBOX_SESSION_ID } from '@/const/session';
|
9
9
|
import { DEFAULT_AGENT_CONFIG } from '@/const/settings';
|
10
10
|
import { TracePayload, TraceTagMap } from '@/const/trace';
|
11
|
-
import { isDeprecatedEdition, isServerMode } from '@/const/version';
|
11
|
+
import { isDeprecatedEdition, isDesktop, isServerMode } from '@/const/version';
|
12
12
|
import {
|
13
13
|
AgentRuntime,
|
14
14
|
AgentRuntimeError,
|
@@ -480,7 +480,9 @@ class ChatService {
|
|
480
480
|
|
481
481
|
const imageList = m.imageList || [];
|
482
482
|
|
483
|
-
const filesContext = isServerMode
|
483
|
+
const filesContext = isServerMode
|
484
|
+
? filesPrompts({ addUrl: !isDesktop, fileList: m.fileList, imageList })
|
485
|
+
: '';
|
484
486
|
return [
|
485
487
|
{ text: (m.content + '\n\n' + filesContext).trim(), type: 'text' },
|
486
488
|
...imageList.map(
|