-
Notifications
You must be signed in to change notification settings - Fork 529
Add support for OpenAI-compatible API provider and optional base URL #68
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
Changes from all commits
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 |
---|---|---|
|
@@ -7,7 +7,8 @@ | |
|
||
interface Config { | ||
apiKey: string; | ||
apiProvider: "openai" | "gemini" | "anthropic"; // Added provider selection | ||
apiProvider: "openai" | "gemini" | "anthropic" | "openai-compatible"; // Added provider selection | ||
baseUrl?: string; // Optional custom API URL | ||
extractionModel: string; | ||
solutionModel: string; | ||
debuggingModel: string; | ||
|
@@ -20,6 +21,7 @@ | |
private defaultConfig: Config = { | ||
apiKey: "", | ||
apiProvider: "gemini", // Default to Gemini | ||
baseUrl: "", // Optional custom API URL | ||
extractionModel: "gemini-2.0-flash", // Default to Flash for faster responses | ||
solutionModel: "gemini-2.0-flash", | ||
debuggingModel: "gemini-2.0-flash", | ||
|
@@ -58,7 +60,7 @@ | |
/** | ||
* Validate and sanitize model selection to ensure only allowed models are used | ||
*/ | ||
private sanitizeModelSelection(model: string, provider: "openai" | "gemini" | "anthropic"): string { | ||
private sanitizeModelSelection(model: string, provider: "openai" | "gemini" | "anthropic" | "openai-compatible"): string { | ||
if (provider === "openai") { | ||
// Only allow gpt-4o and gpt-4o-mini for OpenAI | ||
const allowedModels = ['gpt-4o', 'gpt-4o-mini']; | ||
|
@@ -95,7 +97,7 @@ | |
const config = JSON.parse(configData); | ||
|
||
// Ensure apiProvider is a valid value | ||
if (config.apiProvider !== "openai" && config.apiProvider !== "gemini" && config.apiProvider !== "anthropic") { | ||
if (config.apiProvider !== "openai" && config.apiProvider !== "gemini" && config.apiProvider !== "anthropic" && config.apiProvider !== "openai-compatible") { | ||
config.apiProvider = "gemini"; // Default to Gemini if invalid | ||
} | ||
|
||
|
@@ -175,6 +177,10 @@ | |
updates.solutionModel = "gpt-4o"; | ||
updates.debuggingModel = "gpt-4o"; | ||
} else if (updates.apiProvider === "anthropic") { | ||
updates.extractionModel = "Claude#claude-3-7-sonnet-20250219"; | ||
updates.solutionModel = "Claude#claude-3-7-sonnet-20250219"; | ||
updates.debuggingModel = "Claude#claude-3-7-sonnet-20250219"; | ||
} else if (updates.apiProvider === "openai-compatible") { | ||
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. If |
||
updates.extractionModel = "claude-3-7-sonnet-20250219"; | ||
updates.solutionModel = "claude-3-7-sonnet-20250219"; | ||
updates.debuggingModel = "claude-3-7-sonnet-20250219"; | ||
|
@@ -311,6 +317,8 @@ | |
return this.testGeminiKey(apiKey); | ||
} else if (provider === "anthropic") { | ||
return this.testAnthropicKey(apiKey); | ||
} else if (provider === "openai-compatible") { | ||
return this.testOpenAIKey(apiKey); | ||
} | ||
|
||
return { valid: false, error: "Unknown API provider" }; | ||
|
@@ -325,7 +333,7 @@ | |
// Make a simple API call to test the key | ||
await openai.models.list(); | ||
return { valid: true }; | ||
} catch (error: any) { | ||
Check failure on line 336 in electron/ConfigHelper.ts
|
||
console.error('OpenAI API key test failed:', error); | ||
|
||
// Determine the specific error type for better error messages | ||
|
@@ -358,7 +366,7 @@ | |
return { valid: true }; | ||
} | ||
return { valid: false, error: 'Invalid Gemini API key format.' }; | ||
} catch (error: any) { | ||
Check failure on line 369 in electron/ConfigHelper.ts
|
||
console.error('Gemini API key test failed:', error); | ||
let errorMessage = 'Unknown error validating Gemini API key'; | ||
|
||
|
@@ -383,7 +391,7 @@ | |
return { valid: true }; | ||
} | ||
return { valid: false, error: 'Invalid Anthropic API key format.' }; | ||
} catch (error: any) { | ||
Check failure on line 394 in electron/ConfigHelper.ts
|
||
console.error('Anthropic API key test failed:', error); | ||
let errorMessage = 'Unknown error validating Anthropic API key'; | ||
|
||
|
@@ -396,5 +404,6 @@ | |
} | ||
} | ||
|
||
|
||
// Export a singleton instance | ||
export const configHelper = new ConfigHelper(); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,10 +1,10 @@ | ||
// ProcessingHelper.ts | ||
import fs from "node:fs" | ||
import path from "node:path" | ||
Check failure on line 3 in electron/ProcessingHelper.ts
|
||
import { ScreenshotHelper } from "./ScreenshotHelper" | ||
import { IProcessingHelperDeps } from "./main" | ||
import * as axios from "axios" | ||
import { app, BrowserWindow, dialog } from "electron" | ||
Check failure on line 7 in electron/ProcessingHelper.ts
|
||
import { OpenAI } from "openai" | ||
import { configHelper } from "./ConfigHelper" | ||
import Anthropic from '@anthropic-ai/sdk'; | ||
|
@@ -31,7 +31,7 @@ | |
finishReason: string; | ||
}>; | ||
} | ||
interface AnthropicMessage { | ||
Check failure on line 34 in electron/ProcessingHelper.ts
|
||
role: 'user' | 'assistant'; | ||
content: Array<{ | ||
type: 'text' | 'image'; | ||
|
@@ -56,7 +56,7 @@ | |
|
||
constructor(deps: IProcessingHelperDeps) { | ||
this.deps = deps | ||
this.screenshotHelper = deps.getScreenshotHelper() | ||
|
||
// Initialize AI client based on config | ||
this.initializeAIClient(); | ||
|
@@ -74,12 +74,13 @@ | |
try { | ||
const config = configHelper.loadConfig(); | ||
|
||
if (config.apiProvider === "openai") { | ||
if (config.apiProvider === "openai" || config.apiProvider === "openai-compatible") { | ||
if (config.apiKey) { | ||
this.openaiClient = new OpenAI({ | ||
apiKey: config.apiKey, | ||
timeout: 60000, // 60 second timeout | ||
maxRetries: 2 // Retry up to 2 times | ||
maxRetries: 2, // Retry up to 2 times | ||
baseURL: config.apiProvider === "openai-compatible" ? config.baseUrl : undefined, | ||
}); | ||
this.geminiApiKey = null; | ||
this.anthropicClient = null; | ||
|
@@ -286,7 +287,7 @@ | |
throw new Error("Failed to load screenshot data"); | ||
} | ||
|
||
const result = await this.processScreenshotsHelper(validScreenshots, signal) | ||
|
||
if (!result.success) { | ||
console.log("Processing failed:", result.error) | ||
|
@@ -313,7 +314,7 @@ | |
result.data | ||
) | ||
this.deps.setView("solutions") | ||
} catch (error: any) { | ||
Check failure on line 317 in electron/ProcessingHelper.ts
|
||
mainWindow.webContents.send( | ||
this.deps.PROCESSING_EVENTS.INITIAL_SOLUTION_ERROR, | ||
error | ||
|
@@ -400,11 +401,11 @@ | |
|
||
console.log( | ||
"Combined screenshots for processing:", | ||
validScreenshots.map((s) => s.path) | ||
) | ||
|
||
const result = await this.processExtraScreenshotsHelper( | ||
validScreenshots, | ||
signal | ||
) | ||
|
||
|
@@ -420,7 +421,7 @@ | |
result.error | ||
) | ||
} | ||
} catch (error: any) { | ||
Check failure on line 424 in electron/ProcessingHelper.ts
|
||
if (axios.isCancel(error)) { | ||
mainWindow.webContents.send( | ||
this.deps.PROCESSING_EVENTS.DEBUG_ERROR, | ||
|
@@ -460,7 +461,7 @@ | |
|
||
let problemInfo; | ||
|
||
if (config.apiProvider === "openai") { | ||
if (config.apiProvider === "openai" || config.apiProvider === "openai-compatible") { | ||
// Verify OpenAI client | ||
if (!this.openaiClient) { | ||
this.initializeAIClient(); // Try to reinitialize | ||
|
@@ -506,8 +507,11 @@ | |
try { | ||
const responseText = extractionResponse.choices[0].message.content; | ||
// Handle when OpenAI might wrap the JSON in markdown code blocks | ||
const jsonText = responseText.replace(/```json|```/g, '').trim(); | ||
problemInfo = JSON.parse(jsonText); | ||
const jsonRegex = /```json\n([\s\S]*?)\n```/; | ||
const match = responseText.match(jsonRegex); | ||
if (match && match[1]) { | ||
problemInfo = JSON.parse(match[1]); | ||
} | ||
Comment on lines
+510
to
+514
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. What was the need of the change here? if (match && match[1]) {
problemInfo = JSON.parse(match[1]);
} there's a better way to do this matching |
||
} catch (error) { | ||
console.error("Error parsing OpenAI response:", error); | ||
return { | ||
|
@@ -613,7 +617,7 @@ | |
const responseText = (response.content[0] as { type: 'text', text: string }).text; | ||
const jsonText = responseText.replace(/```json|```/g, '').trim(); | ||
problemInfo = JSON.parse(jsonText); | ||
} catch (error: any) { | ||
Check failure on line 620 in electron/ProcessingHelper.ts
|
||
console.error("Error using Anthropic API:", error); | ||
|
||
// Add specific handling for Claude's limitations | ||
|
@@ -764,7 +768,7 @@ | |
|
||
let responseContent; | ||
|
||
if (config.apiProvider === "openai") { | ||
if (config.apiProvider === "openai" || config.apiProvider === "openai-compatible") { | ||
// OpenAI processing | ||
if (!this.openaiClient) { | ||
return { | ||
|
@@ -889,12 +893,12 @@ | |
} | ||
|
||
// Extract parts from the response | ||
const codeMatch = responseContent.match(/```(?:\w+)?\s*([\s\S]*?)```/); | ||
const code = codeMatch ? codeMatch[1].trim() : responseContent; | ||
|
||
// Extract thoughts, looking for bullet points or numbered lists | ||
const thoughtsRegex = /(?:Thoughts:|Key Insights:|Reasoning:|Approach:)([\s\S]*?)(?:Time complexity:|$)/i; | ||
const thoughtsMatch = responseContent.match(thoughtsRegex); | ||
let thoughts: string[] = []; | ||
|
||
if (thoughtsMatch && thoughtsMatch[1]) { | ||
|
@@ -919,7 +923,7 @@ | |
let timeComplexity = "O(n) - Linear time complexity because we only iterate through the array once. Each element is processed exactly one time, and the hashmap lookups are O(1) operations."; | ||
let spaceComplexity = "O(n) - Linear space complexity because we store elements in the hashmap. In the worst case, we might need to store all elements before finding the solution pair."; | ||
|
||
const timeMatch = responseContent.match(timeComplexityPattern); | ||
if (timeMatch && timeMatch[1]) { | ||
timeComplexity = timeMatch[1].trim(); | ||
if (!timeComplexity.match(/O\([^)]+\)/i)) { | ||
|
@@ -934,7 +938,7 @@ | |
} | ||
} | ||
|
||
const spaceMatch = responseContent.match(spaceComplexityPattern); | ||
if (spaceMatch && spaceMatch[1]) { | ||
spaceComplexity = spaceMatch[1].trim(); | ||
if (!spaceComplexity.match(/O\([^)]+\)/i)) { | ||
|
@@ -1255,7 +1259,7 @@ | |
} | ||
|
||
let extractedCode = "// Debug mode - see analysis below"; | ||
const codeMatch = debugContent.match(/```(?:[a-zA-Z]+)?([\s\S]*?)```/); | ||
if (codeMatch && codeMatch[1]) { | ||
extractedCode = codeMatch[1].trim(); | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -13,7 +13,7 @@ import { Button } from "../ui/button"; | |
import { Settings } from "lucide-react"; | ||
import { useToast } from "../../contexts/toast"; | ||
|
||
type APIProvider = "openai" | "gemini" | "anthropic"; | ||
type APIProvider = "openai" | "gemini" | "anthropic" | "openai-compatible"; | ||
|
||
type AIModel = { | ||
id: string; | ||
|
@@ -181,6 +181,7 @@ export function SettingsDialog({ open: externalOpen, onOpenChange }: SettingsDia | |
const [open, setOpen] = useState(externalOpen || false); | ||
const [apiKey, setApiKey] = useState(""); | ||
const [apiProvider, setApiProvider] = useState<APIProvider>("openai"); | ||
const [baseUrl, setBaseUrl] = useState(""); | ||
const [extractionModel, setExtractionModel] = useState("gpt-4o"); | ||
const [solutionModel, setSolutionModel] = useState("gpt-4o"); | ||
const [debuggingModel, setDebuggingModel] = useState("gpt-4o"); | ||
|
@@ -210,6 +211,7 @@ export function SettingsDialog({ open: externalOpen, onOpenChange }: SettingsDia | |
interface Config { | ||
apiKey?: string; | ||
apiProvider?: APIProvider; | ||
baseUrl?: string; | ||
extractionModel?: string; | ||
solutionModel?: string; | ||
debuggingModel?: string; | ||
|
@@ -220,6 +222,7 @@ export function SettingsDialog({ open: externalOpen, onOpenChange }: SettingsDia | |
.then((config: Config) => { | ||
setApiKey(config.apiKey || ""); | ||
setApiProvider(config.apiProvider || "openai"); | ||
setBaseUrl(config.baseUrl || ""); | ||
setExtractionModel(config.extractionModel || "gpt-4o"); | ||
setSolutionModel(config.solutionModel || "gpt-4o"); | ||
setDebuggingModel(config.debuggingModel || "gpt-4o"); | ||
|
@@ -251,6 +254,11 @@ export function SettingsDialog({ open: externalOpen, onOpenChange }: SettingsDia | |
setExtractionModel("claude-3-7-sonnet-20250219"); | ||
setSolutionModel("claude-3-7-sonnet-20250219"); | ||
setDebuggingModel("claude-3-7-sonnet-20250219"); | ||
} else if (provider === "openai-compatible") { | ||
// For OpenAI-compatible APIs, we'll keep the current models or set defaults | ||
if (!extractionModel) setExtractionModel("gpt-3.5-turbo"); | ||
if (!solutionModel) setSolutionModel("gpt-3.5-turbo"); | ||
if (!debuggingModel) setDebuggingModel("gpt-3.5-turbo"); | ||
Comment on lines
+257
to
+261
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. Here we are creating a new model but in |
||
} | ||
}; | ||
|
||
|
@@ -260,6 +268,7 @@ export function SettingsDialog({ open: externalOpen, onOpenChange }: SettingsDia | |
const result = await window.electronAPI.updateConfig({ | ||
apiKey, | ||
apiProvider, | ||
baseUrl: apiProvider === "openai-compatible" ? baseUrl : "", | ||
extractionModel, | ||
solutionModel, | ||
debuggingModel, | ||
|
@@ -325,9 +334,9 @@ export function SettingsDialog({ open: externalOpen, onOpenChange }: SettingsDia | |
{/* API Provider Selection */} | ||
<div className="space-y-2"> | ||
<label className="text-sm font-medium text-white">API Provider</label> | ||
<div className="flex gap-2"> | ||
<div className="grid grid-cols-2 gap-2 mb-2"> | ||
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. Why this change? |
||
<div | ||
className={`flex-1 p-2 rounded-lg cursor-pointer transition-colors ${ | ||
className={`p-2 rounded-lg cursor-pointer transition-colors ${ | ||
apiProvider === "openai" | ||
? "bg-white/10 border border-white/20" | ||
: "bg-black/30 border border-white/5 hover:bg-white/5" | ||
|
@@ -347,7 +356,7 @@ export function SettingsDialog({ open: externalOpen, onOpenChange }: SettingsDia | |
</div> | ||
</div> | ||
<div | ||
className={`flex-1 p-2 rounded-lg cursor-pointer transition-colors ${ | ||
className={`p-2 rounded-lg cursor-pointer transition-colors ${ | ||
apiProvider === "gemini" | ||
? "bg-white/10 border border-white/20" | ||
: "bg-black/30 border border-white/5 hover:bg-white/5" | ||
|
@@ -367,7 +376,7 @@ export function SettingsDialog({ open: externalOpen, onOpenChange }: SettingsDia | |
</div> | ||
</div> | ||
<div | ||
className={`flex-1 p-2 rounded-lg cursor-pointer transition-colors ${ | ||
className={`p-2 rounded-lg cursor-pointer transition-colors ${ | ||
apiProvider === "anthropic" | ||
? "bg-white/10 border border-white/20" | ||
: "bg-black/30 border border-white/5 hover:bg-white/5" | ||
|
@@ -386,24 +395,46 @@ export function SettingsDialog({ open: externalOpen, onOpenChange }: SettingsDia | |
</div> | ||
</div> | ||
</div> | ||
<div | ||
className={`p-2 rounded-lg cursor-pointer transition-colors ${ | ||
apiProvider === "openai-compatible" | ||
? "bg-white/10 border border-white/20" | ||
: "bg-black/30 border border-white/5 hover:bg-white/5" | ||
}`} | ||
onClick={() => handleProviderChange("openai-compatible")} | ||
> | ||
<div className="flex items-center gap-2"> | ||
<div | ||
className={`w-3 h-3 rounded-full ${ | ||
apiProvider === "openai-compatible" ? "bg-white" : "bg-white/20" | ||
}`} | ||
/> | ||
<div className="flex flex-col"> | ||
<p className="font-medium text-white text-sm">OpenAI Compatible</p> | ||
<p className="text-xs text-white/60">Open Router, etc.</p> | ||
</div> | ||
</div> | ||
</div> | ||
Comment on lines
+398
to
+417
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. Again here, I would rather have you support a new model with everything made specifically for that model rather than creating new at some places and making changes in others |
||
</div> | ||
</div> | ||
|
||
<div className="space-y-2"> | ||
<label className="text-sm font-medium text-white" htmlFor="apiKey"> | ||
{apiProvider === "openai" ? "OpenAI API Key" : | ||
apiProvider === "gemini" ? "Gemini API Key" : | ||
"Anthropic API Key"} | ||
{apiProvider === "openai" ? "OpenAI API Key" : | ||
apiProvider === "gemini" ? "Gemini API Key" : | ||
apiProvider === "anthropic" ? "Anthropic API Key" : | ||
"API Key"} | ||
</label> | ||
<Input | ||
id="apiKey" | ||
type="password" | ||
value={apiKey} | ||
onChange={(e) => setApiKey(e.target.value)} | ||
placeholder={ | ||
apiProvider === "openai" ? "sk-..." : | ||
apiProvider === "openai" ? "sk-..." : | ||
apiProvider === "gemini" ? "Enter your Gemini API key" : | ||
"sk-ant-..." | ||
apiProvider === "anthropic" ? "sk-ant-..." : | ||
"Enter your API key" | ||
} | ||
className="bg-black/50 border-white/10 text-white" | ||
/> | ||
|
@@ -413,11 +444,36 @@ export function SettingsDialog({ open: externalOpen, onOpenChange }: SettingsDia | |
</p> | ||
)} | ||
<p className="text-xs text-white/50"> | ||
Your API key is stored locally and never sent to any server except {apiProvider === "openai" ? "OpenAI" : "Google"} | ||
Your API key is stored locally and never sent to any server except { | ||
apiProvider === "openai" ? "OpenAI" : | ||
apiProvider === "gemini" ? "Google" : | ||
apiProvider === "anthropic" ? "Anthropic" : | ||
"the API provider you specified" | ||
} | ||
</p> | ||
|
||
{apiProvider === "openai-compatible" && ( | ||
<div className="space-y-2"> | ||
<label className="text-sm font-medium text-white" htmlFor="customApiUrl"> | ||
API URL | ||
</label> | ||
<Input | ||
id="baseUrl" | ||
type="text" | ||
value={baseUrl} | ||
onChange={(e) => setBaseUrl(e.target.value)} | ||
placeholder="https://api.openrouter.ai/api/v1" | ||
className="bg-black/50 border-white/10 text-white" | ||
/> | ||
<p className="text-xs text-white/50"> | ||
Enter the base URL for the OpenAI-compatible API (e.g., Open Router) | ||
</p> | ||
</div> | ||
)} | ||
|
||
<div className="mt-2 p-2 rounded-md bg-white/5 border border-white/10"> | ||
<p className="text-xs text-white/80 mb-1">Don't have an API key?</p> | ||
{apiProvider === "openai" ? ( | ||
{apiProvider === "openai" && ( | ||
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. ??? |
||
<> | ||
<p className="text-xs text-white/60 mb-1">1. Create an account at <button | ||
onClick={() => openExternalLink('https://platform.openai.com/signup')} | ||
|
@@ -429,7 +485,8 @@ export function SettingsDialog({ open: externalOpen, onOpenChange }: SettingsDia | |
</p> | ||
<p className="text-xs text-white/60">3. Create a new secret key and paste it here</p> | ||
</> | ||
) : apiProvider === "gemini" ? ( | ||
)} | ||
{apiProvider === "gemini" && ( | ||
<> | ||
<p className="text-xs text-white/60 mb-1">1. Create an account at <button | ||
onClick={() => openExternalLink('https://aistudio.google.com/')} | ||
|
@@ -441,7 +498,8 @@ export function SettingsDialog({ open: externalOpen, onOpenChange }: SettingsDia | |
</p> | ||
<p className="text-xs text-white/60">3. Create a new API key and paste it here</p> | ||
</> | ||
) : ( | ||
)} | ||
{apiProvider === "anthropic" && ( | ||
<> | ||
<p className="text-xs text-white/60 mb-1">1. Create an account at <button | ||
onClick={() => openExternalLink('https://console.anthropic.com/signup')} | ||
|
@@ -454,6 +512,19 @@ export function SettingsDialog({ open: externalOpen, onOpenChange }: SettingsDia | |
<p className="text-xs text-white/60">3. Create a new API key and paste it here</p> | ||
</> | ||
)} | ||
{apiProvider === "openai-compatible" && ( | ||
<> | ||
<p className="text-xs text-white/60 mb-1">1. Create an account at <button | ||
onClick={() => openExternalLink('https://openrouter.ai/')} | ||
className="text-blue-400 hover:underline cursor-pointer">Open Router</button> | ||
</p> | ||
<p className="text-xs text-white/60 mb-1">2. Go to the <button | ||
onClick={() => openExternalLink('https://openrouter.ai/dashboard/api-keys')} | ||
className="text-blue-400 hover:underline cursor-pointer">API Keys</button> section | ||
</p> | ||
<p className="text-xs text-white/60">3. Create a new API key and paste it here</p> | ||
</> | ||
)} | ||
</div> | ||
</div> | ||
|
||
|
@@ -512,6 +583,18 @@ export function SettingsDialog({ open: externalOpen, onOpenChange }: SettingsDia | |
apiProvider === "openai" ? category.openaiModels : | ||
apiProvider === "gemini" ? category.geminiModels : | ||
category.anthropicModels; | ||
|
||
// Determine which state to use based on category key | ||
const currentValue = | ||
category.key === 'extractionModel' ? extractionModel : | ||
category.key === 'solutionModel' ? solutionModel : | ||
debuggingModel; | ||
|
||
// Determine which setter function to use | ||
const setValue = | ||
category.key === 'extractionModel' ? setExtractionModel : | ||
category.key === 'solutionModel' ? setSolutionModel : | ||
setDebuggingModel; | ||
|
||
return ( | ||
<div key={category.key} className="mb-4"> | ||
|
@@ -521,19 +604,18 @@ export function SettingsDialog({ open: externalOpen, onOpenChange }: SettingsDia | |
<p className="text-xs text-white/60 mb-2">{category.description}</p> | ||
|
||
<div className="space-y-2"> | ||
{models.map((m) => { | ||
// Determine which state to use based on category key | ||
const currentValue = | ||
category.key === 'extractionModel' ? extractionModel : | ||
category.key === 'solutionModel' ? solutionModel : | ||
debuggingModel; | ||
|
||
// Determine which setter function to use | ||
const setValue = | ||
category.key === 'extractionModel' ? setExtractionModel : | ||
category.key === 'solutionModel' ? setSolutionModel : | ||
setDebuggingModel; | ||
|
||
{apiProvider === "openai-compatible" && ( | ||
<Input | ||
id="extractionModelCustom" | ||
type="text" | ||
value={currentValue} | ||
onChange={(e) => setValue(e.target.value)} | ||
placeholder="gpt-3.5-turbo" | ||
className="bg-black/50 border-white/10 text-white" | ||
/> | ||
)} | ||
|
||
{apiProvider !== "openai-compatible" && models.map((m) => { | ||
Comment on lines
+607
to
+618
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. Why separate handling? |
||
return ( | ||
<div | ||
key={m.id} | ||
|
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.
Not sure about this change
seems like it beats the point of the default model