Spaces:
Running on Zero
Running on Zero
| import gradio as gr | |
| import json | |
| import pandas as pd | |
| from typing import List, Any | |
| import database | |
| import agent | |
| import config | |
| import spaces | |
| database.init_db() | |
| def _dummy_gpu_warmup(): | |
| return True | |
| def handle_chat_submit(user_message: str, history: List[Any], api_key: str, provider: str, model: str): | |
| """Sohbet girdisini işler ve Gradio 5/6 'messages' formatında yanıt ile logları günceller.""" | |
| if history is None: | |
| history = [] | |
| if not user_message.strip(): | |
| return "", history, "", get_library_dataframe() | |
| chat_history_dicts = [] | |
| for item in history: | |
| if isinstance(item, dict): | |
| role = str(item.get("role", "user")) | |
| raw_content = item.get("content", "") | |
| if isinstance(raw_content, list): | |
| text_parts = [] | |
| for part in raw_content: | |
| if isinstance(part, dict) and "text" in part: | |
| text_parts.append(str(part["text"])) | |
| elif hasattr(part, "text"): | |
| text_parts.append(str(part.text)) | |
| else: | |
| text_parts.append(str(part)) | |
| content_str = "\n".join(text_parts) | |
| else: | |
| content_str = str(raw_content) | |
| if role in ["user", "assistant"] and content_str.strip(): | |
| chat_history_dicts.append({"role": role, "content": content_str}) | |
| elif isinstance(item, (list, tuple)) and len(item) == 2: | |
| u_msg, b_msg = item | |
| if u_msg: | |
| chat_history_dicts.append({"role": "user", "content": str(u_msg)}) | |
| if b_msg: | |
| chat_history_dicts.append({"role": "assistant", "content": str(b_msg)}) | |
| response_text, logs = agent.process_user_query( | |
| user_message=user_message, | |
| chat_history=chat_history_dicts, | |
| api_key=api_key, | |
| provider=provider, | |
| model_name=model | |
| ) | |
| updated_history = [] | |
| for msg in chat_history_dicts: | |
| updated_history.append({"role": msg["role"], "content": str(msg["content"])}) | |
| updated_history.append({"role": "user", "content": str(user_message)}) | |
| updated_history.append({"role": "assistant", "content": str(response_text or "")}) | |
| logs_formatted = json.dumps(logs, indent=2, ensure_ascii=False) | |
| df_lib = get_library_dataframe() | |
| return "", updated_history, logs_formatted, df_lib | |
| def get_library_dataframe(status_filter: str = "hepsi", query: str = ""): | |
| """SQLite veritabanındaki makaleleri Pandas DataFrame olarak döner.""" | |
| papers = database.get_saved_papers(status_filter=status_filter, query=query) | |
| if not papers: | |
| return pd.DataFrame(columns=["ID", "Makale Kimliği", "Başlık", "Yazarlar", "Yıl", "Durum", "Notlar", "Etiketler", "Kaynak"]) | |
| df = pd.DataFrame(papers) | |
| cols_map = { | |
| "id": "ID", | |
| "paper_id": "Makale Kimliği", | |
| "title": "Başlık", | |
| "authors": "Yazarlar", | |
| "published_year": "Yıl", | |
| "status": "Durum", | |
| "notes": "Notlar", | |
| "tags": "Etiketler", | |
| "source": "Kaynak", | |
| "url": "Bağlantı" | |
| } | |
| df = df.rename(columns=cols_map) | |
| existing_cols = [c for c in ["ID", "Makale Kimliği", "Başlık", "Yazarlar", "Yıl", "Durum", "Notlar", "Etiketler", "Kaynak", "Bağlantı"] if c in df.columns] | |
| return df[existing_cols] | |
| def clear_execution_logs(): | |
| agent.EXECUTION_LOGS.clear() | |
| return "[]" | |
| custom_css = """ | |
| .container { max-width: 1200px; margin: 0 auto; } | |
| .header-box { text-align: center; padding: 20px; background: linear-gradient(135deg, #1e293b, #0f172a); border-radius: 12px; margin-bottom: 20px; color: white; } | |
| .header-box h1 { font-size: 2.2rem; font-weight: 700; margin-bottom: 8px; color: #38bdf8; } | |
| .header-box p { font-size: 1.05rem; opacity: 0.9; } | |
| .log-box textarea { font-family: 'Courier New', Courier, monospace; font-size: 0.85rem; background-color: #0d1117; color: #7ee787; } | |
| """ | |
| with gr.Blocks(title="Tool-Calling Akademik Araştırma Asistanı") as demo: | |
| with gr.Column(elem_classes="container"): | |
| gr.HTML(""" | |
| <div class="header-box"> | |
| <h1>🔬 Tool-Calling Akademik Araştırma Asistanı</h1> | |
| <p>ArXiv & Semantic Scholar API Entegrasyonlu | SQLite Veritabanı | Bulut LLM Destekli</p> | |
| </div> | |
| """) | |
| with gr.Tabs() as tabs: | |
| with gr.TabItem("💬 Akademik Asistan", id=0): | |
| with gr.Row(): | |
| with gr.Column(scale=3): | |
| chatbot = gr.Chatbot(height=480) | |
| with gr.Row(): | |
| msg_input = gr.Textbox( | |
| placeholder="Örn: 'Quantum machine learning alanındaki makaleleri ara' veya 'Kütüphanemdeki makaleleri göster'...", | |
| show_label=False, | |
| scale=8 | |
| ) | |
| send_btn = gr.Button("Gönder 🚀", variant="primary", scale=2) | |
| clear_btn = gr.Button("Temizle 🗑️", scale=1) | |
| gr.Markdown("### 💡 Örnek Hızlı Sorular") | |
| with gr.Row(): | |
| ex1 = gr.Button("🔍 'LLM Tool Calling' makalelerini ara") | |
| ex2 = gr.Button("📚 Kayıtlı kütüphanemi getir") | |
| ex3 = gr.Button("💾 Örnek makaleyi kütüphaneme ekle") | |
| ex4 = gr.Button("📝 Makale okuma durumunu 'completed' yap") | |
| with gr.Column(scale=2): | |
| gr.Markdown("### ⚡ Anlık Tool-Call Tetiklenme Özeti") | |
| quick_log_view = gr.Code( | |
| label="Son Fonksiyon Çağrısı (Tool Call Output)", | |
| language="json", | |
| value="[]", | |
| lines=18 | |
| ) | |
| with gr.TabItem("📊 Tool-Call Canlı Loglar", id=1): | |
| gr.Markdown(""" | |
| ### 🛠️ Arka Plan Fonksiyon Çağrı (Tool-Call) Logları | |
| Bu panel, dil modelinin kullanıcı isteği doğrultusunda **hangi fonksiyonları çağırdığını**, | |
| gönderilen **parametreleri** ve dış API/SQLite veritabanından dönen **gerçek JSON çıktılarını** anlık gösterir. | |
| *(Ödev teslimindeki ekran görüntüsü gereksinimi için burayı kullanabilirsiniz).* | |
| """) | |
| full_log_view = gr.Code( | |
| label="Tüm Fonksiyon Çağrı Geçmişi (Full JSON Trace Log)", | |
| language="json", | |
| value="[]", | |
| lines=20 | |
| ) | |
| clear_log_btn = gr.Button("Logları Temizle", variant="secondary") | |
| with gr.TabItem("📚 Kişisel Kütüphanem (SQLite DB)", id=2): | |
| gr.Markdown("### 🗄️ SQLite Veritabanı Canlı Makale Tablosu (`academic_library.db`)") | |
| with gr.Row(): | |
| status_filter_ui = gr.Dropdown( | |
| choices=["hepsi", "unread", "reading", "completed"], | |
| value="hepsi", | |
| label="Durum Filtresi" | |
| ) | |
| search_query_ui = gr.Textbox(placeholder="Tablo içinde ara...", label="Arama") | |
| refresh_db_btn = gr.Button("🔄 Tabloyu Yenile", variant="secondary") | |
| library_table = gr.Dataframe( | |
| value=get_library_dataframe(), | |
| interactive=False, | |
| wrap=True | |
| ) | |
| with gr.TabItem("⚙️ Model & Cloud API Ayarları", id=3): | |
| gr.Markdown(""" | |
| ### ☁️ Bulut LLM ve API Yapılandırması | |
| Yerel bilgisayarınıza **hiçbir ağırlık veya model dosyası indirmenize gerek yoktur**. | |
| Ücretsiz Groq, OpenRouter, OpenAI API anahtarınızı girebilir veya varsayılan akıllı yönlendiriciyi kullanabilirsiniz. | |
| """) | |
| with gr.Row(): | |
| provider_dropdown = gr.Dropdown( | |
| choices=["Groq", "OpenRouter", "Ollama (Remote/Local)", "OpenAI"], | |
| value=config.DEFAULT_PROVIDER, | |
| label="Model Sağlayıcı (Provider)" | |
| ) | |
| model_input = gr.Textbox( | |
| value=config.DEFAULT_MODEL, | |
| label="Model Adı (Model Name)" | |
| ) | |
| api_key_input = gr.Textbox( | |
| type="password", | |
| label="API Anahtarı (Groq / OpenRouter / OpenAI API Key)", | |
| placeholder="gsk_... veya sk-or-v1-..." | |
| ) | |
| gr.Markdown("💡 **İpucu:** Groq API anahtarı tamamen ücretsizdir ve çok hızlı Tool-Calling desteği sunar (groq.com adresinden 1 dakikada alınabilir). API Key girilmediğinde sistem otomatik akıllı Tool-Call yönlendiricisini çalıştırır.") | |
| msg_input.submit( | |
| handle_chat_submit, | |
| inputs=[msg_input, chatbot, api_key_input, provider_dropdown, model_input], | |
| outputs=[msg_input, chatbot, quick_log_view, library_table] | |
| ).then( | |
| lambda logs: logs, inputs=[quick_log_view], outputs=[full_log_view] | |
| ) | |
| send_btn.click( | |
| handle_chat_submit, | |
| inputs=[msg_input, chatbot, api_key_input, provider_dropdown, model_input], | |
| outputs=[msg_input, chatbot, quick_log_view, library_table] | |
| ).then( | |
| lambda logs: logs, inputs=[quick_log_view], outputs=[full_log_view] | |
| ) | |
| clear_btn.click(lambda: ([], "[]"), None, [chatbot, quick_log_view]) | |
| clear_log_btn.click(clear_execution_logs, None, [full_log_view]) | |
| refresh_db_btn.click(get_library_dataframe, inputs=[status_filter_ui, search_query_ui], outputs=[library_table]) | |
| status_filter_ui.change(get_library_dataframe, inputs=[status_filter_ui, search_query_ui], outputs=[library_table]) | |
| search_query_ui.change(get_library_dataframe, inputs=[status_filter_ui, search_query_ui], outputs=[library_table]) | |
| ex1.click(lambda: "LLM Tool Calling alanındaki makaleleri ara", None, msg_input) | |
| ex2.click(lambda: "Kütüphanemdeki kayıtlı makaleleri listele", None, msg_input) | |
| ex3.click(lambda: "Arama sonucunda bulunan makaleyi kütüphaneme kaydet", None, msg_input) | |
| ex4.click(lambda: "Kütüphanemdeki makalenin durumunu 'completed' olarak güncelle", None, msg_input) | |
| if __name__ == "__main__": | |
| demo.launch( | |
| server_name="0.0.0.0", | |
| server_port=7860, | |
| share=False, | |
| css=custom_css, | |
| theme=gr.themes.Soft(primary_hue="indigo", secondary_hue="cyan") | |
| ) | |