Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 68 additions & 0 deletions AUDIO_FEATURE_README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# Audio Recording Feature for Behavioral Questions

## Overview
The Interview Coder app now includes an audio recording feature that helps you practice behavioral interview questions. This feature records your voice, transcribes the question using OpenRouter's GPT-4o audio capabilities, and generates professional answers using the STAR method.

## Features
- **Audio Recording**: Record questions using your computer's microphone
- **Speech-to-Text**: Automatic transcription using OpenRouter GPT-4o audio or OpenAI Whisper
- **Answer Generation**: AI-powered behavioral interview answers using GPT-4o
- **Playback**: Review your recorded audio before processing
- **Professional Answers**: Detailed, structured responses suitable for interviews

## How to Use

### Prerequisites
1. **OpenRouter API Key** (Recommended): For both audio transcription and answer generation using GPT-4o audio
2. **OpenAI API Key** (Alternative): If you prefer to use OpenAI's Whisper for transcription
3. Grant microphone permissions when prompted by your browser/system

### Step-by-Step Usage
1. **Open the App**: Launch the Interview Coder application
2. **Navigate to Queue**: The audio recorder is located in the main Queue interface
3. **Start Recording**: Click the "Start Recording" button (red microphone icon)
4. **Ask Your Question**: Speak your behavioral interview question clearly
5. **Stop Recording**: Click "Stop Recording" when finished
6. **Review (Optional)**: Click "Play" to review your recorded audio
7. **Generate Answer**: Click "Generate Answer" to process the audio
8. **View Results**: The transcribed question and generated answer will appear below

## Technical Details

### Audio Processing
- **OpenRouter Users**: Uses GPT-4o audio model for transcription via multimodal chat completions
- **OpenAI Users**: Uses Whisper-1 model for transcription via dedicated audio API
- **Supported Formats**: WebM (recorded), WAV, MP3
- **Base64 Encoding**: Audio is automatically converted to base64 for OpenRouter processing

### Answer Generation
- Uses GPT-4o model (configurable in settings)
- Follows the STAR method (Situation, Task, Action, Result)
- Generates 300-450 word responses (2-3 minutes when spoken)
- Professional, conversational tone suitable for interviews

### API Key Detection
The app automatically detects your API key type:
- **OpenRouter keys** (`sk-or-...`): Uses multimodal audio API for transcription
- **OpenAI keys** (`sk-...`): Uses traditional Whisper API for transcription

## Troubleshooting

### Common Issues
1. **Microphone Not Working**: Check browser/system permissions for microphone access
2. **No Transcription**: Ensure you're speaking clearly and the recording has audio
3. **API Errors**: Verify your API key is valid and has sufficient credits
4. **Poor Audio Quality**: Try recording in a quieter environment

### Error Messages
- "Failed to start recording": Check microphone permissions
- "No speech detected": The recording may be too quiet or empty
- "API key required": Configure your API key in settings
- "Failed to process audio": Check your internet connection and API key

## Tips for Best Results
1. **Speak Clearly**: Enunciate words clearly for better transcription
2. **Quiet Environment**: Record in a quiet space to minimize background noise
3. **Complete Questions**: Ask full, complete behavioral interview questions
4. **Review Answers**: Use the generated answers as a starting point and personalize them
5. **Practice**: Use the feature regularly to improve your interview skills
27 changes: 0 additions & 27 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,30 +1,3 @@
# CodeInterviewAssist

> ## ⚠️ IMPORTANT NOTICE TO THE COMMUNITY ⚠️
>
> **This is a free, open-source initiative - NOT a full-service product!**
>
> There are numerous paid interview preparation tools charging hundreds of dollars for comprehensive features like live audio capture, automated answer generation, and more. This project is fundamentally different:
>
> - This is a **small, non-profit, community-driven project** with zero financial incentive behind it
> - The entire codebase is freely available for anyone to use, modify, or extend
> - Want features like voice support? You're welcome to integrate tools like OpenAI's Whisper or other APIs
> - New features should come through **community contributions** - it's unreasonable to expect a single maintainer to implement premium features for free
> - The maintainer receives no portfolio benefit, monetary compensation, or recognition for this work
>
> **Before submitting feature requests or expecting personalized support, please understand this project exists purely as a community resource.** If you value what's been created, the best way to show appreciation is by contributing code, documentation, or helping other users.

> ## 🔑 API KEY INFORMATION - UPDATED
>
> We have tested and confirmed that **both Gemini and OpenAI APIs work properly** with the current version. If you are experiencing issues with your API keys:
>
> - Try deleting your API key entry from the config file located in your user data directory
> - Log out and log back in to the application
> - Check your API key dashboard to verify the key is active and has sufficient credits
> - Ensure you're using the correct API key format (OpenAI keys start with "sk-")
>
> The configuration file is stored at: `C:\Users\[USERNAME]\AppData\Roaming\interview-coder-v1\config.json` (on Windows) or `/Users/[USERNAME]/Library/Application Support/interview-coder-v1/config.json` (on macOS)

## Free, Open-Source AI-Powered Interview Preparation Tool

This project provides a powerful alternative to premium coding interview platforms. It delivers the core functionality of paid interview preparation tools but in a free, open-source package. Using your own OpenAI API key, you get access to advanced features like AI-powered problem analysis, solution generation, and debugging assistance - all running locally on your machine.
Expand Down
76 changes: 68 additions & 8 deletions electron/ConfigHelper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { OpenAI } from "openai"

interface Config {
apiKey: string;
apiProvider: "openai" | "gemini" | "anthropic"; // Added provider selection
apiProvider: "openai" | "gemini" | "anthropic" | "openrouter"; // Added provider selection
extractionModel: string;
solutionModel: string;
debuggingModel: string;
Expand Down Expand Up @@ -58,7 +58,7 @@ export class ConfigHelper extends EventEmitter {
/**
* 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" | "openrouter"): string {
if (provider === "openai") {
// Only allow gpt-4o and gpt-4o-mini for OpenAI
const allowedModels = ['gpt-4o', 'gpt-4o-mini'];
Expand All @@ -83,6 +83,14 @@ export class ConfigHelper extends EventEmitter {
return 'claude-3-7-sonnet-20250219';
}
return model;
} else if (provider === "openrouter") {
// Only allow specific OpenRouter models
const allowedModels = ['openai/gpt-4o', 'anthropic/claude-3.5-sonnet', 'google/gemini-pro-1.5'];
if (!allowedModels.includes(model)) {
console.warn(`Invalid OpenRouter model specified: ${model}. Using default model: openai/gpt-4o`);
return 'openai/gpt-4o';
}
return model;
}
// Default fallback
return model;
Expand All @@ -95,7 +103,7 @@ export class ConfigHelper extends EventEmitter {
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 !== "openrouter") {
config.apiProvider = "gemini"; // Default to Gemini if invalid
}

Expand Down Expand Up @@ -153,7 +161,10 @@ export class ConfigHelper extends EventEmitter {
// Auto-detect provider based on API key format if a new key is provided
if (updates.apiKey && !updates.apiProvider) {
// If API key starts with "sk-", it's likely an OpenAI key
if (updates.apiKey.trim().startsWith('sk-')) {
if (updates.apiKey.trim().startsWith('sk-or-')) {
provider = "openrouter";
console.log("Auto-detected OpenRouter API key format");
} else if (updates.apiKey.trim().startsWith('sk-')) {
provider = "openai";
console.log("Auto-detected OpenAI API key format");
} else if (updates.apiKey.trim().startsWith('sk-ant-')) {
Expand All @@ -178,6 +189,10 @@ export class ConfigHelper extends EventEmitter {
updates.extractionModel = "claude-3-7-sonnet-20250219";
updates.solutionModel = "claude-3-7-sonnet-20250219";
updates.debuggingModel = "claude-3-7-sonnet-20250219";
} else if (updates.apiProvider === "openrouter") {
updates.extractionModel = "openai/gpt-4o";
updates.solutionModel = "openai/gpt-4o";
updates.debuggingModel = "openai/gpt-4o";
} else {
updates.extractionModel = "gemini-2.0-flash";
updates.solutionModel = "gemini-2.0-flash";
Expand Down Expand Up @@ -225,10 +240,12 @@ export class ConfigHelper extends EventEmitter {
/**
* Validate the API key format
*/
public isValidApiKeyFormat(apiKey: string, provider?: "openai" | "gemini" | "anthropic" ): boolean {
public isValidApiKeyFormat(apiKey: string, provider?: "openai" | "gemini" | "anthropic" | "openrouter" ): boolean {
// If provider is not specified, attempt to auto-detect
if (!provider) {
if (apiKey.trim().startsWith('sk-')) {
if (apiKey.trim().startsWith('sk-or-')) {
provider = "openrouter";
} else if (apiKey.trim().startsWith('sk-')) {
if (apiKey.trim().startsWith('sk-ant-')) {
provider = "anthropic";
} else {
Expand All @@ -248,6 +265,9 @@ export class ConfigHelper extends EventEmitter {
} else if (provider === "anthropic") {
// Basic format validation for Anthropic API keys
return /^sk-ant-[a-zA-Z0-9]{32,}$/.test(apiKey.trim());
} else if (provider === "openrouter") {
// Basic format validation for OpenRouter API keys
return /^sk-or-[a-zA-Z0-9]{32,}$/.test(apiKey.trim());
}

return false;
Expand Down Expand Up @@ -288,10 +308,13 @@ export class ConfigHelper extends EventEmitter {
/**
* Test API key with the selected provider
*/
public async testApiKey(apiKey: string, provider?: "openai" | "gemini" | "anthropic"): Promise<{valid: boolean, error?: string}> {
public async testApiKey(apiKey: string, provider?: "openai" | "gemini" | "anthropic" | "openrouter"): Promise<{valid: boolean, error?: string}> {
// Auto-detect provider based on key format if not specified
if (!provider) {
if (apiKey.trim().startsWith('sk-')) {
if (apiKey.trim().startsWith('sk-or-')) {
provider = "openrouter";
console.log("Auto-detected OpenRouter API key format for testing");
} else if (apiKey.trim().startsWith('sk-')) {
if (apiKey.trim().startsWith('sk-ant-')) {
provider = "anthropic";
console.log("Auto-detected Anthropic API key format for testing");
Expand All @@ -311,6 +334,8 @@ export class ConfigHelper extends EventEmitter {
return this.testGeminiKey(apiKey);
} else if (provider === "anthropic") {
return this.testAnthropicKey(apiKey);
} else if (provider === "openrouter") {
return this.testOpenRouterKey(apiKey);
}

return { valid: false, error: "Unknown API provider" };
Expand Down Expand Up @@ -394,6 +419,41 @@ export class ConfigHelper extends EventEmitter {
return { valid: false, error: errorMessage };
}
}

/**
* Test OpenRouter API key
*/
private async testOpenRouterKey(apiKey: string): Promise<{valid: boolean, error?: string}> {
try {
const openrouter = new OpenAI({
apiKey,
baseURL: "https://openrouter.ai/api/v1",
defaultHeaders: {
"HTTP-Referer": "https://github.com/your-repo",
"X-Title": "OIC - Online Interview Companion"
}
});
// Make a simple API call to test the key
await openrouter.models.list();
return { valid: true };
} catch (error: any) {
console.error('OpenRouter API key test failed:', error);

let errorMessage = 'Unknown error validating OpenRouter API key';

if (error.status === 401) {
errorMessage = 'Invalid API key. Please check your OpenRouter key and try again.';
} else if (error.status === 429) {
errorMessage = 'Rate limit exceeded. Your OpenRouter API key has reached its request limit or has insufficient quota.';
} else if (error.status === 500) {
errorMessage = 'OpenRouter server error. Please try again later.';
} else if (error.message) {
errorMessage = `Error: ${error.message}`;
}

return { valid: false, error: errorMessage };
}
}
}

// Export a singleton instance
Expand Down
Loading