@lobehub/chat 1.103.1 → 1.103.2
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 +35 -0
- package/apps/desktop/build/icon-beta.ico +0 -0
- package/apps/desktop/build/icon-dev.ico +0 -0
- package/apps/desktop/build/icon-nightly.ico +0 -0
- package/apps/desktop/build/icon.ico +0 -0
- package/apps/desktop/electron.vite.config.ts +4 -2
- package/apps/desktop/package.json +1 -0
- package/apps/desktop/src/main/appBrowsers.ts +2 -2
- package/apps/desktop/src/main/const/env.ts +5 -4
- package/apps/desktop/src/main/const/store.ts +1 -0
- package/apps/desktop/src/main/const/theme.ts +11 -0
- package/apps/desktop/src/main/controllers/BrowserWindowsCtr.ts +1 -1
- package/apps/desktop/src/main/controllers/NotificationCtr.ts +2 -4
- package/apps/desktop/src/main/controllers/SystemCtr.ts +4 -0
- package/apps/desktop/src/main/controllers/TrayMenuCtr.ts +5 -5
- package/apps/desktop/src/main/controllers/index.ts +1 -1
- package/apps/desktop/src/main/core/App.ts +9 -10
- package/apps/desktop/src/main/core/{Browser.ts → browser/Browser.ts} +129 -88
- package/apps/desktop/src/main/core/{BrowserManager.ts → browser/BrowserManager.ts} +13 -3
- package/apps/desktop/src/main/core/{StaticFileServerManager.ts → infrastructure/StaticFileServerManager.ts} +13 -7
- package/apps/desktop/src/main/core/{StoreManager.ts → infrastructure/StoreManager.ts} +1 -1
- package/apps/desktop/src/main/core/{UpdaterManager.ts → infrastructure/UpdaterManager.ts} +1 -1
- package/apps/desktop/src/main/core/{MenuManager.ts → ui/MenuManager.ts} +2 -2
- package/apps/desktop/src/main/core/{ShortcutManager.ts → ui/ShortcutManager.ts} +7 -1
- package/apps/desktop/src/main/core/{Tray.ts → ui/Tray.ts} +61 -59
- package/apps/desktop/src/main/core/{TrayManager.ts → ui/TrayManager.ts} +5 -5
- package/apps/desktop/src/main/shortcuts/config.ts +2 -2
- package/apps/desktop/src/main/types/store.ts +1 -0
- package/changelog/v1.json +12 -0
- package/docs/development/basic/add-new-image-model.mdx +162 -0
- package/docs/development/basic/add-new-image-model.zh-CN.mdx +162 -0
- package/docs/usage/providers/fal.mdx +1 -1
- package/docs/usage/providers/fal.zh-CN.mdx +1 -1
- package/package.json +66 -66
- package/src/app/[variants]/(main)/chat/(workspace)/_layout/Desktop/Portal.tsx +3 -1
- package/src/app/[variants]/(main)/chat/(workspace)/features/AgentSettings/index.tsx +4 -2
- package/src/app/[variants]/(main)/image/@menu/components/SeedNumberInput/index.tsx +1 -1
- package/src/app/[variants]/(main)/image/features/GenerationFeed/BatchItem.tsx +39 -3
- package/src/app/[variants]/(main)/image/features/GenerationFeed/ReferenceImages.tsx +122 -0
- package/src/config/aiModels/fal.ts +31 -7
- package/src/config/aiModels/openai.ts +10 -1
- package/src/features/ElectronTitlebar/WinControl/index.tsx +85 -90
- package/src/features/ElectronTitlebar/hooks/useWatchThemeUpdate.ts +10 -5
- package/src/features/ImageTopicPanel/index.tsx +0 -1
- package/src/features/PluginDevModal/index.tsx +3 -1
- package/src/features/User/__tests__/UserAvatar.test.tsx +5 -4
- package/src/libs/model-runtime/fal/index.ts +1 -1
- package/src/libs/model-runtime/types/image.ts +1 -1
- package/src/libs/model-runtime/utils/openaiCompatibleFactory/index.ts +1 -1
- package/src/libs/model-runtime/utils/response.ts +2 -0
- package/src/libs/model-runtime/utils/streams/google-ai.test.ts +46 -0
- package/src/libs/model-runtime/utils/streams/google-ai.ts +4 -4
- package/src/libs/model-runtime/utils/streams/vertex-ai.ts +6 -8
- package/src/libs/standard-parameters/{meta-schema.test.ts → index.test.ts} +1 -1
- package/src/libs/standard-parameters/index.ts +152 -1
- package/src/server/ld.test.ts +4 -3
- package/src/server/routers/async/image.ts +1 -1
- package/src/services/__tests__/chat.test.ts +3 -4
- package/src/store/chat/slices/message/selectors.test.ts +2 -3
- package/src/store/chat/slices/plugin/action.test.ts +2 -1
- package/src/store/image/slices/generationConfig/action.test.ts +2 -2
- package/src/store/image/slices/generationConfig/action.ts +1 -1
- package/src/store/image/slices/generationConfig/hooks.test.ts +2 -2
- package/src/store/image/slices/generationConfig/hooks.ts +1 -4
- package/src/store/image/slices/generationConfig/initialState.ts +2 -2
- package/src/store/image/slices/generationConfig/selectors.test.ts +2 -2
- package/src/store/image/slices/generationConfig/selectors.ts +1 -1
- package/src/store/user/slices/auth/selectors.test.ts +3 -2
- package/src/types/generation/index.ts +1 -0
- package/docs/development/basic/add-new-ai-image-model.mdx +0 -36
- package/docs/development/basic/add-new-ai-image-model.zh-CN.mdx +0 -0
- package/src/config/paramsSchemas/fal/flux-kontext-dev.ts +0 -8
- package/src/config/paramsSchemas/fal/flux-pro-kontext.ts +0 -11
- package/src/config/paramsSchemas/fal/flux-schnell.ts +0 -9
- package/src/config/paramsSchemas/fal/imagen4.ts +0 -10
- package/src/config/paramsSchemas/openai/gpt-image-1.ts +0 -10
- package/src/libs/standard-parameters/meta-schema.ts +0 -147
- /package/apps/desktop/src/main/core/{I18nManager.ts → infrastructure/I18nManager.ts} +0 -0
- /package/apps/desktop/src/main/core/{IoCContainer.ts → infrastructure/IoCContainer.ts} +0 -0
@@ -5,7 +5,7 @@ import { isDev } from '@/const/env';
|
|
5
5
|
import { UPDATE_CHANNEL as channel, updaterConfig } from '@/modules/updater/configs';
|
6
6
|
import { createLogger } from '@/utils/logger';
|
7
7
|
|
8
|
-
import type { App as AppCore } from '
|
8
|
+
import type { App as AppCore } from '../App';
|
9
9
|
|
10
10
|
// Create logger
|
11
11
|
const logger = createLogger('core:UpdaterManager');
|
@@ -3,12 +3,12 @@ import { Menu } from 'electron';
|
|
3
3
|
import { IMenuPlatform, MenuOptions, createMenuImpl } from '@/menus';
|
4
4
|
import { createLogger } from '@/utils/logger';
|
5
5
|
|
6
|
-
import type { App } from '
|
6
|
+
import type { App } from '../App';
|
7
7
|
|
8
8
|
// Create logger
|
9
9
|
const logger = createLogger('core:MenuManager');
|
10
10
|
|
11
|
-
export
|
11
|
+
export class MenuManager {
|
12
12
|
app: App;
|
13
13
|
private platformImpl: IMenuPlatform;
|
14
14
|
|
@@ -3,7 +3,7 @@ import { globalShortcut } from 'electron';
|
|
3
3
|
import { DEFAULT_SHORTCUTS_CONFIG } from '@/shortcuts';
|
4
4
|
import { createLogger } from '@/utils/logger';
|
5
5
|
|
6
|
-
import type { App } from '
|
6
|
+
import type { App } from '../App';
|
7
7
|
|
8
8
|
// Create logger
|
9
9
|
const logger = createLogger('core:ShortcutManager');
|
@@ -164,6 +164,12 @@ export class ShortcutManager {
|
|
164
164
|
Object.entries(this.shortcutsConfig).forEach(([id, accelerator]) => {
|
165
165
|
logger.debug(`Registering shortcut '${id}' with ${accelerator}`);
|
166
166
|
|
167
|
+
// 只注册在 DEFAULT_SHORTCUTS_CONFIG 中存在的快捷键
|
168
|
+
if (!DEFAULT_SHORTCUTS_CONFIG[id]) {
|
169
|
+
logger.debug(`Skipping shortcut '${id}' - not found in DEFAULT_SHORTCUTS_CONFIG`);
|
170
|
+
return;
|
171
|
+
}
|
172
|
+
|
167
173
|
const method = this.shortcuts.get(id);
|
168
174
|
if (accelerator && method) {
|
169
175
|
this.registerShortcut(accelerator, method);
|
@@ -12,157 +12,157 @@ import { join } from 'node:path';
|
|
12
12
|
import { resourcesDir } from '@/const/dir';
|
13
13
|
import { createLogger } from '@/utils/logger';
|
14
14
|
|
15
|
-
import type { App } from '
|
15
|
+
import type { App } from '../App';
|
16
16
|
|
17
|
-
//
|
17
|
+
// Create logger
|
18
18
|
const logger = createLogger('core:Tray');
|
19
19
|
|
20
20
|
export interface TrayOptions {
|
21
21
|
/**
|
22
|
-
*
|
22
|
+
* Tray icon path (relative to resource directory)
|
23
23
|
*/
|
24
24
|
iconPath: string;
|
25
25
|
|
26
26
|
/**
|
27
|
-
*
|
27
|
+
* Tray identifier
|
28
28
|
*/
|
29
29
|
identifier: string;
|
30
30
|
|
31
31
|
/**
|
32
|
-
*
|
32
|
+
* Tray tooltip text
|
33
33
|
*/
|
34
34
|
tooltip?: string;
|
35
35
|
}
|
36
36
|
|
37
|
-
export
|
37
|
+
export class Tray {
|
38
38
|
private app: App;
|
39
39
|
|
40
40
|
/**
|
41
|
-
*
|
41
|
+
* Internal Electron tray
|
42
42
|
*/
|
43
43
|
private _tray?: ElectronTray;
|
44
44
|
|
45
45
|
/**
|
46
|
-
*
|
46
|
+
* Identifier
|
47
47
|
*/
|
48
48
|
identifier: string;
|
49
49
|
|
50
50
|
/**
|
51
|
-
*
|
51
|
+
* Options when created
|
52
52
|
*/
|
53
53
|
options: TrayOptions;
|
54
54
|
|
55
55
|
/**
|
56
|
-
*
|
56
|
+
* Get tray instance
|
57
57
|
*/
|
58
58
|
get tray() {
|
59
59
|
return this.retrieveOrInitialize();
|
60
60
|
}
|
61
61
|
|
62
62
|
/**
|
63
|
-
*
|
64
|
-
* @param options
|
65
|
-
* @param application
|
63
|
+
* Construct tray object
|
64
|
+
* @param options Tray options
|
65
|
+
* @param application App instance
|
66
66
|
*/
|
67
67
|
constructor(options: TrayOptions, application: App) {
|
68
|
-
logger.debug(
|
69
|
-
logger.debug(
|
68
|
+
logger.debug(`Creating tray instance: ${options.identifier}`);
|
69
|
+
logger.debug(`Tray options: ${JSON.stringify(options)}`);
|
70
70
|
this.app = application;
|
71
71
|
this.identifier = options.identifier;
|
72
72
|
this.options = options;
|
73
73
|
|
74
|
-
//
|
74
|
+
// Initialize
|
75
75
|
this.retrieveOrInitialize();
|
76
76
|
}
|
77
77
|
|
78
78
|
/**
|
79
|
-
*
|
79
|
+
* Initialize tray
|
80
80
|
*/
|
81
81
|
retrieveOrInitialize() {
|
82
|
-
//
|
82
|
+
// If tray already exists and is not destroyed, return it
|
83
83
|
if (this._tray) {
|
84
|
-
logger.debug(`[${this.identifier}]
|
84
|
+
logger.debug(`[${this.identifier}] Returning existing tray instance`);
|
85
85
|
return this._tray;
|
86
86
|
}
|
87
87
|
|
88
88
|
const { iconPath, tooltip } = this.options;
|
89
89
|
|
90
|
-
//
|
91
|
-
logger.info(
|
90
|
+
// Load tray icon
|
91
|
+
logger.info(`Creating new tray instance: ${this.identifier}`);
|
92
92
|
const iconFile = join(resourcesDir, iconPath);
|
93
|
-
logger.debug(`[${this.identifier}]
|
93
|
+
logger.debug(`[${this.identifier}] Loading icon: ${iconFile}`);
|
94
94
|
|
95
95
|
try {
|
96
96
|
const icon = nativeImage.createFromPath(iconFile);
|
97
97
|
this._tray = new ElectronTray(icon);
|
98
98
|
|
99
|
-
//
|
99
|
+
// Set tooltip
|
100
100
|
if (tooltip) {
|
101
|
-
logger.debug(`[${this.identifier}]
|
101
|
+
logger.debug(`[${this.identifier}] Setting tooltip: ${tooltip}`);
|
102
102
|
this._tray.setToolTip(tooltip);
|
103
103
|
}
|
104
104
|
|
105
|
-
//
|
105
|
+
// Set default context menu
|
106
106
|
this.setContextMenu();
|
107
107
|
|
108
|
-
//
|
108
|
+
// Set click event
|
109
109
|
this._tray.on('click', () => {
|
110
|
-
logger.debug(`[${this.identifier}]
|
110
|
+
logger.debug(`[${this.identifier}] Tray clicked`);
|
111
111
|
this.onClick();
|
112
112
|
});
|
113
113
|
|
114
|
-
logger.debug(`[${this.identifier}]
|
114
|
+
logger.debug(`[${this.identifier}] Tray instance created successfully`);
|
115
115
|
return this._tray;
|
116
116
|
} catch (error) {
|
117
|
-
logger.error(`[${this.identifier}]
|
117
|
+
logger.error(`[${this.identifier}] Failed to create tray:`, error);
|
118
118
|
throw error;
|
119
119
|
}
|
120
120
|
}
|
121
121
|
|
122
122
|
/**
|
123
|
-
*
|
124
|
-
* @param template
|
123
|
+
* Set tray context menu
|
124
|
+
* @param template Menu template, if not provided default template will be used
|
125
125
|
*/
|
126
126
|
setContextMenu(template?: MenuItemConstructorOptions[]) {
|
127
|
-
logger.debug(`[${this.identifier}]
|
127
|
+
logger.debug(`[${this.identifier}] Setting tray context menu`);
|
128
128
|
|
129
|
-
//
|
129
|
+
// If no template provided, use default menu
|
130
130
|
const defaultTemplate: MenuItemConstructorOptions[] = template || [
|
131
131
|
{
|
132
132
|
click: () => {
|
133
|
-
logger.debug(`[${this.identifier}]
|
133
|
+
logger.debug(`[${this.identifier}] Menu item "Show Main Window" clicked`);
|
134
134
|
this.app.browserManager.showMainWindow();
|
135
135
|
},
|
136
|
-
label: '
|
136
|
+
label: 'Show Main Window',
|
137
137
|
},
|
138
138
|
{ type: 'separator' },
|
139
139
|
{
|
140
140
|
click: () => {
|
141
|
-
logger.debug(`[${this.identifier}]
|
141
|
+
logger.debug(`[${this.identifier}] Menu item "Quit" clicked`);
|
142
142
|
app.quit();
|
143
143
|
},
|
144
|
-
label: '
|
144
|
+
label: 'Quit',
|
145
145
|
},
|
146
146
|
];
|
147
147
|
|
148
148
|
const contextMenu = Menu.buildFromTemplate(defaultTemplate);
|
149
149
|
this._tray?.setContextMenu(contextMenu);
|
150
|
-
logger.debug(`[${this.identifier}]
|
150
|
+
logger.debug(`[${this.identifier}] Tray context menu has been set`);
|
151
151
|
}
|
152
152
|
|
153
153
|
/**
|
154
|
-
*
|
154
|
+
* Handle tray click event
|
155
155
|
*/
|
156
156
|
onClick() {
|
157
|
-
logger.debug(`[${this.identifier}]
|
157
|
+
logger.debug(`[${this.identifier}] Handling tray click event`);
|
158
158
|
const mainWindow = this.app.browserManager.getMainWindow();
|
159
159
|
|
160
160
|
if (mainWindow) {
|
161
161
|
if (mainWindow.browserWindow.isVisible() && mainWindow.browserWindow.isFocused()) {
|
162
|
-
logger.debug(`[${this.identifier}]
|
162
|
+
logger.debug(`[${this.identifier}] Main window is visible and focused, hiding it now`);
|
163
163
|
mainWindow.hide();
|
164
164
|
} else {
|
165
|
-
logger.debug(`[${this.identifier}]
|
165
|
+
logger.debug(`[${this.identifier}] Showing and focusing main window`);
|
166
166
|
mainWindow.show();
|
167
167
|
mainWindow.browserWindow.focus();
|
168
168
|
}
|
@@ -170,59 +170,61 @@ export default class Tray {
|
|
170
170
|
}
|
171
171
|
|
172
172
|
/**
|
173
|
-
*
|
174
|
-
* @param iconPath
|
173
|
+
* Update tray icon
|
174
|
+
* @param iconPath New icon path (relative to resource directory)
|
175
175
|
*/
|
176
176
|
updateIcon(iconPath: string) {
|
177
|
-
logger.debug(`[${this.identifier}]
|
177
|
+
logger.debug(`[${this.identifier}] Updating icon: ${iconPath}`);
|
178
178
|
try {
|
179
179
|
const iconFile = join(resourcesDir, iconPath);
|
180
180
|
const icon = nativeImage.createFromPath(iconFile);
|
181
181
|
this._tray?.setImage(icon);
|
182
182
|
this.options.iconPath = iconPath;
|
183
|
-
logger.debug(`[${this.identifier}]
|
183
|
+
logger.debug(`[${this.identifier}] Icon updated successfully`);
|
184
184
|
} catch (error) {
|
185
|
-
logger.error(`[${this.identifier}]
|
185
|
+
logger.error(`[${this.identifier}] Failed to update icon:`, error);
|
186
186
|
}
|
187
187
|
}
|
188
188
|
|
189
189
|
/**
|
190
|
-
*
|
191
|
-
* @param tooltip
|
190
|
+
* Update tooltip text
|
191
|
+
* @param tooltip New tooltip text
|
192
192
|
*/
|
193
193
|
updateTooltip(tooltip: string) {
|
194
|
-
logger.debug(`[${this.identifier}]
|
194
|
+
logger.debug(`[${this.identifier}] Updating tooltip: ${tooltip}`);
|
195
195
|
this._tray?.setToolTip(tooltip);
|
196
196
|
this.options.tooltip = tooltip;
|
197
197
|
}
|
198
198
|
|
199
199
|
/**
|
200
|
-
*
|
201
|
-
* @param options
|
200
|
+
* Display balloon notification (only supported on Windows)
|
201
|
+
* @param options Balloon options
|
202
202
|
*/
|
203
203
|
displayBalloon(options: DisplayBalloonOptions) {
|
204
204
|
if (process.platform === 'win32' && this._tray) {
|
205
|
-
logger.debug(
|
205
|
+
logger.debug(
|
206
|
+
`[${this.identifier}] Displaying balloon notification: ${JSON.stringify(options)}`,
|
207
|
+
);
|
206
208
|
this._tray.displayBalloon(options);
|
207
209
|
} else {
|
208
|
-
logger.debug(`[${this.identifier}]
|
210
|
+
logger.debug(`[${this.identifier}] Balloon notification is only supported on Windows`);
|
209
211
|
}
|
210
212
|
}
|
211
213
|
|
212
214
|
/**
|
213
|
-
*
|
215
|
+
* Broadcast event
|
214
216
|
*/
|
215
217
|
broadcast = <T extends MainBroadcastEventKey>(channel: T, data?: MainBroadcastParams<T>) => {
|
216
|
-
logger.debug(
|
217
|
-
//
|
218
|
+
logger.debug(`Broadcasting to tray ${this.identifier}, channel: ${channel}`);
|
219
|
+
// Can forward message to main window through App instance's browserManager
|
218
220
|
this.app.browserManager.getMainWindow()?.broadcast(channel, data);
|
219
221
|
};
|
220
222
|
|
221
223
|
/**
|
222
|
-
*
|
224
|
+
* Destroy tray instance
|
223
225
|
*/
|
224
226
|
destroy() {
|
225
|
-
logger.debug(
|
227
|
+
logger.debug(`Destroying tray instance: ${this.identifier}`);
|
226
228
|
if (this._tray) {
|
227
229
|
this._tray.destroy();
|
228
230
|
this._tray = undefined;
|
@@ -5,8 +5,8 @@ import { name } from '@/../../package.json';
|
|
5
5
|
import { isMac } from '@/const/env';
|
6
6
|
import { createLogger } from '@/utils/logger';
|
7
7
|
|
8
|
-
import type { App } from '
|
9
|
-
import Tray,
|
8
|
+
import type { App } from '../App';
|
9
|
+
import { Tray, TrayOptions } from './Tray';
|
10
10
|
|
11
11
|
// 创建日志记录器
|
12
12
|
const logger = createLogger('core:TrayManager');
|
@@ -16,7 +16,7 @@ const logger = createLogger('core:TrayManager');
|
|
16
16
|
*/
|
17
17
|
export type TrayIdentifiers = 'main';
|
18
18
|
|
19
|
-
export
|
19
|
+
export class TrayManager {
|
20
20
|
app: App;
|
21
21
|
|
22
22
|
/**
|
@@ -61,8 +61,8 @@ export default class TrayManager {
|
|
61
61
|
? 'tray-dark.png'
|
62
62
|
: 'tray-light.png'
|
63
63
|
: 'tray.png',
|
64
|
-
identifier: 'main', //
|
65
|
-
tooltip: name, //
|
64
|
+
identifier: 'main', // Use app icon, ensure this file exists in resources directory
|
65
|
+
tooltip: name, // Can use app.getName() or localized string
|
66
66
|
});
|
67
67
|
}
|
68
68
|
|
@@ -5,7 +5,7 @@ export const ShortcutActionEnum = {
|
|
5
5
|
/**
|
6
6
|
* 显示/隐藏主窗口
|
7
7
|
*/
|
8
|
-
|
8
|
+
showMainWindow: 'showMainWindow',
|
9
9
|
} as const;
|
10
10
|
|
11
11
|
export type ShortcutActionType = (typeof ShortcutActionEnum)[keyof typeof ShortcutActionEnum];
|
@@ -14,5 +14,5 @@ export type ShortcutActionType = (typeof ShortcutActionEnum)[keyof typeof Shortc
|
|
14
14
|
* 默认快捷键配置
|
15
15
|
*/
|
16
16
|
export const DEFAULT_SHORTCUTS_CONFIG: Record<ShortcutActionType, string> = {
|
17
|
-
[ShortcutActionEnum.
|
17
|
+
[ShortcutActionEnum.showMainWindow]: 'Control+E',
|
18
18
|
};
|
package/changelog/v1.json
CHANGED
@@ -1,4 +1,16 @@
|
|
1
1
|
[
|
2
|
+
{
|
3
|
+
"children": {
|
4
|
+
"fixes": [
|
5
|
+
"Fix chat stream in desktop and update shortcut."
|
6
|
+
],
|
7
|
+
"improvements": [
|
8
|
+
"Add cached token count to usage of GoogleAI and VertexAI, fix desktop titlebar style in window, fix sub topic width in md responsive."
|
9
|
+
]
|
10
|
+
},
|
11
|
+
"date": "2025-07-24",
|
12
|
+
"version": "1.103.2"
|
13
|
+
},
|
2
14
|
{
|
3
15
|
"children": {
|
4
16
|
"improvements": [
|
@@ -0,0 +1,162 @@
|
|
1
|
+
# Adding New Image Models
|
2
|
+
|
3
|
+
> Learn more about the AI image generation modal design in the [AI Image Generation Modal Design Discussion](https://github.com/lobehub/lobe-chat/discussions/7442)
|
4
|
+
|
5
|
+
## Parameter Standardization
|
6
|
+
|
7
|
+
All image generation models must use the standard parameters defined in `src/libs/standard-parameters/index.ts`. This ensures parameter consistency across different Providers, creating a more unified user experience.
|
8
|
+
|
9
|
+
**Supported Standard Parameters**:
|
10
|
+
|
11
|
+
- `prompt` (required): Text prompt for image generation
|
12
|
+
- `aspectRatio`: Aspect ratio (e.g., "16:9", "1:1")
|
13
|
+
- `width` / `height`: Image dimensions
|
14
|
+
- `size`: Preset dimensions (e.g., "1024x1024")
|
15
|
+
- `seed`: Random seed
|
16
|
+
- `steps`: Generation steps
|
17
|
+
- `cfg`: Guidance scale
|
18
|
+
- For other parameters, please check the source file
|
19
|
+
|
20
|
+
## OpenAI Compatible Models
|
21
|
+
|
22
|
+
These models can be requested using the OpenAI SDK, with request parameters and return values consistent with DALL-E and GPT-Image-X series.
|
23
|
+
|
24
|
+
Taking Zhipu's CogView-4 as an example, which is an OpenAI-compatible model, you can add it by adding the model configuration in the corresponding AI models file `src/config/aiModels/zhipu.ts`:
|
25
|
+
|
26
|
+
```ts
|
27
|
+
const zhipuImageModels: AIImageModelCard[] = [
|
28
|
+
// Add model configuration
|
29
|
+
// https://bigmodel.cn/dev/howuse/image-generation-model/cogview-4
|
30
|
+
{
|
31
|
+
description:
|
32
|
+
'CogView-4 is the first open-source text-to-image model from Zhipu that supports Chinese character generation, with comprehensive improvements in semantic understanding, image generation quality, and Chinese-English text generation capabilities.',
|
33
|
+
displayName: 'CogView-4',
|
34
|
+
enabled: true,
|
35
|
+
id: 'cogview-4',
|
36
|
+
parameters: {
|
37
|
+
prompt: {
|
38
|
+
default: '',
|
39
|
+
},
|
40
|
+
size: {
|
41
|
+
default: '1024x1024',
|
42
|
+
enum: ['1024x1024', '768x1344', '864x1152', '1344x768', '1152x864', '1440x720', '720x1440'],
|
43
|
+
},
|
44
|
+
},
|
45
|
+
releasedAt: '2025-03-04',
|
46
|
+
type: 'image',
|
47
|
+
},
|
48
|
+
];
|
49
|
+
```
|
50
|
+
|
51
|
+
## Non-OpenAI Compatible Models
|
52
|
+
|
53
|
+
For image generation models that are not compatible with OpenAI format, you need to implement a custom `createImage` method. There are two main implementation approaches:
|
54
|
+
|
55
|
+
### Method 1: Using OpenAI Compatible Factory
|
56
|
+
|
57
|
+
Most Providers use `openaiCompatibleFactory` for OpenAI compatibility. You can pass in a custom `createImage` function (reference [PR #8534](https://github.com/lobehub/lobe-chat/pull/8534)).
|
58
|
+
|
59
|
+
**Implementation Steps**:
|
60
|
+
|
61
|
+
1. **Read Provider documentation and standard parameter definitions**
|
62
|
+
- Review the Provider's image generation API documentation to understand request and response formats
|
63
|
+
- Read `src/libs/standard-parameters/index.ts` to understand supported parameters
|
64
|
+
- Add image model configuration in the corresponding AI models file
|
65
|
+
|
66
|
+
2. **Implement custom createImage method**
|
67
|
+
- Create a standalone image generation function that accepts standard parameters
|
68
|
+
- Convert standard parameters to Provider-specific format
|
69
|
+
- Call the Provider's image generation API
|
70
|
+
- Return a unified response format (imageUrl and optional width/height)
|
71
|
+
|
72
|
+
3. **Add tests**
|
73
|
+
- Write unit tests covering success scenarios
|
74
|
+
- Test various error cases and edge conditions
|
75
|
+
|
76
|
+
**Code Example**:
|
77
|
+
|
78
|
+
```ts
|
79
|
+
// src/libs/model-runtime/provider-name/createImage.ts
|
80
|
+
export const createProviderImage = async (
|
81
|
+
payload: ImageGenerationPayload,
|
82
|
+
options: any,
|
83
|
+
): Promise<ImageGenerationResponse> => {
|
84
|
+
const { model, prompt, ...params } = payload;
|
85
|
+
|
86
|
+
// Call Provider's native API
|
87
|
+
const result = await callProviderAPI({
|
88
|
+
model,
|
89
|
+
prompt,
|
90
|
+
// Convert parameter format
|
91
|
+
custom_param: params.width,
|
92
|
+
// ...
|
93
|
+
});
|
94
|
+
|
95
|
+
// Return unified format
|
96
|
+
return {
|
97
|
+
created: Date.now(),
|
98
|
+
data: [{ url: result.imageUrl }],
|
99
|
+
};
|
100
|
+
};
|
101
|
+
```
|
102
|
+
|
103
|
+
```ts
|
104
|
+
// src/libs/model-runtime/provider-name/index.ts
|
105
|
+
export const LobeProviderAI = openaiCompatibleFactory({
|
106
|
+
constructorOptions: {
|
107
|
+
// ... other configurations
|
108
|
+
},
|
109
|
+
createImage: createProviderImage, // Pass custom implementation
|
110
|
+
provider: ModelProvider.ProviderName,
|
111
|
+
});
|
112
|
+
```
|
113
|
+
|
114
|
+
### Method 2: Direct Implementation in Provider Class
|
115
|
+
|
116
|
+
If your Provider has an independent class implementation, you can directly add the `createImage` method in the class (reference [PR #8503](https://github.com/lobehub/lobe-chat/pull/8503)).
|
117
|
+
|
118
|
+
**Implementation Steps**:
|
119
|
+
|
120
|
+
1. **Read Provider documentation and standard parameter definitions**
|
121
|
+
- Review the Provider's image generation API documentation
|
122
|
+
- Read `src/libs/standard-parameters/index.ts`
|
123
|
+
- Add image model configuration in the corresponding AI models file
|
124
|
+
|
125
|
+
2. **Implement createImage method in Provider class**
|
126
|
+
- Add the `createImage` method directly in the class
|
127
|
+
- Handle parameter conversion and API calls
|
128
|
+
- Return a unified response format
|
129
|
+
|
130
|
+
3. **Add tests**
|
131
|
+
- Write comprehensive test cases for the new method
|
132
|
+
|
133
|
+
**Code Example**:
|
134
|
+
|
135
|
+
```ts
|
136
|
+
// src/libs/model-runtime/provider-name/index.ts
|
137
|
+
export class LobeProviderAI {
|
138
|
+
async createImage(
|
139
|
+
payload: ImageGenerationPayload,
|
140
|
+
options?: ChatStreamCallbacks,
|
141
|
+
): Promise<ImageGenerationResponse> {
|
142
|
+
const { model, prompt, ...params } = payload;
|
143
|
+
|
144
|
+
// Call native API and handle response
|
145
|
+
const result = await this.client.generateImage({
|
146
|
+
model,
|
147
|
+
prompt,
|
148
|
+
// Parameter conversion
|
149
|
+
});
|
150
|
+
|
151
|
+
return {
|
152
|
+
created: Date.now(),
|
153
|
+
data: [{ url: result.url }],
|
154
|
+
};
|
155
|
+
}
|
156
|
+
}
|
157
|
+
```
|
158
|
+
|
159
|
+
### Important Notes
|
160
|
+
|
161
|
+
- **Testing Requirements**: Add comprehensive unit tests for custom implementations, ensuring coverage of success scenarios and various error cases
|
162
|
+
- **Error Handling**: Use `AgentRuntimeError` consistently for error wrapping to maintain error message consistency
|