bigIncrements('id'); $table->string('driver', 50)->unique(); $table->string('name', 120); $table->text('api_key')->nullable(); // encrypted by the model cast $table->string('base_url', 255)->nullable(); $table->string('transcription_model', 120)->nullable(); $table->string('analysis_model', 120)->nullable(); $table->string('language', 20)->default('en'); $table->json('extra_config')->nullable(); $table->boolean('is_enabled')->default(1); $table->boolean('is_active')->default(0); $table->string('last_test_status', 20)->nullable(); $table->text('last_test_message')->nullable(); $table->timestamp('last_tested_at')->nullable(); $table->unsignedSmallInteger('sort_order')->default(0); $table->timestamps(); }); } $now = now(); foreach (self::catalogue() as $row) { $exists = DB::table('ai_voice_providers')->where('driver', $row['driver'])->exists(); if (!$exists) { DB::table('ai_voice_providers')->insert($row + [ 'created_at' => $now, 'updated_at' => $now, ]); } } } public function down(): void { Schema::dropIfExists('ai_voice_providers'); } /** * Built-in providers. `is_active` is deliberately 0 for all of them: the * feature stays off until an administrator saves a key and selects a * provider. `mock` needs no key and makes no external call — it exists so * the pipeline can be exercised without vendor credentials. */ private static function catalogue(): array { return [ [ 'driver' => 'openai', 'name' => 'OpenAI (Whisper + GPT)', 'base_url' => 'https://api.openai.com/v1', 'transcription_model' => 'whisper-1', 'analysis_model' => 'gpt-4o-mini', 'language' => 'en', 'extra_config' => null, 'is_enabled' => 1, 'is_active' => 0, 'sort_order' => 1, ], [ 'driver' => 'gemini', 'name' => 'Google Gemini', 'base_url' => 'https://generativelanguage.googleapis.com/v1beta', 'transcription_model' => 'gemini-2.5-flash', 'analysis_model' => 'gemini-2.5-flash', 'language' => 'en', 'extra_config' => null, 'is_enabled' => 1, 'is_active' => 0, 'sort_order' => 2, ], [ 'driver' => 'groq', 'name' => 'Groq (Whisper + Llama)', 'base_url' => 'https://api.groq.com/openai/v1', 'transcription_model' => 'whisper-large-v3', 'analysis_model' => 'llama-3.3-70b-versatile', 'language' => 'en', 'extra_config' => null, 'is_enabled' => 1, 'is_active' => 0, 'sort_order' => 3, ], [ 'driver' => 'deepgram', 'name' => 'Deepgram (Nova)', 'base_url' => 'https://api.deepgram.com/v1', 'transcription_model' => 'nova-3', 'analysis_model' => null, 'language' => 'en', 'extra_config' => null, 'is_enabled' => 1, 'is_active' => 0, 'sort_order' => 4, ], [ 'driver' => 'assemblyai', 'name' => 'AssemblyAI (Universal)', 'base_url' => 'https://api.assemblyai.com', 'transcription_model' => 'universal', 'analysis_model' => null, 'language' => 'en', 'extra_config' => null, 'is_enabled' => 1, 'is_active' => 0, 'sort_order' => 5, ], [ 'driver' => 'mock', 'name' => 'Mock (local test, no API call)', 'base_url' => null, 'transcription_model' => null, 'analysis_model' => null, 'language' => 'en', 'extra_config' => null, 'is_enabled' => 0, 'is_active' => 0, 'sort_order' => 99, ], ]; } };