Karan6124 commited on
Commit
db48c8b
·
1 Parent(s): 1bc3290

revert LLMService back to official Google GenAI SDK

Browse files
Files changed (1) hide show
  1. app/services/llm.py +44 -66
app/services/llm.py CHANGED
@@ -1,22 +1,22 @@
1
  import os
2
  import json
3
- import logging
4
- import urllib.request
5
  from typing import Dict, Any, Optional
 
6
  from dotenv import load_dotenv
7
  from app.observability.metrics import LLM_TOKENS
8
 
9
  load_dotenv()
10
 
11
- # Load and validate key (using the GEMINI_API_KEY env variable to hold the OpenRouter key)
12
  api_key = os.getenv("GEMINI_API_KEY")
13
  if not api_key:
14
  raise ValueError("GEMINI_API_KEY environment variable is missing from .env")
15
 
16
- # Load configured OpenRouter model (default to google/gemini-2.5-flash)
17
- DEFAULT_MODEL = os.getenv("GEMINI_MODEL", "google/gemini-2.5-flash")
18
 
19
- logger = logging.getLogger(__name__)
 
20
 
21
  class LLMService:
22
  @staticmethod
@@ -28,75 +28,52 @@ class LLMService:
28
  json_output: bool = False
29
  ) -> str:
30
  """
31
- Invokes Gemini model via OpenRouter API and returns the text response.
32
- If json_output is True, configures the request to return a JSON object.
33
  """
 
34
  active_model = model_name or DEFAULT_MODEL
35
- logger.info(f"LLM Service calling OpenRouter model: {active_model}")
36
 
37
- # Build OpenRouter messages format
38
- messages = []
39
- if system_instruction:
40
- messages.append({"role": "system", "content": system_instruction})
41
- messages.append({"role": "user", "content": prompt})
42
-
43
- payload = {
44
- "model": active_model,
45
- "messages": messages,
46
- "temperature": temperature
47
  }
48
 
49
  if json_output:
50
- payload["response_format"] = {"type": "json_object"}
51
-
52
- headers = {
53
- "Authorization": f"Bearer {api_key}",
54
- "Content-Type": "application/json",
55
- "HTTP-Referer": "https://github.com/google/antigravity", # Required by OpenRouter
56
- "X-Title": "AgentBond AI"
57
- }
58
-
59
- # Use python's built-in urllib to execute the HTTP POST request (no packages needed!)
60
- req = urllib.request.Request(
61
- "https://openrouter.ai/api/v1/chat/completions",
62
- data=json.dumps(payload).encode("utf-8"),
63
- headers=headers,
64
- method="POST"
65
  )
 
 
 
 
 
 
66
 
67
  try:
68
- with urllib.request.urlopen(req) as response:
69
- res_data = json.loads(response.read().decode("utf-8"))
70
-
71
- choices = res_data.get("choices", [])
72
- if not choices:
73
- raise RuntimeError(f"OpenRouter response contained no choices: {res_data}")
74
-
75
- text_content = choices[0].get("message", {}).get("content", "")
76
- if not text_content:
77
- raise RuntimeError("OpenRouter model returned an empty response.")
78
-
79
- # Track token usage in Prometheus
80
- usage = res_data.get("usage", {})
81
- if usage:
82
- prompt_tokens = usage.get("prompt_tokens", 0)
83
- completion_tokens = usage.get("completion_tokens", 0)
84
-
85
- LLM_TOKENS.labels(
86
- model_name=active_model,
87
- token_type="input"
88
- ).inc(prompt_tokens)
89
-
90
- LLM_TOKENS.labels(
91
- model_name=active_model,
92
- token_type="output"
93
- ).inc(completion_tokens)
94
-
95
- return text_content
96
 
 
 
 
 
 
97
  except Exception as e:
98
- logger.error(f"Error calling OpenRouter API: {str(e)}")
99
- raise
 
 
100
 
101
  @staticmethod
102
  def call_gemini_json(
@@ -106,7 +83,7 @@ class LLMService:
106
  temperature: float = 0.2
107
  ) -> Dict[str, Any]:
108
  """
109
- Calls OpenRouter forcing a JSON structure and parses the result into a python dictionary.
110
  """
111
  raw_response = LLMService.call_gemini(
112
  prompt=prompt,
@@ -119,6 +96,7 @@ class LLMService:
119
  try:
120
  return json.loads(raw_response)
121
  except json.JSONDecodeError as e:
 
122
  clean_str = raw_response.strip()
123
  if clean_str.startswith("```json"):
124
  clean_str = clean_str.split("```json")[1].split("```")[0].strip()
@@ -127,4 +105,4 @@ class LLMService:
127
  try:
128
  return json.loads(clean_str)
129
  except Exception:
130
- raise ValueError(f"Failed to parse JSON response from LLM. Raw output: {raw_response}") from e
 
1
  import os
2
  import json
 
 
3
  from typing import Dict, Any, Optional
4
+ import google.generativeai as genai
5
  from dotenv import load_dotenv
6
  from app.observability.metrics import LLM_TOKENS
7
 
8
  load_dotenv()
9
 
10
+ # Load and validate key
11
  api_key = os.getenv("GEMINI_API_KEY")
12
  if not api_key:
13
  raise ValueError("GEMINI_API_KEY environment variable is missing from .env")
14
 
15
+ # Configure Google GenAI
16
+ genai.configure(api_key=api_key)
17
 
18
+ # Load configured model name (default to gemini-2.5-flash)
19
+ DEFAULT_MODEL = os.getenv("GEMINI_MODEL", "gemini-2.5-flash")
20
 
21
  class LLMService:
22
  @staticmethod
 
28
  json_output: bool = False
29
  ) -> str:
30
  """
31
+ Invokes Gemini LLM model and returns the text response.
32
+ If json_output is True, configures the request to return application/json.
33
  """
34
+ # Fallback to default model if none specified
35
  active_model = model_name or DEFAULT_MODEL
 
36
 
37
+ # Define generation config
38
+ generation_config = {
39
+ "temperature": temperature,
 
 
 
 
 
 
 
40
  }
41
 
42
  if json_output:
43
+ generation_config["response_mime_type"] = "application/json"
44
+
45
+ # Initialize the model
46
+ model = genai.GenerativeModel(
47
+ model_name=active_model,
48
+ generation_config=generation_config,
49
+ system_instruction=system_instruction
 
 
 
 
 
 
 
 
50
  )
51
+
52
+ # Generate response
53
+ response = model.generate_content(prompt)
54
+
55
+ if not response.text:
56
+ raise RuntimeError("Gemini model returned an empty response.")
57
 
58
  try:
59
+ usage = response.usage_metadata
60
+ if usage:
61
+ # Track Input Tokens (Prompt)
62
+ LLM_TOKENS.labels(
63
+ model_name=active_model,
64
+ token_type="input"
65
+ ).inc(usage.prompt_token_count)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
66
 
67
+ # Track Output Tokens (Response)
68
+ LLM_TOKENS.labels(
69
+ model_name=active_model,
70
+ token_type="output"
71
+ ).inc(usage.candidates_token_count)
72
  except Exception as e:
73
+ # Prevent token tracking errors from breaking the core execution
74
+ pass
75
+
76
+ return response.text
77
 
78
  @staticmethod
79
  def call_gemini_json(
 
83
  temperature: float = 0.2
84
  ) -> Dict[str, Any]:
85
  """
86
+ Calls Gemini forcing a JSON structure and parses the result into a python dictionary.
87
  """
88
  raw_response = LLMService.call_gemini(
89
  prompt=prompt,
 
96
  try:
97
  return json.loads(raw_response)
98
  except json.JSONDecodeError as e:
99
+ # Simple fallback if JSON parsing fails
100
  clean_str = raw_response.strip()
101
  if clean_str.startswith("```json"):
102
  clean_str = clean_str.split("```json")[1].split("```")[0].strip()
 
105
  try:
106
  return json.loads(clean_str)
107
  except Exception:
108
+ raise ValueError(f"Failed to parse JSON response from Gemini. Raw output: {raw_response}") from e