Spaces:
Sleeping
Sleeping
Commit ·
a8d7d80
1
Parent(s): cb3e125
Enhanced compact context: ALL 13 indices, clusters, prev_analysis, conv_history
Browse files- prompts.py +132 -25
prompts.py
CHANGED
|
@@ -731,7 +731,7 @@ def format_weather_context(weather_data: dict) -> str:
|
|
| 731 |
def build_compact_context(context: Dict) -> str:
|
| 732 |
"""
|
| 733 |
Build a compressed context string using abbreviations and key-value format.
|
| 734 |
-
Captures
|
| 735 |
|
| 736 |
Format: KEY:value pairs, one per line, grouped by category.
|
| 737 |
"""
|
|
@@ -742,55 +742,120 @@ def build_compact_context(context: Dict) -> str:
|
|
| 742 |
if field:
|
| 743 |
lines.append(f"[FIELD] {field.get('name','?')} | {field.get('crop_type','?')} | {field.get('area_acres',0):.1f}ac")
|
| 744 |
|
| 745 |
-
# --- Vegetation Indices (compact key:value format) ---
|
| 746 |
veg = context.get("vegetation_indices", {})
|
| 747 |
if veg:
|
| 748 |
-
|
|
|
|
| 749 |
for k in ["ndvi", "evi", "ndre", "smi", "ndwi"]:
|
| 750 |
-
|
| 751 |
-
|
| 752 |
-
|
| 753 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 754 |
|
| 755 |
# --- Health Summary (single line) ---
|
| 756 |
health = context.get("health_summary", {})
|
| 757 |
if health:
|
| 758 |
score = health.get("overall_stress", health.get("stress_score", health.get("average_stress_score", 0)))
|
| 759 |
status = health.get("status", health.get("crop_health", "unknown"))
|
| 760 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 761 |
|
| 762 |
-
# --- Stressed Patches (count + top
|
| 763 |
patches = context.get("stressed_patches", [])
|
| 764 |
if patches:
|
| 765 |
lines.append(f"[STRESS] {len(patches)} patches")
|
| 766 |
-
for p in patches[:
|
| 767 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 768 |
|
| 769 |
# --- SAR Bands (compact) ---
|
| 770 |
sar = context.get("sar_bands", {})
|
| 771 |
if sar:
|
| 772 |
sar_parts = []
|
| 773 |
-
|
| 774 |
-
|
| 775 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 776 |
if sar_parts:
|
| 777 |
-
lines.append(f"[SAR] " + " | ".join(sar_parts))
|
| 778 |
|
| 779 |
-
# --- Weather (compressed) ---
|
| 780 |
weather = context.get("weather", {})
|
| 781 |
if weather:
|
| 782 |
current = weather.get("current", {})
|
| 783 |
if current:
|
| 784 |
lines.append(f"[WX] T:{current.get('temp',0):.0f}°C H:{current.get('humidity',0):.0f}% Rain:{current.get('precip',0):.0f}mm")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 785 |
stress = weather.get("stress_indicators", {})
|
| 786 |
flags = []
|
| 787 |
if stress.get("current_heat_stress"): flags.append("HEAT")
|
|
|
|
| 788 |
if stress.get("drought_risk"): flags.append("DROUGHT")
|
| 789 |
if stress.get("suitable_for_irrigation"): flags.append("OK_IRRIG")
|
|
|
|
| 790 |
if flags:
|
| 791 |
lines.append(f"[WX_ALERT] " + ",".join(flags))
|
| 792 |
|
| 793 |
-
# --- Soil (compact) ---
|
| 794 |
soil = context.get("soil_indicators", {})
|
| 795 |
if soil:
|
| 796 |
soil_parts = []
|
|
@@ -801,20 +866,62 @@ def build_compact_context(context: Dict) -> str:
|
|
| 801 |
if soil_parts:
|
| 802 |
lines.append(f"[SOIL] " + " | ".join(soil_parts))
|
| 803 |
|
| 804 |
-
# --- Trends (
|
| 805 |
trends = context.get("historical_trends", {})
|
| 806 |
-
if trends
|
| 807 |
-
|
| 808 |
-
|
| 809 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 810 |
zones = context.get("zone_analysis", {})
|
| 811 |
-
if zones
|
| 812 |
-
|
| 813 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 814 |
|
| 815 |
return "\n".join(lines) if lines else "No data"
|
| 816 |
|
| 817 |
|
|
|
|
| 818 |
# =============================================================================
|
| 819 |
# COMPRESSED STAGE PROMPTS - Reduce Token Usage
|
| 820 |
# =============================================================================
|
|
|
|
| 731 |
def build_compact_context(context: Dict) -> str:
|
| 732 |
"""
|
| 733 |
Build a compressed context string using abbreviations and key-value format.
|
| 734 |
+
Captures ALL essential data in ~50% fewer tokens.
|
| 735 |
|
| 736 |
Format: KEY:value pairs, one per line, grouped by category.
|
| 737 |
"""
|
|
|
|
| 742 |
if field:
|
| 743 |
lines.append(f"[FIELD] {field.get('name','?')} | {field.get('crop_type','?')} | {field.get('area_acres',0):.1f}ac")
|
| 744 |
|
| 745 |
+
# --- ALL Vegetation Indices (compact key:value format) ---
|
| 746 |
veg = context.get("vegetation_indices", {})
|
| 747 |
if veg:
|
| 748 |
+
# Primary indices (most important)
|
| 749 |
+
primary = []
|
| 750 |
for k in ["ndvi", "evi", "ndre", "smi", "ndwi"]:
|
| 751 |
+
val = veg.get(k) or veg.get(k.upper())
|
| 752 |
+
if val is not None:
|
| 753 |
+
try:
|
| 754 |
+
primary.append(f"{k.upper()}:{float(val):.2f}")
|
| 755 |
+
except (ValueError, TypeError):
|
| 756 |
+
pass
|
| 757 |
+
if primary:
|
| 758 |
+
lines.append(f"[VEG1] " + " | ".join(primary))
|
| 759 |
+
|
| 760 |
+
# Secondary indices (stress/health indicators)
|
| 761 |
+
secondary = []
|
| 762 |
+
for k in ["psri", "pri", "mcari", "osavi", "reci"]:
|
| 763 |
+
val = veg.get(k) or veg.get(k.upper())
|
| 764 |
+
if val is not None:
|
| 765 |
+
try:
|
| 766 |
+
secondary.append(f"{k.upper()}:{float(val):.2f}")
|
| 767 |
+
except (ValueError, TypeError):
|
| 768 |
+
pass
|
| 769 |
+
if secondary:
|
| 770 |
+
lines.append(f"[VEG2] " + " | ".join(secondary))
|
| 771 |
+
|
| 772 |
+
# Soil indices
|
| 773 |
+
soil_idx = []
|
| 774 |
+
for k in ["sasi", "somi", "sfi"]:
|
| 775 |
+
val = veg.get(k) or veg.get(k.upper())
|
| 776 |
+
if val is not None:
|
| 777 |
+
try:
|
| 778 |
+
soil_idx.append(f"{k.upper()}:{float(val):.2f}")
|
| 779 |
+
except (ValueError, TypeError):
|
| 780 |
+
pass
|
| 781 |
+
if soil_idx:
|
| 782 |
+
lines.append(f"[SOIL_IDX] " + " | ".join(soil_idx))
|
| 783 |
|
| 784 |
# --- Health Summary (single line) ---
|
| 785 |
health = context.get("health_summary", {})
|
| 786 |
if health:
|
| 787 |
score = health.get("overall_stress", health.get("stress_score", health.get("average_stress_score", 0)))
|
| 788 |
status = health.get("status", health.get("crop_health", "unknown"))
|
| 789 |
+
conf = health.get("confidence_score", health.get("confidence", 0))
|
| 790 |
+
try:
|
| 791 |
+
lines.append(f"[HEALTH] score:{float(score):.2f} status:{status} conf:{float(conf):.2f}")
|
| 792 |
+
except (ValueError, TypeError):
|
| 793 |
+
lines.append(f"[HEALTH] status:{status}")
|
| 794 |
|
| 795 |
+
# --- Stressed Patches (count + top 5) ---
|
| 796 |
patches = context.get("stressed_patches", [])
|
| 797 |
if patches:
|
| 798 |
lines.append(f"[STRESS] {len(patches)} patches")
|
| 799 |
+
for p in patches[:5]: # Top 5 patches
|
| 800 |
+
pid = p.get('patch_id', p.get('id', '?'))
|
| 801 |
+
score = p.get('stress_score', p.get('score', 0))
|
| 802 |
+
try:
|
| 803 |
+
lines.append(f" P{pid}:{float(score):.2f}")
|
| 804 |
+
except (ValueError, TypeError):
|
| 805 |
+
lines.append(f" P{pid}")
|
| 806 |
+
|
| 807 |
+
# --- Clustering Data (critical for zone analysis) ---
|
| 808 |
+
stress_analysis = context.get("stress_analysis", {})
|
| 809 |
+
clusters = stress_analysis.get("cluster_statistics", [])
|
| 810 |
+
if clusters:
|
| 811 |
+
lines.append(f"[CLUSTERS] {len(clusters)} zones")
|
| 812 |
+
for c in clusters[:3]: # Top 3 clusters
|
| 813 |
+
cid = c.get('cluster_id', '?')
|
| 814 |
+
pct = c.get('percentage', 0)
|
| 815 |
+
stress = c.get('stress_score', {}).get('mean', 0) if isinstance(c.get('stress_score'), dict) else 0
|
| 816 |
+
lines.append(f" C{cid}:{pct:.1f}% stress:{stress:.2f}")
|
| 817 |
|
| 818 |
# --- SAR Bands (compact) ---
|
| 819 |
sar = context.get("sar_bands", {})
|
| 820 |
if sar:
|
| 821 |
sar_parts = []
|
| 822 |
+
for k in ["vv", "vh", "ratio", "VV", "VH"]:
|
| 823 |
+
if k.lower() in sar or k in sar:
|
| 824 |
+
val = sar.get(k.lower()) or sar.get(k)
|
| 825 |
+
if val is not None:
|
| 826 |
+
try:
|
| 827 |
+
sar_parts.append(f"{k.upper()}:{float(val):.2f}")
|
| 828 |
+
except (ValueError, TypeError):
|
| 829 |
+
pass
|
| 830 |
if sar_parts:
|
| 831 |
+
lines.append(f"[SAR] " + " | ".join(sar_parts[:3]))
|
| 832 |
|
| 833 |
+
# --- Weather (compressed with forecast) ---
|
| 834 |
weather = context.get("weather", {})
|
| 835 |
if weather:
|
| 836 |
current = weather.get("current", {})
|
| 837 |
if current:
|
| 838 |
lines.append(f"[WX] T:{current.get('temp',0):.0f}°C H:{current.get('humidity',0):.0f}% Rain:{current.get('precip',0):.0f}mm")
|
| 839 |
+
|
| 840 |
+
# Add 3-day forecast summary
|
| 841 |
+
forecast = weather.get("forecast_7d", weather.get("forecast", []))
|
| 842 |
+
if forecast and len(forecast) > 0:
|
| 843 |
+
rain_days = sum(1 for d in forecast[:3] if d.get('precipitation', 0) > 5)
|
| 844 |
+
max_temp = max((d.get('temp_max', 0) for d in forecast[:3]), default=0)
|
| 845 |
+
lines.append(f"[FORECAST] 3d_rain_days:{rain_days} max_T:{max_temp:.0f}°C")
|
| 846 |
+
|
| 847 |
+
# Weather alerts
|
| 848 |
stress = weather.get("stress_indicators", {})
|
| 849 |
flags = []
|
| 850 |
if stress.get("current_heat_stress"): flags.append("HEAT")
|
| 851 |
+
if stress.get("predicted_heat_stress"): flags.append("HEAT_RISK")
|
| 852 |
if stress.get("drought_risk"): flags.append("DROUGHT")
|
| 853 |
if stress.get("suitable_for_irrigation"): flags.append("OK_IRRIG")
|
| 854 |
+
if stress.get("suitable_for_spraying"): flags.append("OK_SPRAY")
|
| 855 |
if flags:
|
| 856 |
lines.append(f"[WX_ALERT] " + ",".join(flags))
|
| 857 |
|
| 858 |
+
# --- Soil Indicators (compact) ---
|
| 859 |
soil = context.get("soil_indicators", {})
|
| 860 |
if soil:
|
| 861 |
soil_parts = []
|
|
|
|
| 866 |
if soil_parts:
|
| 867 |
lines.append(f"[SOIL] " + " | ".join(soil_parts))
|
| 868 |
|
| 869 |
+
# --- Historical Trends (more detail) ---
|
| 870 |
trends = context.get("historical_trends", {})
|
| 871 |
+
if trends:
|
| 872 |
+
summary = trends.get("summary", "")
|
| 873 |
+
if summary:
|
| 874 |
+
lines.append(f"[TREND] {summary[:120]}")
|
| 875 |
+
# Add specific trend data
|
| 876 |
+
ndvi_trend = trends.get("ndvi_change") or trends.get("NDVI_change")
|
| 877 |
+
smi_trend = trends.get("smi_change") or trends.get("SMI_change")
|
| 878 |
+
if ndvi_trend or smi_trend:
|
| 879 |
+
parts = []
|
| 880 |
+
if ndvi_trend: parts.append(f"NDVI:{ndvi_trend:+.2f}")
|
| 881 |
+
if smi_trend: parts.append(f"SMI:{smi_trend:+.2f}")
|
| 882 |
+
if parts:
|
| 883 |
+
lines.append(f"[TREND_DATA] " + " ".join(parts))
|
| 884 |
+
|
| 885 |
+
# --- Zone Analysis (all critical zones) ---
|
| 886 |
zones = context.get("zone_analysis", {})
|
| 887 |
+
if zones:
|
| 888 |
+
priority_zones = zones.get("priority_zones", [])
|
| 889 |
+
if priority_zones:
|
| 890 |
+
lines.append(f"[ZONES] {len(priority_zones)} priority areas")
|
| 891 |
+
for z in priority_zones[:3]:
|
| 892 |
+
loc = z.get('location', '?')
|
| 893 |
+
score = z.get('stress_score', 0)
|
| 894 |
+
lines.append(f" {loc}: stress:{score:.2f}")
|
| 895 |
+
elif zones.get("most_critical"):
|
| 896 |
+
mc = zones["most_critical"]
|
| 897 |
+
lines.append(f"[ZONE_ALERT] {mc.get('location','?')} stress:{mc.get('stress_score',0):.2f}")
|
| 898 |
+
|
| 899 |
+
# --- Previous Analysis (LLM insights from satellite) ---
|
| 900 |
+
prev = context.get("previous_analysis", {})
|
| 901 |
+
if prev:
|
| 902 |
+
rec = prev.get("recommendation", prev.get("recommendations", ""))
|
| 903 |
+
if rec:
|
| 904 |
+
rec_text = rec[0] if isinstance(rec, list) else str(rec)
|
| 905 |
+
lines.append(f"[PREV_REC] {rec_text[:80]}")
|
| 906 |
+
|
| 907 |
+
concerns = prev.get("key_concerns", [])
|
| 908 |
+
if concerns and isinstance(concerns, list):
|
| 909 |
+
lines.append(f"[CONCERNS] " + ", ".join(str(c)[:30] for c in concerns[:3]))
|
| 910 |
+
|
| 911 |
+
# --- Conversation History (for follow-ups) ---
|
| 912 |
+
conv = context.get("conversation_history", [])
|
| 913 |
+
if conv:
|
| 914 |
+
lines.append(f"[CONV] {len(conv)} prior turns")
|
| 915 |
+
if len(conv) > 0:
|
| 916 |
+
last = conv[-1]
|
| 917 |
+
role = last.get("role", "")
|
| 918 |
+
content = last.get("content", "")[:50]
|
| 919 |
+
lines.append(f" Last: {role}: {content}...")
|
| 920 |
|
| 921 |
return "\n".join(lines) if lines else "No data"
|
| 922 |
|
| 923 |
|
| 924 |
+
|
| 925 |
# =============================================================================
|
| 926 |
# COMPRESSED STAGE PROMPTS - Reduce Token Usage
|
| 927 |
# =============================================================================
|