Skip to content
Open
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
28 changes: 25 additions & 3 deletions backend/app/db.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,10 +138,24 @@ def _legacy_init_db(self):
groqApiKey TEXT,
openaiApiKey TEXT,
anthropicApiKey TEXT,
ollamaApiKey TEXT
ollamaApiKey TEXT,
geminiApiKey TEXT,
perplexityApiKey TEXT
)
""")

# Migration: Add geminiApiKey and perplexityApiKey columns to existing settings table
try:
cursor.execute("ALTER TABLE settings ADD COLUMN geminiApiKey TEXT")
logger.info("Added geminiApiKey column to settings table")
except sqlite3.OperationalError:
pass # Column already exists
try:
cursor.execute("ALTER TABLE settings ADD COLUMN perplexityApiKey TEXT")
logger.info("Added perplexityApiKey column to settings table")
except sqlite3.OperationalError:
pass # Column already exists

# Create transcript_settings table
cursor.execute("""
CREATE TABLE IF NOT EXISTS transcript_settings (
Expand Down Expand Up @@ -581,7 +595,7 @@ async def save_model_config(self, provider: str, model: str, whisperModel: str):

async def save_api_key(self, api_key: str, provider: str):
"""Save the API key"""
provider_list = ["openai", "claude", "groq", "ollama"]
provider_list = ["openai", "claude", "groq", "ollama", "gemini", "perplexity"]
if provider not in provider_list:
raise ValueError(f"Invalid provider: {provider}")
if provider == "openai":
Expand All @@ -592,6 +606,10 @@ async def save_api_key(self, api_key: str, provider: str):
api_key_name = "groqApiKey"
elif provider == "ollama":
api_key_name = "ollamaApiKey"
elif provider == "gemini":
api_key_name = "geminiApiKey"
elif provider == "perplexity":
api_key_name = "perplexityApiKey"

try:
async with self._get_connection() as conn:
Expand Down Expand Up @@ -626,7 +644,7 @@ async def save_api_key(self, api_key: str, provider: str):

async def get_api_key(self, provider: str):
"""Get the API key"""
provider_list = ["openai", "claude", "groq", "ollama"]
provider_list = ["openai", "claude", "groq", "ollama", "gemini", "perplexity"]
if provider not in provider_list:
raise ValueError(f"Invalid provider: {provider}")
if provider == "openai":
Expand All @@ -637,6 +655,10 @@ async def get_api_key(self, provider: str):
api_key_name = "groqApiKey"
elif provider == "ollama":
api_key_name = "ollamaApiKey"
elif provider == "gemini":
api_key_name = "geminiApiKey"
elif provider == "perplexity":
api_key_name = "perplexityApiKey"
async with self._get_connection() as conn:
cursor = await conn.execute(f"SELECT {api_key_name} FROM settings WHERE id = '1'")
row = await cursor.fetchone()
Expand Down
4 changes: 2 additions & 2 deletions backend/app/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -225,11 +225,11 @@ async def process_transcript_background(process_id: str, transcript: TranscriptR
if not transcript.text or not transcript.text.strip():
raise ValueError("Empty transcript text provided")

if transcript.model in ["claude", "groq", "openai"]:
if transcript.model in ["claude", "groq", "openai", "gemini", "perplexity"]:
# Check if API key is available for cloud providers
api_key = await processor.db.get_api_key(transcript.model)
if not api_key:
provider_names = {"claude": "Anthropic", "groq": "Groq", "openai": "OpenAI"}
provider_names = {"claude": "Anthropic", "groq": "Groq", "openai": "OpenAI", "gemini": "Google Gemini", "perplexity": "Perplexity"}
raise ValueError(f"{provider_names.get(transcript.model, transcript.model)} API key not configured. Please set your API key in the model settings.")

_, all_json_data = await processor.process_transcript(
Expand Down
18 changes: 17 additions & 1 deletion backend/app/transcript_processor.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@
from pydantic_ai.models.anthropic import AnthropicModel
from pydantic_ai.models.groq import GroqModel
from pydantic_ai.models.openai import OpenAIModel
from pydantic_ai.models.gemini import GeminiModel
from pydantic_ai.providers.openai import OpenAIProvider
from pydantic_ai.providers.groq import GroqProvider
from pydantic_ai.providers.anthropic import AnthropicProvider
from pydantic_ai.providers.gemini import GeminiProvider

import logging
import os
Expand Down Expand Up @@ -90,7 +92,7 @@ async def process_transcript(self, text: str, model: str, model_name: str, chunk

Args:
text: The transcript text.
model: The AI model provider ('claude', 'ollama', 'groq', 'openai').
model: The AI model provider ('claude', 'ollama', 'groq', 'openai', 'gemini', 'perplexity').
model_name: The specific model name.
chunk_size: The size of each text chunk.
overlap: The overlap between consecutive chunks.
Expand Down Expand Up @@ -142,6 +144,20 @@ async def process_transcript(self, text: str, model: str, model_name: str, chunk
llm = OpenAIModel(model_name, provider=OpenAIProvider(api_key=api_key))
logger.info(f"Using OpenAI model: {model_name}")
# --- END OPENAI SUPPORT ---
elif model == "gemini":
api_key = await db.get_api_key("gemini")
if not api_key: raise ValueError("GEMINI_API_KEY not configured")
llm = GeminiModel(model_name, provider=GeminiProvider(api_key=api_key))
logger.info(f"Using Gemini model: {model_name}")
elif model == "perplexity":
api_key = await db.get_api_key("perplexity")
if not api_key: raise ValueError("PERPLEXITY_API_KEY not configured")
perplexity_base_url = "https://api.perplexity.ai"
llm = OpenAIModel(
model_name=model_name,
provider=OpenAIProvider(api_key=api_key, base_url=perplexity_base_url)
)
logger.info(f"Using Perplexity model: {model_name}")
else:
logger.error(f"Unsupported model provider requested: {model}")
raise ValueError(f"Unsupported model provider: {model}")
Expand Down
21 changes: 19 additions & 2 deletions frontend/src/components/ModelSettingsModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import { cn } from '@/lib/utils';
import { toast } from 'sonner';

export interface ModelConfig {
provider: 'ollama' | 'groq' | 'claude' | 'openai' | 'openrouter';
provider: 'ollama' | 'groq' | 'claude' | 'openai' | 'openrouter' | 'gemini' | 'perplexity';
model: string;
whisperModel: string;
apiKey?: string | null;
Expand Down Expand Up @@ -159,13 +159,28 @@ export function ModelSettingsModal({
'gpt-3.5-turbo-1106'
],
openrouter: openRouterModels.map((m) => m.id),
gemini: [
'gemini-2.0-flash-exp',
'gemini-exp-1206',
'gemini-2.0-flash-thinking-exp-1219',
'gemini-1.5-pro',
'gemini-1.5-flash',
'gemini-1.5-flash-8b'
],
perplexity: [
'llama-3.1-sonar-small-128k-online',
'llama-3.1-sonar-large-128k-online',
'llama-3.1-sonar-huge-128k-online'
],
};

const requiresApiKey =
modelConfig.provider === 'claude' ||
modelConfig.provider === 'groq' ||
modelConfig.provider === 'openai' ||
modelConfig.provider === 'openrouter';
modelConfig.provider === 'openrouter' ||
modelConfig.provider === 'gemini' ||
modelConfig.provider === 'perplexity';

// Check if Ollama endpoint has changed but models haven't been fetched yet
const ollamaEndpointChanged = modelConfig.provider === 'ollama' &&
Expand Down Expand Up @@ -524,6 +539,8 @@ export function ModelSettingsModal({
<SelectItem value="ollama">Ollama</SelectItem>
<SelectItem value="openai">OpenAI</SelectItem>
<SelectItem value="openrouter">OpenRouter</SelectItem>
<SelectItem value="gemini">Google Gemini</SelectItem>
<SelectItem value="perplexity">Perplexity</SelectItem>
</SelectContent>
</Select>

Expand Down