Skip to content

Commit f337c57

Browse files
committed
refactor(core): 提取共享基类和类型,消除重复代码
- 新增 AbstractAdapterRegistry 抽象基类,统一 Text/Image Registry 实现 - 提取共享类型 ConnectionSchema、BaseProvider、BaseModel 到 shared/types.ts - 重命名 history/errors.ts 中的 StorageError 为 HistoryStorageError 避免命名冲突 - 移除 model/errors.ts 中重复的 ModelConfigError 定义 - 删除 model/defaults.ts 中未使用的 clearModelsCache 函数 - TextAdapterRegistry: 273 → 95 行,ImageAdapterRegistry: 186 → 84 行
1 parent 8c10d62 commit f337c57

File tree

12 files changed

+438
-440
lines changed

12 files changed

+438
-440
lines changed
Lines changed: 256 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,256 @@
1+
/**
2+
* 适配器注册表抽象基类
3+
* 提供文本和图像适配器注册表的共享逻辑
4+
*
5+
* @template TAdapter 适配器类型
6+
* @template TProvider Provider 元数据类型
7+
* @template TModel Model 元数据类型
8+
* @template TConnectionConfig 连接配置类型(用于动态模型获取)
9+
*/
10+
export abstract class AbstractAdapterRegistry<
11+
TAdapter,
12+
TProvider extends { id: string; name: string; supportsDynamicModels: boolean },
13+
TModel extends { id: string },
14+
TConnectionConfig = Record<string, unknown>
15+
> {
16+
protected adapters: Map<string, TAdapter> = new Map();
17+
protected staticModelsCache: Map<string, TModel[]> = new Map();
18+
19+
constructor() {
20+
this.initializeAdapters();
21+
}
22+
23+
/**
24+
* 子类必须实现:初始化并注册所有适配器
25+
*/
26+
protected abstract initializeAdapters(): void;
27+
28+
/**
29+
* 子类必须实现:从适配器获取 Provider 元数据
30+
*/
31+
protected abstract getProviderFromAdapter(adapter: TAdapter): TProvider;
32+
33+
/**
34+
* 子类必须实现:从适配器获取静态模型列表
35+
*/
36+
protected abstract getModelsFromAdapter(adapter: TAdapter): TModel[];
37+
38+
/**
39+
* 子类必须实现:调用适配器的异步模型获取方法
40+
*/
41+
protected abstract getModelsAsyncFromAdapter(
42+
adapter: TAdapter,
43+
connectionConfig: TConnectionConfig
44+
): Promise<TModel[]>;
45+
46+
/**
47+
* 子类可选实现:获取错误消息的提供商类型描述
48+
*/
49+
protected getProviderTypeDescription(): string {
50+
return '提供商';
51+
}
52+
53+
/**
54+
* 预加载所有 Provider 的静态模型到缓存
55+
*/
56+
protected preloadStaticModels(): void {
57+
this.adapters.forEach((adapter, providerId) => {
58+
const provider = this.getProviderFromAdapter(adapter);
59+
if (provider.id === providerId) {
60+
this.staticModelsCache.set(providerId, this.getModelsFromAdapter(adapter));
61+
}
62+
});
63+
}
64+
65+
// ===== 基础适配器管理 =====
66+
67+
/**
68+
* 通过 providerId 获取适配器实例
69+
* @param providerId Provider ID(自动转换为小写)
70+
* @returns 适配器实例
71+
* @throws {Error} 当 providerId 不存在时
72+
*/
73+
public getAdapter(providerId: string): TAdapter {
74+
const adapter = this.adapters.get(providerId.toLowerCase());
75+
if (!adapter) {
76+
throw new Error(`未知${this.getProviderTypeDescription()}: ${providerId}`);
77+
}
78+
return adapter;
79+
}
80+
81+
// ===== 元数据查询 =====
82+
83+
/**
84+
* 获取所有已注册的 Provider 元数据
85+
* @returns Provider 元数据数组
86+
*/
87+
public getAllProviders(): TProvider[] {
88+
const providers: TProvider[] = [];
89+
const seenIds = new Set<string>();
90+
91+
this.adapters.forEach((adapter) => {
92+
const provider = this.getProviderFromAdapter(adapter);
93+
if (!seenIds.has(provider.id)) {
94+
seenIds.add(provider.id);
95+
providers.push(provider);
96+
}
97+
});
98+
99+
return providers;
100+
}
101+
102+
// ===== 静态模型获取(即时可用) =====
103+
104+
/**
105+
* 获取静态模型列表(带缓存)
106+
* @param providerId Provider ID
107+
* @returns 静态模型数组
108+
*/
109+
public getStaticModels(providerId: string): TModel[] {
110+
const normalizedId = providerId.toLowerCase();
111+
112+
// 尝试从缓存获取
113+
if (this.staticModelsCache.has(normalizedId)) {
114+
return this.staticModelsCache.get(normalizedId)!;
115+
}
116+
117+
// 如果缓存未命中,从适配器获取
118+
const adapter = this.getAdapter(normalizedId);
119+
const models = this.getModelsFromAdapter(adapter);
120+
this.staticModelsCache.set(normalizedId, models);
121+
return models;
122+
}
123+
124+
// ===== 动态模型获取(需要连接配置) =====
125+
126+
/**
127+
* 动态获取模型列表
128+
* @param providerId Provider ID
129+
* @param connectionConfig 连接配置
130+
* @returns 动态获取的模型数组
131+
* @throws {Error} 当 Provider 不支持动态获取时
132+
*/
133+
public async getDynamicModels(
134+
providerId: string,
135+
connectionConfig: TConnectionConfig
136+
): Promise<TModel[]> {
137+
const adapter = this.getAdapter(providerId);
138+
const provider = this.getProviderFromAdapter(adapter);
139+
140+
if (!provider.supportsDynamicModels) {
141+
throw new Error(`${provider.name} 不支持动态模型获取`);
142+
}
143+
144+
try {
145+
return await this.getModelsAsyncFromAdapter(adapter, connectionConfig);
146+
} catch (error) {
147+
console.warn(`动态获取模型失败 (${providerId}):`, error);
148+
throw error;
149+
}
150+
}
151+
152+
// ===== 统一的模型获取接口(自动选择静态或动态) =====
153+
154+
/**
155+
* 统一的模型获取接口
156+
* 优先动态获取,失败则 fallback 到静态模型
157+
*
158+
* @param providerId Provider ID
159+
* @param connectionConfig 连接配置(可选,提供时尝试动态获取)
160+
* @returns 模型数组
161+
*/
162+
public async getModels(
163+
providerId: string,
164+
connectionConfig?: TConnectionConfig
165+
): Promise<TModel[]> {
166+
const adapter = this.getAdapter(providerId);
167+
const provider = this.getProviderFromAdapter(adapter);
168+
169+
// 如果支持动态获取且提供了连接配置,尝试动态获取
170+
if (provider.supportsDynamicModels && connectionConfig) {
171+
try {
172+
const dynamicModels = await this.getDynamicModels(providerId, connectionConfig);
173+
174+
// 合并静态和动态模型,动态模型优先
175+
const staticModels = this.getStaticModels(providerId);
176+
const dynamicIds = new Set(dynamicModels.map((m) => m.id));
177+
const mergedModels = [
178+
...dynamicModels,
179+
...staticModels.filter((m) => !dynamicIds.has(m.id))
180+
];
181+
182+
return mergedModels;
183+
} catch (error) {
184+
console.warn(
185+
`动态模型加载失败 (${providerId}),回退到静态模型:`,
186+
error
187+
);
188+
// 降级到静态模型
189+
return this.getStaticModels(providerId);
190+
}
191+
}
192+
193+
// 返回静态模型
194+
return this.getStaticModels(providerId);
195+
}
196+
197+
/**
198+
* 获取所有静态模型的组合视图
199+
* @returns Provider-Model 对数组
200+
*/
201+
public getAllStaticModels(): Array<{ provider: TProvider; model: TModel }> {
202+
const result: Array<{ provider: TProvider; model: TModel }> = [];
203+
204+
for (const provider of this.getAllProviders()) {
205+
const models = this.getStaticModels(provider.id);
206+
for (const model of models) {
207+
result.push({ provider, model });
208+
}
209+
}
210+
211+
return result;
212+
}
213+
214+
// ===== 能力检查 =====
215+
216+
/**
217+
* 检查 Provider 是否支持动态模型获取
218+
* @param providerId Provider ID
219+
* @returns 是否支持动态获取
220+
*/
221+
public supportsDynamicModels(providerId: string): boolean {
222+
try {
223+
const adapter = this.getAdapter(providerId);
224+
return this.getProviderFromAdapter(adapter).supportsDynamicModels;
225+
} catch {
226+
return false;
227+
}
228+
}
229+
230+
// ===== 验证方法 =====
231+
232+
/**
233+
* 验证 Provider 和 Model 组合是否有效
234+
* @param providerId Provider ID
235+
* @param modelId Model ID
236+
* @returns 是否有效
237+
*/
238+
public validateProviderModel(providerId: string, modelId: string): boolean {
239+
try {
240+
const models = this.getStaticModels(providerId);
241+
return models.some((model) => model.id === modelId);
242+
} catch {
243+
return false;
244+
}
245+
}
246+
247+
// ===== 辅助方法:清除缓存 =====
248+
249+
/**
250+
* 清除静态模型缓存并重新加载
251+
*/
252+
public clearCache(): void {
253+
this.staticModelsCache.clear();
254+
this.preloadStaticModels();
255+
}
256+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { AbstractAdapterRegistry } from './abstract-registry';

packages/core/src/services/history/errors.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,15 +42,17 @@ export class RecordNotFoundError extends HistoryError {
4242
}
4343

4444
/**
45-
* 存储错误
45+
* 历史记录存储错误
46+
* 注意:此类与 storage/errors.ts 中的 StorageError 不同,
47+
* 专用于历史记录模块的存储操作错误
4648
*/
47-
export class StorageError extends HistoryError {
49+
export class HistoryStorageError extends HistoryError {
4850
constructor(
4951
message: string,
5052
public operation: 'read' | 'write' | 'delete' | 'init' | 'storage'
5153
) {
5254
super(message);
53-
this.name = 'StorageError';
55+
this.name = 'HistoryStorageError';
5456
}
5557
}
5658

packages/core/src/services/history/manager.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { IHistoryManager, PromptRecord, PromptRecordChain } from './types';
22
import { IStorageProvider } from '../storage/types';
33
import { StorageAdapter } from '../storage/adapter';
4-
import { RecordNotFoundError, RecordValidationError, StorageError, HistoryError } from './errors';
4+
import { RecordNotFoundError, RecordValidationError, HistoryStorageError, HistoryError } from './errors';
55
import { v4 as uuidv4 } from 'uuid';
66
import { IModelManager } from '../model/types';
77
import { CORE_SERVICE_KEYS } from '../../constants/storage-keys';
@@ -74,9 +74,9 @@ export class HistoryManager implements IHistoryManager {
7474
throw err;
7575
}
7676
if (err.message?.includes('Get')) {
77-
throw new StorageError('Failed to get history records', 'read');
77+
throw new HistoryStorageError('Failed to get history records', 'read');
7878
} else {
79-
throw new StorageError('Failed to save history records', 'write');
79+
throw new HistoryStorageError('Failed to save history records', 'write');
8080
}
8181
}
8282
}
@@ -95,7 +95,7 @@ export class HistoryManager implements IHistoryManager {
9595
// 直接返回记录,排序逻辑由调用者根据需求处理
9696
return records;
9797
} catch (err) {
98-
throw new StorageError('Failed to get history records', 'read');
98+
throw new HistoryStorageError('Failed to get history records', 'read');
9999
}
100100
}
101101

@@ -134,7 +134,7 @@ export class HistoryManager implements IHistoryManager {
134134
if (err instanceof RecordNotFoundError) {
135135
throw err;
136136
}
137-
throw new StorageError('Failed to delete record', 'delete');
137+
throw new HistoryStorageError('Failed to delete record', 'delete');
138138
}
139139
}
140140

@@ -166,7 +166,7 @@ export class HistoryManager implements IHistoryManager {
166166
try {
167167
await this.storage.removeItem(this.storageKey);
168168
} catch (err) {
169-
throw new StorageError('Failed to clear history', 'delete');
169+
throw new HistoryStorageError('Failed to clear history', 'delete');
170170
}
171171
}
172172

@@ -303,7 +303,7 @@ export class HistoryManager implements IHistoryManager {
303303
if (err instanceof RecordNotFoundError || err instanceof HistoryError) {
304304
throw err;
305305
}
306-
throw new StorageError('Failed to get chain', 'read');
306+
throw new HistoryStorageError('Failed to get chain', 'read');
307307
}
308308
}
309309

0 commit comments

Comments
 (0)