Spaces:
Sleeping
Sleeping
Commit ·
fb59133
1
Parent(s): 1699b79
Add time_series_data support for LLM context with historical + forecast data
Browse files- app.py +1 -0
- llm_analysis.py +57 -4
app.py
CHANGED
|
@@ -134,6 +134,7 @@ class HeatmapRequest(BaseModel):
|
|
| 134 |
metric: str # e.g., "soil_moisture", "pest_risk"
|
| 135 |
gaussian_sigma: float = 1.5
|
| 136 |
show_field_boundary: bool = True
|
|
|
|
| 137 |
|
| 138 |
|
| 139 |
class HeatmapResponse(BaseModel):
|
|
|
|
| 134 |
metric: str # e.g., "soil_moisture", "pest_risk"
|
| 135 |
gaussian_sigma: float = 1.5
|
| 136 |
show_field_boundary: bool = True
|
| 137 |
+
time_series_data: Optional[Dict[str, Any]] = None # Historical + forecast time series from Flutter cache
|
| 138 |
|
| 139 |
|
| 140 |
class HeatmapResponse(BaseModel):
|
llm_analysis.py
CHANGED
|
@@ -58,7 +58,7 @@ def call_gemini_with_fallback(prompt: str) -> str:
|
|
| 58 |
return call_groq(prompt)
|
| 59 |
|
| 60 |
def prepare_indices_context(summary_report: Dict, crop_type: str, farmer_context: Dict,
|
| 61 |
-
temporal_stats: Dict = None) -> str:
|
| 62 |
"""
|
| 63 |
Prepare a comprehensive context string for the LLM including temporal statistics.
|
| 64 |
|
|
@@ -126,6 +126,57 @@ VEGETATION INDICES DATA (ALL 13 INDICES):
|
|
| 126 |
latest_rolling = float(np.nanmean(t_stats['rolling_avg_3'][-1]))
|
| 127 |
context += f"\n - Latest Rolling Average (3-period): {latest_rolling:.4f}"
|
| 128 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 129 |
return context
|
| 130 |
|
| 131 |
def format_stress_context(stress_context: Dict) -> str:
|
|
@@ -185,7 +236,8 @@ def format_stress_context(stress_context: Dict) -> str:
|
|
| 185 |
|
| 186 |
def analyze_with_llm(summary_report: Dict, crop_type: str, farmer_context: Dict,
|
| 187 |
center_lat: float, center_lon: float, field_size_hectares: float,
|
| 188 |
-
temporal_stats: Dict = None, stress_context: Dict = None
|
|
|
|
| 189 |
"""
|
| 190 |
Analyze vegetation indices using Gemini LLM and extract soil insights.
|
| 191 |
|
|
@@ -198,12 +250,13 @@ def analyze_with_llm(summary_report: Dict, crop_type: str, farmer_context: Dict,
|
|
| 198 |
field_size_hectares: Field size
|
| 199 |
temporal_stats: Dictionary with temporal statistics
|
| 200 |
stress_context: Dictionary with stress detection results (clustering, anomalies)
|
|
|
|
| 201 |
|
| 202 |
Returns:
|
| 203 |
Dictionary with structured LLM analysis results
|
| 204 |
"""
|
| 205 |
-
# Prepare context with temporal statistics
|
| 206 |
-
indices_context = prepare_indices_context(summary_report, crop_type, farmer_context, temporal_stats)
|
| 207 |
|
| 208 |
# Prepare stress context
|
| 209 |
stress_text = format_stress_context(stress_context)
|
|
|
|
| 58 |
return call_groq(prompt)
|
| 59 |
|
| 60 |
def prepare_indices_context(summary_report: Dict, crop_type: str, farmer_context: Dict,
|
| 61 |
+
temporal_stats: Dict = None, time_series_data: Dict = None) -> str:
|
| 62 |
"""
|
| 63 |
Prepare a comprehensive context string for the LLM including temporal statistics.
|
| 64 |
|
|
|
|
| 126 |
latest_rolling = float(np.nanmean(t_stats['rolling_avg_3'][-1]))
|
| 127 |
context += f"\n - Latest Rolling Average (3-period): {latest_rolling:.4f}"
|
| 128 |
|
| 129 |
+
# Add time series data if provided (from Flutter cached data)
|
| 130 |
+
if time_series_data:
|
| 131 |
+
context += "\n\nTIME SERIES DATA (HISTORICAL + FORECAST):\n"
|
| 132 |
+
context += "=" * 45 + "\n"
|
| 133 |
+
|
| 134 |
+
for index_name, ts_data in time_series_data.items():
|
| 135 |
+
context += f"\n{index_name}:\n"
|
| 136 |
+
|
| 137 |
+
# Historical data summary
|
| 138 |
+
if 'historical' in ts_data and ts_data['historical']:
|
| 139 |
+
hist = ts_data['historical']
|
| 140 |
+
hist_count = len(hist)
|
| 141 |
+
if hist_count > 0:
|
| 142 |
+
# Get first and last values
|
| 143 |
+
first_hist = hist[0]
|
| 144 |
+
last_hist = hist[-1]
|
| 145 |
+
first_val = first_hist.get('value', 0)
|
| 146 |
+
last_val = last_hist.get('value', 0)
|
| 147 |
+
first_date = first_hist.get('date', 'N/A')
|
| 148 |
+
last_date = last_hist.get('date', 'N/A')
|
| 149 |
+
|
| 150 |
+
context += f" Historical ({hist_count} data points):\n"
|
| 151 |
+
context += f" - Start: {first_date} = {first_val:.4f}\n"
|
| 152 |
+
context += f" - End: {last_date} = {last_val:.4f}\n"
|
| 153 |
+
context += f" - Historical Change: {'+' if last_val > first_val else ''}{last_val - first_val:.4f}\n"
|
| 154 |
+
|
| 155 |
+
# Calculate average
|
| 156 |
+
avg_hist = sum(h.get('value', 0) for h in hist) / hist_count
|
| 157 |
+
context += f" - Average: {avg_hist:.4f}\n"
|
| 158 |
+
|
| 159 |
+
# Forecast data summary
|
| 160 |
+
if 'forecast' in ts_data and ts_data['forecast']:
|
| 161 |
+
fcast = ts_data['forecast']
|
| 162 |
+
fcast_count = len(fcast)
|
| 163 |
+
if fcast_count > 0:
|
| 164 |
+
first_fcast = fcast[0]
|
| 165 |
+
last_fcast = fcast[-1]
|
| 166 |
+
first_val = first_fcast.get('value', 0)
|
| 167 |
+
last_val = last_fcast.get('value', 0)
|
| 168 |
+
first_date = first_fcast.get('date', 'N/A')
|
| 169 |
+
last_date = last_fcast.get('date', 'N/A')
|
| 170 |
+
|
| 171 |
+
context += f" Forecast ({fcast_count} days ahead):\n"
|
| 172 |
+
context += f" - Start: {first_date} = {first_val:.4f}\n"
|
| 173 |
+
context += f" - End: {last_date} = {last_val:.4f}\n"
|
| 174 |
+
context += f" - Predicted Change: {'+' if last_val > first_val else ''}{last_val - first_val:.4f}\n"
|
| 175 |
+
|
| 176 |
+
# Calculate forecast average
|
| 177 |
+
avg_fcast = sum(f.get('value', 0) for f in fcast) / fcast_count
|
| 178 |
+
context += f" - Forecast Average: {avg_fcast:.4f}\n"
|
| 179 |
+
|
| 180 |
return context
|
| 181 |
|
| 182 |
def format_stress_context(stress_context: Dict) -> str:
|
|
|
|
| 236 |
|
| 237 |
def analyze_with_llm(summary_report: Dict, crop_type: str, farmer_context: Dict,
|
| 238 |
center_lat: float, center_lon: float, field_size_hectares: float,
|
| 239 |
+
temporal_stats: Dict = None, stress_context: Dict = None,
|
| 240 |
+
time_series_data: Dict = None) -> Dict[str, Any]:
|
| 241 |
"""
|
| 242 |
Analyze vegetation indices using Gemini LLM and extract soil insights.
|
| 243 |
|
|
|
|
| 250 |
field_size_hectares: Field size
|
| 251 |
temporal_stats: Dictionary with temporal statistics
|
| 252 |
stress_context: Dictionary with stress detection results (clustering, anomalies)
|
| 253 |
+
time_series_data: Dictionary with historical and forecast time series from Flutter cache
|
| 254 |
|
| 255 |
Returns:
|
| 256 |
Dictionary with structured LLM analysis results
|
| 257 |
"""
|
| 258 |
+
# Prepare context with temporal statistics and time series data
|
| 259 |
+
indices_context = prepare_indices_context(summary_report, crop_type, farmer_context, temporal_stats, time_series_data)
|
| 260 |
|
| 261 |
# Prepare stress context
|
| 262 |
stress_text = format_stress_context(stress_context)
|