Skip to content

Commit 69d854a

Browse files
committed
feat(model):重构模型管理器以支持动态提供商配置
- 统一文本模型的连接配置结构,移除冗余字段 - 引入 Provider 注册表机制,支持动态加载模型提供商 -优化模型编辑界面,使用表单布局提升用户体验 - 添加 Provider 切换功能,自动更新连接配置和模型列表 - 改进 API Key 处理逻辑,增强安全性与遮罩显示- 实现基于 Schema 的动态连接字段渲染-重构新增/编辑模型流程,确保配置一致性 - 更新核心类型定义,导出适配器注册表相关接口- 修复 OpenAI 提供商名称始终显示为 'OpenAI' 的问题 - 优化模型能力标签展示,支持流式传输、工具调用等特性标识
1 parent 5de5614 commit 69d854a

File tree

11 files changed

+774
-505
lines changed

11 files changed

+774
-505
lines changed

packages/core/src/index.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,22 @@ export * from './services/history/errors'
1818
export { ElectronHistoryManagerProxy } from './services/history/electron-proxy'
1919

2020
// 导出LLM服务相关
21-
export type { ILLMService, Message, StreamHandlers, LLMResponse, ModelInfo, ModelOption } from './services/llm/types'
21+
export type {
22+
ILLMService,
23+
Message,
24+
StreamHandlers,
25+
LLMResponse,
26+
ModelInfo,
27+
ModelOption,
28+
ITextAdapterRegistry,
29+
ITextProviderAdapter,
30+
TextProvider,
31+
TextModel,
32+
TextModelConfig,
33+
ConnectionSchema
34+
} from './services/llm/types'
2235
export { LLMService, createLLMService } from './services/llm/service'
36+
export { TextAdapterRegistry, createTextAdapterRegistry } from './services/llm/adapters/registry'
2337
export { ElectronLLMProxy } from './services/llm/electron-proxy'
2438
export * from './services/llm/errors'
2539

packages/core/src/services/model/converter.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -257,10 +257,10 @@ function createProviderMeta(providerId: string, legacy: ModelConfig): TextProvid
257257
}
258258
};
259259
} else {
260-
// OpenAI 及兼容 API
260+
// OpenAI 及兼容 API - 始终使用 'OpenAI' 作为 Provider 名称
261261
return {
262262
id: 'openai',
263-
name: legacy.provider === 'openai' ? 'OpenAI' : legacy.name,
263+
name: 'OpenAI',
264264
description: 'OpenAI GPT models and OpenAI-compatible APIs',
265265
requiresApiKey: true,
266266
defaultBaseURL: legacy.baseURL || 'https://api.openai.com/v1',

packages/ui/src/components/ModelManager.vue

Lines changed: 574 additions & 266 deletions
Large diffs are not rendered by default.

packages/ui/src/composables/useAppInitializer.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,10 @@ import {
2828
createImageModelManager,
2929
createImageService,
3030
createImageAdapterRegistry,
31+
createTextAdapterRegistry,
3132
type IImageModelManager,
3233
type IImageService,
34+
type ITextAdapterRegistry,
3335
type IModelManager,
3436
type ITemplateManager,
3537
type IHistoryManager,
@@ -68,6 +70,7 @@ export function useAppInitializer(): {
6870
let imageModelManager: IImageModelManager | undefined;
6971
let imageService: IImageService | undefined;
7072
let imageAdapterRegistryInstance: ReturnType<typeof createImageAdapterRegistry> | undefined;
73+
let textAdapterRegistryInstance: ITextAdapterRegistry | undefined;
7174

7275
if (isRunningInElectron()) {
7376
console.log('[AppInitializer] 检测到Electron环境,等待API就绪...');
@@ -90,6 +93,10 @@ export function useAppInitializer(): {
9093
llmService = new ElectronLLMProxy();
9194
promptService = new ElectronPromptServiceProxy();
9295
preferenceService = new ElectronPreferenceServiceProxy();
96+
97+
// 文本模型适配器注册表(本地实例,不需要代理)
98+
textAdapterRegistryInstance = createTextAdapterRegistry();
99+
93100
// 图像相关(Electron 渲染进程代理)
94101
const { ElectronImageModelManagerProxy, ElectronImageServiceProxy } = await import('@prompt-optimizer/core')
95102
imageAdapterRegistryInstance = createImageAdapterRegistry();
@@ -119,6 +126,7 @@ export function useAppInitializer(): {
119126
preferenceService, // 使用从core包导入的ElectronPreferenceServiceProxy
120127
compareService, // 直接使用,无需代理
121128
contextRepo, // 使用Electron代理
129+
textAdapterRegistry: textAdapterRegistryInstance,
122130
imageModelManager,
123131
imageService,
124132
imageAdapterRegistry: imageAdapterRegistryInstance,
@@ -137,6 +145,10 @@ export function useAppInitializer(): {
137145

138146
// Services with no dependencies or only storage
139147
const modelManagerInstance = createModelManager(storageProvider);
148+
149+
// 文本模型适配器注册表(本地实例)
150+
textAdapterRegistryInstance = createTextAdapterRegistry();
151+
140152
// 图像模型管理器(独立存储空间)
141153
const imageAdapterRegistry = await import('@prompt-optimizer/core').then(m => m.createImageAdapterRegistry())
142154
imageAdapterRegistryInstance = imageAdapterRegistry
@@ -253,6 +265,7 @@ export function useAppInitializer(): {
253265
preferenceService, // 使用从core包导入的PreferenceService
254266
compareService, // 直接使用
255267
contextRepo, // 上下文仓库
268+
textAdapterRegistry: textAdapterRegistryInstance,
256269
imageModelManager: imageModelManagerInstance,
257270
imageService,
258271
imageAdapterRegistry: imageAdapterRegistryInstance,

packages/ui/src/i18n/locales/en-US.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -462,6 +462,14 @@ export default {
462462
apiError: 'API error: {error}'
463463
},
464464

465+
// Model Capabilities
466+
capabilities: {
467+
tools: 'Tool Calling',
468+
reasoning: 'Reasoning',
469+
streaming: 'Streaming',
470+
vision: 'Vision'
471+
},
472+
465473
// Status Text
466474
disabled: 'Disabled',
467475

packages/ui/src/i18n/locales/zh-CN.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -465,6 +465,14 @@
465465
// 状态文本
466466
disabled: '已禁用',
467467

468+
// 模型能力标签
469+
capabilities: {
470+
tools: '工具调用',
471+
reasoning: '推理模式',
472+
streaming: '流式输出',
473+
vision: '视觉理解'
474+
},
475+
468476
// 无障碍标签
469477
testConnectionAriaLabel: '测试连接到{name}',
470478
editModelAriaLabel: '编辑模型{name}',

packages/ui/src/i18n/locales/zh-TW.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -462,6 +462,14 @@ export default {
462462
apiError: 'API錯誤:{error}'
463463
},
464464

465+
// 模型能力標籤
466+
capabilities: {
467+
tools: '工具呼叫',
468+
reasoning: '推理模式',
469+
streaming: '串流輸出',
470+
vision: '視覺理解'
471+
},
472+
465473
// 狀態文字
466474
disabled: '已停用',
467475

packages/ui/src/types/services.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ import type {
1111
ContextRepo,
1212
IImageModelManager,
1313
IImageService,
14-
IImageAdapterRegistry
14+
IImageAdapterRegistry,
15+
ITextAdapterRegistry
1516
} from '@prompt-optimizer/core'
1617

1718
/**
@@ -28,6 +29,8 @@ export interface AppServices {
2829
preferenceService: IPreferenceService;
2930
compareService: ICompareService;
3031
contextRepo: ContextRepo;
32+
// 文本模型适配器注册表(本地实例)
33+
textAdapterRegistry?: ITextAdapterRegistry;
3134
// 图像相关(Web 优先,可选)
3235
imageModelManager?: IImageModelManager;
3336
imageService?: IImageService;

packages/ui/tests/unit/OptimizationModeSelector.test.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ describe('OptimizationModeSelector', () => {
2121

2222
it('renders correctly with text content', () => {
2323
const wrapper = mount(OptimizationModeSelector, {
24+
props: {
25+
modelValue: 'system'
26+
},
2427
global: {
2528
plugins: [i18n]
2629
}
@@ -29,4 +32,4 @@ describe('OptimizationModeSelector', () => {
2932
expect(wrapper.text()).toContain('System Prompt')
3033
expect(wrapper.text()).toContain('User Prompt')
3134
})
32-
})
35+
})

packages/ui/tests/unit/components/ImageModelManager.spec.ts

Lines changed: 49 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,13 @@ vi.mock('vue-i18n', () => ({
1414
}))
1515

1616
// Mock the useImageModelManager composable
17+
const mockLoadConfigs = vi.fn().mockResolvedValue(undefined)
18+
const mockInitialize = vi.fn(async () => {
19+
await mockLoadConfigs()
20+
})
21+
const mockUpdateConfig = vi.fn().mockResolvedValue(undefined)
22+
const mockDeleteConfig = vi.fn().mockResolvedValue(undefined)
23+
1724
vi.mock('../../../src/composables/useImageModelManager', () => ({
1825
useImageModelManager: () => ({
1926
providers: ref([
@@ -27,21 +34,33 @@ vi.mock('../../../src/composables/useImageModelManager', () => ({
2734
{
2835
id: 'gpt-image-1',
2936
name: 'GPT Image 1',
30-
enabled: true
37+
enabled: true,
38+
model: {
39+
id: 'gpt-image-1',
40+
capabilities: {
41+
text2image: true,
42+
image2image: false
43+
}
44+
}
3145
}
3246
]),
3347
models: ref([
3448
{
3549
id: 'gpt-image-1',
3650
name: 'GPT Image 1',
37-
enabled: true
51+
enabled: true,
52+
capabilities: {
53+
text2image: true,
54+
image2image: false
55+
}
3856
}
3957
]),
4058
isLoading: ref(false),
4159
error: ref(null),
42-
addModel: vi.fn().mockResolvedValue(undefined),
43-
updateModel: vi.fn().mockResolvedValue(undefined),
44-
deleteModel: vi.fn().mockResolvedValue(undefined),
60+
initialize: mockInitialize,
61+
loadConfigs: mockLoadConfigs,
62+
updateConfig: mockUpdateConfig,
63+
deleteConfig: mockDeleteConfig,
4564
testConnection: vi.fn().mockResolvedValue({ success: true }),
4665
enableModel: vi.fn().mockResolvedValue(undefined),
4766
disableModel: vi.fn().mockResolvedValue(undefined),
@@ -55,14 +74,36 @@ describe('ImageModelManager', () => {
5574

5675
beforeEach(() => {
5776
vi.clearAllMocks()
77+
mockInitialize.mockClear()
78+
mockLoadConfigs.mockClear()
79+
mockUpdateConfig.mockClear()
80+
mockDeleteConfig.mockClear()
5881
})
5982

83+
const mockImageRegistry = {
84+
getAvailableProviders: vi.fn().mockReturnValue([
85+
{
86+
value: 'openai',
87+
label: 'OpenAI',
88+
aliases: []
89+
}
90+
])
91+
}
92+
93+
const mockImageService = {
94+
testConnection: vi.fn().mockResolvedValue({ success: true })
95+
}
96+
6097
const createWrapper = (props = {}) => {
6198
return mount(ImageModelManager, {
6299
props: {
63100
...props
64101
},
65102
global: {
103+
provide: {
104+
imageRegistry: mockImageRegistry,
105+
imageService: mockImageService
106+
},
66107
stubs: {
67108
// Stub all NaiveUI components
68109
'n-card': { template: '<div><slot name="header" /><slot /></div>' },
@@ -88,12 +129,14 @@ describe('ImageModelManager', () => {
88129
it('应该正确渲染组件', () => {
89130
wrapper = createWrapper()
90131
expect(wrapper.vm).toBeDefined()
132+
expect(mockInitialize).toHaveBeenCalled()
91133
})
92134

93135
it('应该加载图像模型提供商', () => {
94136
wrapper = createWrapper()
95137
// 验证组件正常挂载即可,具体业务逻辑由 useImageModelManager 处理
96138
expect(wrapper.vm).toBeDefined()
139+
expect(mockLoadConfigs).toHaveBeenCalled()
97140
})
98141
})
99-
})
142+
})

0 commit comments

Comments
 (0)