@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.
Files changed (79) hide show
  1. package/CHANGELOG.md +35 -0
  2. package/apps/desktop/build/icon-beta.ico +0 -0
  3. package/apps/desktop/build/icon-dev.ico +0 -0
  4. package/apps/desktop/build/icon-nightly.ico +0 -0
  5. package/apps/desktop/build/icon.ico +0 -0
  6. package/apps/desktop/electron.vite.config.ts +4 -2
  7. package/apps/desktop/package.json +1 -0
  8. package/apps/desktop/src/main/appBrowsers.ts +2 -2
  9. package/apps/desktop/src/main/const/env.ts +5 -4
  10. package/apps/desktop/src/main/const/store.ts +1 -0
  11. package/apps/desktop/src/main/const/theme.ts +11 -0
  12. package/apps/desktop/src/main/controllers/BrowserWindowsCtr.ts +1 -1
  13. package/apps/desktop/src/main/controllers/NotificationCtr.ts +2 -4
  14. package/apps/desktop/src/main/controllers/SystemCtr.ts +4 -0
  15. package/apps/desktop/src/main/controllers/TrayMenuCtr.ts +5 -5
  16. package/apps/desktop/src/main/controllers/index.ts +1 -1
  17. package/apps/desktop/src/main/core/App.ts +9 -10
  18. package/apps/desktop/src/main/core/{Browser.ts → browser/Browser.ts} +129 -88
  19. package/apps/desktop/src/main/core/{BrowserManager.ts → browser/BrowserManager.ts} +13 -3
  20. package/apps/desktop/src/main/core/{StaticFileServerManager.ts → infrastructure/StaticFileServerManager.ts} +13 -7
  21. package/apps/desktop/src/main/core/{StoreManager.ts → infrastructure/StoreManager.ts} +1 -1
  22. package/apps/desktop/src/main/core/{UpdaterManager.ts → infrastructure/UpdaterManager.ts} +1 -1
  23. package/apps/desktop/src/main/core/{MenuManager.ts → ui/MenuManager.ts} +2 -2
  24. package/apps/desktop/src/main/core/{ShortcutManager.ts → ui/ShortcutManager.ts} +7 -1
  25. package/apps/desktop/src/main/core/{Tray.ts → ui/Tray.ts} +61 -59
  26. package/apps/desktop/src/main/core/{TrayManager.ts → ui/TrayManager.ts} +5 -5
  27. package/apps/desktop/src/main/shortcuts/config.ts +2 -2
  28. package/apps/desktop/src/main/types/store.ts +1 -0
  29. package/changelog/v1.json +12 -0
  30. package/docs/development/basic/add-new-image-model.mdx +162 -0
  31. package/docs/development/basic/add-new-image-model.zh-CN.mdx +162 -0
  32. package/docs/usage/providers/fal.mdx +1 -1
  33. package/docs/usage/providers/fal.zh-CN.mdx +1 -1
  34. package/package.json +66 -66
  35. package/src/app/[variants]/(main)/chat/(workspace)/_layout/Desktop/Portal.tsx +3 -1
  36. package/src/app/[variants]/(main)/chat/(workspace)/features/AgentSettings/index.tsx +4 -2
  37. package/src/app/[variants]/(main)/image/@menu/components/SeedNumberInput/index.tsx +1 -1
  38. package/src/app/[variants]/(main)/image/features/GenerationFeed/BatchItem.tsx +39 -3
  39. package/src/app/[variants]/(main)/image/features/GenerationFeed/ReferenceImages.tsx +122 -0
  40. package/src/config/aiModels/fal.ts +31 -7
  41. package/src/config/aiModels/openai.ts +10 -1
  42. package/src/features/ElectronTitlebar/WinControl/index.tsx +85 -90
  43. package/src/features/ElectronTitlebar/hooks/useWatchThemeUpdate.ts +10 -5
  44. package/src/features/ImageTopicPanel/index.tsx +0 -1
  45. package/src/features/PluginDevModal/index.tsx +3 -1
  46. package/src/features/User/__tests__/UserAvatar.test.tsx +5 -4
  47. package/src/libs/model-runtime/fal/index.ts +1 -1
  48. package/src/libs/model-runtime/types/image.ts +1 -1
  49. package/src/libs/model-runtime/utils/openaiCompatibleFactory/index.ts +1 -1
  50. package/src/libs/model-runtime/utils/response.ts +2 -0
  51. package/src/libs/model-runtime/utils/streams/google-ai.test.ts +46 -0
  52. package/src/libs/model-runtime/utils/streams/google-ai.ts +4 -4
  53. package/src/libs/model-runtime/utils/streams/vertex-ai.ts +6 -8
  54. package/src/libs/standard-parameters/{meta-schema.test.ts → index.test.ts} +1 -1
  55. package/src/libs/standard-parameters/index.ts +152 -1
  56. package/src/server/ld.test.ts +4 -3
  57. package/src/server/routers/async/image.ts +1 -1
  58. package/src/services/__tests__/chat.test.ts +3 -4
  59. package/src/store/chat/slices/message/selectors.test.ts +2 -3
  60. package/src/store/chat/slices/plugin/action.test.ts +2 -1
  61. package/src/store/image/slices/generationConfig/action.test.ts +2 -2
  62. package/src/store/image/slices/generationConfig/action.ts +1 -1
  63. package/src/store/image/slices/generationConfig/hooks.test.ts +2 -2
  64. package/src/store/image/slices/generationConfig/hooks.ts +1 -4
  65. package/src/store/image/slices/generationConfig/initialState.ts +2 -2
  66. package/src/store/image/slices/generationConfig/selectors.test.ts +2 -2
  67. package/src/store/image/slices/generationConfig/selectors.ts +1 -1
  68. package/src/store/user/slices/auth/selectors.test.ts +3 -2
  69. package/src/types/generation/index.ts +1 -0
  70. package/docs/development/basic/add-new-ai-image-model.mdx +0 -36
  71. package/docs/development/basic/add-new-ai-image-model.zh-CN.mdx +0 -0
  72. package/src/config/paramsSchemas/fal/flux-kontext-dev.ts +0 -8
  73. package/src/config/paramsSchemas/fal/flux-pro-kontext.ts +0 -11
  74. package/src/config/paramsSchemas/fal/flux-schnell.ts +0 -9
  75. package/src/config/paramsSchemas/fal/imagen4.ts +0 -10
  76. package/src/config/paramsSchemas/openai/gpt-image-1.ts +0 -10
  77. package/src/libs/standard-parameters/meta-schema.ts +0 -147
  78. /package/apps/desktop/src/main/core/{I18nManager.ts → infrastructure/I18nManager.ts} +0 -0
  79. /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 './App';
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 './App';
6
+ import type { App } from '../App';
7
7
 
8
8
  // Create logger
9
9
  const logger = createLogger('core:MenuManager');
10
10
 
11
- export default class MenuManager {
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 './App';
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 './App';
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 default class Tray {
37
+ export class Tray {
38
38
  private app: App;
39
39
 
40
40
  /**
41
- * 内部 Electron 托盘
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(`创建托盘实例: ${options.identifier}`);
69
- logger.debug(`托盘选项: ${JSON.stringify(options)}`);
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(`创建新的托盘实例: ${this.identifier}`);
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}] 加载图标: ${iconFile}`);
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}] 设置提示文本: ${tooltip}`);
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}] 创建托盘失败:`, error);
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}] 更新图标: ${iconPath}`);
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}] 更新图标失败:`, error);
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}] 更新提示文本: ${tooltip}`);
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
- * 显示气泡通知(仅在 Windows 上支持)
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(`[${this.identifier}] 显示气泡通知: ${JSON.stringify(options)}`);
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}] 气泡通知仅在 Windows 上支持`);
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(`向托盘 ${this.identifier} 广播, 频道: ${channel}`);
217
- // 可以通过 App 实例的 browserManager 将消息转发到主窗口
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(`销毁托盘实例: ${this.identifier}`);
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 './App';
9
- import Tray, { TrayOptions } from './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 default class TrayManager {
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, // 可以使用 app.getName() 或本地化字符串
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
- toggleMainWindow: 'toggleMainWindow',
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.toggleMainWindow]: 'CommandOrControl+E',
17
+ [ShortcutActionEnum.showMainWindow]: 'Control+E',
18
18
  };
@@ -11,6 +11,7 @@ export interface ElectronMainStore {
11
11
  networkProxy: NetworkProxySettings;
12
12
  shortcuts: Record<string, string>;
13
13
  storagePath: string;
14
+ themeMode: 'dark' | 'light' | 'auto';
14
15
  }
15
16
 
16
17
  export type StoreKey = keyof ElectronMainStore;
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