@tombcato/ai-selector-core 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.
- package/dist/api.d.ts +14 -0
- package/dist/api.d.ts.map +1 -0
- package/dist/api.js +139 -0
- package/dist/api.js.map +1 -0
- package/dist/config.d.ts +18 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +85 -0
- package/dist/config.js.map +1 -0
- package/dist/i18n.d.ts +53 -0
- package/dist/i18n.d.ts.map +1 -0
- package/dist/i18n.js +51 -0
- package/dist/i18n.js.map +1 -0
- package/dist/icons.d.ts +10 -0
- package/dist/icons.d.ts.map +1 -0
- package/dist/icons.js +22 -0
- package/dist/icons.js.map +1 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +19 -0
- package/dist/index.js.map +1 -0
- package/dist/models.d.ts +11 -0
- package/dist/models.d.ts.map +1 -0
- package/dist/models.js +199 -0
- package/dist/models.js.map +1 -0
- package/dist/providers.d.ts +42 -0
- package/dist/providers.d.ts.map +1 -0
- package/dist/providers.js +229 -0
- package/dist/providers.js.map +1 -0
- package/dist/storage.d.ts +31 -0
- package/dist/storage.d.ts.map +1 -0
- package/dist/storage.js +65 -0
- package/dist/storage.js.map +1 -0
- package/dist/strategies.d.ts +54 -0
- package/dist/strategies.d.ts.map +1 -0
- package/dist/strategies.js +184 -0
- package/dist/strategies.js.map +1 -0
- package/dist/types.d.ts +122 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +6 -0
- package/dist/types.js.map +1 -0
- package/dist/utils.d.ts +2 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +4 -0
- package/dist/utils.js.map +1 -0
- package/package.json +36 -0
- package/src/api.ts +154 -0
- package/src/config.ts +104 -0
- package/src/i18n.ts +54 -0
- package/src/index.ts +76 -0
- package/src/models.ts +202 -0
- package/src/providers.ts +239 -0
- package/src/storage.ts +78 -0
- package/src/strategies.ts +251 -0
- package/src/styles.css +244 -0
- package/src/types.ts +151 -0
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import type { Model } from './types';
|
|
2
|
+
export interface ProviderStrategy {
|
|
3
|
+
format: string;
|
|
4
|
+
getModelsEndpoint?: (baseUrl: string, apiKey: string) => string;
|
|
5
|
+
parseModelsResponse?: (data: any) => Model[];
|
|
6
|
+
getChatEndpoint: (baseUrl: string, apiKey: string, model: string) => string;
|
|
7
|
+
buildChatPayload: (model: string, messages: Array<{
|
|
8
|
+
role: string;
|
|
9
|
+
content: string;
|
|
10
|
+
}>, maxTokens: number) => Record<string, unknown>;
|
|
11
|
+
parseChatResponse: (data: any) => string;
|
|
12
|
+
buildHeaders: (apiKey: string) => Record<string, string>;
|
|
13
|
+
}
|
|
14
|
+
export declare const defaultParseModelsResponse: (data: any) => Model[];
|
|
15
|
+
export declare const strategyRegistry: Record<string, ProviderStrategy>;
|
|
16
|
+
export declare function getStrategy(format: string): ProviderStrategy;
|
|
17
|
+
export interface DirectChatOptions {
|
|
18
|
+
apiFormat: string;
|
|
19
|
+
baseUrl: string;
|
|
20
|
+
apiKey: string;
|
|
21
|
+
model: string;
|
|
22
|
+
messages: Array<{
|
|
23
|
+
role: string;
|
|
24
|
+
content: string;
|
|
25
|
+
}>;
|
|
26
|
+
maxTokens?: number;
|
|
27
|
+
}
|
|
28
|
+
export interface DirectChatResult {
|
|
29
|
+
success: boolean;
|
|
30
|
+
content?: string;
|
|
31
|
+
message?: string;
|
|
32
|
+
latencyMs?: number;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* 纯前端直连 AI 厂商进行聊天
|
|
36
|
+
* 注意: 这会将 API Key 暴露在浏览器中,仅适用于 Demo/测试场景
|
|
37
|
+
*/
|
|
38
|
+
export declare function sendDirectChat(options: DirectChatOptions): Promise<DirectChatResult>;
|
|
39
|
+
export interface TestConnectionOptions {
|
|
40
|
+
apiFormat: string;
|
|
41
|
+
baseUrl: string;
|
|
42
|
+
apiKey: string;
|
|
43
|
+
model: string;
|
|
44
|
+
}
|
|
45
|
+
export interface TestConnectionResult {
|
|
46
|
+
success: boolean;
|
|
47
|
+
latencyMs?: number;
|
|
48
|
+
message?: string;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* 测试 AI 厂商连接 (通过发送一个简单的聊天请求)
|
|
52
|
+
*/
|
|
53
|
+
export declare function testDirectConnection(options: TestConnectionOptions): Promise<TestConnectionResult>;
|
|
54
|
+
//# sourceMappingURL=strategies.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"strategies.d.ts","sourceRoot":"","sources":["../src/strategies.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAY,KAAK,EAAE,MAAM,SAAS,CAAC;AAE/C,MAAM,WAAW,gBAAgB;IAC7B,MAAM,EAAE,MAAM,CAAC;IAEf,iBAAiB,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,MAAM,CAAC;IAChE,mBAAmB,CAAC,EAAE,CAAC,IAAI,EAAE,GAAG,KAAK,KAAK,EAAE,CAAC;IAE7C,eAAe,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;IAC5E,gBAAgB,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,EAAE,SAAS,EAAE,MAAM,KAAK,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACpI,iBAAiB,EAAE,CAAC,IAAI,EAAE,GAAG,KAAK,MAAM,CAAC;IAEzC,YAAY,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC5D;AAED,eAAO,MAAM,0BAA0B,GAAI,MAAM,GAAG,KAAG,KAAK,EAmB3D,CAAC;AAsGF,eAAO,MAAM,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAK7D,CAAC;AAEF,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,gBAAgB,CAE5D;AAMD,MAAM,WAAW,iBAAiB;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACnD,SAAS,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,gBAAgB;IAC7B,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;GAGG;AACH,wBAAsB,cAAc,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CA0C1F;AAMD,MAAM,WAAW,qBAAqB;IAClC,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,oBAAoB;IACjC,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;GAEG;AACH,wBAAsB,oBAAoB,CAAC,OAAO,EAAE,qBAAqB,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAexG"}
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
export const defaultParseModelsResponse = (data) => {
|
|
2
|
+
if (Array.isArray(data?.data)) {
|
|
3
|
+
return data.data
|
|
4
|
+
.filter((m) => m.id)
|
|
5
|
+
.map((m) => {
|
|
6
|
+
const name = m.name ||
|
|
7
|
+
(m.id.split('/').pop() ?? '')
|
|
8
|
+
.replace(/[-_]/g, ' ')
|
|
9
|
+
.replace(/\b\w/g, (c) => c.toUpperCase());
|
|
10
|
+
return {
|
|
11
|
+
id: m.id,
|
|
12
|
+
name,
|
|
13
|
+
created: m.created || 0,
|
|
14
|
+
};
|
|
15
|
+
})
|
|
16
|
+
.sort((a, b) => (b.created || 0) - (a.created || 0)); // 最新的排在前面
|
|
17
|
+
}
|
|
18
|
+
return [];
|
|
19
|
+
};
|
|
20
|
+
const openaiStrategy = {
|
|
21
|
+
format: 'openai',
|
|
22
|
+
getModelsEndpoint: (baseUrl) => `${baseUrl}/models`,
|
|
23
|
+
getChatEndpoint: (baseUrl) => `${baseUrl}/chat/completions`,
|
|
24
|
+
buildHeaders: (apiKey) => ({
|
|
25
|
+
'Content-Type': 'application/json',
|
|
26
|
+
'Authorization': `Bearer ${apiKey}`,
|
|
27
|
+
}),
|
|
28
|
+
buildChatPayload: (model, messages, maxTokens) => ({
|
|
29
|
+
model,
|
|
30
|
+
messages,
|
|
31
|
+
max_tokens: maxTokens,
|
|
32
|
+
}),
|
|
33
|
+
parseChatResponse: (data) => {
|
|
34
|
+
return data.choices?.[0]?.message?.content || '';
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
const anthropicStrategy = {
|
|
38
|
+
format: 'anthropic',
|
|
39
|
+
getChatEndpoint: (baseUrl) => `${baseUrl}/messages`,
|
|
40
|
+
buildHeaders: (apiKey) => ({
|
|
41
|
+
'Content-Type': 'application/json',
|
|
42
|
+
'x-api-key': apiKey,
|
|
43
|
+
'anthropic-version': '2023-06-01',
|
|
44
|
+
}),
|
|
45
|
+
buildChatPayload: (model, messages, maxTokens) => ({
|
|
46
|
+
model,
|
|
47
|
+
messages,
|
|
48
|
+
max_tokens: maxTokens,
|
|
49
|
+
}),
|
|
50
|
+
parseChatResponse: (data) => {
|
|
51
|
+
return data.content?.[0]?.text || '';
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
const geminiStrategy = {
|
|
55
|
+
format: 'gemini',
|
|
56
|
+
getModelsEndpoint: (baseUrl, apiKey) => `${baseUrl}/models?key=${apiKey}`,
|
|
57
|
+
getChatEndpoint: (baseUrl, apiKey, model) => `${baseUrl}/models/${model}:generateContent?key=${apiKey}`,
|
|
58
|
+
buildHeaders: () => ({
|
|
59
|
+
'Content-Type': 'application/json',
|
|
60
|
+
}),
|
|
61
|
+
buildChatPayload: (_model, messages, maxTokens) => {
|
|
62
|
+
// 转换 OpenAI 格式的 messages 为 Gemini 格式
|
|
63
|
+
const contents = messages.map(m => ({
|
|
64
|
+
role: m.role === 'assistant' ? 'model' : 'user',
|
|
65
|
+
parts: [{ text: m.content }],
|
|
66
|
+
}));
|
|
67
|
+
return {
|
|
68
|
+
contents,
|
|
69
|
+
generationConfig: { maxOutputTokens: maxTokens },
|
|
70
|
+
};
|
|
71
|
+
},
|
|
72
|
+
parseChatResponse: (data) => {
|
|
73
|
+
return data.candidates?.[0]?.content?.parts?.[0]?.text || '';
|
|
74
|
+
},
|
|
75
|
+
// Gemini 返回格式: { models: [{ name: "models/gemini-pro", ... }] }
|
|
76
|
+
parseModelsResponse: (data) => {
|
|
77
|
+
if (Array.isArray(data.models)) {
|
|
78
|
+
return data.models
|
|
79
|
+
.filter((m) => m.supportedGenerationMethods?.includes('generateContent'))
|
|
80
|
+
.map((m) => ({
|
|
81
|
+
id: m.name.replace('models/', ''), // "models/gemini-pro" -> "gemini-pro"
|
|
82
|
+
name: m.displayName || m.name.replace('models/', ''),
|
|
83
|
+
created: m.created || 0,
|
|
84
|
+
}))
|
|
85
|
+
.sort((a, b) => (b.created || 0) - (a.created || 0));
|
|
86
|
+
}
|
|
87
|
+
return [];
|
|
88
|
+
},
|
|
89
|
+
};
|
|
90
|
+
const cohereStrategy = {
|
|
91
|
+
format: 'cohere',
|
|
92
|
+
getModelsEndpoint: (baseUrl) => `${baseUrl}/models`,
|
|
93
|
+
getChatEndpoint: (baseUrl) => `${baseUrl}/chat`,
|
|
94
|
+
buildHeaders: (apiKey) => ({
|
|
95
|
+
'Content-Type': 'application/json',
|
|
96
|
+
'Authorization': `Bearer ${apiKey}`,
|
|
97
|
+
}),
|
|
98
|
+
buildChatPayload: (model, messages, maxTokens) => {
|
|
99
|
+
// Cohere 使用不同的格式: message 是当前消息, chat_history 是历史
|
|
100
|
+
const lastMessage = messages[messages.length - 1];
|
|
101
|
+
const chatHistory = messages.slice(0, -1).map(m => ({
|
|
102
|
+
role: m.role === 'assistant' ? 'CHATBOT' : 'USER',
|
|
103
|
+
message: m.content,
|
|
104
|
+
}));
|
|
105
|
+
return {
|
|
106
|
+
model,
|
|
107
|
+
message: lastMessage.content,
|
|
108
|
+
chat_history: chatHistory,
|
|
109
|
+
max_tokens: maxTokens,
|
|
110
|
+
};
|
|
111
|
+
},
|
|
112
|
+
parseChatResponse: (data) => {
|
|
113
|
+
return data.text || '';
|
|
114
|
+
},
|
|
115
|
+
};
|
|
116
|
+
export const strategyRegistry = {
|
|
117
|
+
openai: openaiStrategy,
|
|
118
|
+
anthropic: anthropicStrategy,
|
|
119
|
+
gemini: geminiStrategy,
|
|
120
|
+
cohere: cohereStrategy,
|
|
121
|
+
};
|
|
122
|
+
export function getStrategy(format) {
|
|
123
|
+
return strategyRegistry[format] || openaiStrategy;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* 纯前端直连 AI 厂商进行聊天
|
|
127
|
+
* 注意: 这会将 API Key 暴露在浏览器中,仅适用于 Demo/测试场景
|
|
128
|
+
*/
|
|
129
|
+
export async function sendDirectChat(options) {
|
|
130
|
+
const { apiFormat, baseUrl, apiKey, model, messages, maxTokens = 2048 } = options;
|
|
131
|
+
const strategy = getStrategy(apiFormat);
|
|
132
|
+
const endpoint = strategy.getChatEndpoint(baseUrl, apiKey, model);
|
|
133
|
+
const headers = strategy.buildHeaders(apiKey);
|
|
134
|
+
const payload = strategy.buildChatPayload(model, messages, maxTokens);
|
|
135
|
+
const startTime = performance.now();
|
|
136
|
+
try {
|
|
137
|
+
const response = await fetch(endpoint, {
|
|
138
|
+
method: 'POST',
|
|
139
|
+
headers,
|
|
140
|
+
body: JSON.stringify(payload),
|
|
141
|
+
});
|
|
142
|
+
const latencyMs = Math.round(performance.now() - startTime);
|
|
143
|
+
if (!response.ok) {
|
|
144
|
+
const errorData = await response.json().catch(() => ({}));
|
|
145
|
+
return {
|
|
146
|
+
success: false,
|
|
147
|
+
message: errorData.error?.message || `HTTP ${response.status}: ${response.statusText}`,
|
|
148
|
+
latencyMs,
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
const data = await response.json();
|
|
152
|
+
const content = strategy.parseChatResponse(data);
|
|
153
|
+
return {
|
|
154
|
+
success: true,
|
|
155
|
+
content,
|
|
156
|
+
latencyMs,
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
catch (e) {
|
|
160
|
+
return {
|
|
161
|
+
success: false,
|
|
162
|
+
message: e instanceof Error ? e.message : '网络错误',
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* 测试 AI 厂商连接 (通过发送一个简单的聊天请求)
|
|
168
|
+
*/
|
|
169
|
+
export async function testDirectConnection(options) {
|
|
170
|
+
const result = await sendDirectChat({
|
|
171
|
+
apiFormat: options.apiFormat,
|
|
172
|
+
baseUrl: options.baseUrl,
|
|
173
|
+
apiKey: options.apiKey,
|
|
174
|
+
model: options.model,
|
|
175
|
+
messages: [{ role: 'user', content: 'Hi' }],
|
|
176
|
+
maxTokens: 5, // 最小 token 数,节省成本
|
|
177
|
+
});
|
|
178
|
+
return {
|
|
179
|
+
success: result.success,
|
|
180
|
+
latencyMs: result.latencyMs,
|
|
181
|
+
message: result.success ? undefined : result.message,
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
//# sourceMappingURL=strategies.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"strategies.js","sourceRoot":"","sources":["../src/strategies.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AI Provider Selector - Core Types
|
|
3
|
+
* Framework-agnostic type definitions
|
|
4
|
+
*/
|
|
5
|
+
export type ApiFormat = 'openai' | 'anthropic' | 'gemini' | 'cohere';
|
|
6
|
+
export type FetcherActionType = 'fetchModels' | 'checkConnection';
|
|
7
|
+
export interface FetcherParams {
|
|
8
|
+
type: FetcherActionType;
|
|
9
|
+
providerId: string;
|
|
10
|
+
baseUrl: string;
|
|
11
|
+
apiKey?: string;
|
|
12
|
+
modelId?: string;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Custom fetcher for retrieving models and checking connection.
|
|
16
|
+
* If provided, this will bypass internal fetch logic.
|
|
17
|
+
*
|
|
18
|
+
* - type='fetchModels': returns Promise<Model[]>
|
|
19
|
+
* - type='checkConnection': returns Promise<{ success: boolean; latency?: number; message?: string }>
|
|
20
|
+
*/
|
|
21
|
+
export type ModelFetcher = (params: FetcherParams) => Promise<any>;
|
|
22
|
+
export interface Provider {
|
|
23
|
+
id: string;
|
|
24
|
+
name: string;
|
|
25
|
+
baseUrl: string;
|
|
26
|
+
needsApiKey: boolean;
|
|
27
|
+
apiFormat: ApiFormat;
|
|
28
|
+
supportsModelsApi: boolean;
|
|
29
|
+
icon?: string;
|
|
30
|
+
}
|
|
31
|
+
/** 自定义 Provider 定义 */
|
|
32
|
+
export interface CustomProviderDefinition {
|
|
33
|
+
name: string;
|
|
34
|
+
baseUrl: string;
|
|
35
|
+
needsApiKey: boolean;
|
|
36
|
+
apiFormat: ApiFormat;
|
|
37
|
+
supportsModelsApi?: boolean;
|
|
38
|
+
icon?: string;
|
|
39
|
+
/** 静态模型列表(当不支持 /models API 时使用) */
|
|
40
|
+
models?: Model[];
|
|
41
|
+
}
|
|
42
|
+
export interface Model {
|
|
43
|
+
id: string;
|
|
44
|
+
name: string;
|
|
45
|
+
}
|
|
46
|
+
export interface AIConfig {
|
|
47
|
+
providerId: string;
|
|
48
|
+
apiKey: string;
|
|
49
|
+
model: string;
|
|
50
|
+
modelName?: string;
|
|
51
|
+
baseUrl?: string;
|
|
52
|
+
}
|
|
53
|
+
export interface TestConnectionOptions {
|
|
54
|
+
provider: Provider;
|
|
55
|
+
apiKey: string;
|
|
56
|
+
model?: string;
|
|
57
|
+
baseUrl?: string;
|
|
58
|
+
proxyUrl?: string;
|
|
59
|
+
}
|
|
60
|
+
export interface TestConnectionResult {
|
|
61
|
+
success: boolean;
|
|
62
|
+
latencyMs?: number;
|
|
63
|
+
message?: string;
|
|
64
|
+
}
|
|
65
|
+
export interface FetchModelsOptions {
|
|
66
|
+
provider: Provider;
|
|
67
|
+
apiKey?: string;
|
|
68
|
+
baseUrl?: string;
|
|
69
|
+
proxyUrl?: string;
|
|
70
|
+
fallbackToStatic?: boolean;
|
|
71
|
+
}
|
|
72
|
+
export interface StorageAdapter {
|
|
73
|
+
get(key: string): string | null;
|
|
74
|
+
set(key: string, value: string): void;
|
|
75
|
+
remove(key: string): void;
|
|
76
|
+
}
|
|
77
|
+
/** 自定义 Provider 定义 */
|
|
78
|
+
export interface CustomProviderDefinition {
|
|
79
|
+
name: string;
|
|
80
|
+
baseUrl: string;
|
|
81
|
+
needsApiKey: boolean;
|
|
82
|
+
apiFormat: ApiFormat;
|
|
83
|
+
supportsModelsApi?: boolean;
|
|
84
|
+
icon?: string;
|
|
85
|
+
/** 静态模型列表(当不支持 /models API 时使用) */
|
|
86
|
+
models?: Model[];
|
|
87
|
+
}
|
|
88
|
+
/** Provider 配置 JSON */
|
|
89
|
+
export interface ProviderConfig {
|
|
90
|
+
/** 模式:default=内置+自定义,customOnly=只用自定义 */
|
|
91
|
+
mode: 'default' | 'customOnly';
|
|
92
|
+
/** 白名单:只显示这些内置 provider(仅 mode=default) */
|
|
93
|
+
include?: string[];
|
|
94
|
+
/** 黑名单:隐藏这些内置 provider(仅 mode=default) */
|
|
95
|
+
exclude?: string[];
|
|
96
|
+
/** 自定义 Providers */
|
|
97
|
+
custom?: Record<string, CustomProviderDefinition>;
|
|
98
|
+
}
|
|
99
|
+
/** AIConfigForm 组件的 Props */
|
|
100
|
+
export interface AIConfigFormProps {
|
|
101
|
+
/**
|
|
102
|
+
* 自定义请求处理器
|
|
103
|
+
* 用于接管获取模型列表和连通性测试的请求
|
|
104
|
+
* 如果提供此函数,proxyUrl 在这两种操作中将被忽略
|
|
105
|
+
*/
|
|
106
|
+
modelFetcher?: ModelFetcher;
|
|
107
|
+
/** 后端代理地址(必需,除非提供了 modelFetcher) */
|
|
108
|
+
proxyUrl?: string;
|
|
109
|
+
/** Provider 配置(可选,默认使用全部内置 Providers) */
|
|
110
|
+
config?: ProviderConfig;
|
|
111
|
+
/** 初始配置(可选,用于编辑已有配置) */
|
|
112
|
+
initialConfig?: Partial<AIConfig>;
|
|
113
|
+
/** 表单标题 */
|
|
114
|
+
title?: string;
|
|
115
|
+
/** 是否显示配置预览区域 */
|
|
116
|
+
showPreview?: boolean;
|
|
117
|
+
/** 保存按钮文本 */
|
|
118
|
+
saveButtonText?: string;
|
|
119
|
+
/** 是否禁用整个表单 */
|
|
120
|
+
disabled?: boolean;
|
|
121
|
+
}
|
|
122
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;GAGG"}
|
package/dist/utils.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAAuH"}
|
package/dist/utils.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,qBAAqB,CAAC,IAAY,EAAE,IAAY,IAAY,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC,MAAM,IAAI,CAAC,GAAG,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;IAAC,CAAC,EAAE,CAAC;AAAC,CAAC,CAAC,OAAO,IAAI,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@tombcato/ai-selector-core",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Framework-agnostic AI Provider Selector core utilities",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"module": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"import": "./dist/index.js",
|
|
12
|
+
"types": "./dist/index.d.ts"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist",
|
|
17
|
+
"src"
|
|
18
|
+
],
|
|
19
|
+
"devDependencies": {
|
|
20
|
+
"typescript": "^5.3.0"
|
|
21
|
+
},
|
|
22
|
+
"keywords": [
|
|
23
|
+
"ai",
|
|
24
|
+
"provider",
|
|
25
|
+
"openai",
|
|
26
|
+
"anthropic",
|
|
27
|
+
"gemini",
|
|
28
|
+
"deepseek",
|
|
29
|
+
"selector"
|
|
30
|
+
],
|
|
31
|
+
"license": "MIT",
|
|
32
|
+
"scripts": {
|
|
33
|
+
"build": "tsc",
|
|
34
|
+
"dev": "tsc --watch"
|
|
35
|
+
}
|
|
36
|
+
}
|
package/src/api.ts
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AI Provider Selector - API Functions
|
|
3
|
+
* Connection testing and model fetching logic
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type {
|
|
7
|
+
Provider,
|
|
8
|
+
Model,
|
|
9
|
+
TestConnectionOptions,
|
|
10
|
+
TestConnectionResult,
|
|
11
|
+
FetchModelsOptions
|
|
12
|
+
} from './types';
|
|
13
|
+
import { getStaticModels } from './models';
|
|
14
|
+
import { getStrategy, defaultParseModelsResponse } from './strategies';
|
|
15
|
+
|
|
16
|
+
const DEFAULT_TIMEOUT = 30000;
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Test connection to an AI provider
|
|
20
|
+
*/
|
|
21
|
+
export async function testConnection(options: TestConnectionOptions): Promise<TestConnectionResult> {
|
|
22
|
+
const { provider, apiKey, model, baseUrl, proxyUrl } = options;
|
|
23
|
+
const actualBaseUrl = baseUrl || provider.baseUrl;
|
|
24
|
+
const startTime = Date.now();
|
|
25
|
+
|
|
26
|
+
try {
|
|
27
|
+
// If proxy URL is provided, use it
|
|
28
|
+
if (proxyUrl) {
|
|
29
|
+
const response = await fetch(`${proxyUrl}/test`, {
|
|
30
|
+
method: 'POST',
|
|
31
|
+
headers: { 'Content-Type': 'application/json' },
|
|
32
|
+
body: JSON.stringify({
|
|
33
|
+
provider_id: provider.id,
|
|
34
|
+
api_key: apiKey,
|
|
35
|
+
model: model || '',
|
|
36
|
+
base_url: baseUrl || provider.baseUrl,
|
|
37
|
+
api_format: provider.apiFormat,
|
|
38
|
+
}),
|
|
39
|
+
});
|
|
40
|
+
const data = await response.json();
|
|
41
|
+
return {
|
|
42
|
+
success: data.success,
|
|
43
|
+
latencyMs: data.latency_ms || (Date.now() - startTime),
|
|
44
|
+
message: data.message,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Direct API call (for browser-compatible providers)
|
|
49
|
+
const strategy = getStrategy(provider.apiFormat);
|
|
50
|
+
const targetModel = model || '';
|
|
51
|
+
|
|
52
|
+
// If no model provided, we cannot test (user must select model first)
|
|
53
|
+
if (!targetModel) {
|
|
54
|
+
return {
|
|
55
|
+
success: false,
|
|
56
|
+
latencyMs: 0,
|
|
57
|
+
message: '请先选择模型 (Please select a model)'
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const headers = strategy.buildHeaders(apiKey);
|
|
62
|
+
// 使用聊天接口进行测试,发送最简单的请求
|
|
63
|
+
const testPayload = strategy.buildChatPayload(targetModel, [{ role: 'user', content: 'Hi' }], 5);
|
|
64
|
+
const endpoint = strategy.getChatEndpoint(actualBaseUrl, apiKey, targetModel);
|
|
65
|
+
|
|
66
|
+
const response = await fetch(endpoint, {
|
|
67
|
+
method: 'POST',
|
|
68
|
+
headers,
|
|
69
|
+
body: JSON.stringify(testPayload),
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
const latencyMs = Date.now() - startTime;
|
|
73
|
+
|
|
74
|
+
if (response.ok) {
|
|
75
|
+
return { success: true, latencyMs, message: '连接成功' };
|
|
76
|
+
} else {
|
|
77
|
+
const errorText = await response.text();
|
|
78
|
+
return {
|
|
79
|
+
success: false,
|
|
80
|
+
latencyMs,
|
|
81
|
+
message: `HTTP ${response.status}: ${errorText.slice(0, 200)}`
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
} catch (error) {
|
|
85
|
+
return {
|
|
86
|
+
success: false,
|
|
87
|
+
latencyMs: Date.now() - startTime,
|
|
88
|
+
message: error instanceof Error ? error.message : String(error),
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Fetch available models from a provider
|
|
95
|
+
*/
|
|
96
|
+
export async function fetchModels(options: FetchModelsOptions): Promise<Model[]> {
|
|
97
|
+
const { provider, apiKey, baseUrl, proxyUrl, fallbackToStatic = true } = options;
|
|
98
|
+
|
|
99
|
+
// If proxy URL is provided, use it
|
|
100
|
+
if (proxyUrl) {
|
|
101
|
+
try {
|
|
102
|
+
const response = await fetch(`${proxyUrl}/models`, {
|
|
103
|
+
method: 'POST',
|
|
104
|
+
headers: { 'Content-Type': 'application/json' },
|
|
105
|
+
body: JSON.stringify({
|
|
106
|
+
provider_id: provider.id,
|
|
107
|
+
api_key: apiKey || undefined,
|
|
108
|
+
base_url: baseUrl || provider.baseUrl,
|
|
109
|
+
}),
|
|
110
|
+
});
|
|
111
|
+
const data = await response.json();
|
|
112
|
+
if (data.success && data.models?.length > 0) {
|
|
113
|
+
return data.models;
|
|
114
|
+
}
|
|
115
|
+
} catch (error) {
|
|
116
|
+
console.warn('Failed to fetch models via proxy:', error);
|
|
117
|
+
if (!fallbackToStatic) throw error;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Direct API call
|
|
122
|
+
if (!proxyUrl && provider.supportsModelsApi) {
|
|
123
|
+
try {
|
|
124
|
+
const strategy = getStrategy(provider.apiFormat);
|
|
125
|
+
if (strategy.getModelsEndpoint) {
|
|
126
|
+
const endpoint = strategy.getModelsEndpoint(baseUrl || provider.baseUrl, apiKey || '');
|
|
127
|
+
const headers = strategy.buildHeaders(apiKey || '');
|
|
128
|
+
|
|
129
|
+
const response = await fetch(endpoint, {
|
|
130
|
+
method: 'GET',
|
|
131
|
+
headers
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
if (response.ok) {
|
|
135
|
+
const data = await response.json();
|
|
136
|
+
const parser = strategy.parseModelsResponse || defaultParseModelsResponse;
|
|
137
|
+
return parser(data);
|
|
138
|
+
} else {
|
|
139
|
+
if (!fallbackToStatic) throw new Error(`HTTP ${response.status}`);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
} catch (e) {
|
|
143
|
+
console.warn('Failed to fetch models directly:', e);
|
|
144
|
+
if (!fallbackToStatic) throw e;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if (!fallbackToStatic && provider.supportsModelsApi) {
|
|
149
|
+
throw new Error('Failed to fetch models');
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Fallback to static models
|
|
153
|
+
return getStaticModels(provider.id);
|
|
154
|
+
}
|