@lobehub/lobehub 2.0.0-next.241 → 2.0.0-next.243
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/.cursor/rules/desktop-feature-implementation.mdc +2 -2
- package/.cursor/rules/desktop-local-tools-implement.mdc +2 -2
- package/.github/workflows/test.yml +13 -5
- package/CHANGELOG.md +52 -0
- package/apps/desktop/Development.md +1 -6
- package/apps/desktop/README.md +2 -17
- package/apps/desktop/README.zh-CN.md +1 -15
- package/apps/desktop/build/Icon-beta.Assets.car +0 -0
- package/apps/desktop/build/Icon-beta.icns +0 -0
- package/apps/desktop/build/icon-beta.ico +0 -0
- package/apps/desktop/build/icon-beta.png +0 -0
- package/apps/desktop/src/main/controllers/index.ts +1 -1
- package/apps/desktop/src/main/controllers/registry.ts +0 -9
- package/apps/desktop/src/main/core/App.ts +1 -11
- package/apps/desktop/src/main/core/browser/Browser.ts +278 -457
- package/apps/desktop/src/main/core/browser/WindowStateManager.ts +180 -0
- package/apps/desktop/src/main/core/browser/WindowThemeManager.ts +167 -0
- package/apps/desktop/src/main/core/browser/__tests__/WindowStateManager.test.ts +237 -0
- package/apps/desktop/src/main/core/browser/__tests__/WindowThemeManager.test.ts +240 -0
- package/apps/desktop/src/main/exports.d.ts +1 -1
- package/apps/desktop/src/main/exports.ts +1 -1
- package/apps/desktop/src/main/utils/__tests__/http-headers.test.ts +131 -0
- package/apps/desktop/src/main/utils/http-headers.ts +61 -0
- package/apps/desktop/src/main/utils/ipc/__tests__/base.test.ts +1 -22
- package/apps/desktop/src/main/utils/ipc/base.ts +0 -20
- package/apps/desktop/src/main/utils/ipc/index.ts +1 -9
- package/changelog/v1.json +10 -0
- package/locales/ar/models.json +48 -7
- package/locales/ar/plugin.json +9 -0
- package/locales/ar/providers.json +1 -0
- package/locales/bg-BG/models.json +35 -7
- package/locales/bg-BG/plugin.json +9 -0
- package/locales/bg-BG/providers.json +1 -0
- package/locales/de-DE/models.json +26 -6
- package/locales/de-DE/plugin.json +9 -0
- package/locales/de-DE/providers.json +1 -0
- package/locales/en-US/models.json +10 -10
- package/locales/en-US/oauth.json +0 -1
- package/locales/en-US/providers.json +1 -0
- package/locales/en-US/subscription.json +2 -2
- package/locales/es-ES/models.json +42 -7
- package/locales/es-ES/plugin.json +9 -0
- package/locales/es-ES/providers.json +1 -0
- package/locales/fa-IR/models.json +52 -10
- package/locales/fa-IR/plugin.json +9 -0
- package/locales/fa-IR/providers.json +1 -0
- package/locales/fr-FR/models.json +36 -7
- package/locales/fr-FR/plugin.json +9 -0
- package/locales/fr-FR/providers.json +1 -0
- package/locales/it-IT/models.json +42 -7
- package/locales/it-IT/plugin.json +9 -0
- package/locales/it-IT/providers.json +1 -0
- package/locales/ja-JP/models.json +35 -6
- package/locales/ja-JP/plugin.json +9 -0
- package/locales/ja-JP/providers.json +1 -0
- package/locales/ko-KR/models.json +28 -7
- package/locales/ko-KR/plugin.json +9 -0
- package/locales/ko-KR/providers.json +1 -0
- package/locales/nl-NL/models.json +35 -6
- package/locales/nl-NL/plugin.json +9 -0
- package/locales/nl-NL/providers.json +1 -0
- package/locales/pl-PL/models.json +36 -7
- package/locales/pl-PL/plugin.json +9 -0
- package/locales/pl-PL/providers.json +1 -0
- package/locales/pt-BR/models.json +35 -6
- package/locales/pt-BR/plugin.json +9 -0
- package/locales/pt-BR/providers.json +1 -0
- package/locales/ru-RU/models.json +35 -7
- package/locales/ru-RU/plugin.json +9 -0
- package/locales/ru-RU/providers.json +1 -0
- package/locales/tr-TR/models.json +5 -7
- package/locales/tr-TR/plugin.json +9 -0
- package/locales/tr-TR/providers.json +1 -0
- package/locales/vi-VN/models.json +5 -5
- package/locales/vi-VN/plugin.json +9 -0
- package/locales/vi-VN/providers.json +1 -0
- package/locales/zh-CN/models.json +48 -6
- package/locales/zh-CN/oauth.json +0 -1
- package/locales/zh-CN/providers.json +1 -0
- package/locales/zh-CN/subscription.json +1 -1
- package/locales/zh-TW/models.json +10 -10
- package/locales/zh-TW/plugin.json +9 -0
- package/locales/zh-TW/providers.json +1 -0
- package/package.json +1 -1
- package/src/features/ChatInput/InputEditor/Placeholder.tsx +4 -1
- package/src/locales/default/subscription.ts +2 -3
- package/src/server/services/memory/userMemory/extract.ts +46 -6
- package/apps/desktop/src/main/controllers/UploadFileServerCtr.ts +0 -33
- package/apps/desktop/src/main/controllers/__tests__/UploadFileServerCtr.test.ts +0 -55
- package/src/server/modules/ElectronIPCClient/index.ts +0 -92
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
+
|
|
3
|
+
import { WindowThemeManager } from '../WindowThemeManager';
|
|
4
|
+
|
|
5
|
+
// Use vi.hoisted to define mocks before hoisting
|
|
6
|
+
const { mockNativeTheme, mockBrowserWindow } = vi.hoisted(() => ({
|
|
7
|
+
mockBrowserWindow: {
|
|
8
|
+
isDestroyed: vi.fn().mockReturnValue(false),
|
|
9
|
+
setBackgroundColor: vi.fn(),
|
|
10
|
+
setTitleBarOverlay: vi.fn(),
|
|
11
|
+
},
|
|
12
|
+
mockNativeTheme: {
|
|
13
|
+
off: vi.fn(),
|
|
14
|
+
on: vi.fn(),
|
|
15
|
+
shouldUseDarkColors: false,
|
|
16
|
+
},
|
|
17
|
+
}));
|
|
18
|
+
|
|
19
|
+
vi.mock('electron', () => ({
|
|
20
|
+
nativeTheme: mockNativeTheme,
|
|
21
|
+
}));
|
|
22
|
+
|
|
23
|
+
vi.mock('@/utils/logger', () => ({
|
|
24
|
+
createLogger: () => ({
|
|
25
|
+
debug: vi.fn(),
|
|
26
|
+
error: vi.fn(),
|
|
27
|
+
info: vi.fn(),
|
|
28
|
+
warn: vi.fn(),
|
|
29
|
+
}),
|
|
30
|
+
}));
|
|
31
|
+
|
|
32
|
+
vi.mock('@/const/dir', () => ({
|
|
33
|
+
buildDir: '/mock/build',
|
|
34
|
+
}));
|
|
35
|
+
|
|
36
|
+
vi.mock('@/const/env', () => ({
|
|
37
|
+
isDev: false,
|
|
38
|
+
isWindows: true,
|
|
39
|
+
}));
|
|
40
|
+
|
|
41
|
+
vi.mock('@/const/theme', () => ({
|
|
42
|
+
BACKGROUND_DARK: '#1a1a1a',
|
|
43
|
+
BACKGROUND_LIGHT: '#ffffff',
|
|
44
|
+
SYMBOL_COLOR_DARK: '#ffffff',
|
|
45
|
+
SYMBOL_COLOR_LIGHT: '#000000',
|
|
46
|
+
THEME_CHANGE_DELAY: 0,
|
|
47
|
+
TITLE_BAR_HEIGHT: 32,
|
|
48
|
+
}));
|
|
49
|
+
|
|
50
|
+
describe('WindowThemeManager', () => {
|
|
51
|
+
let manager: WindowThemeManager;
|
|
52
|
+
|
|
53
|
+
beforeEach(() => {
|
|
54
|
+
vi.clearAllMocks();
|
|
55
|
+
vi.useFakeTimers();
|
|
56
|
+
|
|
57
|
+
mockNativeTheme.shouldUseDarkColors = false;
|
|
58
|
+
mockBrowserWindow.isDestroyed.mockReturnValue(false);
|
|
59
|
+
|
|
60
|
+
manager = new WindowThemeManager('test-window');
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
afterEach(() => {
|
|
64
|
+
vi.useRealTimers();
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
describe('isDarkMode', () => {
|
|
68
|
+
it('should return true when shouldUseDarkColors is true', () => {
|
|
69
|
+
mockNativeTheme.shouldUseDarkColors = true;
|
|
70
|
+
|
|
71
|
+
expect(manager.isDarkMode).toBe(true);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it('should return false when shouldUseDarkColors is false', () => {
|
|
75
|
+
mockNativeTheme.shouldUseDarkColors = false;
|
|
76
|
+
|
|
77
|
+
expect(manager.isDarkMode).toBe(false);
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
describe('getPlatformConfig', () => {
|
|
82
|
+
it('should return Windows dark theme config when in dark mode', () => {
|
|
83
|
+
mockNativeTheme.shouldUseDarkColors = true;
|
|
84
|
+
|
|
85
|
+
const config = manager.getPlatformConfig();
|
|
86
|
+
|
|
87
|
+
expect(config).toEqual({
|
|
88
|
+
backgroundColor: '#1a1a1a',
|
|
89
|
+
icon: undefined,
|
|
90
|
+
titleBarOverlay: {
|
|
91
|
+
color: '#1a1a1a',
|
|
92
|
+
height: 32,
|
|
93
|
+
symbolColor: '#ffffff',
|
|
94
|
+
},
|
|
95
|
+
titleBarStyle: 'hidden',
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it('should return Windows light theme config when in light mode', () => {
|
|
100
|
+
mockNativeTheme.shouldUseDarkColors = false;
|
|
101
|
+
|
|
102
|
+
const config = manager.getPlatformConfig();
|
|
103
|
+
|
|
104
|
+
expect(config).toEqual({
|
|
105
|
+
backgroundColor: '#ffffff',
|
|
106
|
+
icon: undefined,
|
|
107
|
+
titleBarOverlay: {
|
|
108
|
+
color: '#ffffff',
|
|
109
|
+
height: 32,
|
|
110
|
+
symbolColor: '#000000',
|
|
111
|
+
},
|
|
112
|
+
titleBarStyle: 'hidden',
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
describe('attach', () => {
|
|
118
|
+
it('should setup theme listener', () => {
|
|
119
|
+
manager.attach(mockBrowserWindow as any);
|
|
120
|
+
|
|
121
|
+
expect(mockNativeTheme.on).toHaveBeenCalledWith('updated', expect.any(Function));
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
it('should apply initial visual effects', () => {
|
|
125
|
+
manager.attach(mockBrowserWindow as any);
|
|
126
|
+
|
|
127
|
+
expect(mockBrowserWindow.setBackgroundColor).toHaveBeenCalled();
|
|
128
|
+
expect(mockBrowserWindow.setTitleBarOverlay).toHaveBeenCalled();
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
it('should not setup duplicate listeners', () => {
|
|
132
|
+
manager.attach(mockBrowserWindow as any);
|
|
133
|
+
manager.attach(mockBrowserWindow as any);
|
|
134
|
+
|
|
135
|
+
expect(mockNativeTheme.on).toHaveBeenCalledTimes(1);
|
|
136
|
+
});
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
describe('cleanup', () => {
|
|
140
|
+
it('should remove theme listener', () => {
|
|
141
|
+
manager.attach(mockBrowserWindow as any);
|
|
142
|
+
manager.cleanup();
|
|
143
|
+
|
|
144
|
+
expect(mockNativeTheme.off).toHaveBeenCalledWith('updated', expect.any(Function));
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
it('should not throw if cleanup called without attach', () => {
|
|
148
|
+
expect(() => manager.cleanup()).not.toThrow();
|
|
149
|
+
expect(mockNativeTheme.off).not.toHaveBeenCalled();
|
|
150
|
+
});
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
describe('handleAppThemeChange', () => {
|
|
154
|
+
it('should reapply visual effects after delay', () => {
|
|
155
|
+
manager.attach(mockBrowserWindow as any);
|
|
156
|
+
mockBrowserWindow.setBackgroundColor.mockClear();
|
|
157
|
+
mockBrowserWindow.setTitleBarOverlay.mockClear();
|
|
158
|
+
|
|
159
|
+
manager.handleAppThemeChange();
|
|
160
|
+
vi.advanceTimersByTime(0);
|
|
161
|
+
|
|
162
|
+
expect(mockBrowserWindow.setBackgroundColor).toHaveBeenCalled();
|
|
163
|
+
expect(mockBrowserWindow.setTitleBarOverlay).toHaveBeenCalled();
|
|
164
|
+
});
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
describe('reapplyVisualEffects', () => {
|
|
168
|
+
it('should apply visual effects', () => {
|
|
169
|
+
manager.attach(mockBrowserWindow as any);
|
|
170
|
+
mockBrowserWindow.setBackgroundColor.mockClear();
|
|
171
|
+
|
|
172
|
+
manager.reapplyVisualEffects();
|
|
173
|
+
|
|
174
|
+
expect(mockBrowserWindow.setBackgroundColor).toHaveBeenCalled();
|
|
175
|
+
});
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
describe('applyVisualEffects', () => {
|
|
179
|
+
it('should apply dark theme when in dark mode', () => {
|
|
180
|
+
mockNativeTheme.shouldUseDarkColors = true;
|
|
181
|
+
manager.attach(mockBrowserWindow as any);
|
|
182
|
+
|
|
183
|
+
expect(mockBrowserWindow.setBackgroundColor).toHaveBeenCalledWith('#1a1a1a');
|
|
184
|
+
expect(mockBrowserWindow.setTitleBarOverlay).toHaveBeenCalledWith({
|
|
185
|
+
color: '#1a1a1a',
|
|
186
|
+
height: 32,
|
|
187
|
+
symbolColor: '#ffffff',
|
|
188
|
+
});
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
it('should apply light theme when in light mode', () => {
|
|
192
|
+
mockNativeTheme.shouldUseDarkColors = false;
|
|
193
|
+
manager.attach(mockBrowserWindow as any);
|
|
194
|
+
|
|
195
|
+
expect(mockBrowserWindow.setBackgroundColor).toHaveBeenCalledWith('#ffffff');
|
|
196
|
+
expect(mockBrowserWindow.setTitleBarOverlay).toHaveBeenCalledWith({
|
|
197
|
+
color: '#ffffff',
|
|
198
|
+
height: 32,
|
|
199
|
+
symbolColor: '#000000',
|
|
200
|
+
});
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
it('should not apply effects when window is destroyed', () => {
|
|
204
|
+
manager.attach(mockBrowserWindow as any);
|
|
205
|
+
mockBrowserWindow.setBackgroundColor.mockClear();
|
|
206
|
+
mockBrowserWindow.isDestroyed.mockReturnValue(true);
|
|
207
|
+
|
|
208
|
+
manager.reapplyVisualEffects();
|
|
209
|
+
|
|
210
|
+
expect(mockBrowserWindow.setBackgroundColor).not.toHaveBeenCalled();
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
it('should not apply effects when no window attached', () => {
|
|
214
|
+
// Manager without attached window
|
|
215
|
+
const freshManager = new WindowThemeManager('fresh-window');
|
|
216
|
+
|
|
217
|
+
// Should not throw
|
|
218
|
+
expect(() => freshManager.reapplyVisualEffects()).not.toThrow();
|
|
219
|
+
});
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
describe('theme change listener', () => {
|
|
223
|
+
it('should reapply visual effects on system theme change', () => {
|
|
224
|
+
manager.attach(mockBrowserWindow as any);
|
|
225
|
+
|
|
226
|
+
// Get the theme change handler
|
|
227
|
+
const themeHandler = mockNativeTheme.on.mock.calls.find((call) => call[0] === 'updated')?.[1];
|
|
228
|
+
|
|
229
|
+
expect(themeHandler).toBeDefined();
|
|
230
|
+
|
|
231
|
+
mockBrowserWindow.setBackgroundColor.mockClear();
|
|
232
|
+
|
|
233
|
+
// Simulate theme change
|
|
234
|
+
themeHandler();
|
|
235
|
+
vi.advanceTimersByTime(0);
|
|
236
|
+
|
|
237
|
+
expect(mockBrowserWindow.setBackgroundColor).toHaveBeenCalled();
|
|
238
|
+
});
|
|
239
|
+
});
|
|
240
|
+
});
|
|
@@ -5,4 +5,4 @@ declare module '@lobechat/electron-client-ipc' {
|
|
|
5
5
|
interface DesktopIpcServicesMap extends DesktopIpcServices {}
|
|
6
6
|
}
|
|
7
7
|
|
|
8
|
-
export { type DesktopIpcServices
|
|
8
|
+
export { type DesktopIpcServices } from './controllers/registry';
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// Export types for renderer/server to use
|
|
2
|
-
export type { DesktopIpcServices
|
|
2
|
+
export type { DesktopIpcServices } from './controllers/registry';
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
deleteResponseHeader,
|
|
5
|
+
getResponseHeader,
|
|
6
|
+
hasResponseHeader,
|
|
7
|
+
setResponseHeader,
|
|
8
|
+
} from '../http-headers';
|
|
9
|
+
|
|
10
|
+
describe('http-headers utilities', () => {
|
|
11
|
+
describe('setResponseHeader', () => {
|
|
12
|
+
it('should set a new header', () => {
|
|
13
|
+
const headers: Record<string, string[]> = {};
|
|
14
|
+
|
|
15
|
+
setResponseHeader(headers, 'Content-Type', 'application/json');
|
|
16
|
+
|
|
17
|
+
expect(headers['Content-Type']).toEqual(['application/json']);
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it('should replace existing header with same case', () => {
|
|
21
|
+
const headers: Record<string, string[]> = {
|
|
22
|
+
'Content-Type': ['text/html'],
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
setResponseHeader(headers, 'Content-Type', 'application/json');
|
|
26
|
+
|
|
27
|
+
expect(headers['Content-Type']).toEqual(['application/json']);
|
|
28
|
+
expect(Object.keys(headers)).toHaveLength(1);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('should replace existing header with different case', () => {
|
|
32
|
+
const headers: Record<string, string[]> = {
|
|
33
|
+
'content-type': ['text/html'],
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
setResponseHeader(headers, 'Content-Type', 'application/json');
|
|
37
|
+
|
|
38
|
+
expect(headers['Content-Type']).toEqual(['application/json']);
|
|
39
|
+
expect(headers['content-type']).toBeUndefined();
|
|
40
|
+
expect(Object.keys(headers)).toHaveLength(1);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it('should handle array values', () => {
|
|
44
|
+
const headers: Record<string, string[]> = {};
|
|
45
|
+
|
|
46
|
+
setResponseHeader(headers, 'Set-Cookie', ['a=1', 'b=2']);
|
|
47
|
+
|
|
48
|
+
expect(headers['Set-Cookie']).toEqual(['a=1', 'b=2']);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it('should replace multiple headers with different cases', () => {
|
|
52
|
+
const headers: Record<string, string[]> = {
|
|
53
|
+
'ACCESS-CONTROL-ALLOW-ORIGIN': ['*'],
|
|
54
|
+
'access-control-allow-origin': ['http://localhost'],
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
setResponseHeader(headers, 'Access-Control-Allow-Origin', 'http://example.com');
|
|
58
|
+
|
|
59
|
+
expect(headers['Access-Control-Allow-Origin']).toEqual(['http://example.com']);
|
|
60
|
+
expect(Object.keys(headers)).toHaveLength(1);
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
describe('hasResponseHeader', () => {
|
|
65
|
+
it('should return true for existing header', () => {
|
|
66
|
+
const headers = { 'Content-Type': ['application/json'] };
|
|
67
|
+
|
|
68
|
+
expect(hasResponseHeader(headers, 'Content-Type')).toBe(true);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it('should return true for existing header with different case', () => {
|
|
72
|
+
const headers = { 'content-type': ['application/json'] };
|
|
73
|
+
|
|
74
|
+
expect(hasResponseHeader(headers, 'Content-Type')).toBe(true);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('should return false for non-existing header', () => {
|
|
78
|
+
const headers = { 'Content-Type': ['application/json'] };
|
|
79
|
+
|
|
80
|
+
expect(hasResponseHeader(headers, 'Authorization')).toBe(false);
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
describe('getResponseHeader', () => {
|
|
85
|
+
it('should get header value', () => {
|
|
86
|
+
const headers = { 'Content-Type': ['application/json'] };
|
|
87
|
+
|
|
88
|
+
expect(getResponseHeader(headers, 'Content-Type')).toEqual(['application/json']);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it('should get header value with different case', () => {
|
|
92
|
+
const headers = { 'content-type': ['application/json'] };
|
|
93
|
+
|
|
94
|
+
expect(getResponseHeader(headers, 'Content-Type')).toEqual(['application/json']);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it('should return undefined for non-existing header', () => {
|
|
98
|
+
const headers = { 'Content-Type': ['application/json'] };
|
|
99
|
+
|
|
100
|
+
expect(getResponseHeader(headers, 'Authorization')).toBeUndefined();
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
describe('deleteResponseHeader', () => {
|
|
105
|
+
it('should delete existing header', () => {
|
|
106
|
+
const headers: Record<string, string[]> = { 'Content-Type': ['application/json'] };
|
|
107
|
+
|
|
108
|
+
const result = deleteResponseHeader(headers, 'Content-Type');
|
|
109
|
+
|
|
110
|
+
expect(result).toBe(true);
|
|
111
|
+
expect(headers['Content-Type']).toBeUndefined();
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
it('should delete header with different case', () => {
|
|
115
|
+
const headers: Record<string, string[]> = { 'content-type': ['application/json'] };
|
|
116
|
+
|
|
117
|
+
const result = deleteResponseHeader(headers, 'Content-Type');
|
|
118
|
+
|
|
119
|
+
expect(result).toBe(true);
|
|
120
|
+
expect(headers['content-type']).toBeUndefined();
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
it('should return false for non-existing header', () => {
|
|
124
|
+
const headers: Record<string, string[]> = { 'Content-Type': ['application/json'] };
|
|
125
|
+
|
|
126
|
+
const result = deleteResponseHeader(headers, 'Authorization');
|
|
127
|
+
|
|
128
|
+
expect(result).toBe(false);
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
});
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HTTP headers utilities for Electron webRequest
|
|
3
|
+
*
|
|
4
|
+
* Electron's webRequest responseHeaders is a plain JS object where keys are case-sensitive,
|
|
5
|
+
* but HTTP headers are case-insensitive per spec. These utilities handle this mismatch.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
type ElectronResponseHeaders = Record<string, string[]>;
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Set a header value, replacing any existing header with the same name (case-insensitive)
|
|
12
|
+
*/
|
|
13
|
+
export function setResponseHeader(
|
|
14
|
+
headers: ElectronResponseHeaders,
|
|
15
|
+
name: string,
|
|
16
|
+
value: string | string[],
|
|
17
|
+
): void {
|
|
18
|
+
// Delete any existing header with same name (case-insensitive)
|
|
19
|
+
for (const key of Object.keys(headers)) {
|
|
20
|
+
if (key.toLowerCase() === name.toLowerCase()) {
|
|
21
|
+
delete headers[key];
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
headers[name] = Array.isArray(value) ? value : [value];
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Check if a header exists (case-insensitive)
|
|
29
|
+
*/
|
|
30
|
+
export function hasResponseHeader(headers: ElectronResponseHeaders, name: string): boolean {
|
|
31
|
+
return Object.keys(headers).some((key) => key.toLowerCase() === name.toLowerCase());
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Get a header value (case-insensitive)
|
|
36
|
+
*/
|
|
37
|
+
export function getResponseHeader(
|
|
38
|
+
headers: ElectronResponseHeaders,
|
|
39
|
+
name: string,
|
|
40
|
+
): string[] | undefined {
|
|
41
|
+
for (const key of Object.keys(headers)) {
|
|
42
|
+
if (key.toLowerCase() === name.toLowerCase()) {
|
|
43
|
+
return headers[key];
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return undefined;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Delete a header (case-insensitive)
|
|
51
|
+
*/
|
|
52
|
+
export function deleteResponseHeader(headers: ElectronResponseHeaders, name: string): boolean {
|
|
53
|
+
let deleted = false;
|
|
54
|
+
for (const key of Object.keys(headers)) {
|
|
55
|
+
if (key.toLowerCase() === name.toLowerCase()) {
|
|
56
|
+
delete headers[key];
|
|
57
|
+
deleted = true;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return deleted;
|
|
61
|
+
}
|
|
@@ -1,13 +1,7 @@
|
|
|
1
1
|
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
2
2
|
|
|
3
3
|
import type { IpcContext } from '../base';
|
|
4
|
-
import {
|
|
5
|
-
IpcMethod,
|
|
6
|
-
IpcServerMethod,
|
|
7
|
-
IpcService,
|
|
8
|
-
getIpcContext,
|
|
9
|
-
getServerMethodMetadata,
|
|
10
|
-
} from '../base';
|
|
4
|
+
import { IpcMethod, IpcService, getIpcContext } from '../base';
|
|
11
5
|
|
|
12
6
|
const { ipcMainHandleMock } = vi.hoisted(() => ({
|
|
13
7
|
ipcMainHandleMock: vi.fn(),
|
|
@@ -73,19 +67,4 @@ describe('ipc service base', () => {
|
|
|
73
67
|
expect(service.invokedWith).toBe('test');
|
|
74
68
|
expect(ipcMainHandleMock).toHaveBeenCalledWith('direct.run', expect.any(Function));
|
|
75
69
|
});
|
|
76
|
-
|
|
77
|
-
it('collects server method metadata for decorators', () => {
|
|
78
|
-
class ServerService extends IpcService {
|
|
79
|
-
static readonly groupName = 'server';
|
|
80
|
-
|
|
81
|
-
@IpcServerMethod()
|
|
82
|
-
fetch(_: string) {
|
|
83
|
-
return 'ok';
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
const metadata = getServerMethodMetadata(ServerService);
|
|
88
|
-
expect(metadata).toBeDefined();
|
|
89
|
-
expect(metadata?.get('fetch')).toBe('fetch');
|
|
90
|
-
});
|
|
91
70
|
});
|
|
@@ -10,7 +10,6 @@ export interface IpcContext {
|
|
|
10
10
|
|
|
11
11
|
// Metadata storage for decorated methods
|
|
12
12
|
const methodMetadata = new WeakMap<any, Map<string, string>>();
|
|
13
|
-
const serverMethodMetadata = new WeakMap<any, Map<string, string>>();
|
|
14
13
|
const ipcContextStorage = new AsyncLocalStorage<IpcContext>();
|
|
15
14
|
|
|
16
15
|
// Decorator for IPC methods
|
|
@@ -29,21 +28,6 @@ export function IpcMethod() {
|
|
|
29
28
|
};
|
|
30
29
|
}
|
|
31
30
|
|
|
32
|
-
export function IpcServerMethod(channelName?: string) {
|
|
33
|
-
return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
|
|
34
|
-
const { constructor } = target;
|
|
35
|
-
|
|
36
|
-
if (!serverMethodMetadata.has(constructor)) {
|
|
37
|
-
serverMethodMetadata.set(constructor, new Map());
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
const methods = serverMethodMetadata.get(constructor)!;
|
|
41
|
-
methods.set(propertyKey, channelName || propertyKey);
|
|
42
|
-
|
|
43
|
-
return descriptor;
|
|
44
|
-
};
|
|
45
|
-
}
|
|
46
|
-
|
|
47
31
|
// Handler registry for IPC methods
|
|
48
32
|
export class IpcHandler {
|
|
49
33
|
private static instance: IpcHandler;
|
|
@@ -157,10 +141,6 @@ export type CreateServicesResult<T extends readonly IpcServiceConstructor[]> = {
|
|
|
157
141
|
[K in T[number] as K['groupName']]: InstanceType<K>;
|
|
158
142
|
};
|
|
159
143
|
|
|
160
|
-
export function getServerMethodMetadata(target: IpcServiceConstructor) {
|
|
161
|
-
return serverMethodMetadata.get(target);
|
|
162
|
-
}
|
|
163
|
-
|
|
164
144
|
export function getIpcContext() {
|
|
165
145
|
return ipcContextStorage.getStore();
|
|
166
146
|
}
|
|
@@ -1,11 +1,3 @@
|
|
|
1
1
|
export type { CreateServicesResult, IpcContext, IpcServiceConstructor } from './base';
|
|
2
|
-
export {
|
|
3
|
-
createServices,
|
|
4
|
-
getIpcContext,
|
|
5
|
-
getServerMethodMetadata,
|
|
6
|
-
IpcMethod,
|
|
7
|
-
IpcServerMethod,
|
|
8
|
-
IpcService,
|
|
9
|
-
runWithIpcContext,
|
|
10
|
-
} from './base';
|
|
2
|
+
export { createServices, getIpcContext, IpcMethod, IpcService, runWithIpcContext } from './base';
|
|
11
3
|
export type { ExtractServiceMethods, MergeIpcService } from './utility';
|
package/changelog/v1.json
CHANGED
package/locales/ar/models.json
CHANGED
|
@@ -271,20 +271,20 @@
|
|
|
271
271
|
"chatgpt-4o-latest.description": "ChatGPT-4o هو نموذج ديناميكي يتم تحديثه في الوقت الفعلي، يجمع بين الفهم العميق والقدرة على التوليد لتلبية احتياجات الاستخدام الواسعة مثل دعم العملاء والتعليم والدعم الفني.",
|
|
272
272
|
"claude-2.0.description": "Claude 2 يقدم تحسينات رئيسية للمؤسسات، بما في ذلك سياق 200 ألف رمز، تقليل الهلوسة، دعم التعليمات النظامية، وميزة جديدة: استدعاء الأدوات.",
|
|
273
273
|
"claude-2.1.description": "Claude 2 يقدم تحسينات رئيسية للمؤسسات، بما في ذلك سياق 200 ألف رمز، تقليل الهلوسة، دعم التعليمات النظامية، وميزة جديدة: استدعاء الأدوات.",
|
|
274
|
-
"claude-3-5-haiku-20241022.description": "Claude 3.5 Haiku هو أسرع نموذج من الجيل التالي
|
|
274
|
+
"claude-3-5-haiku-20241022.description": "Claude 3.5 Haiku هو أسرع نموذج من الجيل التالي من Anthropic، مع تحسينات في المهارات وتفوق على النموذج الرائد السابق Claude 3 Opus في العديد من المعايير.",
|
|
275
275
|
"claude-3-5-haiku-latest.description": "Claude 3.5 Haiku يقدم استجابات سريعة للمهام الخفيفة.",
|
|
276
|
-
"claude-3-7-sonnet-20250219.description": "Claude 3.7
|
|
276
|
+
"claude-3-7-sonnet-20250219.description": "Claude Sonnet 3.7 هو أذكى نموذج من Anthropic وأول نموذج هجيني للاستدلال في السوق، يدعم الاستجابات الفورية أو التفكير المطول مع تحكم دقيق.",
|
|
277
277
|
"claude-3-7-sonnet-latest.description": "Claude 3.7 Sonnet هو أحدث وأقوى نموذج من Anthropic للمهام المعقدة، يتميز بالأداء العالي، الذكاء، الطلاقة، والفهم العميق.",
|
|
278
278
|
"claude-3-haiku-20240307.description": "Claude 3 Haiku هو أسرع وأصغر نموذج من Anthropic، مصمم لتقديم استجابات شبه فورية بأداء سريع ودقيق.",
|
|
279
279
|
"claude-3-opus-20240229.description": "Claude 3 Opus هو أقوى نموذج من Anthropic للمهام المعقدة، يتميز بالأداء العالي، الذكاء، الطلاقة، والفهم.",
|
|
280
280
|
"claude-3-sonnet-20240229.description": "Claude 3 Sonnet يوازن بين الذكاء والسرعة لتلبية احتياجات المؤسسات، ويوفر فائدة عالية بتكلفة أقل ونشر موثوق على نطاق واسع.",
|
|
281
|
-
"claude-haiku-4-5-20251001.description": "Claude Haiku 4.5 هو أسرع
|
|
281
|
+
"claude-haiku-4-5-20251001.description": "Claude Haiku 4.5 هو أسرع وأكثر نماذج Haiku ذكاءً من Anthropic، يتميز بسرعة البرق وقدرة على التفكير المطول.",
|
|
282
282
|
"claude-opus-4-1-20250805-thinking.description": "Claude Opus 4.1 Thinking هو إصدار متقدم يمكنه عرض عملية تفكيره.",
|
|
283
283
|
"claude-opus-4-1-20250805.description": "Claude Opus 4.1 هو أحدث وأقوى نموذج من Anthropic للمهام المعقدة، يتميز بالأداء العالي، الذكاء، الطلاقة، والفهم.",
|
|
284
|
-
"claude-opus-4-20250514.description": "Claude Opus 4 هو أقوى نموذج من Anthropic للمهام المعقدة للغاية،
|
|
284
|
+
"claude-opus-4-20250514.description": "Claude Opus 4 هو أقوى نموذج من Anthropic للمهام المعقدة للغاية، يتفوق في الأداء والذكاء والطلاقة والفهم.",
|
|
285
285
|
"claude-opus-4-5-20251101.description": "Claude Opus 4.5 هو النموذج الرائد من Anthropic، يجمع بين الذكاء الاستثنائي والأداء القابل للتوسع، مثالي للمهام المعقدة التي تتطلب استجابات عالية الجودة وتفكير متقدم.",
|
|
286
286
|
"claude-sonnet-4-20250514-thinking.description": "Claude Sonnet 4 Thinking يمكنه تقديم استجابات شبه فورية أو تفكير متسلسل مرئي.",
|
|
287
|
-
"claude-sonnet-4-20250514.description": "Claude Sonnet 4
|
|
287
|
+
"claude-sonnet-4-20250514.description": "Claude Sonnet 4 هو أذكى نموذج من Anthropic حتى الآن، يوفر استجابات شبه فورية أو تفكير متسلسل خطوة بخطوة مع تحكم دقيق لمستخدمي واجهة البرمجة.",
|
|
288
288
|
"claude-sonnet-4-5-20250929.description": "Claude Sonnet 4.5 هو أذكى نموذج من Anthropic حتى الآن.",
|
|
289
289
|
"codegeex-4.description": "CodeGeeX-4 هو مساعد برمجة ذكي يدعم الأسئلة والأجوبة متعددة اللغات وإكمال الشيفرة لزيادة إنتاجية المطورين.",
|
|
290
290
|
"codegeex4-all-9b.description": "CodeGeeX4-ALL-9B هو نموذج توليد شيفرة متعدد اللغات يدعم الإكمال والتوليد، تفسير الشيفرة، البحث عبر الإنترنت، استدعاء الوظائف، وأسئلة وأجوبة على مستوى المستودع، ويغطي مجموعة واسعة من سيناريوهات تطوير البرمجيات. يُعد من أفضل نماذج الشيفرة تحت 10B.",
|
|
@@ -355,7 +355,7 @@
|
|
|
355
355
|
"deepseek-ai/deepseek-v3.1-terminus.description": "DeepSeek V3.1 هو نموذج تفكير من الجيل التالي يتمتع بقدرات أقوى في التفكير المعقد وسلسلة التفكير لمهام التحليل العميق.",
|
|
356
356
|
"deepseek-ai/deepseek-v3.1.description": "DeepSeek V3.1 هو نموذج تفكير من الجيل التالي يتمتع بقدرات أقوى في التفكير المعقد وسلسلة التفكير لمهام التحليل العميق.",
|
|
357
357
|
"deepseek-ai/deepseek-vl2.description": "DeepSeek-VL2 هو نموذج رؤية-لغة MoE يعتمد على DeepSeekMoE-27B مع تنشيط متفرق، ويحقق أداءً قويًا باستخدام 4.5 مليار معلمة نشطة فقط. يتميز في الأسئلة البصرية، وOCR، وفهم المستندات/الجداول/المخططات، والتأريض البصري.",
|
|
358
|
-
"deepseek-chat.description": "
|
|
358
|
+
"deepseek-chat.description": "DeepSeek V3.2 يوازن بين الاستدلال وطول المخرجات لمهام الأسئلة والأجوبة اليومية والمهام الوكيلة. يحقق نتائج مماثلة لـ GPT-5 في المعايير العامة، وهو أول من يدمج التفكير في استخدام الأدوات، مما يجعله رائدًا في تقييمات الوكلاء مفتوحة المصدر.",
|
|
359
359
|
"deepseek-coder-33B-instruct.description": "DeepSeek Coder 33B هو نموذج لغة برمجية تم تدريبه على 2 تريليون رمز (87٪ كود، 13٪ نص صيني/إنجليزي). يقدم نافذة سياق 16K ومهام الإكمال في المنتصف، ويوفر إكمال كود على مستوى المشاريع وملء مقاطع الكود.",
|
|
360
360
|
"deepseek-coder-v2.description": "DeepSeek Coder V2 هو نموذج كود MoE مفتوح المصدر يتميز بأداء قوي في مهام البرمجة، ويضاهي GPT-4 Turbo.",
|
|
361
361
|
"deepseek-coder-v2:236b.description": "DeepSeek Coder V2 هو نموذج كود MoE مفتوح المصدر يتميز بأداء قوي في مهام البرمجة، ويضاهي GPT-4 Turbo.",
|
|
@@ -378,7 +378,7 @@
|
|
|
378
378
|
"deepseek-r1-fast-online.description": "الإصدار الكامل السريع من DeepSeek R1 مع بحث ويب في الوقت الحقيقي، يجمع بين قدرات بحجم 671B واستجابة أسرع.",
|
|
379
379
|
"deepseek-r1-online.description": "الإصدار الكامل من DeepSeek R1 مع 671 مليار معلمة وبحث ويب في الوقت الحقيقي، يوفر فهمًا وتوليدًا أقوى.",
|
|
380
380
|
"deepseek-r1.description": "يستخدم DeepSeek-R1 بيانات البداية الباردة قبل التعلم المعزز ويؤدي أداءً مماثلًا لـ OpenAI-o1 في الرياضيات، والبرمجة، والتفكير.",
|
|
381
|
-
"deepseek-reasoner.description": "
|
|
381
|
+
"deepseek-reasoner.description": "DeepSeek V3.2 Thinking هو نموذج استدلال عميق يولد سلسلة من الأفكار قبل المخرجات لتحقيق دقة أعلى، مع نتائج تنافسية عالية واستدلال يقارن بـ Gemini-3.0-Pro.",
|
|
382
382
|
"deepseek-v2.description": "DeepSeek V2 هو نموذج MoE فعال لمعالجة منخفضة التكلفة.",
|
|
383
383
|
"deepseek-v2:236b.description": "DeepSeek V2 236B هو نموذج DeepSeek الموجه للبرمجة مع قدرات قوية في توليد الكود.",
|
|
384
384
|
"deepseek-v3-0324.description": "DeepSeek-V3-0324 هو نموذج MoE يحتوي على 671 مليار معلمة يتميز بقوة في البرمجة، والقدرات التقنية، وفهم السياق، والتعامل مع النصوص الطويلة.",
|
|
@@ -471,6 +471,47 @@
|
|
|
471
471
|
"ernie-tiny-8k.description": "ERNIE Tiny 8K هو نموذج فائق الخفة للأسئلة البسيطة، والتصنيف، والاستدلال منخفض التكلفة.",
|
|
472
472
|
"ernie-x1-turbo-32k.description": "ERNIE X1 Turbo 32K هو نموذج تفكير سريع بسياق 32K للاستدلال المعقد والدردشة متعددة الأدوار.",
|
|
473
473
|
"ernie-x1.1-preview.description": "معاينة ERNIE X1.1 هو نموذج تفكير مخصص للتقييم والاختبار.",
|
|
474
|
+
"fal-ai/bytedance/seedream/v4.description": "Seedream 4.0، من تطوير فريق ByteDance Seed، يدعم إدخال النصوص والصور لتوليد صور عالية الجودة وقابلة للتحكم بدرجة كبيرة من خلال الأوامر.",
|
|
475
|
+
"fal-ai/flux-kontext/dev.description": "نموذج FLUX.1 يركز على تحرير الصور، ويدعم إدخال النصوص والصور.",
|
|
476
|
+
"fal-ai/flux-pro/kontext.description": "FLUX.1 Kontext [pro] يقبل النصوص وصور مرجعية كمدخلات، مما يتيح تعديلات محلية مستهدفة وتحولات معقدة في المشهد العام.",
|
|
477
|
+
"fal-ai/flux/krea.description": "Flux Krea [dev] هو نموذج لتوليد الصور يتميز بميول جمالية نحو صور أكثر واقعية وطبيعية.",
|
|
478
|
+
"fal-ai/flux/schnell.description": "FLUX.1 [schnell] هو نموذج لتوليد الصور يحتوي على 12 مليار معلمة، مصمم لإنتاج صور عالية الجودة بسرعة.",
|
|
479
|
+
"fal-ai/hunyuan-image/v3.description": "نموذج قوي لتوليد الصور متعدد الوسائط أصلي.",
|
|
480
|
+
"fal-ai/imagen4/preview.description": "نموذج عالي الجودة لتوليد الصور من Google.",
|
|
481
|
+
"fal-ai/nano-banana.description": "Nano Banana هو أحدث وأسرع وأكثر نماذج Google كفاءةً لتوليد وتحرير الصور من خلال المحادثة.",
|
|
482
|
+
"fal-ai/qwen-image-edit.description": "نموذج احترافي لتحرير الصور من فريق Qwen، يدعم التعديلات الدلالية والمظهرية، تحرير النصوص بدقة باللغتين الصينية والإنجليزية، نقل الأنماط، التدوير، والمزيد.",
|
|
483
|
+
"fal-ai/qwen-image.description": "نموذج قوي لتوليد الصور من فريق Qwen يتميز بعرض نصوص صينية قوية وأنماط بصرية متنوعة.",
|
|
484
|
+
"flux-1-schnell.description": "نموذج تحويل النص إلى صورة يحتوي على 12 مليار معلمة من Black Forest Labs يستخدم تقنيات تقطير الانتشار العدائي الكامن لتوليد صور عالية الجودة في 1-4 خطوات. ينافس البدائل المغلقة ومتاح بموجب ترخيص Apache-2.0 للاستخدام الشخصي والبحثي والتجاري.",
|
|
485
|
+
"flux-dev.description": "FLUX.1 [dev] هو نموذج مفتوح الأوزان ومقطر للاستخدام غير التجاري. يحافظ على جودة صور قريبة من المستوى الاحترافي واتباع التعليمات مع كفاءة تشغيل أعلى مقارنة بالنماذج القياسية من نفس الحجم.",
|
|
486
|
+
"flux-kontext-max.description": "توليد وتحرير صور سياقية متقدمة، تجمع بين النصوص والصور لتحقيق نتائج دقيقة ومتسقة.",
|
|
487
|
+
"flux-kontext-pro.description": "توليد وتحرير صور سياقية متقدمة، تجمع بين النصوص والصور لتحقيق نتائج دقيقة ومتسقة.",
|
|
488
|
+
"flux-merged.description": "FLUX.1-merged يجمع بين الميزات العميقة المستكشفة في \"DEV\" مع مزايا السرعة العالية في \"Schnell\"، مما يوسع حدود الأداء ويوسع نطاق التطبيقات.",
|
|
489
|
+
"flux-pro-1.1-ultra.description": "توليد صور بدقة فائقة تصل إلى 4 ميغابكسل، تنتج صورًا واضحة في غضون 10 ثوانٍ.",
|
|
490
|
+
"flux-pro-1.1.description": "نموذج توليد صور احترافي مطور يتميز بجودة صور ممتازة واتباع دقيق للأوامر.",
|
|
491
|
+
"flux-pro.description": "نموذج توليد صور تجاري من الدرجة الأولى بجودة صور لا مثيل لها ومخرجات متنوعة.",
|
|
492
|
+
"flux-schnell.description": "FLUX.1 [schnell] هو أكثر النماذج مفتوحة المصدر تقدمًا في توليد الصور بعدد خطوات قليلة، يتفوق على المنافسين المماثلين وحتى النماذج غير المقطرة القوية مثل Midjourney v6.0 و DALL-E 3 (HD). تم ضبطه بدقة للحفاظ على تنوع ما قبل التدريب، مما يحسن بشكل كبير من جودة الصور، واتباع التعليمات، وتنوع الحجم/النسبة، والتعامل مع الخطوط، وتنوع المخرجات.",
|
|
493
|
+
"flux.1-schnell.description": "FLUX.1-schnell هو نموذج عالي الأداء لتوليد الصور يدعم أنماطًا متعددة بسرعة.",
|
|
494
|
+
"gemini-1.0-pro-001.description": "Gemini 1.0 Pro 001 (Tuning) يوفر أداءً مستقرًا وقابلًا للضبط للمهام المعقدة.",
|
|
495
|
+
"gemini-1.0-pro-002.description": "Gemini 1.0 Pro 002 (Tuning) يوفر دعمًا متعدد الوسائط قويًا للمهام المعقدة.",
|
|
496
|
+
"gemini-1.0-pro-latest.description": "Gemini 1.0 Pro هو نموذج الذكاء الاصطناعي عالي الأداء من Google المصمم لتوسيع نطاق المهام.",
|
|
497
|
+
"gemini-1.5-flash-001.description": "Gemini 1.5 Flash 001 هو نموذج متعدد الوسائط فعال لتوسيع التطبيقات على نطاق واسع.",
|
|
498
|
+
"gemini-1.5-flash-002.description": "Gemini 1.5 Flash 002 هو نموذج متعدد الوسائط فعال مصمم للنشر الواسع.",
|
|
499
|
+
"gemini-1.5-flash-8b-exp-0924.description": "Gemini 1.5 Flash 8B 0924 هو أحدث نموذج تجريبي يحقق مكاسب ملحوظة في استخدامات النصوص والمتعددة الوسائط.",
|
|
500
|
+
"gemini-1.5-flash-8b-latest.description": "Gemini 1.5 Flash 8B هو نموذج متعدد الوسائط فعال مصمم للنشر الواسع.",
|
|
501
|
+
"gemini-1.5-flash-8b.description": "Gemini 1.5 Flash 8B هو نموذج متعدد الوسائط فعال لتوسيع التطبيقات على نطاق واسع.",
|
|
502
|
+
"gemini-1.5-flash-exp-0827.description": "Gemini 1.5 Flash 0827 يقدم معالجة متعددة الوسائط محسّنة للمهام المعقدة.",
|
|
503
|
+
"gemini-1.5-flash-latest.description": "Gemini 1.5 Flash هو أحدث نموذج ذكاء اصطناعي متعدد الوسائط من Google يتميز بمعالجة سريعة ويدعم إدخال النصوص والصور والفيديو لتوسيع المهام بكفاءة.",
|
|
504
|
+
"gemini-1.5-pro-001.description": "Gemini 1.5 Pro 001 هو حل ذكاء اصطناعي متعدد الوسائط قابل للتوسع للمهام المعقدة.",
|
|
505
|
+
"gemini-1.5-pro-002.description": "Gemini 1.5 Pro 002 هو أحدث نموذج جاهز للإنتاج بجودة مخرجات أعلى، خاصة في الرياضيات والسياقات الطويلة والمهام البصرية.",
|
|
506
|
+
"gemini-1.5-pro-exp-0801.description": "Gemini 1.5 Pro 0801 يوفر معالجة متعددة الوسائط قوية مع مرونة أكبر لتطوير التطبيقات.",
|
|
507
|
+
"gemini-1.5-pro-exp-0827.description": "Gemini 1.5 Pro 0827 يطبق أحدث التحسينات لمعالجة متعددة الوسائط أكثر كفاءة.",
|
|
508
|
+
"gemini-1.5-pro-latest.description": "Gemini 1.5 Pro يدعم ما يصل إلى 2 مليون رمز، وهو نموذج متعدد الوسائط متوسط الحجم مثالي للمهام المعقدة.",
|
|
509
|
+
"gemini-2.0-flash-001.description": "Gemini 2.0 Flash يقدم ميزات الجيل التالي بما في ذلك السرعة الاستثنائية، واستخدام الأدوات الأصلية، والتوليد متعدد الوسائط، وسياق يصل إلى مليون رمز.",
|
|
510
|
+
"gemini-2.0-flash-exp-image-generation.description": "نموذج تجريبي من Gemini 2.0 Flash يدعم توليد الصور.",
|
|
511
|
+
"gemini-2.0-flash-exp.description": "إصدار من Gemini 2.0 Flash محسن لتقليل التكلفة وتقليل التأخير.",
|
|
512
|
+
"gemini-2.0-flash-lite-001.description": "إصدار من Gemini 2.0 Flash محسن لتقليل التكلفة وتقليل التأخير.",
|
|
513
|
+
"gemini-2.0-flash-lite.description": "إصدار من Gemini 2.0 Flash محسن لتقليل التكلفة وتقليل التأخير.",
|
|
514
|
+
"gemini-2.0-flash.description": "Gemini 2.0 Flash يقدم ميزات الجيل التالي بما في ذلك السرعة الاستثنائية، واستخدام الأدوات الأصلية، والتوليد متعدد الوسائط، وسياق يصل إلى مليون رمز.",
|
|
474
515
|
"meta.llama3-8b-instruct-v1:0.description": "ميتا لاما 3 هو نموذج لغوي مفتوح المصدر مخصص للمطورين والباحثين والشركات، صُمم لمساعدتهم في بناء أفكار الذكاء الاصطناعي التوليدي، وتجربتها، وتوسيع نطاقها بشكل مسؤول. يُعد جزءًا من البنية التحتية للابتكار المجتمعي العالمي، وهو مناسب للبيئات ذات الموارد المحدودة، والأجهزة الطرفية، وأوقات التدريب الأسرع.",
|
|
475
516
|
"meta/Llama-3.2-11B-Vision-Instruct.description": "قدرات قوية في الاستدلال الصوري على الصور عالية الدقة، مناسب لتطبيقات الفهم البصري.",
|
|
476
517
|
"meta/Llama-3.2-90B-Vision-Instruct.description": "استدلال صوري متقدم لتطبيقات الوكلاء المعتمدين على الفهم البصري.",
|