apiKey = config('services.gemini.api_key', ''); } /** * 키워드 → 트렌딩 제목 5개 생성 */ public function generateTrendingTitles(string $keyword): array { $prompt = <<callGemini($prompt); if (! $result) { return []; } $parsed = $this->parseJsonResponse($result); return $parsed['titles'] ?? []; } /** * 제목 → 장면별 시나리오 생성 */ public function generateScenario(string $title, string $keyword = ''): array { $prompt = <<callGemini($prompt); if (! $result) { return []; } return $this->parseJsonResponse($result) ?: []; } /** * Gemini API 호출 */ private function callGemini(string $prompt): ?string { if (empty($this->apiKey)) { Log::error('GeminiScriptService: API 키가 설정되지 않았습니다.'); return null; } try { $url = "https://generativelanguage.googleapis.com/v1beta/models/{$this->model}:generateContent"; $response = Http::withHeaders([ 'x-goog-api-key' => $this->apiKey, ])->timeout(60)->post($url, [ 'contents' => [ [ 'parts' => [ ['text' => $prompt], ], ], ], 'generationConfig' => [ 'temperature' => 0.9, 'maxOutputTokens' => 4096, 'responseMimeType' => 'application/json', ], ]); if (! $response->successful()) { Log::error('GeminiScriptService: API 호출 실패', [ 'status' => $response->status(), 'body' => $response->body(), ]); return null; } $data = $response->json(); $text = $data['candidates'][0]['content']['parts'][0]['text'] ?? null; return $text; } catch (\Exception $e) { Log::error('GeminiScriptService: 예외 발생', ['error' => $e->getMessage()]); return null; } } /** * JSON 응답 파싱 (코드블록 제거 포함) */ private function parseJsonResponse(string $text): ?array { // 코드블록 제거 (```json ... ```) $text = preg_replace('/^```(?:json)?\s*/m', '', $text); $text = preg_replace('/```\s*$/m', '', $text); $text = trim($text); $decoded = json_decode($text, true); if (json_last_error() !== JSON_ERROR_NONE) { Log::warning('GeminiScriptService: JSON 파싱 실패', [ 'error' => json_last_error_msg(), 'text' => substr($text, 0, 500), ]); return null; } return $decoded; } }