-
-
Notifications
You must be signed in to change notification settings - Fork 7
Refactor Model Selection Logic #425
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
b72c0f6
5c4f50b
5bd124a
573bede
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -19,103 +19,75 @@ export function generateUUID(): string { | |
|
|
||
| export async function getModel(requireVision: boolean = false) { | ||
| const selectedModel = await getSelectedModel(); | ||
| console.log(`[getModel] User-selected model: ${selectedModel}`); | ||
|
|
||
| const xaiApiKey = process.env.XAI_API_KEY; | ||
| const gemini3ProApiKey = process.env.GEMINI_3_PRO_API_KEY; | ||
| const awsAccessKeyId = process.env.AWS_ACCESS_KEY_ID; | ||
| const awsSecretAccessKey = process.env.AWS_SECRET_ACCESS_KEY; | ||
| const awsRegion = process.env.AWS_REGION; | ||
| const bedrockModelId = process.env.BEDROCK_MODEL_ID || 'anthropic.claude-3-5-sonnet-20241022-v2:0'; | ||
| const bedrockModelId = process.env.BEDROCK_MODEL_ID || 'anthropic.claude-3-5-sonnet-20240620-v1:0'; | ||
| const openaiApiKey = process.env.OPENAI_API_KEY; | ||
|
|
||
| if (selectedModel) { | ||
| switch (selectedModel) { | ||
| case 'Grok 4.2': | ||
| if (xaiApiKey) { | ||
| const xai = createXai({ | ||
| apiKey: xaiApiKey, | ||
| baseURL: 'https://api.x.ai/v1', | ||
| }); | ||
| try { | ||
| return xai('grok-4-fast-non-reasoning'); | ||
| } catch (error) { | ||
| console.error('Selected model "Grok 4.2" is configured but failed to initialize.', error); | ||
| throw new Error('Failed to initialize selected model.'); | ||
| } | ||
| } else { | ||
| console.error('User selected "Grok 4.2" but XAI_API_KEY is not set.'); | ||
| throw new Error('Selected model is not configured.'); | ||
| } | ||
| case 'Gemini 3': | ||
| if (gemini3ProApiKey) { | ||
| const google = createGoogleGenerativeAI({ | ||
| apiKey: gemini3ProApiKey, | ||
| }); | ||
| try { | ||
| return google('gemini-3-pro-preview'); | ||
| } catch (error) { | ||
| console.error('Selected model "Gemini 3" is configured but failed to initialize.', error); | ||
| throw new Error('Failed to initialize selected model.'); | ||
| } | ||
| } else { | ||
| console.error('User selected "Gemini 3" but GEMINI_3_PRO_API_KEY is not set.'); | ||
| throw new Error('Selected model is not configured.'); | ||
| } | ||
| case 'GPT-5.1': | ||
| if (openaiApiKey) { | ||
| const openai = createOpenAI({ | ||
| apiKey: openaiApiKey, | ||
| }); | ||
| return openai('gpt-4o'); | ||
| } else { | ||
| console.error('User selected "GPT-5.1" but OPENAI_API_KEY is not set.'); | ||
| throw new Error('Selected model is not configured.'); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| // Default behavior: Grok -> Gemini -> Bedrock -> OpenAI | ||
| if (xaiApiKey) { | ||
| const xai = createXai({ | ||
| apiKey: xaiApiKey, | ||
| baseURL: 'https://api.x.ai/v1', | ||
| }); | ||
| try { | ||
| const modelInitializers = { | ||
| 'QCX-Terra': () => { | ||
| if (!awsAccessKeyId || !awsSecretAccessKey) { | ||
| throw new Error('AWS credentials for QCX-Terra are not configured.'); | ||
| } | ||
| const bedrock = createAmazonBedrock({ | ||
| bedrockOptions: { | ||
| region: awsRegion, | ||
| credentials: { accessKeyId: awsAccessKeyId, secretAccessKey: awsSecretAccessKey }, | ||
| }, | ||
| }); | ||
| return bedrock(bedrockModelId, { additionalModelRequestFields: { top_k: 250 } }); | ||
| }, | ||
|
Comment on lines
+33
to
+44
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Also, SuggestionHarden the Bedrock initializer to validate the full required configuration: if (!awsAccessKeyId || !awsSecretAccessKey || !awsRegion) {
throw new Error('AWS credentials/region for QCX-Terra are not configured.');
}Optionally validate Reply with "@CharlieHelps yes please" if you'd like me to add a commit with this suggestion.
Comment on lines
+29
to
+44
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The Bedrock defaults/parameters changed ( SuggestionAt minimum, document these defaults with a brief rationale and consider moving them to config/constants so they’re easier to tune and test. If const DEFAULT_BEDROCK_MODEL_ID = process.env.BEDROCK_MODEL_ID ?? '...';
const BEDROCK_TOP_K = Number(process.env.BEDROCK_TOP_K ?? 250);Reply with "@CharlieHelps yes please" if you want me to add a commit that centralizes these defaults and documents them. |
||
| 'Grok 4.2': () => { | ||
| if (!xaiApiKey) throw new Error('XAI_API_KEY for Grok 4.2 is not set.'); | ||
| const xai = createXai({ apiKey: xaiApiKey, baseURL: 'https://api.x.ai/v1' }); | ||
| return xai('grok-4-fast-non-reasoning'); | ||
| } catch (error) { | ||
| console.warn('xAI API unavailable, falling back to next provider:'); | ||
| } | ||
| } | ||
| }, | ||
| 'Gemini 3': () => { | ||
| if (!gemini3ProApiKey) throw new Error('GEMINI_3_PRO_API_KEY for Gemini 3 is not set.'); | ||
| const google = createGoogleGenerativeAI({ apiKey: gemini3ProApiKey }); | ||
| return google('gemini-3-pro-preview'); | ||
| }, | ||
| 'GPT-5.1': () => { | ||
| if (!openaiApiKey) throw new Error('OPENAI_API_KEY for GPT-5.1 is not set.'); | ||
| const openai = createOpenAI({ apiKey: openaiApiKey }); | ||
| return openai('gpt-4o'); | ||
| }, | ||
| }; | ||
|
|
||
| if (gemini3ProApiKey) { | ||
| const google = createGoogleGenerativeAI({ | ||
| apiKey: gemini3ProApiKey, | ||
| }); | ||
| if (selectedModel && selectedModel in modelInitializers) { | ||
| try { | ||
| return google('gemini-3-pro-preview'); | ||
| console.log(`[getModel] Initializing user-selected model: ${selectedModel}`); | ||
| return modelInitializers[selectedModel as keyof typeof modelInitializers](); | ||
| } catch (error) { | ||
| console.warn('Gemini 3 Pro API unavailable, falling back to next provider:', error); | ||
| console.error(`[getModel] Failed to initialize selected model "${selectedModel}":`, error); | ||
| // Fallback to default if the selected model fails for any reason. | ||
| } | ||
| } | ||
|
|
||
| if (awsAccessKeyId && awsSecretAccessKey) { | ||
| const bedrock = createAmazonBedrock({ | ||
| bedrockOptions: { | ||
| region: awsRegion, | ||
| credentials: { | ||
| accessKeyId: awsAccessKeyId, | ||
| secretAccessKey: awsSecretAccessKey, | ||
| }, | ||
| }, | ||
| }); | ||
| const model = bedrock(bedrockModelId, { | ||
| additionalModelRequestFields: { top_k: 350 }, | ||
| }); | ||
| return model; | ||
| // Default fallback logic if no model is selected or initialization fails | ||
| console.log('[getModel] No valid model selected, proceeding with default fallback chain.'); | ||
| const fallbackProviders = [ | ||
| { name: 'Grok', key: xaiApiKey, init: modelInitializers['Grok 4.2'] }, | ||
| { name: 'Gemini', key: gemini3ProApiKey, init: modelInitializers['Gemini 3'] }, | ||
| { name: 'Bedrock', key: awsAccessKeyId && awsSecretAccessKey, init: modelInitializers['QCX-Terra'] }, | ||
| { name: 'OpenAI', key: openaiApiKey, init: modelInitializers['GPT-5.1'] }, | ||
| ]; | ||
|
|
||
| for (const provider of fallbackProviders) { | ||
| if (provider.key) { | ||
| try { | ||
| console.log(`[getModel] Attempting to use default provider: ${provider.name}`); | ||
| return provider.init(); | ||
| } catch (error) { | ||
| console.warn(`[getModel] ${provider.name} API unavailable, falling back to next provider:`, error); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| const openai = createOpenAI({ | ||
| apiKey: openaiApiKey, | ||
| }); | ||
| return openai('gpt-4o'); | ||
| throw new Error('No valid AI providers are configured. Please check your environment variables.'); | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
requireVisionis still accepted bygetModel()but is never used in the new selection logic. That’s a behavior smell: callers may setrequireVision=trueexpecting vision-capable models, but the function now ignores it entirely and may return a non-vision model.If
requireVisionis not supported anymore, remove it (and update call sites). If it is supported, the model map should encode capabilities and the selection/fallback chain should filter accordingly.Suggestion
Model the capability in the initializer config and filter by it, e.g.
Then apply
isEligible(...)both for the selected model and the fallback chain.Reply with "@CharlieHelps yes please" if you'd like me to add a commit with this suggestion.