import gradio as gr
import spaces
import torch
from transformers import AutoModelForCausalLM, AutoTokenizer
import requests
import json
import re
from datetime import datetime, timedelta
# ==========================================
# 1. API Fonksiyonları (Tools)
# ==========================================
def get_earthquakes(start_date: str, end_date: str, min_magnitude: float):
"""
Belirli tarihler arasında ve minimum büyüklükte olan depremleri getirir.
"""
try:
url = f"https://earthquake.usgs.gov/fdsnws/event/1/query?format=geojson&starttime={start_date}&endtime={end_date}&minmagnitude={min_magnitude}"
res = requests.get(url, timeout=10)
data = res.json()
count = data.get('metadata', {}).get('count', 0)
features = data.get('features', [])[:5] # limit to 5 to avoid huge responses
results = []
for f in features:
props = f.get('properties', {})
results.append({
"place": props.get('place'),
"magnitude": props.get('mag'),
"time": props.get('time')
})
return {"count": count, "top_earthquakes": results}
except Exception as e:
return {"error": str(e)}
def get_wildfires(limit: int = 5):
"""
Dünya genelindeki son aktif orman yangınlarını (NASA EONET üzerinden) getirir.
"""
try:
url = "https://eonet.gsfc.nasa.gov/api/v3/events?category=wildfires&status=open"
res = requests.get(url, timeout=10)
data = res.json()
events = data.get('events', [])[:limit]
results = []
for e in events:
geom = e.get('geometry', [{}])[0]
results.append({
"title": e.get('title'),
"date": geom.get('date'),
"coordinates": geom.get('coordinates')
})
return {"count": len(results), "wildfires": results}
except Exception as e:
return {"error": str(e)}
available_functions = {
"get_earthquakes": get_earthquakes,
"get_wildfires": get_wildfires
}
tools = [
{
"type": "function",
"function": {
"name": "get_earthquakes",
"description": "Belirli tarihler arasında meydana gelen depremleri getirir (USGS API).",
"parameters": {
"type": "object",
"properties": {
"start_date": {"type": "string", "description": "Başlangıç tarihi (YYYY-MM-DD)"},
"end_date": {"type": "string", "description": "Bitiş tarihi (YYYY-MM-DD)"},
"min_magnitude": {"type": "number", "description": "Minimum deprem büyüklüğü (örn: 5.0)"}
},
"required": ["start_date", "end_date", "min_magnitude"]
}
}
},
{
"type": "function",
"function": {
"name": "get_wildfires",
"description": "Güncel aktif orman yangınlarını NASA EONET üzerinden getirir.",
"parameters": {
"type": "object",
"properties": {
"limit": {"type": "integer", "description": "Getirilecek maksimum yangın sayısı"}
},
"required": ["limit"]
}
}
}
]
# ==========================================
# 2. Model Yükleme (ZeroGPU Uyumluluğu)
# ==========================================
model_id = "Qwen/Qwen2.5-7B-Instruct"
print(f"Model yükleniyor: {model_id}...")
try:
tokenizer = AutoTokenizer.from_pretrained(model_id)
model = AutoModelForCausalLM.from_pretrained(
model_id,
torch_dtype=torch.bfloat16,
device_map="auto"
)
print("Model başarıyla yüklendi!")
except Exception as e:
print(f"Model yüklenirken hata oluştu: {e}")
model = None
tokenizer = None
# ==========================================
# 3. Agentic Loop & Tool Execution
# ==========================================
@spaces.GPU(duration=60)
def generate_step(messages):
"""
Modeli bir adım çalıştırır ve üretilen metni döndürür.
ZeroGPU ortamında GPU'yu kullanarak çalışır.
"""
text = tokenizer.apply_chat_template(messages, tools=tools, tokenize=False, add_generation_prompt=True)
inputs = tokenizer(text, return_tensors="pt").to(model.device)
outputs = model.generate(**inputs, max_new_tokens=1024, temperature=0.3)
response_text = tokenizer.decode(outputs[0][inputs.input_ids.shape[1]:], skip_special_tokens=False)
# Qwen2.5 tool call formatında bitiş token'larını temizleme
response_text = response_text.replace("<|im_end|>", "")
return response_text
def parse_tool_calls(text):
"""
Qwen2.5 formatındaki tool call etiketlerini parse eder.
{"name": "...", "arguments": {...}}
"""
pattern = r"\s*(.*?)\s*"
matches = re.findall(pattern, text, re.DOTALL)
calls = []
for match in matches:
try:
calls.append(json.loads(match))
except Exception as e:
print(f"Tool parse hatası: {e} - Veri: {match}")
return calls
def chat_interface(user_input, history):
if not model:
yield "Model yüklenemediği için yanıt verilemiyor."
return
# Sistem mesajı ve geçmişi hazırlama
messages = [
{"role": "system", "content": "Sen yetenekli bir afet takip asistanısın. Kullanıcı sorularına cevap verirken gerektiğinde 'get_earthquakes' ve 'get_wildfires' araçlarını kullan. Araçların sonuçlarını inceleyerek doğal dilde cevap ver. Bugünkü tarih: " + datetime.now().strftime("%Y-%m-%d")}
]
for user_msg, asst_msg in history:
messages.append({"role": "user", "content": user_msg})
messages.append({"role": "assistant", "content": asst_msg})
messages.append({"role": "user", "content": user_input})
final_output = ""
for _ in range(5):
try:
response_text = generate_step(messages)
messages.append({"role": "assistant", "content": response_text})
except Exception as e:
yield final_output + f"\n[Hata]: {str(e)}"
break
tool_calls = parse_tool_calls(response_text)
reasoning_text = re.sub(r".*?", "", response_text, flags=re.DOTALL).strip()
if not tool_calls:
if reasoning_text:
final_output += f"\n\n{reasoning_text}"
yield final_output.strip()
break
if reasoning_text:
final_output += f"\n\n🤔 *Düşünce: {reasoning_text}*"
for call in tool_calls:
func_name = call.get("name")
args = call.get("arguments", {})
final_output += f"\n\n🛠️ **Araç Kullanılıyor:** `{func_name}` \n📥 **Girdi:** `{args}`"
if func_name in available_functions:
try:
result = available_functions[func_name](**args)
res_json = json.dumps(result, ensure_ascii=False)
final_output += f"\n✅ **Çıktı:** `{res_json[:300]}...`"
messages.append({"role": "tool", "name": func_name, "content": res_json})
except Exception as e:
error_msg = {"error": str(e)}
final_output += f"\n⚠️ **Hata:** `{str(e)}`"
messages.append({"role": "tool", "name": func_name, "content": json.dumps(error_msg, ensure_ascii=False)})
else:
error_msg = {"error": f"Bilinmeyen fonksiyon: {func_name}"}
final_output += f"\n⚠️ **Hata:** Bilinmeyen araç"
messages.append({"role": "tool", "name": func_name, "content": json.dumps(error_msg, ensure_ascii=False)})
yield final_output + "\n\n*(Araç sonuçları işleniyor...)*"
# ==========================================
# 4. Gradio UI
# ==========================================
with gr.Blocks() as demo:
gr.Markdown("# 🌍 Afet Takip Asistanı")
chatbot = gr.Chatbot(height=500)
msg = gr.Textbox(label="Mesajınız", placeholder="Örn: Aktif yangınları listele")
gr.Examples(
examples=[
"2026-07-25 ile 2026-07-30 tarihleri arasındaki 5.5 üzeri depremleri listele.",
"Şu anki aktif büyük orman yangınları hangileri? 3 tanesini getir.",
"Son 7 günde (2026-07-23 ile 2026-07-30) 6.0'dan büyük deprem oldu mu? Ayrıca şu an aktif orman yangını var mı?"
],
inputs=msg
)
def respond(user_message, chat_history):
if not user_message.strip():
return chat_history, ""
chat_history.append((user_message, ""))
for bot_message in chat_interface(user_message, chat_history[:-1]):
chat_history[-1] = (user_message, bot_message)
yield chat_history, ""
msg.submit(respond, [msg, chatbot], [chatbot, msg], api_name=False)
if __name__ == "__main__":
demo.launch()