@@ -7,7 +7,7 @@ import { OpenAI } from "openai"
7
7
8
8
interface Config {
9
9
apiKey : string ;
10
- apiProvider : "openai" | "gemini" ; // Added provider selection
10
+ apiProvider : "openai" | "gemini" | "anthropic" ; // Added provider selection
11
11
extractionModel : string ;
12
12
solutionModel : string ;
13
13
debuggingModel : string ;
@@ -58,7 +58,7 @@ export class ConfigHelper extends EventEmitter {
58
58
/**
59
59
* Validate and sanitize model selection to ensure only allowed models are used
60
60
*/
61
- private sanitizeModelSelection ( model : string , provider : "openai" | "gemini" ) : string {
61
+ private sanitizeModelSelection ( model : string , provider : "openai" | "gemini" | "anthropic" ) : string {
62
62
if ( provider === "openai" ) {
63
63
// Only allow gpt-4o and gpt-4o-mini for OpenAI
64
64
const allowedModels = [ 'gpt-4o' , 'gpt-4o-mini' ] ;
@@ -67,15 +67,25 @@ export class ConfigHelper extends EventEmitter {
67
67
return 'gpt-4o' ;
68
68
}
69
69
return model ;
70
- } else {
70
+ } else if ( provider === "gemini" ) {
71
71
// Only allow gemini-1.5-pro and gemini-2.0-flash for Gemini
72
72
const allowedModels = [ 'gemini-1.5-pro' , 'gemini-2.0-flash' ] ;
73
73
if ( ! allowedModels . includes ( model ) ) {
74
74
console . warn ( `Invalid Gemini model specified: ${ model } . Using default model: gemini-2.0-flash` ) ;
75
75
return 'gemini-2.0-flash' ; // Changed default to flash
76
76
}
77
77
return model ;
78
+ } else if ( provider === "anthropic" ) {
79
+ // Only allow Claude models
80
+ const allowedModels = [ 'claude-3-opus-20240229' , 'claude-3-sonnet-20240229' , 'claude-3-haiku-20240307' ] ;
81
+ if ( ! allowedModels . includes ( model ) ) {
82
+ console . warn ( `Invalid Anthropic model specified: ${ model } . Using default model: claude-3-opus-20240229` ) ;
83
+ return 'claude-3-opus-20240229' ;
84
+ }
85
+ return model ;
78
86
}
87
+ // Default fallback
88
+ return model ;
79
89
}
80
90
81
91
public loadConfig ( ) : Config {
@@ -85,7 +95,7 @@ export class ConfigHelper extends EventEmitter {
85
95
const config = JSON . parse ( configData ) ;
86
96
87
97
// Ensure apiProvider is a valid value
88
- if ( config . apiProvider !== "openai" && config . apiProvider !== "gemini" ) {
98
+ if ( config . apiProvider !== "openai" && config . apiProvider !== "gemini" && config . apiProvider !== "anthropic" ) {
89
99
config . apiProvider = "gemini" ; // Default to Gemini if invalid
90
100
}
91
101
@@ -146,6 +156,9 @@ export class ConfigHelper extends EventEmitter {
146
156
if ( updates . apiKey . trim ( ) . startsWith ( 'sk-' ) ) {
147
157
provider = "openai" ;
148
158
console . log ( "Auto-detected OpenAI API key format" ) ;
159
+ } else if ( updates . apiKey . trim ( ) . startsWith ( 'sk-ant-' ) ) {
160
+ provider = "anthropic" ;
161
+ console . log ( "Auto-detected Anthropic API key format" ) ;
149
162
} else {
150
163
provider = "gemini" ;
151
164
console . log ( "Using Gemini API key format (default)" ) ;
@@ -161,6 +174,10 @@ export class ConfigHelper extends EventEmitter {
161
174
updates . extractionModel = "gpt-4o" ;
162
175
updates . solutionModel = "gpt-4o" ;
163
176
updates . debuggingModel = "gpt-4o" ;
177
+ } else if ( updates . apiProvider === "anthropic" ) {
178
+ updates . extractionModel = "claude-3-opus-20240229" ;
179
+ updates . solutionModel = "claude-3-opus-20240229" ;
180
+ updates . debuggingModel = "claude-3-opus-20240229" ;
164
181
} else {
165
182
updates . extractionModel = "gemini-2.0-flash" ;
166
183
updates . solutionModel = "gemini-2.0-flash" ;
@@ -208,11 +225,15 @@ export class ConfigHelper extends EventEmitter {
208
225
/**
209
226
* Validate the API key format
210
227
*/
211
- public isValidApiKeyFormat ( apiKey : string , provider ?: "openai" | "gemini" ) : boolean {
228
+ public isValidApiKeyFormat ( apiKey : string , provider ?: "openai" | "gemini" | "anthropic" ) : boolean {
212
229
// If provider is not specified, attempt to auto-detect
213
230
if ( ! provider ) {
214
231
if ( apiKey . trim ( ) . startsWith ( 'sk-' ) ) {
215
- provider = "openai" ;
232
+ if ( apiKey . trim ( ) . startsWith ( 'sk-ant-' ) ) {
233
+ provider = "anthropic" ;
234
+ } else {
235
+ provider = "openai" ;
236
+ }
216
237
} else {
217
238
provider = "gemini" ;
218
239
}
@@ -224,6 +245,9 @@ export class ConfigHelper extends EventEmitter {
224
245
} else if ( provider === "gemini" ) {
225
246
// Basic format validation for Gemini API keys (usually alphanumeric with no specific prefix)
226
247
return apiKey . trim ( ) . length >= 10 ; // Assuming Gemini keys are at least 10 chars
248
+ } else if ( provider === "anthropic" ) {
249
+ // Basic format validation for Anthropic API keys
250
+ return / ^ s k - a n t - [ a - z A - Z 0 - 9 ] { 32 , } $ / . test ( apiKey . trim ( ) ) ;
227
251
}
228
252
229
253
return false ;
@@ -264,12 +288,17 @@ export class ConfigHelper extends EventEmitter {
264
288
/**
265
289
* Test API key with the selected provider
266
290
*/
267
- public async testApiKey ( apiKey : string , provider ?: "openai" | "gemini" ) : Promise < { valid : boolean , error ?: string } > {
291
+ public async testApiKey ( apiKey : string , provider ?: "openai" | "gemini" | "anthropic" ) : Promise < { valid : boolean , error ?: string } > {
268
292
// Auto-detect provider based on key format if not specified
269
293
if ( ! provider ) {
270
294
if ( apiKey . trim ( ) . startsWith ( 'sk-' ) ) {
271
- provider = "openai" ;
272
- console . log ( "Auto-detected OpenAI API key format for testing" ) ;
295
+ if ( apiKey . trim ( ) . startsWith ( 'sk-ant-' ) ) {
296
+ provider = "anthropic" ;
297
+ console . log ( "Auto-detected Anthropic API key format for testing" ) ;
298
+ } else {
299
+ provider = "openai" ;
300
+ console . log ( "Auto-detected OpenAI API key format for testing" ) ;
301
+ }
273
302
} else {
274
303
provider = "gemini" ;
275
304
console . log ( "Using Gemini API key format for testing (default)" ) ;
@@ -280,6 +309,8 @@ export class ConfigHelper extends EventEmitter {
280
309
return this . testOpenAIKey ( apiKey ) ;
281
310
} else if ( provider === "gemini" ) {
282
311
return this . testGeminiKey ( apiKey ) ;
312
+ } else if ( provider === "anthropic" ) {
313
+ return this . testAnthropicKey ( apiKey ) ;
283
314
}
284
315
285
316
return { valid : false , error : "Unknown API provider" } ;
@@ -338,6 +369,31 @@ export class ConfigHelper extends EventEmitter {
338
369
return { valid : false , error : errorMessage } ;
339
370
}
340
371
}
372
+
373
+ /**
374
+ * Test Anthropic API key
375
+ * Note: This is a simplified implementation since we don't have the actual Anthropic client
376
+ */
377
+ private async testAnthropicKey ( apiKey : string ) : Promise < { valid : boolean , error ?: string } > {
378
+ try {
379
+ // For now, we'll just do a basic check to ensure the key exists and has valid format
380
+ // In production, you would connect to the Anthropic API and validate the key
381
+ if ( apiKey && / ^ s k - a n t - [ a - z A - Z 0 - 9 ] { 32 , } $ / . test ( apiKey . trim ( ) ) ) {
382
+ // Here you would actually validate the key with an Anthropic API call
383
+ return { valid : true } ;
384
+ }
385
+ return { valid : false , error : 'Invalid Anthropic API key format.' } ;
386
+ } catch ( error : any ) {
387
+ console . error ( 'Anthropic API key test failed:' , error ) ;
388
+ let errorMessage = 'Unknown error validating Anthropic API key' ;
389
+
390
+ if ( error . message ) {
391
+ errorMessage = `Error: ${ error . message } ` ;
392
+ }
393
+
394
+ return { valid : false , error : errorMessage } ;
395
+ }
396
+ }
341
397
}
342
398
343
399
// Export a singleton instance
0 commit comments