provider-kit 0.1.0

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.
@@ -0,0 +1,316 @@
1
+ /**
2
+ * Provider Registry - 简化的 Provider 管理
3
+ *
4
+ * 设计原则:
5
+ * 1. 最小配置 - 只需 id + baseUrl + apiKey
6
+ * 2. 自动发现 - 调用 /models 自动获取模型列表
7
+ * 3. 统一接口 - 所有 OpenAI 兼容 provider 用同一个适配器
8
+ */
9
+
10
+ import { OpenAICompatibleProvider, PRESET_PROVIDERS, createProvider, listPresetProviders } from './openai-compatible.js';
11
+ import { AnthropicAdapter, createAnthropicProvider } from './anthropic-adapter.js';
12
+ import { GeminiAdapter, createGeminiProvider } from './gemini-adapter.js';
13
+ import { AzureOpenAIAdapter, createAzureOpenAIProvider } from './azure-adapter.js';
14
+ import { CohereAdapter, createCohereProvider } from './cohere-adapter.js';
15
+ import { BedrockProxyAdapter, createBedrockProxyProvider } from './bedrock-adapter.js';
16
+ import { persistentConfig } from '../core/persistent-config.js';
17
+
18
+ class ProviderRegistry {
19
+ constructor() {
20
+ this.providers = new Map(); // id -> provider instance
21
+ this.models = new Map(); // providerId -> [modelIds]
22
+ this.presets = PRESET_PROVIDERS;
23
+ }
24
+
25
+ /**
26
+ * 获取或创建 Provider
27
+ */
28
+ getProvider(providerId) {
29
+ // 已存在
30
+ if (this.providers.has(providerId)) {
31
+ return this.providers.get(providerId);
32
+ }
33
+
34
+ // 从预设创建
35
+ const preset = this.presets[providerId];
36
+ if (preset) {
37
+ const apiKey = persistentConfig.getApiKey(providerId);
38
+
39
+ // 特殊处理: Anthropic 使用专用适配器
40
+ if (providerId === 'anthropic' || preset.special) {
41
+ const provider = this.createSpecialProvider(providerId, apiKey, preset);
42
+ if (provider) {
43
+ this.providers.set(providerId, provider);
44
+ return provider;
45
+ }
46
+ }
47
+
48
+ // 默认使用 OpenAI 兼容适配器
49
+ const provider = createProvider(providerId, apiKey);
50
+ this.providers.set(providerId, provider);
51
+ return provider;
52
+ }
53
+
54
+ return null;
55
+ }
56
+
57
+ /**
58
+ * 创建特殊 Provider (Anthropic, Azure, Gemini, Cohere, Bedrock 等)
59
+ */
60
+ createSpecialProvider(providerId, apiKey, preset) {
61
+ switch (providerId) {
62
+ case 'anthropic':
63
+ return createAnthropicProvider(apiKey, preset);
64
+
65
+ case 'gemini':
66
+ case 'google':
67
+ return createGeminiProvider(apiKey, preset);
68
+
69
+ case 'azure':
70
+ return createAzureOpenAIProvider({ apiKey, ...preset });
71
+
72
+ case 'cohere':
73
+ return createCohereProvider(apiKey, preset);
74
+
75
+ case 'bedrock-proxy':
76
+ return createBedrockProxyProvider(apiKey, preset);
77
+
78
+ // 未来可添加其他特殊 provider
79
+
80
+ default:
81
+ console.warn(`[Provider] Special provider ${providerId} not implemented, using OpenAI-compatible`);
82
+ return null;
83
+ }
84
+ }
85
+
86
+ /**
87
+ * 配置 Provider
88
+ */
89
+ async configure(providerId, options = {}) {
90
+ const { apiKey, baseUrl, defaultModel } = options;
91
+
92
+ // 保存 API Key
93
+ if (apiKey) {
94
+ persistentConfig.setApiKey(providerId, apiKey);
95
+ }
96
+
97
+ // 创建或更新 provider
98
+ let provider = this.providers.get(providerId);
99
+
100
+ if (!provider) {
101
+ provider = createProvider(providerId, apiKey || persistentConfig.getApiKey(providerId), {
102
+ baseUrl,
103
+ defaultModel
104
+ });
105
+ this.providers.set(providerId, provider);
106
+ }
107
+
108
+ // 连接并获取模型列表
109
+ try {
110
+ await provider.connect(apiKey);
111
+ await this.refreshModels(providerId);
112
+ return { success: true, provider };
113
+ } catch (e) {
114
+ return { success: false, error: e.message };
115
+ }
116
+ }
117
+
118
+ /**
119
+ * 刷新模型列表
120
+ */
121
+ async refreshModels(providerId) {
122
+ const provider = this.getProvider(providerId);
123
+ if (!provider) return [];
124
+
125
+ try {
126
+ const models = await provider.fetchModels();
127
+ this.models.set(providerId, models);
128
+ return models;
129
+ } catch (e) {
130
+ // 如果失败,返回本地模型列表
131
+ return provider.getModels();
132
+ }
133
+ }
134
+
135
+ /**
136
+ * 获取模型列表
137
+ */
138
+ getModels(providerId) {
139
+ // 优先使用缓存的模型列表
140
+ if (this.models.has(providerId)) {
141
+ return this.models.get(providerId);
142
+ }
143
+
144
+ // 从 provider 获取
145
+ const provider = this.getProvider(providerId);
146
+ return provider ? provider.getModels() : [];
147
+ }
148
+
149
+ /**
150
+ * 列出所有预设 Provider
151
+ */
152
+ listPresets() {
153
+ return listPresetProviders();
154
+ }
155
+
156
+ /**
157
+ * 列出已配置的 Provider
158
+ */
159
+ listConfigured() {
160
+ const configured = [];
161
+ const current = persistentConfig.getPreference('currentProvider');
162
+
163
+ for (const [id, provider] of this.providers) {
164
+ configured.push({
165
+ id,
166
+ name: provider.name,
167
+ nameCn: provider.nameCn,
168
+ connected: provider.connected,
169
+ modelCount: provider.getModels().length,
170
+ isCurrent: id === current,
171
+ hasApiKey: !!provider.apiKey
172
+ });
173
+ }
174
+
175
+ return configured;
176
+ }
177
+
178
+ /**
179
+ * 列出所有 Provider(预设 + 已配置)
180
+ */
181
+ listAll() {
182
+ const result = [];
183
+ const current = persistentConfig.getPreference('currentProvider');
184
+ const configuredIds = new Set(this.providers.keys());
185
+
186
+ // 已配置的
187
+ for (const [id, provider] of this.providers) {
188
+ result.push({
189
+ id,
190
+ name: provider.name,
191
+ nameCn: provider.nameCn,
192
+ connected: provider.connected,
193
+ isCurrent: id === current,
194
+ hasApiKey: !!provider.apiKey,
195
+ modelCount: provider.getModels().length,
196
+ configured: true
197
+ });
198
+ }
199
+
200
+ // 未配置的预设
201
+ for (const [id, preset] of Object.entries(this.presets)) {
202
+ if (!configuredIds.has(id)) {
203
+ const hasKey = !!persistentConfig.getApiKey(id);
204
+ result.push({
205
+ id,
206
+ name: preset.name,
207
+ nameCn: preset.nameCn,
208
+ connected: false,
209
+ isCurrent: id === current,
210
+ hasApiKey: hasKey,
211
+ modelCount: 0,
212
+ configured: false,
213
+ description: preset.description || ''
214
+ });
215
+ }
216
+ }
217
+
218
+ return result;
219
+ }
220
+
221
+ /**
222
+ * 设置当前 Provider
223
+ */
224
+ setCurrent(providerId, model = null) {
225
+ persistentConfig.setPreference('currentProvider', providerId);
226
+ if (model) {
227
+ persistentConfig.setPreference('currentModel', model);
228
+ } else {
229
+ // 使用默认模型
230
+ const provider = this.getProvider(providerId);
231
+ if (provider && provider.defaultModel) {
232
+ persistentConfig.setPreference('currentModel', provider.defaultModel);
233
+ }
234
+ }
235
+ }
236
+
237
+ /**
238
+ * 获取当前 Provider
239
+ */
240
+ getCurrent() {
241
+ const providerId = persistentConfig.getPreference('currentProvider');
242
+ const model = persistentConfig.getPreference('currentModel');
243
+
244
+ if (!providerId) return null;
245
+
246
+ const provider = this.getProvider(providerId);
247
+ return {
248
+ providerId,
249
+ model,
250
+ provider
251
+ };
252
+ }
253
+
254
+ /**
255
+ * 移除 Provider
256
+ */
257
+ remove(providerId) {
258
+ this.providers.delete(providerId);
259
+ this.models.delete(providerId);
260
+ persistentConfig.removeApiKey(providerId);
261
+ }
262
+
263
+ /**
264
+ * 发送聊天消息
265
+ */
266
+ async chat(messages, options = {}) {
267
+ const { providerId, model } = options;
268
+
269
+ // 使用指定的或当前的 provider
270
+ let pid = providerId || persistentConfig.getPreference('currentProvider');
271
+ let m = model || persistentConfig.getPreference('currentModel');
272
+
273
+ if (!pid) {
274
+ throw new Error('No provider configured. Run: connect <provider>');
275
+ }
276
+
277
+ const provider = this.getProvider(pid);
278
+ if (!provider) {
279
+ throw new Error(`Provider ${pid} not found`);
280
+ }
281
+
282
+ // 确保已连接
283
+ if (!provider.connected && !provider.skipAuth) {
284
+ const apiKey = persistentConfig.getApiKey(pid);
285
+ if (!apiKey) {
286
+ throw new Error(`No API key for ${pid}. Run: connect ${pid}`);
287
+ }
288
+ await provider.connect(apiKey);
289
+ }
290
+
291
+ return provider.chat(m, messages, options);
292
+ }
293
+
294
+ /**
295
+ * 获取统计信息
296
+ */
297
+ getStats() {
298
+ let totalModels = 0;
299
+ for (const models of this.models.values()) {
300
+ totalModels += models.length;
301
+ }
302
+
303
+ return {
304
+ providersConfigured: this.providers.size,
305
+ presetsAvailable: Object.keys(this.presets).length,
306
+ totalModels
307
+ };
308
+ }
309
+ }
310
+
311
+ // 单例
312
+ export const providerRegistry = new ProviderRegistry();
313
+ export default providerRegistry;
314
+
315
+ // 兼容旧 API
316
+ export { PRESET_PROVIDERS, createProvider, listPresetProviders };