Update app.py
Browse files
app.py
CHANGED
|
@@ -1,12 +1,25 @@
|
|
| 1 |
# app.py — ClimaMind on Hugging Face Spaces (Gradio)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2 |
import os, time, json, random
|
| 3 |
import requests
|
| 4 |
import gradio as gr
|
| 5 |
|
|
|
|
| 6 |
PROVIDER = os.getenv("PROVIDER", "hf_model").strip()
|
| 7 |
MODEL_ID = os.getenv("MODEL_ID", "MBZUAI-IFM/K2-Think-SFT").strip()
|
| 8 |
HF_TOKEN = os.getenv("HF_TOKEN", "").strip()
|
| 9 |
|
|
|
|
| 10 |
def _get(url, params=None, headers=None, timeout=12, retries=2, backoff=1.6):
|
| 11 |
for i in range(retries + 1):
|
| 12 |
try:
|
|
@@ -63,6 +76,7 @@ def fetch_factors(lat, lon):
|
|
| 63 |
}
|
| 64 |
return {"factors": factors, "raw": wx}
|
| 65 |
|
|
|
|
| 66 |
def drying_index(temp_c, rh, wind_kmh, cloud_frac=None):
|
| 67 |
base = (temp_c or 0) * 1.2 + (wind_kmh or 0) * 0.8 - (rh or 0) * 0.9
|
| 68 |
if cloud_frac is not None:
|
|
@@ -73,6 +87,7 @@ def heat_stress_index(temp_c, rh, wind_kmh):
|
|
| 73 |
hs = (temp_c or 0) * 1.1 + (rh or 0) * 0.3 - (wind_kmh or 0) * 0.2
|
| 74 |
return max(0, min(100, round(hs)))
|
| 75 |
|
|
|
|
| 76 |
PROMPT = """You are ClimaMind, a climate reasoning assistant. Use ONLY the observations provided and return STRICT JSON.
|
| 77 |
|
| 78 |
Location: {loc} (lat={lat}, lon={lon}), local time: {t_local}
|
|
@@ -86,12 +101,13 @@ Steps:
|
|
| 86 |
3) Give a concise recommendation with time window(s) and a confidence.
|
| 87 |
4) Output a short WHY-TRACE (3 bullets).
|
| 88 |
Return JSON ONLY:
|
| 89 |
-
{
|
| 90 |
"answer": "...",
|
| 91 |
"why_trace": ["...", "...", "..."],
|
| 92 |
"risk_badge": "Low"|"Moderate"|"High"
|
| 93 |
-
}
|
| 94 |
|
|
|
|
| 95 |
def call_stub(_prompt:str)->str:
|
| 96 |
return json.dumps({
|
| 97 |
"answer": "Based on 32°C, 50% RH and 12 km/h wind, cotton dries in ~2–3h (faster after 2pm).",
|
|
@@ -104,6 +120,7 @@ def call_stub(_prompt:str)->str:
|
|
| 104 |
})
|
| 105 |
|
| 106 |
def call_hf_model(prompt:str)->str:
|
|
|
|
| 107 |
from huggingface_hub import InferenceClient
|
| 108 |
client = InferenceClient(model=MODEL_ID, token=(HF_TOKEN or None))
|
| 109 |
out = client.text_generation(
|
|
@@ -133,7 +150,7 @@ def _ensure_local_loaded():
|
|
| 133 |
model = AutoModelForCausalLM.from_pretrained(
|
| 134 |
MODEL_ID,
|
| 135 |
trust_remote_code=True,
|
| 136 |
-
device_map="auto",
|
| 137 |
quantization_config=bnb_cfg,
|
| 138 |
low_cpu_mem_usage=True,
|
| 139 |
)
|
|
@@ -176,6 +193,7 @@ def reason_answer(loc, coords, factors, query):
|
|
| 176 |
else:
|
| 177 |
raw = call_stub(prompt)
|
| 178 |
|
|
|
|
| 179 |
start, end = raw.find("{"), raw.rfind("}")
|
| 180 |
if start == -1 or end == -1:
|
| 181 |
return {
|
|
@@ -192,6 +210,7 @@ def reason_answer(loc, coords, factors, query):
|
|
| 192 |
"risk_badge": "Low"
|
| 193 |
}
|
| 194 |
|
|
|
|
| 195 |
def app(city, question):
|
| 196 |
geo = geocode_city(city)
|
| 197 |
data = fetch_factors(geo["lat"], geo["lon"])
|
|
@@ -227,9 +246,12 @@ demo = gr.Interface(
|
|
| 227 |
outputs=gr.Markdown(label="ClimaMind"),
|
| 228 |
title="ClimaMind — K2-Think + Live Climate Data",
|
| 229 |
description="Provider = hf_model (Inference API) | local (GPU Space) | stub (offline). Configure env in Space settings.",
|
| 230 |
-
allow_flagging="never"
|
|
|
|
| 231 |
)
|
| 232 |
-
|
|
|
|
|
|
|
| 233 |
|
| 234 |
if __name__ == "__main__":
|
| 235 |
demo.launch()
|
|
|
|
| 1 |
# app.py — ClimaMind on Hugging Face Spaces (Gradio)
|
| 2 |
+
# Modes:
|
| 3 |
+
# PROVIDER=hf_model (default) -> calls HF Inference API for K2 (recommended for demo)
|
| 4 |
+
# PROVIDER=local -> loads model with transformers (requires GPU Space)
|
| 5 |
+
# PROVIDER=stub -> offline canned answers
|
| 6 |
+
#
|
| 7 |
+
# Space Secrets / Variables to set:
|
| 8 |
+
# HF_TOKEN -> your Hugging Face token (Read + Inference permissions)
|
| 9 |
+
# MODEL_ID -> default: MBZUAI-IFM/K2-Think-SFT (fallback: LLM360/K2-Think)
|
| 10 |
+
# PROVIDER -> "hf_model" | "local" | "stub"
|
| 11 |
+
# HF_HUB_DISABLE_TELEMETRY=1 (optional)
|
| 12 |
+
|
| 13 |
import os, time, json, random
|
| 14 |
import requests
|
| 15 |
import gradio as gr
|
| 16 |
|
| 17 |
+
# -------- Config --------
|
| 18 |
PROVIDER = os.getenv("PROVIDER", "hf_model").strip()
|
| 19 |
MODEL_ID = os.getenv("MODEL_ID", "MBZUAI-IFM/K2-Think-SFT").strip()
|
| 20 |
HF_TOKEN = os.getenv("HF_TOKEN", "").strip()
|
| 21 |
|
| 22 |
+
# -------- Data fetch (Open-Meteo + OpenAQ) --------
|
| 23 |
def _get(url, params=None, headers=None, timeout=12, retries=2, backoff=1.6):
|
| 24 |
for i in range(retries + 1):
|
| 25 |
try:
|
|
|
|
| 76 |
}
|
| 77 |
return {"factors": factors, "raw": wx}
|
| 78 |
|
| 79 |
+
# -------- Indices --------
|
| 80 |
def drying_index(temp_c, rh, wind_kmh, cloud_frac=None):
|
| 81 |
base = (temp_c or 0) * 1.2 + (wind_kmh or 0) * 0.8 - (rh or 0) * 0.9
|
| 82 |
if cloud_frac is not None:
|
|
|
|
| 87 |
hs = (temp_c or 0) * 1.1 + (rh or 0) * 0.3 - (wind_kmh or 0) * 0.2
|
| 88 |
return max(0, min(100, round(hs)))
|
| 89 |
|
| 90 |
+
# -------- Prompt --------
|
| 91 |
PROMPT = """You are ClimaMind, a climate reasoning assistant. Use ONLY the observations provided and return STRICT JSON.
|
| 92 |
|
| 93 |
Location: {loc} (lat={lat}, lon={lon}), local time: {t_local}
|
|
|
|
| 101 |
3) Give a concise recommendation with time window(s) and a confidence.
|
| 102 |
4) Output a short WHY-TRACE (3 bullets).
|
| 103 |
Return JSON ONLY:
|
| 104 |
+
{
|
| 105 |
"answer": "...",
|
| 106 |
"why_trace": ["...", "...", "..."],
|
| 107 |
"risk_badge": "Low"|"Moderate"|"High"
|
| 108 |
+
}"""
|
| 109 |
|
| 110 |
+
# -------- Reasoning providers --------
|
| 111 |
def call_stub(_prompt:str)->str:
|
| 112 |
return json.dumps({
|
| 113 |
"answer": "Based on 32°C, 50% RH and 12 km/h wind, cotton dries in ~2–3h (faster after 2pm).",
|
|
|
|
| 120 |
})
|
| 121 |
|
| 122 |
def call_hf_model(prompt:str)->str:
|
| 123 |
+
# Hugging Face Inference API (serverless).
|
| 124 |
from huggingface_hub import InferenceClient
|
| 125 |
client = InferenceClient(model=MODEL_ID, token=(HF_TOKEN or None))
|
| 126 |
out = client.text_generation(
|
|
|
|
| 150 |
model = AutoModelForCausalLM.from_pretrained(
|
| 151 |
MODEL_ID,
|
| 152 |
trust_remote_code=True,
|
| 153 |
+
device_map="auto", # allows CPU offload if needed
|
| 154 |
quantization_config=bnb_cfg,
|
| 155 |
low_cpu_mem_usage=True,
|
| 156 |
)
|
|
|
|
| 193 |
else:
|
| 194 |
raw = call_stub(prompt)
|
| 195 |
|
| 196 |
+
# Extract largest JSON block
|
| 197 |
start, end = raw.find("{"), raw.rfind("}")
|
| 198 |
if start == -1 or end == -1:
|
| 199 |
return {
|
|
|
|
| 210 |
"risk_badge": "Low"
|
| 211 |
}
|
| 212 |
|
| 213 |
+
# -------- Gradio UI --------
|
| 214 |
def app(city, question):
|
| 215 |
geo = geocode_city(city)
|
| 216 |
data = fetch_factors(geo["lat"], geo["lon"])
|
|
|
|
| 246 |
outputs=gr.Markdown(label="ClimaMind"),
|
| 247 |
title="ClimaMind — K2-Think + Live Climate Data",
|
| 248 |
description="Provider = hf_model (Inference API) | local (GPU Space) | stub (offline). Configure env in Space settings.",
|
| 249 |
+
allow_flagging="never",
|
| 250 |
+
concurrency_limit=2, # <— replaces deprecated queue(concurrency_count=...)
|
| 251 |
)
|
| 252 |
+
|
| 253 |
+
# Optional queue for request buffering (no deprecated args)
|
| 254 |
+
demo.queue(max_size=8)
|
| 255 |
|
| 256 |
if __name__ == "__main__":
|
| 257 |
demo.launch()
|