Aniket2006 commited on
Commit
af7d9a9
·
1 Parent(s): ce698ef

Add 21 Groq API keys with fallback mechanism

Browse files
Files changed (3) hide show
  1. app.py +17 -2
  2. groq_client.py +186 -0
  3. llm_analysis.py +4 -80
app.py CHANGED
@@ -476,8 +476,9 @@ def generate_heatmap_image(data: np.ndarray, index_type: str, gaussian_sigma: fl
476
  # LLM ANALYSIS (for risk metrics)
477
  # ============================================================================
478
 
479
- # Groq API keys with cascading fallback
480
  GROQ_API_KEYS = [
 
481
  "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",
482
  "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",
483
  "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",
@@ -486,7 +487,21 @@ GROQ_API_KEYS = [
486
  "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",
487
  "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",
488
  "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",
489
- "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"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
490
  ]
491
 
492
  def run_llm_analysis(metric: str, stress_context: dict, indices_data: dict,
 
476
  # LLM ANALYSIS (for risk metrics)
477
  # ============================================================================
478
 
479
+ # Groq API keys with cascading fallback (21 keys total)
480
  GROQ_API_KEYS = [
481
+ # Original keys
482
  "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",
483
  "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",
484
  "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",
 
487
  "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",
488
  "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",
489
  "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",
490
+ "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",
491
+ # Keys added 2025-12-09 (batch 1)
492
+ "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",
493
+ "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",
494
+ "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",
495
+ "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",
496
+ "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",
497
+ "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",
498
+ "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",
499
+ "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",
500
+ "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",
501
+ # Keys added 2025-12-09 (batch 2)
502
+ "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",
503
+ "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",
504
+ "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",
505
  ]
506
 
507
  def run_llm_analysis(metric: str, stress_context: dict, indices_data: dict,
groq_client.py ADDED
@@ -0,0 +1,186 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ AGROW Centralized Groq Client
3
+ =============================
4
+ Shared LLM client with key rotation and fallback for all AGROW backend services.
5
+
6
+ Features:
7
+ - Centralized API key pool (15 keys)
8
+ - Automatic key rotation on rate limits (429)
9
+ - Exponential backoff per key
10
+ - Detailed logging for debugging
11
+ """
12
+
13
+ import time
14
+ import logging
15
+ from typing import Optional
16
+
17
+ # ============================================================================
18
+ # LOGGING
19
+ # ============================================================================
20
+ logger = logging.getLogger("GroqClient")
21
+
22
+ # ============================================================================
23
+ # GROQ API CONFIGURATION
24
+ # ============================================================================
25
+
26
+ # Full pool of Groq API keys for rotation
27
+ GROQ_API_KEYS = [
28
+ # Original keys
29
+ "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",
30
+ "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",
31
+ "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",
32
+ "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",
33
+ "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",
34
+ "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",
35
+ "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",
36
+ "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",
37
+ "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",
38
+ # Keys added 2025-12-09 (batch 1)
39
+ "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",
40
+ "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",
41
+ "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",
42
+ "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",
43
+ "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",
44
+ "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",
45
+ "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",
46
+ "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",
47
+ "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",
48
+ # Keys added 2025-12-09 (batch 2)
49
+ "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",
50
+ "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",
51
+ "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",
52
+ ]
53
+
54
+ GROQ_MODEL = "llama-3.3-70b-versatile"
55
+
56
+ logger.info(f"[GroqClient] Initialized with {len(GROQ_API_KEYS)} API keys")
57
+
58
+
59
+ def call_groq(
60
+ prompt: str,
61
+ system_prompt: str = "You are an expert agricultural AI analyst. Always respond with valid JSON only, no markdown code blocks.",
62
+ max_tokens: int = 2048,
63
+ temperature: float = 0.7,
64
+ ) -> str:
65
+ """
66
+ Call Groq API with automatic key rotation and fallback.
67
+
68
+ Args:
69
+ prompt: User prompt to send
70
+ system_prompt: System message for the LLM
71
+ max_tokens: Maximum tokens in response
72
+ temperature: Sampling temperature
73
+
74
+ Returns:
75
+ Response text from the LLM
76
+
77
+ Raises:
78
+ ValueError: If all API keys fail
79
+ """
80
+ from groq import Groq
81
+
82
+ if not GROQ_API_KEYS:
83
+ raise ValueError("No GROQ_API_KEYS configured")
84
+
85
+ max_attempts_per_key = 3
86
+ base_backoff = 1.0
87
+ last_error = None
88
+
89
+ # Iterate through all available keys
90
+ for key_idx, api_key in enumerate(GROQ_API_KEYS):
91
+ try:
92
+ client = Groq(api_key=api_key)
93
+ logger.info(f"[GroqClient] Trying key {key_idx+1}/{len(GROQ_API_KEYS)}")
94
+
95
+ # Retry logic per key (for network/timeout issues)
96
+ for attempt in range(1, max_attempts_per_key + 1):
97
+ try:
98
+ chat_completion = client.chat.completions.create(
99
+ messages=[
100
+ {"role": "system", "content": system_prompt},
101
+ {"role": "user", "content": prompt}
102
+ ],
103
+ model=GROQ_MODEL,
104
+ temperature=temperature,
105
+ max_tokens=max_tokens,
106
+ )
107
+
108
+ response_text = chat_completion.choices[0].message.content
109
+ logger.info(f"[GroqClient] Success with key {key_idx+1}! Response: {len(response_text)} chars")
110
+ return response_text
111
+
112
+ except Exception as e:
113
+ error_str = str(e)
114
+ logger.warning(f"[GroqClient] Key {key_idx+1} attempt {attempt} failed: {error_str[:100]}")
115
+
116
+ # Rate limit (429) - immediately switch to next key
117
+ if "rate_limit" in error_str.lower() or "429" in error_str:
118
+ logger.info(f"[GroqClient] Rate limited. Switching to next key...")
119
+ break
120
+
121
+ # Other errors - backoff and retry same key
122
+ if attempt < max_attempts_per_key:
123
+ wait = base_backoff * (2 ** (attempt - 1))
124
+ logger.info(f"[GroqClient] Backing off {wait}s...")
125
+ time.sleep(wait)
126
+ else:
127
+ # Move to next key
128
+ raise e
129
+
130
+ except Exception as e:
131
+ last_error = str(e)
132
+ logger.warning(f"[GroqClient] Key {key_idx+1} failed completely: {str(e)[:100]}")
133
+ continue
134
+
135
+ raise ValueError(f"All {len(GROQ_API_KEYS)} Groq API keys failed. Last error: {last_error}")
136
+
137
+
138
+
139
+ # Backward compatibility alias
140
+ def call_gemini_with_fallback(prompt: str, keys=None, url=None) -> str:
141
+ """Backward compatible wrapper - now uses Groq."""
142
+ return call_groq(prompt)
143
+
144
+
145
+ def call_groq_whisper(audio_filename: str) -> str:
146
+ """
147
+ Transcribe audio using Groq Whisper model.
148
+ Uses centralized key rotation.
149
+ """
150
+ from groq import Groq
151
+
152
+ if not GROQ_API_KEYS:
153
+ raise ValueError("No GROQ_API_KEYS configured")
154
+
155
+ last_error = None
156
+
157
+ # Iterate through keys
158
+ for key_idx, api_key in enumerate(GROQ_API_KEYS):
159
+ try:
160
+ client = Groq(api_key=api_key)
161
+ logger.info(f"[GroqClient-Whisper] Trying key {key_idx+1}/{len(GROQ_API_KEYS)}")
162
+
163
+ with open(audio_filename, "rb") as file:
164
+ transcription = client.audio.transcriptions.create(
165
+ file=(audio_filename, file.read()),
166
+ model="whisper-large-v3",
167
+ response_format="json",
168
+ language="en",
169
+ temperature=0.0
170
+ )
171
+
172
+ logger.info(f"[GroqClient-Whisper] Success with key {key_idx+1}")
173
+ return transcription.text
174
+
175
+ except Exception as e:
176
+ error_str = str(e)
177
+ logger.warning(f"[GroqClient-Whisper] Key {key_idx+1} failed: {error_str[:100]}")
178
+
179
+ # If rate limit, try next key immediately
180
+ if "rate_limit" in error_str.lower() or "429" in error_str:
181
+ continue
182
+
183
+ last_error = str(e)
184
+ continue
185
+
186
+ raise ValueError(f"All keys failed for Whisper. Last error: {last_error}")
llm_analysis.py CHANGED
@@ -2,95 +2,19 @@
2
  LLM Integration for Vegetation Indices Analysis
3
  ================================================
4
 
5
-
6
  This module integrates with Groq API to analyze vegetation indices
7
  and provide comprehensive soil and crop insights.
8
- # Trigger redeploy for robust key rotation
9
  """
10
 
11
  import os
12
  import json
13
  import numpy as np
14
- import time
15
  from typing import Dict, Any, List, Optional
16
 
17
- # ============================================================================
18
- # GROQ API CONFIGURATION
19
- # ============================================================================
20
-
21
- # ============================================================================
22
- # GROQ API CONFIGURATION
23
- # ============================================================================
24
-
25
- GROQ_API_KEYS = [
26
- "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",
27
- "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",
28
- "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",
29
- "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",
30
- "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",
31
- "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",
32
- "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",
33
- "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",
34
- "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"
35
- ]
36
- GROQ_MODEL = "llama-3.3-70b-versatile"
37
-
38
-
39
- def call_groq(prompt: str) -> str:
40
- """Call Groq API for LLM completion with Key Rotation."""
41
- from groq import Groq
42
-
43
- if not GROQ_API_KEYS:
44
- raise ValueError("No GROQ_API_KEYS configured")
45
-
46
- max_attempts = 3
47
- base_backoff = 1.0
48
- last_error = None
49
-
50
- # Iterate through all available keys
51
- for key_idx, api_key in enumerate(GROQ_API_KEYS):
52
- try:
53
- client = Groq(api_key=api_key)
54
- print(f"[LLM Analysis] Trying key {key_idx+1}/{len(GROQ_API_KEYS)}")
55
-
56
- # Retry logic for EACH key (networking/timeout issues)
57
- for attempt in range(1, max_attempts + 1):
58
- try:
59
- chat_completion = client.chat.completions.create(
60
- messages=[
61
- {"role": "system", "content": "You are an expert agricultural AI. Respond with valid JSON only."},
62
- {"role": "user", "content": prompt}
63
- ],
64
- model=GROQ_MODEL,
65
- temperature=0.7,
66
- max_tokens=2048,
67
- )
68
- return chat_completion.choices[0].message.content
69
- except Exception as e:
70
- # If it's a rate limit (429), break inner loop to try next key immediately
71
- if "429" in str(e) or "rate_limit" in str(e).lower():
72
- print(f"[LLM Analysis] Key {key_idx+1} rate limited. Switching to next key...")
73
- break
74
-
75
- if attempt < max_attempts:
76
- time.sleep(base_backoff * (2 ** (attempt - 1)))
77
- else:
78
- raise e # Propagate to outer loop to try next key
79
-
80
- except Exception as e:
81
- last_error = str(e)
82
- print(f"[LLM Analysis] Key {key_idx+1} failed: {e}")
83
- continue
84
-
85
- # If all keys fail
86
- raise ValueError(f"All {len(GROQ_API_KEYS)} Groq API keys failed. Last error: {last_error}")
87
-
88
- raise ValueError("All Groq API attempts failed")
89
-
90
-
91
- # Backward compatibility alias
92
- def call_gemini_with_fallback(prompt: str) -> str:
93
- return call_groq(prompt)
94
 
95
  def prepare_indices_context(summary_report: Dict, crop_type: str, farmer_context: Dict,
96
  temporal_stats: Dict = None, time_series_data: Dict = None) -> str:
 
2
  LLM Integration for Vegetation Indices Analysis
3
  ================================================
4
 
 
5
  This module integrates with Groq API to analyze vegetation indices
6
  and provide comprehensive soil and crop insights.
 
7
  """
8
 
9
  import os
10
  import json
11
  import numpy as np
 
12
  from typing import Dict, Any, List, Optional
13
 
14
+ # Import from centralized Groq client
15
+ import sys
16
+ sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
17
+ from groq_client import call_groq, call_gemini_with_fallback
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
18
 
19
  def prepare_indices_context(summary_report: Dict, crop_type: str, farmer_context: Dict,
20
  temporal_stats: Dict = None, time_series_data: Dict = None) -> str: