-
Notifications
You must be signed in to change notification settings - Fork 76
Pass API keys for docker build #41
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 2 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 |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| # Environment variables for Browser Operator DevTools | ||
| # Copy this file to .env and set your API keys | ||
|
|
||
| # OpenAI API Key (for GPT models) | ||
| OPENAI_API_KEY=sk-your-openai-api-key-here | ||
|
|
||
| # OpenRouter API Key (for multiple LLM providers) | ||
| OPENROUTER_API_KEY=sk-or-your-openrouter-api-key-here | ||
|
|
||
| # Groq API Key (for fast inference) | ||
| GROQ_API_KEY=gsk_your-groq-api-key-here | ||
|
|
||
| # LiteLLM API Key (for self-hosted LLM proxy) | ||
| LITELLM_API_KEY=your-litellm-api-key-here | ||
|
|
||
| # Usage: | ||
| # 1. Copy this file: cp .env.example .env | ||
| # 2. Set your API keys in the .env file | ||
| # 3. Build and run: docker-compose up --build | ||
| # | ||
| # The API keys will be embedded into the Docker image during build time | ||
| # and used as fallbacks when localStorage is empty. | ||
| # | ||
| # Priority order: | ||
| # 1. localStorage (user settings in DevTools) | ||
| # 2. Environment variables (this .env file) | ||
| # 3. Empty (no API key configured) |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,65 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| #!/bin/sh | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Docker entrypoint script for Browser Operator DevTools | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # This script generates runtime configuration from environment variables | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # and starts nginx to serve the DevTools frontend | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| set -e | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Log configuration status to container logs | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| echo "DevTools runtime configuration generated:" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| [ -n "${OPENAI_API_KEY}" ] && echo " ✓ OPENAI_API_KEY configured" || echo " ✗ OPENAI_API_KEY not set" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| [ -n "${OPENROUTER_API_KEY}" ] && echo " ✓ OPENROUTER_API_KEY configured" || echo " ✗ OPENROUTER_API_KEY not set" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| [ -n "${GROQ_API_KEY}" ] && echo " ✓ GROQ_API_KEY configured" || echo " ✗ GROQ_API_KEY not set" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| [ -n "${LITELLM_API_KEY}" ] && echo " ✓ LITELLM_API_KEY configured" || echo " ✗ LITELLM_API_KEY not set" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Inject API keys directly into DevTools JavaScript files | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| echo "🔧 Injecting API keys directly into JavaScript files..." | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Find and modify the main DevTools entry point files | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for js_file in /usr/share/nginx/html/entrypoints/*/devtools_app.js /usr/share/nginx/html/entrypoints/*/inspector_main.js; do | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if [ -f "$js_file" ]; then | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| echo " ✓ Injecting into $(basename "$js_file")" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Inject a global configuration object at the beginning of the file | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| sed -i '1i\ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Runtime API key injection\ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| window.__RUNTIME_CONFIG__ = {\ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| OPENAI_API_KEY: "'"${OPENAI_API_KEY:-}"'",\ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| OPENROUTER_API_KEY: "'"${OPENROUTER_API_KEY:-}"'",\ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| GROQ_API_KEY: "'"${GROQ_API_KEY:-}"'",\ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| LITELLM_API_KEY: "'"${LITELLM_API_KEY:-}"'",\ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| timestamp: "'"$(date -Iseconds)"'",\ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| source: "docker-runtime"\ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| };\ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Auto-save to localStorage\ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (window.__RUNTIME_CONFIG__.OPENAI_API_KEY) localStorage.setItem("ai_chat_api_key", window.__RUNTIME_CONFIG__.OPENAI_API_KEY);\ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (window.__RUNTIME_CONFIG__.OPENROUTER_API_KEY) localStorage.setItem("ai_chat_openrouter_api_key", window.__RUNTIME_CONFIG__.OPENROUTER_API_KEY);\ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (window.__RUNTIME_CONFIG__.GROQ_API_KEY) localStorage.setItem("ai_chat_groq_api_key", window.__RUNTIME_CONFIG__.GROQ_API_KEY);\ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (window.__RUNTIME_CONFIG__.LITELLM_API_KEY) localStorage.setItem("ai_chat_litellm_api_key", window.__RUNTIME_CONFIG__.LITELLM_API_KEY);\ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| console.log("[RUNTIME-CONFIG] API keys injected directly into JavaScript:", {hasOpenAI: !!window.__RUNTIME_CONFIG__.OPENAI_API_KEY, hasOpenRouter: !!window.__RUNTIME_CONFIG__.OPENROUTER_API_KEY, hasGroq: !!window.__RUNTIME_CONFIG__.GROQ_API_KEY, hasLiteLLM: !!window.__RUNTIME_CONFIG__.LITELLM_API_KEY});\ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ' "$js_file" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| done | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Also inject into AI Chat panel files specifically | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for js_file in /usr/share/nginx/html/panels/ai_chat/*.js; do | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if [ -f "$js_file" ]; then | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| echo " ✓ Injecting into AI Chat panel $(basename "$js_file")" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| sed -i '1i\ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Runtime API key injection for AI Chat\ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (typeof window !== "undefined" && !window.__RUNTIME_CONFIG__) {\ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| window.__RUNTIME_CONFIG__ = {\ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| OPENAI_API_KEY: "'"${OPENAI_API_KEY:-}"'",\ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| OPENROUTER_API_KEY: "'"${OPENROUTER_API_KEY:-}"'",\ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| GROQ_API_KEY: "'"${GROQ_API_KEY:-}"'",\ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| LITELLM_API_KEY: "'"${LITELLM_API_KEY:-}"'",\ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| timestamp: "'"$(date -Iseconds)"'",\ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| source: "docker-runtime"\ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| };\ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }\ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ' "$js_file" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| done | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+43
to
+61
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. 🛠️ Refactor suggestion Apply the same idempotency/escaping to AI Chat panel files. Mirror the guard and escaped values so we don't prepend twice. -# Also inject into AI Chat panel files specifically
+# Also inject into AI Chat panel files specifically (idempotent)
for js_file in /usr/share/nginx/html/panels/ai_chat/*.js; do
if [ -f "$js_file" ]; then
echo " ✓ Injecting into AI Chat panel $(basename "$js_file")"
- sed -i '1i\
+ if grep -q "__RUNTIME_CONFIG__" "$js_file"; then
+ echo " ↷ Already injected, skipping"
+ continue
+ fi
+ sed -i '1i\
+/* RUNTIME_CONFIG: injected */\
// Runtime API key injection for AI Chat\
if (typeof window !== "undefined" && !window.__RUNTIME_CONFIG__) {\
window.__RUNTIME_CONFIG__ = {\
- OPENAI_API_KEY: "'"${OPENAI_API_KEY:-}"'",\
- OPENROUTER_API_KEY: "'"${OPENROUTER_API_KEY:-}"'",\
- GROQ_API_KEY: "'"${GROQ_API_KEY:-}"'",\
- LITELLM_API_KEY: "'"${LITELLM_API_KEY:-}"'",\
+ OPENAI_API_KEY: "'"${OPENAI_API_KEY_JS}"'",\
+ OPENROUTER_API_KEY: "'"${OPENROUTER_API_KEY_JS}"'",\
+ GROQ_API_KEY: "'"${GROQ_API_KEY_JS}"'",\
+ LITELLM_API_KEY: "'"${LITELLM_API_KEY_JS}"'",\
timestamp: "'"$(date -Iseconds)"'",\
source: "docker-runtime"\
};\
}\
' "$js_file"
fi
done📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Start nginx in foreground | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| echo "Starting nginx..." | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| exec nginx -g 'daemon off;' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -7,6 +7,7 @@ import { LLMBaseProvider } from './LLMProvider.js'; | |||||||||||||||||||||||||||||||||||||
| import { LLMRetryManager } from './LLMErrorHandler.js'; | ||||||||||||||||||||||||||||||||||||||
| import { LLMResponseParser } from './LLMResponseParser.js'; | ||||||||||||||||||||||||||||||||||||||
| import { createLogger } from '../core/Logger.js'; | ||||||||||||||||||||||||||||||||||||||
| import { getEnvironmentConfig } from '../core/EnvironmentConfig.js'; | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| const logger = createLogger('GroqProvider'); | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
|
|
@@ -38,10 +39,29 @@ export class GroqProvider extends LLMBaseProvider { | |||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| readonly name: LLMProvider = 'groq'; | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| private readonly envConfig = getEnvironmentConfig(); | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| constructor(private readonly apiKey: string) { | ||||||||||||||||||||||||||||||||||||||
| super(); | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||
| * Get the API key with fallback hierarchy: | ||||||||||||||||||||||||||||||||||||||
| * 1. Constructor parameter (for backward compatibility) | ||||||||||||||||||||||||||||||||||||||
| * 2. localStorage (user-configured) | ||||||||||||||||||||||||||||||||||||||
| * 3. Build-time environment config | ||||||||||||||||||||||||||||||||||||||
| * 4. Empty string | ||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||
| private getApiKey(): string { | ||||||||||||||||||||||||||||||||||||||
| // Constructor parameter (highest priority for backward compatibility) | ||||||||||||||||||||||||||||||||||||||
| if (this.getApiKey() && this.getApiKey().trim() !== '') { | ||||||||||||||||||||||||||||||||||||||
| return this.getApiKey().trim(); | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| // Use environment config which handles localStorage -> build-time -> empty fallback | ||||||||||||||||||||||||||||||||||||||
| return this.envConfig.getApiKey('groq'); | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+55
to
+63
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. Fix infinite recursion in getApiKey().
private getApiKey(): string {
// Constructor parameter (highest priority for backward compatibility)
- if (this.getApiKey() && this.getApiKey().trim() !== '') {
- return this.getApiKey().trim();
+ if (this.apiKey && this.apiKey.trim() !== '') {
+ return this.apiKey.trim();
}
// Use environment config which handles localStorage -> build-time -> empty fallback
- return this.envConfig.getApiKey('groq');
+ return this.envConfig.getApiKey('groq')?.trim() ?? '';
}📝 Committable suggestion
Suggested change
🧰 Tools🪛 ESLint[error] 60-60: Trailing spaces not allowed. (@stylistic/no-trailing-spaces) 🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||
| * Get the chat completions endpoint URL | ||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||
|
|
@@ -92,7 +112,7 @@ export class GroqProvider extends LLMBaseProvider { | |||||||||||||||||||||||||||||||||||||
| method: 'POST', | ||||||||||||||||||||||||||||||||||||||
| headers: { | ||||||||||||||||||||||||||||||||||||||
| 'Content-Type': 'application/json', | ||||||||||||||||||||||||||||||||||||||
| 'Authorization': `Bearer ${this.apiKey}`, | ||||||||||||||||||||||||||||||||||||||
| 'Authorization': `Bearer ${this.getApiKey()}`, | ||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||
|
Comment on lines
114
to
116
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. 🛠️ Refactor suggestion Unquote Authorization header and guard against empty key.
- 'Authorization': `Bearer ${this.getApiKey()}`,
+ Authorization: (() => {
+ const k = this.getApiKey();
+ if (!k) throw new Error('Groq API key is missing. Configure it in Settings.');
+ return `Bearer ${k}`;
+ })(),- 'Authorization': `Bearer ${this.getApiKey()}`,
+ Authorization: (() => {
+ const k = this.getApiKey();
+ if (!k) throw new Error('Groq API key is missing. Configure it in Settings.');
+ return `Bearer ${k}`;
+ })(),Also applies to: 281-283 🧰 Tools🪛 ESLint[error] 115-115: Unnecessarily quoted property 'Authorization' found. (@stylistic/quote-props) 🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||
| body: JSON.stringify(payloadBody), | ||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||
|
|
@@ -259,7 +279,7 @@ export class GroqProvider extends LLMBaseProvider { | |||||||||||||||||||||||||||||||||||||
| const response = await fetch(this.getModelsEndpoint(), { | ||||||||||||||||||||||||||||||||||||||
| method: 'GET', | ||||||||||||||||||||||||||||||||||||||
| headers: { | ||||||||||||||||||||||||||||||||||||||
| 'Authorization': `Bearer ${this.apiKey}`, | ||||||||||||||||||||||||||||||||||||||
| 'Authorization': `Bearer ${this.getApiKey()}`, | ||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
|
|
@@ -432,29 +452,15 @@ export class GroqProvider extends LLMBaseProvider { | |||||||||||||||||||||||||||||||||||||
| * Validate that required credentials are available for Groq | ||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||
| validateCredentials(): {isValid: boolean, message: string, missingItems?: string[]} { | ||||||||||||||||||||||||||||||||||||||
| const storageKeys = this.getCredentialStorageKeys(); | ||||||||||||||||||||||||||||||||||||||
| const apiKey = localStorage.getItem(storageKeys.apiKey!); | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| if (!apiKey) { | ||||||||||||||||||||||||||||||||||||||
| return { | ||||||||||||||||||||||||||||||||||||||
| isValid: false, | ||||||||||||||||||||||||||||||||||||||
| message: 'Groq API key is required. Please add your API key in Settings.', | ||||||||||||||||||||||||||||||||||||||
| missingItems: ['API Key'] | ||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| return { | ||||||||||||||||||||||||||||||||||||||
| isValid: true, | ||||||||||||||||||||||||||||||||||||||
| message: 'Groq credentials are configured correctly.' | ||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||
| return this.envConfig.validateCredentials('groq'); | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||
| * Get the storage keys this provider uses for credentials | ||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||
| getCredentialStorageKeys(): {apiKey: string} { | ||||||||||||||||||||||||||||||||||||||
| return { | ||||||||||||||||||||||||||||||||||||||
| apiKey: 'ai_chat_groq_api_key' | ||||||||||||||||||||||||||||||||||||||
| apiKey: this.envConfig.getStorageKey('groq') | ||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -7,6 +7,7 @@ import { LLMBaseProvider } from './LLMProvider.js'; | |||||||||||||||||||
| import { LLMRetryManager } from './LLMErrorHandler.js'; | ||||||||||||||||||||
| import { LLMResponseParser } from './LLMResponseParser.js'; | ||||||||||||||||||||
| import { createLogger } from '../core/Logger.js'; | ||||||||||||||||||||
| import { getEnvironmentConfig } from '../core/EnvironmentConfig.js'; | ||||||||||||||||||||
|
|
||||||||||||||||||||
| const logger = createLogger('OpenAIProvider'); | ||||||||||||||||||||
|
|
||||||||||||||||||||
|
|
@@ -42,10 +43,29 @@ export class OpenAIProvider extends LLMBaseProvider { | |||||||||||||||||||
|
|
||||||||||||||||||||
| readonly name: LLMProvider = 'openai'; | ||||||||||||||||||||
|
|
||||||||||||||||||||
| private readonly envConfig = getEnvironmentConfig(); | ||||||||||||||||||||
|
|
||||||||||||||||||||
| constructor(private readonly apiKey: string) { | ||||||||||||||||||||
| super(); | ||||||||||||||||||||
| } | ||||||||||||||||||||
|
|
||||||||||||||||||||
| /** | ||||||||||||||||||||
| * Get the API key with fallback hierarchy: | ||||||||||||||||||||
| * 1. Constructor parameter (for backward compatibility) | ||||||||||||||||||||
| * 2. localStorage (user-configured) | ||||||||||||||||||||
| * 3. Build-time environment config | ||||||||||||||||||||
| * 4. Empty string | ||||||||||||||||||||
| */ | ||||||||||||||||||||
| private getApiKey(): string { | ||||||||||||||||||||
| // Constructor parameter (highest priority for backward compatibility) | ||||||||||||||||||||
| if (this.apiKey && this.apiKey.trim() !== '') { | ||||||||||||||||||||
| return this.apiKey.trim(); | ||||||||||||||||||||
| } | ||||||||||||||||||||
|
|
||||||||||||||||||||
| // Use environment config which handles localStorage -> build-time -> empty fallback | ||||||||||||||||||||
| return this.envConfig.getApiKey('openai'); | ||||||||||||||||||||
| } | ||||||||||||||||||||
|
|
||||||||||||||||||||
| /** | ||||||||||||||||||||
| * Determines the model family based on the model name | ||||||||||||||||||||
| */ | ||||||||||||||||||||
|
|
@@ -280,7 +300,7 @@ export class OpenAIProvider extends LLMBaseProvider { | |||||||||||||||||||
| metadata: { | ||||||||||||||||||||
| provider: 'openai', | ||||||||||||||||||||
| errorType: 'api_error', | ||||||||||||||||||||
| hasApiKey: !!this.apiKey | ||||||||||||||||||||
| hasApiKey: !!this.getApiKey() | ||||||||||||||||||||
| } | ||||||||||||||||||||
| }, context.traceId); | ||||||||||||||||||||
| } | ||||||||||||||||||||
|
|
@@ -299,7 +319,7 @@ export class OpenAIProvider extends LLMBaseProvider { | |||||||||||||||||||
| method: 'POST', | ||||||||||||||||||||
| headers: { | ||||||||||||||||||||
| 'Content-Type': 'application/json', | ||||||||||||||||||||
| Authorization: `Bearer ${this.apiKey}`, | ||||||||||||||||||||
| Authorization: `Bearer ${this.getApiKey()}`, | ||||||||||||||||||||
| }, | ||||||||||||||||||||
|
Comment on lines
321
to
323
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. 🛠️ Refactor suggestion Avoid sending empty Authorization header. If no key is present, fail fast with a clear error instead of - Authorization: `Bearer ${this.getApiKey()}`,
+ ...(this.getApiKey()
+ ? { Authorization: `Bearer ${this.getApiKey()}` }
+ : (() => { throw new Error('OpenAI API key is missing. Configure it in Settings.'); })()),📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||
| body: JSON.stringify(payloadBody), | ||||||||||||||||||||
| }); | ||||||||||||||||||||
|
|
||||||||||||||||||||
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.
Make injection idempotent and safely escape API key values.
Current sed prepends on every start and doesn’t escape quotes/backslashes in env values. Add a marker/guard and escape values before injecting.
📝 Committable suggestion
🤖 Prompt for AI Agents