File size: 13,794 Bytes
bde1021 729e882 bde1021 0893bfd 0ed18a5 0893bfd 0ed18a5 0893bfd 2e64dd6 264a379 44edf13 4999836 bde1021 42215b3 bde1021 42215b3 bde1021 33b64d8 bde1021 42215b3 bde1021 4999836 bde1021 42215b3 bde1021 44edf13 33b64d8 44edf13 3ff4918 855b657 3ff4918 2e64dd6 3ff4918 33b64d8 3ff4918 33b64d8 bde1021 3ff4918 bde1021 89a987c 264a379 83d6018 264a379 344cf4f 264a379 44edf13 344cf4f 8134074 44edf13 2e64dd6 44edf13 8134074 2e64dd6 264a379 0eb3d3e 344cf4f 2e64dd6 344cf4f 2e64dd6 495ca32 344cf4f 495ca32 2e64dd6 344cf4f 264a379 2e64dd6 264a379 2e64dd6 22bd0f8 2e64dd6 344cf4f 264a379 344cf4f 2e64dd6 344cf4f 22bd0f8 2e64dd6 264a379 344cf4f 264a379 344cf4f 2e64dd6 264a379 344cf4f 2e64dd6 344cf4f 2e64dd6 344cf4f 264a379 89a987c 344cf4f 2e64dd6 0eb3d3e 2e64dd6 89a987c 344cf4f 264a379 2e64dd6 344cf4f 2e64dd6 0eb3d3e 344cf4f 0eb3d3e 344cf4f 264a379 2e64dd6 344cf4f 264a379 2e64dd6 344cf4f 2e64dd6 344cf4f 83d6018 344cf4f 83d6018 495ca32 83d6018 22bd0f8 264a379 83d6018 264a379 83d6018 344cf4f 83d6018 264a379 83d6018 264a379 83d6018 bfebfdb 22bd0f8 264a379 83d6018 bde1021 44edf13 bde1021 42215b3 bde1021 44edf13 bde1021 e286c93 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 | import gradio as gr
import pandas as pd
import numpy as np
from PIL import Image
import os
import tensorflow as tf
import glob
import requests
# ── HACK PRO STARÉ MODELY Z TEACHABLE MACHINE ─────────────────
original_init = tf.keras.layers.DepthwiseConv2D.__init__
def patched_init(self, *args, **kwargs):
if 'groups' in kwargs:
kwargs.pop('groups')
original_init(self, *args, **kwargs)
tf.keras.layers.DepthwiseConv2D.__init__ = patched_init
# ──────────────────────────────────────────────────────────────
# Načtení API klíčů z prostředí Hugging Face
api_key_openrouter = os.environ.get("OPENROUTER_API_KEY")
api_key_gemini = os.environ.get("GEMINI_API_KEY")
# ── Funkce pro načtení všech CSV dat ──
def nacti_vsechna_data():
vsechna_data_text = ""
soubory = glob.glob("*.csv")
if not soubory:
return "Pozor: V repozitáři nebyla nalezena žádná data (.csv)."
for soubor in soubory:
try:
df = pd.read_csv(soubor, sep=None, engine='python')
vsechna_data_text += f"\n--- OBSAH SOUBORU (Vzorek 15 řádků): {soubor} ---\n"
vsechna_data_text += df.head(15).to_string() + "\n"
except Exception as e:
vsechna_data_text += f"\nChyba při čtení {soubor}: {str(e)}\n"
return vsechna_data_text
# ── Funkce pro model Teachable Machine ────────────
def nacti_model():
if not os.path.exists("keras_model.h5") or not os.path.exists("labels.txt"):
return None, "Chybí soubory modelu (keras_model.h5, labels.txt)."
model = tf.keras.models.load_model("keras_model.h5", compile=False)
labels = open("labels.txt", "r", encoding="utf-8").read().strip().split("\n")
return model, labels
GLOBAL_MODEL, GLOBAL_LABELS = nacti_model()
# ── Funkce pro vyhodnocení střechy ─────────────
def vyhodnot_strechu(obrazek):
if obrazek is None:
return "Prosím, nahrajte snímek střechy."
model, labels = GLOBAL_MODEL, GLOBAL_LABELS
if model is None:
return labels
img = obrazek.resize((224, 224))
arr = np.array(img, dtype=np.float32) / 255.0
arr = np.expand_dims(arr, axis=0)
predikce = model.predict(arr)[0]
index = np.argmax(predikce)
skore = float(predikce[index]) * 100
label = labels[index]
vysledek = f"Analýza: {label} ({skore:.1f}%)\n\n"
label_lower = label.lower()
if skore < 70:
vysledek += "⚠️ Závěr: Výsledek je nejednoznačný (spolehlivost je nižší než 70 %).\n"
vysledek += "💡 Doporučení: Umělá inteligence si v tomto případě není dostatečně jistá. Pro přesné posouzení doporučujeme fyzickou obhlídku střechy technikem."
elif "nevhod" in label_lower or "2" in label_lower:
vysledek += "❌ Závěr: Střecha NENÍ vhodná pro FVE"
elif "vhod" in label_lower or "0" in label_lower:
vysledek += "✅ Závěr: Střecha JE vhodná pro FVE"
else:
vysledek += "❌ Závěr: Střecha NENÍ vhodná pro FVE (Neurčitý výsledek)"
return vysledek
# ── JÁDRO S PODROBNÝM TRASOVÁNÍM HISTORIE VÝBĚRU (ROUTING LOG) ──
def proved_api_volani(otazka, historie):
log_list = []
log_list.append("⏱️ **[00:00]** Inicializace požadavku uživatele.")
data_text = nacti_vsechna_data()
log_list.append(f"📄 **[Data]** Úspěšně načteny lokální CSV soubory ({len(data_text)} znaků v kontextu).")
prompt = (
"Jsi špičkový datový analytik a expert na fotovoltaiku pro firmu DAMR58H v Ústeckém kraji. "
"Odpovídej vždy věcně, logicky a VÝHRADNĚ PERFEKTNÍ GRAMATICKOU ČEŠTINOU.\n\n"
"Pravidla pro analýzu:\n"
"1. Pokud se uživatel ptá na data ze souborů CSV, NEKOPÍRUJ suše strukturu sloupců. "
"Místo toho se podívej na hodnoty, hledej trendy, porovnávej obce a dělej reálné energetické závěry.\n"
"2. Pravidlo o 70% jistotě a fyzické obhlídce technikem aplikuj POUZE tehdy, když vyhodnocuješ konkrétní skóre z analýzy FOTOGRAFIE střechy. Nemíchej to do tabulek.\n\n"
f"Tady jsou data z našich souborů k analýze:\n{data_text}\n\n"
f"Otázka: {otazka}"
)
historie.append([otazka, "⏳ Spouštím vyhledávání volného serveru..."])
yield historie, "### 🤖 Aktivní model: Vyhledávám...", "\n".join(log_list)
# KROK 1: Pokus o OpenRouter servery
if api_key_openrouter:
log_list.append("🔗 **[OpenRouter]** API klíč detekován. Sestavuji prioritní frontu free modelů.")
url_or = "https://openrouter.ai/api/v1/chat/completions"
headers_or = {
"Authorization": f"Bearer {api_key_openrouter}",
"Content-Type": "application/json",
"HTTP-Referer": "https://huggingface.co",
"X-Title": "Solar Advisor"
}
modely_or = [
"meta-llama/llama-3.3-70b-instruct:free",
"meta-llama/llama-3.2-3b-instruct:free",
"openrouter/free"
]
for model in modely_or:
model_short = model.split('/')[-1]
log_list.append(f"📡 **[Požadavek]** Odesílám dotaz na endpoint: `{model}`")
yield historie, f"### 🤖 OpenRouter: Zkouším `{model_short}`...", "\n".join(log_list)
payload = {
"model": model,
"messages": [{"role": "user", "content": prompt}],
"temperature": 0.5
}
try:
res = requests.post(url_or, headers=headers_or, json=payload, timeout=18)
if res.status_code == 200:
data = res.json()
realny_model = data.get("model", model)
odpoved = data["choices"][0]["message"]["content"]
log_list.append(f"✅ **[Odezva]** Server odpověděl úspěšně (Kód 200).")
log_list.append(f"🏷️ **[Identifikace]** OpenRouter přidělil reálnou instanci: `{realny_model}`")
# FILTR PROTI "MRDKÁM": Pokud router podhodil slabý model
if model == "openrouter/free":
nevhodne_prazdne_modely = ["llama-3-8b", "pygmalion", "gemma-7b"]
if any(x in realny_model.lower() for x in nevhodne_prazdne_modely):
log_list.append(f"❌ **[Filtr]** Odmítnuto! Přidělený model `{realny_model}` nesplňuje kvalitativní kritéria pro češtinu. Zahazuji spojení.")
yield historie, "### ⚠️ OpenRouter podstrčil slabý model. Přepínám...", "\n".join(log_list)
break # Vyskočí z OpenRouteru a padá do KROKU 2 (Gemini)
historie[-1][1] = odpoved
log_list.append("🏁 **[Dokončeno]** Data úspěšně vygenerována and odeslána do chatu.")
yield historie, f"### 🤖 Aktivní model: `{realny_model.split('/')[-1]}` (OpenRouter)", "\n".join(log_list)
return
else:
log_list.append(f"⚠️ **[Chyba]** Model `{model_short}` vrátil status kód {res.status_code}. Zkouším další v pořadí.")
except Exception as e:
log_list.append(f"💥 **[Selhání]** Spojení s `{model_short}` spadlo na timeout nebo síťovou chybu.")
continue
else:
log_list.append("⚠️ **[OpenRouter]** Klíč OPENROUTER_API_KEY chybí. Přeskakuji celou OpenRouter sadu.")
# KROK 2: Totální záloha přes Google Gemini API
if api_key_gemini:
log_list.append("🚀 **[Fallback]** Inicializuji nouzový most na Google Cloud (Gemini API)...")
yield historie, "### 🚀 Nouzový režim: Připojuji se na Google Gemini API...", "\n".join(log_list)
url_gemini = f"https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-flash:generateContent?key={api_key_gemini}"
payload_gemini = {
"contents": [{"parts": [{"text": prompt}]}],
"generationConfig": {"temperature": 0.4}
}
try:
res = requests.post(url_gemini, json=payload_gemini, timeout=20)
if res.status_code == 200:
data = res.json()
odpoved = data['candidates'][0]['content']['parts'][0]['text']
log_list.append("✅ **[Odezva]** Google Gemini 1.5 Flash zareagoval okamžitě s kódem 200.")
log_list.append("🏁 **[Dokončeno]** Výstup vygenerován záložní infrastrukturou Google.")
historie[-1][1] = odpoved
yield historie, "### ⚡ Aktivní model: `Gemini 1.5 Flash` (Google Cloud)", "\n".join(log_list)
return
else:
log_list.append(f"❌ **[Kritická chyba]** Google API odmítlo požadavek se statusem {res.status_code}.")
status_g = f"Chyba {res.status_code}"
except Exception as e:
log_list.append(f"💥 **[Kritická chyba]** Google API je nedostupné nebo vypršel časový limit.")
status_g = "Network/Timeout"
else:
log_list.append("❌ **[Kritická chyba]** Záložní klíč GEMINI_API_KEY není v systému definován.")
status_g = "Klíč chybí"
# Pokud selhalo úplně všechno
log_list.append("🚨 **[FATAL]** Systém vyčerpal všechny dostupné servery i nouzové zálohy. Komunikace přerusená.")
historie[-1][1] = f"❌ Selhal OpenRouter i záložní Google API. (Detaily: Gemini status: {status_g}). Zkus to poslat znovu za minutku."
yield historie, "### 🤖 Aktivní model: Žádný (Všechny sítě přetíženy)", "\n".join(log_list)
# ── OPRAVENÁ LOGIKA OVLÁDACÍCH TLAČÍTEK (OPRAVENO 'v' -> 'in') ──
def chatbot_odpoved_manual(otazka, historie):
if not otazka.strip():
yield "", historie, "### 🤖 Aktivní model: Bez zátěže", "Žádný dotaz ke zpracování."
return
historie = historie or []
for nova_hist, status, log in proved_api_volani(otazka, historie):
yield "", nova_hist, status, log
def undo_posledni(historie):
if historie:
historie.pop()
return historie, "### 🤖 Aktivní model: Poslední zpráva smazána", "🗑️ Poslední log vyčištěn smazáním zprávy."
def upravit_posledni(historie):
if not historie:
return "", historie, "### 🤖 Aktivní model: Žrádná historie k úpravě", "Žádný log k dispozici."
posledni_dotaz = historie[-1][0]
historie.pop()
return posledni_dotaz, historie, "### 🤖 Aktivní model: Upravujete zprávu...", f"✏️ Upravuje se dotaz: '{posledni_dotaz}'"
def resend_funkce(historie):
if not historie:
yield historie, "### 🤖 Aktivní model: Žádná historie k odeslání", "Není co posílat znovu."
return
posledni_otazka = historie[-1][0]
historie.pop()
for nova_hist, status, log in proved_api_volani(posledni_otazka, historie):
yield nova_hist, status, log
# ── UI Rozhraní (Gradio) ──────────────────────────
with gr.Blocks(title="Solární poradce") as demo:
gr.Markdown("# Solární poradce pro Ústecký kraj")
with gr.Tabs():
with gr.TabItem("Analýza střechy"):
gr.Markdown("### Nahrajte snímek střechy")
with gr.Row():
with gr.Column():
vstup_img = gr.Image(type="pil", label="Vyberte obrázek")
analyzovat_btn = gr.Button("Vyhodnotit", variant="primary")
with gr.Column():
vystup_text = gr.Textbox(label="Výsledek", lines=4)
analyzovat_btn.click(fn=vyhodnot_strechu, inputs=vstup_img, outputs=vystup_text)
with gr.TabItem("Chatbot s daty"):
status_model = gr.Markdown("### 🤖 Aktivní model: Bez zátěže")
chatbot = gr.Chatbot(label="Odpovědi solárního asistenta", show_copy_button=True)
vstup_text = gr.Textbox(label="Váš dotaz", placeholder="Napište otázku a stiskněte Enter...")
with gr.Row():
btn_undo = gr.Button("↩️ Zpět (Undo)", variant="secondary", size="sm")
btn_edit = gr.Button("✏️ Upravit (Edit)", variant="secondary", size="sm")
btn_resend = gr.Button("🔄 Znovu poslat (Resend)", variant="primary", size="sm")
with gr.Accordion("🔍 Zobrazit historii hledání modelu (Routing Log)", open=False):
log_vystup = gr.Markdown("Zatím neproběhlo žádné vyhledávání modelu.")
vstup_text.submit(
fn=chatbot_odpoved_manual,
inputs=[vstup_text, chatbot],
outputs=[vstup_text, chatbot, status_model, log_vystup]
)
btn_undo.click(fn=undo_posledni, inputs=[chatbot], outputs=[chatbot, status_model, log_vystup])
btn_edit.click(fn=upravit_posledni, inputs=[chatbot], outputs=[vstup_text, chatbot, status_model, log_vystup])
btn_resend.click(fn=resend_funkce, inputs=[chatbot], outputs=[chatbot, status_model, log_vystup])
demo.launch() |