| 312 | raise HTTPException(status_code=500, detail=str(e)) |
| 313 | |
| 314 | def call_llm(system_prompt: str, user_prompt: str) -> str: |
| 315 | gemini_key = os.getenv("GEMINI_API_KEY") |
| 316 | openai_key = os.getenv("OPENAI_API_KEY") |
| 317 | |
| 318 | if not gemini_key and not openai_key: |
| 319 | raise ValueError("Neither GEMINI_API_KEY nor OPENAI_API_KEY environment variable is set.") |
| 320 | |
| 321 | if gemini_key: |
| 322 | url = f"https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-flash:generateContent?key={gemini_key}" |
| 323 | payload = { |
| 324 | "contents": [ |
| 325 | { |
| 326 | "role": "user", |
| 327 | "parts": [{"text": f"{system_prompt}\n\nUser Prompt:\n{user_prompt}"}] |
| 328 | } |
| 329 | ] |
| 330 | } |
| 331 | res = requests.post(url, json=payload, timeout=30) |
| 332 | if res.status_code != 200: |
| 333 | raise RuntimeError(f"Gemini API returned error {res.status_code}: {res.text}") |
| 334 | data = res.json() |
| 335 | try: |
| 336 | return data["candidates"][0]["content"]["parts"][0]["text"] |
| 337 | except (KeyError, IndexError) as e: |
| 338 | raise RuntimeError(f"Failed to parse Gemini API response: {data}") from e |
| 339 | else: |
| 340 | url = "https://api.openai.com/v1/chat/completions" |
| 341 | headers = { |
| 342 | "Content-Type": "application/json", |
| 343 | "Authorization": f"Bearer {openai_key}" |
| 344 | } |
| 345 | payload = { |
| 346 | "model": "gpt-4o-mini", |
| 347 | "messages": [ |
| 348 | {"role": "system", "content": system_prompt}, |
| 349 | {"role": "user", "content": user_prompt} |
| 350 | ] |
| 351 | } |
| 352 | res = requests.post(url, json=payload, headers=headers, timeout=30) |
| 353 | if res.status_code != 200: |
| 354 | raise RuntimeError(f"OpenAI API returned error {res.status_code}: {res.text}") |
| 355 | data = res.json() |
| 356 | try: |
| 357 | return data["choices"][0]["message"]["content"] |
| 358 | except (KeyError, IndexError) as e: |
| 359 | raise RuntimeError(f"Failed to parse OpenAI API response: {data}") from e |
| 360 | |
| 361 | |
| 362 | def parse_json_from_llm(text: str) -> dict: |