@spacex110/core 0.1.11
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/dist/index.d.ts +620 -0
- package/dist/index.js +6401 -0
- package/dist/index.js.map +1 -0
- package/dist/index.umd.js +16 -0
- package/dist/index.umd.js.map +1 -0
- package/package.json +41 -0
- package/src/api.ts +158 -0
- package/src/channel-manager.ts +790 -0
- package/src/config.ts +104 -0
- package/src/i18n.ts +66 -0
- package/src/index.ts +97 -0
- package/src/models.ts +202 -0
- package/src/providers.ts +249 -0
- package/src/storage.ts +135 -0
- package/src/strategies.ts +469 -0
- package/src/styles.css +246 -0
- package/src/types.ts +163 -0
package/src/providers.ts
ADDED
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AI Provider Selector - Provider Definitions
|
|
3
|
+
* All supported AI providers metadata
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { Provider } from './types';
|
|
7
|
+
|
|
8
|
+
const ICON_CDN_BASE = 'https://registry.npmmirror.com/@lobehub/icons-static-svg/1.77.0/files/icons';
|
|
9
|
+
|
|
10
|
+
// Provider IDs
|
|
11
|
+
export const PROVIDER_ID = {
|
|
12
|
+
OPENAI: 'openai',
|
|
13
|
+
ANTHROPIC: 'anthropic',
|
|
14
|
+
GEMINI: 'gemini',
|
|
15
|
+
DEEPSEEK: 'deepseek',
|
|
16
|
+
OPENROUTER: 'openrouter',
|
|
17
|
+
GROQ: 'groq',
|
|
18
|
+
MISTRAL: 'mistral',
|
|
19
|
+
MOONSHOT: 'moonshot',
|
|
20
|
+
QWEN: 'qwen',
|
|
21
|
+
ZHIPU: 'zhipu',
|
|
22
|
+
SILICONFLOW: 'siliconflow',
|
|
23
|
+
XAI: 'xai',
|
|
24
|
+
TOGETHER: 'together',
|
|
25
|
+
FIREWORKS: 'fireworks',
|
|
26
|
+
DEEPINFRA: 'deepinfra',
|
|
27
|
+
PERPLEXITY: 'perplexity',
|
|
28
|
+
COHERE: 'cohere',
|
|
29
|
+
OLLAMA: 'ollama',
|
|
30
|
+
DOUBAO: 'doubao',
|
|
31
|
+
MINIMAX: 'minimax',
|
|
32
|
+
CUSTOM: 'custom',
|
|
33
|
+
} as const;
|
|
34
|
+
|
|
35
|
+
export type ProviderId = typeof PROVIDER_ID[keyof typeof PROVIDER_ID];
|
|
36
|
+
|
|
37
|
+
export const PROVIDERS: Record<string, Provider> = {
|
|
38
|
+
[PROVIDER_ID.OPENAI]: {
|
|
39
|
+
id: PROVIDER_ID.OPENAI,
|
|
40
|
+
name: 'OpenAI',
|
|
41
|
+
baseUrl: 'https://api.openai.com/v1',
|
|
42
|
+
needsApiKey: true,
|
|
43
|
+
apiFormat: 'openai',
|
|
44
|
+
supportsModelsApi: true,
|
|
45
|
+
icon: `${ICON_CDN_BASE}/openai.svg`,
|
|
46
|
+
},
|
|
47
|
+
[PROVIDER_ID.ANTHROPIC]: {
|
|
48
|
+
id: PROVIDER_ID.ANTHROPIC,
|
|
49
|
+
name: 'Anthropic (Claude)',
|
|
50
|
+
baseUrl: 'https://api.anthropic.com/v1',
|
|
51
|
+
needsApiKey: true,
|
|
52
|
+
apiFormat: 'anthropic',
|
|
53
|
+
supportsModelsApi: true,
|
|
54
|
+
icon: `${ICON_CDN_BASE}/anthropic.svg`,
|
|
55
|
+
},
|
|
56
|
+
[PROVIDER_ID.GEMINI]: {
|
|
57
|
+
id: PROVIDER_ID.GEMINI,
|
|
58
|
+
name: 'Google Gemini',
|
|
59
|
+
baseUrl: 'https://generativelanguage.googleapis.com/v1beta',
|
|
60
|
+
needsApiKey: true,
|
|
61
|
+
apiFormat: 'gemini',
|
|
62
|
+
supportsModelsApi: true,
|
|
63
|
+
icon: `${ICON_CDN_BASE}/gemini.svg`,
|
|
64
|
+
},
|
|
65
|
+
[PROVIDER_ID.OPENROUTER]: {
|
|
66
|
+
id: PROVIDER_ID.OPENROUTER,
|
|
67
|
+
name: 'OpenRouter',
|
|
68
|
+
baseUrl: 'https://openrouter.ai/api/v1',
|
|
69
|
+
needsApiKey: true,
|
|
70
|
+
apiFormat: 'openai',
|
|
71
|
+
supportsModelsApi: true,
|
|
72
|
+
icon: `${ICON_CDN_BASE}/openrouter.svg`,
|
|
73
|
+
},
|
|
74
|
+
[PROVIDER_ID.DEEPSEEK]: {
|
|
75
|
+
id: PROVIDER_ID.DEEPSEEK,
|
|
76
|
+
name: 'DeepSeek',
|
|
77
|
+
baseUrl: 'https://api.deepseek.com',
|
|
78
|
+
needsApiKey: true,
|
|
79
|
+
apiFormat: 'openai',
|
|
80
|
+
supportsModelsApi: true,
|
|
81
|
+
icon: `${ICON_CDN_BASE}/deepseek.svg`,
|
|
82
|
+
},
|
|
83
|
+
[PROVIDER_ID.MOONSHOT]: {
|
|
84
|
+
id: PROVIDER_ID.MOONSHOT,
|
|
85
|
+
name: 'Moonshot (Kimi)',
|
|
86
|
+
baseUrl: 'https://api.moonshot.cn/v1',
|
|
87
|
+
needsApiKey: true,
|
|
88
|
+
apiFormat: 'openai',
|
|
89
|
+
supportsModelsApi: true,
|
|
90
|
+
icon: `${ICON_CDN_BASE}/moonshot.svg`,
|
|
91
|
+
},
|
|
92
|
+
[PROVIDER_ID.QWEN]: {
|
|
93
|
+
id: PROVIDER_ID.QWEN,
|
|
94
|
+
name: '通义千问 (Qwen)',
|
|
95
|
+
baseUrl: 'https://dashscope.aliyuncs.com/compatible-mode/v1',
|
|
96
|
+
needsApiKey: true,
|
|
97
|
+
apiFormat: 'openai',
|
|
98
|
+
supportsModelsApi: true,
|
|
99
|
+
icon: `${ICON_CDN_BASE}/qwen.svg`,
|
|
100
|
+
},
|
|
101
|
+
[PROVIDER_ID.ZHIPU]: {
|
|
102
|
+
id: PROVIDER_ID.ZHIPU,
|
|
103
|
+
name: '智谱 AI (GLM)',
|
|
104
|
+
baseUrl: 'https://open.bigmodel.cn/api/paas/v4',
|
|
105
|
+
needsApiKey: true,
|
|
106
|
+
apiFormat: 'openai',
|
|
107
|
+
supportsModelsApi: true,
|
|
108
|
+
icon: `${ICON_CDN_BASE}/zhipu.svg`,
|
|
109
|
+
},
|
|
110
|
+
[PROVIDER_ID.SILICONFLOW]: {
|
|
111
|
+
id: PROVIDER_ID.SILICONFLOW,
|
|
112
|
+
name: '硅基流动 (siliconflow)',
|
|
113
|
+
baseUrl: 'https://api.siliconflow.cn/v1',
|
|
114
|
+
needsApiKey: true,
|
|
115
|
+
apiFormat: 'openai',
|
|
116
|
+
supportsModelsApi: true,
|
|
117
|
+
icon: `${ICON_CDN_BASE}/siliconcloud.svg`,
|
|
118
|
+
},
|
|
119
|
+
|
|
120
|
+
[PROVIDER_ID.DOUBAO]: {
|
|
121
|
+
id: PROVIDER_ID.DOUBAO,
|
|
122
|
+
name: '火山方舟 (Doubao)',
|
|
123
|
+
baseUrl: 'https://ark.cn-beijing.volces.com/api/v3',
|
|
124
|
+
needsApiKey: true,
|
|
125
|
+
apiFormat: 'openai',
|
|
126
|
+
supportsModelsApi: true,
|
|
127
|
+
icon: `${ICON_CDN_BASE}/doubao.svg`,
|
|
128
|
+
},
|
|
129
|
+
[PROVIDER_ID.MINIMAX]: {
|
|
130
|
+
id: PROVIDER_ID.MINIMAX,
|
|
131
|
+
name: 'MiniMax',
|
|
132
|
+
baseUrl: 'https://api.minimax.io/v1',
|
|
133
|
+
needsApiKey: true,
|
|
134
|
+
apiFormat: 'openai',
|
|
135
|
+
supportsModelsApi: false,
|
|
136
|
+
icon: `${ICON_CDN_BASE}/minimax.svg`,
|
|
137
|
+
},
|
|
138
|
+
[PROVIDER_ID.XAI]: {
|
|
139
|
+
id: PROVIDER_ID.XAI,
|
|
140
|
+
name: 'xAI (Grok)',
|
|
141
|
+
baseUrl: 'https://api.x.ai/v1',
|
|
142
|
+
needsApiKey: true,
|
|
143
|
+
apiFormat: 'openai',
|
|
144
|
+
supportsModelsApi: true,
|
|
145
|
+
icon: `${ICON_CDN_BASE}/grok.svg`,
|
|
146
|
+
},
|
|
147
|
+
[PROVIDER_ID.GROQ]: {
|
|
148
|
+
id: PROVIDER_ID.GROQ,
|
|
149
|
+
name: 'Groq',
|
|
150
|
+
baseUrl: 'https://api.groq.com/openai/v1',
|
|
151
|
+
needsApiKey: true,
|
|
152
|
+
apiFormat: 'openai',
|
|
153
|
+
supportsModelsApi: true,
|
|
154
|
+
icon: `${ICON_CDN_BASE}/groq.svg`,
|
|
155
|
+
},
|
|
156
|
+
[PROVIDER_ID.MISTRAL]: {
|
|
157
|
+
id: PROVIDER_ID.MISTRAL,
|
|
158
|
+
name: 'Mistral AI',
|
|
159
|
+
baseUrl: 'https://api.mistral.ai/v1',
|
|
160
|
+
needsApiKey: true,
|
|
161
|
+
apiFormat: 'openai',
|
|
162
|
+
supportsModelsApi: true,
|
|
163
|
+
icon: `${ICON_CDN_BASE}/mistral.svg`,
|
|
164
|
+
},
|
|
165
|
+
[PROVIDER_ID.TOGETHER]: {
|
|
166
|
+
id: PROVIDER_ID.TOGETHER,
|
|
167
|
+
name: 'Together AI',
|
|
168
|
+
baseUrl: 'https://api.together.xyz/v1',
|
|
169
|
+
needsApiKey: true,
|
|
170
|
+
apiFormat: 'openai',
|
|
171
|
+
supportsModelsApi: true,
|
|
172
|
+
icon: `${ICON_CDN_BASE}/together.svg`,
|
|
173
|
+
},
|
|
174
|
+
[PROVIDER_ID.FIREWORKS]: {
|
|
175
|
+
id: PROVIDER_ID.FIREWORKS,
|
|
176
|
+
name: 'Fireworks AI',
|
|
177
|
+
baseUrl: 'https://api.fireworks.ai/inference/v1',
|
|
178
|
+
needsApiKey: true,
|
|
179
|
+
apiFormat: 'openai',
|
|
180
|
+
supportsModelsApi: true,
|
|
181
|
+
icon: `${ICON_CDN_BASE}/fireworks.svg`,
|
|
182
|
+
},
|
|
183
|
+
[PROVIDER_ID.DEEPINFRA]: {
|
|
184
|
+
id: PROVIDER_ID.DEEPINFRA,
|
|
185
|
+
name: 'DeepInfra',
|
|
186
|
+
baseUrl: 'https://api.deepinfra.com/v1/openai',
|
|
187
|
+
needsApiKey: true,
|
|
188
|
+
apiFormat: 'openai',
|
|
189
|
+
supportsModelsApi: true,
|
|
190
|
+
icon: `${ICON_CDN_BASE}/deepinfra.svg`,
|
|
191
|
+
},
|
|
192
|
+
[PROVIDER_ID.PERPLEXITY]: {
|
|
193
|
+
id: PROVIDER_ID.PERPLEXITY,
|
|
194
|
+
name: 'Perplexity',
|
|
195
|
+
baseUrl: 'https://api.perplexity.ai',
|
|
196
|
+
needsApiKey: true,
|
|
197
|
+
apiFormat: 'openai',
|
|
198
|
+
supportsModelsApi: false,
|
|
199
|
+
icon: `${ICON_CDN_BASE}/perplexity.svg`,
|
|
200
|
+
},
|
|
201
|
+
[PROVIDER_ID.COHERE]: {
|
|
202
|
+
id: PROVIDER_ID.COHERE,
|
|
203
|
+
name: 'Cohere',
|
|
204
|
+
baseUrl: 'https://api.cohere.com/v2',
|
|
205
|
+
needsApiKey: true,
|
|
206
|
+
apiFormat: 'cohere',
|
|
207
|
+
supportsModelsApi: true,
|
|
208
|
+
icon: `${ICON_CDN_BASE}/cohere.svg`,
|
|
209
|
+
},
|
|
210
|
+
[PROVIDER_ID.OLLAMA]: {
|
|
211
|
+
id: PROVIDER_ID.OLLAMA,
|
|
212
|
+
name: 'Ollama (Local)',
|
|
213
|
+
baseUrl: 'http://localhost:11434/v1',
|
|
214
|
+
needsApiKey: false,
|
|
215
|
+
apiFormat: 'openai',
|
|
216
|
+
supportsModelsApi: true,
|
|
217
|
+
icon: `${ICON_CDN_BASE}/ollama.svg`,
|
|
218
|
+
},
|
|
219
|
+
[PROVIDER_ID.CUSTOM]: {
|
|
220
|
+
id: PROVIDER_ID.CUSTOM,
|
|
221
|
+
name: '自定义 (Custom)',
|
|
222
|
+
baseUrl: '',
|
|
223
|
+
needsApiKey: false,
|
|
224
|
+
apiFormat: 'openai',
|
|
225
|
+
supportsModelsApi: true,
|
|
226
|
+
icon: '',
|
|
227
|
+
},
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Get a provider by ID
|
|
232
|
+
*/
|
|
233
|
+
export function getProvider(id: string): Provider | undefined {
|
|
234
|
+
return PROVIDERS[id];
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Get all providers as an array
|
|
239
|
+
*/
|
|
240
|
+
export function getAllProviders(): Provider[] {
|
|
241
|
+
return Object.values(PROVIDERS);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Get providers by API format
|
|
246
|
+
*/
|
|
247
|
+
export function getProvidersByFormat(format: Provider['apiFormat']): Provider[] {
|
|
248
|
+
return getAllProviders().filter(p => p.apiFormat === format);
|
|
249
|
+
}
|
package/src/storage.ts
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AI Provider Selector - Storage Utilities
|
|
3
|
+
* Abstraction layer for config persistence
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import CryptoJS from 'crypto-js';
|
|
7
|
+
import type { AIConfig, StorageAdapter } from './types';
|
|
8
|
+
|
|
9
|
+
const STORAGE_KEY = 'ai_provider_config';
|
|
10
|
+
|
|
11
|
+
// 默认加密密钥(可在初始化时自定义)
|
|
12
|
+
const DEFAULT_SECRET = 'aiselector';
|
|
13
|
+
|
|
14
|
+
// AES 加密/解密
|
|
15
|
+
function encrypt(str: string, secret: string = DEFAULT_SECRET): string {
|
|
16
|
+
try {
|
|
17
|
+
return CryptoJS.AES.encrypt(str, secret).toString();
|
|
18
|
+
} catch {
|
|
19
|
+
return str;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function decrypt(str: string, secret: string = DEFAULT_SECRET): string {
|
|
24
|
+
try {
|
|
25
|
+
const bytes = CryptoJS.AES.decrypt(str, secret);
|
|
26
|
+
return bytes.toString(CryptoJS.enc.Utf8);
|
|
27
|
+
} catch {
|
|
28
|
+
return str;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* 加密存储适配器(默认)
|
|
34
|
+
* 使用 AES 加密存储,防止 API Key 明文泄露
|
|
35
|
+
*/
|
|
36
|
+
export const encryptedStorageAdapter: StorageAdapter = {
|
|
37
|
+
get: (key: string) => {
|
|
38
|
+
if (typeof window === 'undefined') return null;
|
|
39
|
+
try {
|
|
40
|
+
const encrypted = localStorage.getItem(key);
|
|
41
|
+
if (!encrypted) return null;
|
|
42
|
+
return decrypt(encrypted);
|
|
43
|
+
} catch {
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
set: (key: string, value: string) => {
|
|
48
|
+
if (typeof window === 'undefined') return;
|
|
49
|
+
try {
|
|
50
|
+
localStorage.setItem(key, encrypt(value));
|
|
51
|
+
} catch {
|
|
52
|
+
// ignore
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
remove: (key: string) => {
|
|
56
|
+
if (typeof window === 'undefined') return;
|
|
57
|
+
localStorage.removeItem(key);
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* 明文存储适配器(不推荐)
|
|
63
|
+
*/
|
|
64
|
+
export const plainStorageAdapter: StorageAdapter = {
|
|
65
|
+
get: (key: string) => {
|
|
66
|
+
if (typeof window === 'undefined') return null;
|
|
67
|
+
return localStorage.getItem(key);
|
|
68
|
+
},
|
|
69
|
+
set: (key: string, value: string) => {
|
|
70
|
+
if (typeof window === 'undefined') return;
|
|
71
|
+
localStorage.setItem(key, value);
|
|
72
|
+
},
|
|
73
|
+
remove: (key: string) => {
|
|
74
|
+
if (typeof window === 'undefined') return;
|
|
75
|
+
localStorage.removeItem(key);
|
|
76
|
+
},
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
/** @deprecated 使用 encryptedStorageAdapter 或 plainStorageAdapter */
|
|
80
|
+
export const localStorageAdapter = plainStorageAdapter;
|
|
81
|
+
|
|
82
|
+
/** 默认存储适配器(加密) */
|
|
83
|
+
export const defaultStorageAdapter = encryptedStorageAdapter;
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Create a config storage instance
|
|
87
|
+
*/
|
|
88
|
+
export interface StorageOptions {
|
|
89
|
+
serialize?: (data: any) => string;
|
|
90
|
+
deserialize?: (data: string) => any;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export function createConfigStorage(
|
|
94
|
+
adapter: StorageAdapter = defaultStorageAdapter,
|
|
95
|
+
options: StorageOptions = {}
|
|
96
|
+
) {
|
|
97
|
+
const serialize = options.serialize || JSON.stringify;
|
|
98
|
+
const deserialize = options.deserialize || JSON.parse;
|
|
99
|
+
|
|
100
|
+
return {
|
|
101
|
+
/**
|
|
102
|
+
* Save AI config
|
|
103
|
+
*/
|
|
104
|
+
save(config: AIConfig): void {
|
|
105
|
+
try {
|
|
106
|
+
const serialized = serialize(config);
|
|
107
|
+
adapter.set(STORAGE_KEY, serialized);
|
|
108
|
+
} catch (e) {
|
|
109
|
+
console.error('Failed to save config:', e);
|
|
110
|
+
}
|
|
111
|
+
},
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Load AI config
|
|
115
|
+
*/
|
|
116
|
+
load(): AIConfig | null {
|
|
117
|
+
const raw = adapter.get(STORAGE_KEY);
|
|
118
|
+
if (!raw) return null;
|
|
119
|
+
try {
|
|
120
|
+
return deserialize(raw) as AIConfig;
|
|
121
|
+
} catch (e) {
|
|
122
|
+
console.error('Failed to load config:', e);
|
|
123
|
+
return null;
|
|
124
|
+
}
|
|
125
|
+
},
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Clear AI config
|
|
129
|
+
*/
|
|
130
|
+
clear(): void {
|
|
131
|
+
adapter.remove(STORAGE_KEY);
|
|
132
|
+
},
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
|