import os import io import gradio as gr import pandas as pd import requests import datetime import time import base64 import json import logging import tempfile from typing import List, Tuple, Dict, Any # Set logging level for debugging logging.basicConfig(level=logging.INFO) # ----------------------------------------------------------------- # 1. API CLIENT INITIALIZATION # ----------------------------------------------------------------- GEMINI_CLIENT = None API_STATUSES = {} def initialize_api(name, env_key, client_class=None): key = os.environ.get(env_key) status = "❌" client = None if not key or "YOUR_" in key.upper() or len(key) < 10: status = "⚠️ (Кілт Жоқ/Жарамсыз)" API_STATUSES[name] = status return None, status try: if client_class: from google import genai client = genai.Client(api_key=key) status = "✅" except Exception as e: status = f"❌ (Қате: {e.__class__.__name__})" API_STATUSES[name] = status return client, status # Gemini Initialization try: from google import genai GEMINI_CLIENT, status = initialize_api('Gemini', 'GEMINI_API_KEY', genai.Client) except Exception: API_STATUSES['Gemini'] = "❌ (genai пакеті жоқ)" # Non-client Keys NEWS_API_KEY = os.environ.get('NEWS_API_KEY') OPENWEATHER_API_KEY = os.environ.get('OPENWEATHER_API_KEY') OPENAI_KEY = os.environ.get('OPENAI_API_KEY') STABILITY_API_KEY = os.environ.get('STABILITY_API_KEY') initialize_api('OpenAI Mod', 'OPENAI_API_KEY') initialize_api('News', 'NEWS_API_KEY') initialize_api('Weather', 'OPENWEATHER_API_KEY') initialize_api('Stability (SDXL)', 'STABILITY_API_KEY') API_STATUS_NOTE = " | ".join([f"{k} {v}" for k, v in API_STATUSES.items()]) print(f"[{datetime.datetime.now().strftime('%H:%M:%S')}] API Status: {API_STATUS_NOTE}") # ----------------------------------------------------------------- # 2. CORE FUNCTIONS (V59.0 - Modern Chat Implementation) # ----------------------------------------------------------------- # 2.1. Direct Gemini Text Chat (FIXED for 'messages' type) def text_chat_gemini(message: str, chat_history: List[Dict[str, Any]]): """ Handles the modern Gradio ChatInterface (list of dicts format). e.g., history = [{"role": "user", "content": "..."}, {"role": "assistant", "content": "..."}] """ if not message: return "" if GEMINI_CLIENT is None or API_STATUSES.get('Gemini') != "✅": return "❌ Gemini API кілті орнатылмаған немесе жарамсыз. Қызметті Hugging Face Secrets-те тексеріңіз." try: # Convert Gradio 'messages' format to Gemini 'contents' format contents = [] for entry in chat_history: # Map Gradio 'assistant' role to Gemini 'model' role role = "model" if entry["role"] == "assistant" else entry["role"] contents.append({"role": role, "parts": [{"text": entry["content"]}]}) # Add the current user message contents.append({"role": "user", "parts": [{"text": message}]}) system_instruction = "Сіз 'SQG Quantum Leap AI' атты интеллектуалды порталсыз. Жауаптарыңызды толығымен қазақ тілінде беріңіз. Достық, ақпараттық және кәсіби тонды сақтаңыз. Сіз Баян-Өлгий аймағына бағытталған ақпараттық орталықсыз." response = GEMINI_CLIENT.models.generate_content( model='gemini-2.5-flash', contents=contents, system_instruction=system_instruction ) return response.text.strip() except Exception as e: return f"❌ Gemini Чат Қатесі: {e.__class__.__name__}. API кілтін тексеріңіз немесе лимит туралы ақпаратты қараңыз." # 2.2. Weather Prediction def get_weather_and_prediction(): lat, lon = 48.97, 89.97 output = "OpenWeatherMap Қатесі ❌." prediction_output = "Gemini болжамы жоқ." if not OPENWEATHER_API_KEY or API_STATUSES.get('Weather') != "✅": return "OpenWeatherMap Key орнатылмаған немесе қате ❌", prediction_output forecast_url = f"https://api.openweathermap.org/data/2.5/forecast?lat={lat}&lon={lon}&appid={OPENWEATHER_API_KEY}&units=metric&lang=kz" try: response = requests.get(forecast_url, timeout=5) response.raise_for_status() forecast_data = response.json() current_data = forecast_data['list'][0] temp = current_data['main']['temp'] wind_speed = current_data['wind']['speed'] desc = current_data['weather'][0]['description'].capitalize() output = f"⛈️ Өлгий Ауа Райы (OpenWeatherMap)\n\n* Температура: **{temp} °C**\n* Жағдайы: **{desc}**\n* Жел: **{wind_speed} м/с**" prediction_points = forecast_data['list'][1:4] if GEMINI_CLIENT and API_STATUSES.get('Gemini') == "✅": weather_text = json.dumps(prediction_points) prompt = f""" Сіз ауа райын талдаушысыз. Келесі JSON деректері Өлгий қаласының алдағы 9 сағаттағы ауа райы болжамдарын көрсетеді. Толығымен қазақ тілінде, қарапайым сөздермен түсіндіріп беріңіз. JSON деректері: {weather_text} """ gemini_response = GEMINI_CLIENT.models.generate_content( model='gemini-2.5-flash', contents=prompt ) prediction_output = gemini_response.text.strip() return output, prediction_output except Exception as e: return f"Белгісіз Қате: {e.__class__.__name__}", prediction_output # 2.3. Image Generation (Stability AI SDXL-Turbo) def generate_image_sdxl(prompt): if not STABILITY_API_KEY or API_STATUSES.get('Stability (SDXL)') != "✅": return None, "Stability API Key орнатылмаған немесе қате ❌" if not prompt: return None, "Мәтінді енгізіңіз." url = "https://api.stability.ai/v2beta/stable-image/generate/sd3" headers = { "authorization": f"Bearer {STABILITY_API_KEY}", "accept": "image/*" } data = { "prompt": f"Detailed cinematic photo, Kazakh culture theme, high resolution, soft lighting, 8k, {prompt}", "output_format": "jpeg", "aspect_ratio": "1:1", "model": "sd3-medium" } try: response = requests.post(url, headers=headers, files={'none': ''}, data=data, timeout=30) response.raise_for_status() if response.status_code == 200: base64_img = base64.b64encode(response.content).decode("utf-8") return f"data:image/jpeg;base64,{base64_img}", "Сурет сәтті жасалды ✅" else: return None, f"SDXL Қатесі: {response.status_code} - {response.text[:100]}" except Exception as e: return None, f"Сурет Генерациялау Қатесі: {e.__class__.__name__}" # 2.4. AI Voice Chat (Gemini) def process_voice_chat(audio_path): if not audio_path or GEMINI_CLIENT is None or API_STATUSES.get('Gemini') != "✅": return "Дауыс жазбасы жоқ", "Gemini API Қатесі ❌" audio_file = None try: transcribe_prompt = "Transcribe the audio and provide ONLY the text in the original language. Do not add any extra commentary." audio_file = GEMINI_CLIENT.upload_file(file=audio_path) transcribe_response = GEMINI_CLIENT.models.generate_content( model='gemini-2.5-flash', contents=[transcribe_prompt, audio_file] ) original_text = transcribe_response.text.strip() chat_prompt = f"Сіз 'SQG Quantum Leap AI'-сіз. Сізге келесі сұрақ қойылды. Жауабыңызды толығымен **қазақ тілінде** беріңіз:\n\nСұрақ: {original_text}" chat_response = GEMINI_CLIENT.models.generate_content( model='gemini-2.5-flash', contents=chat_prompt ) GEMINI_CLIENT.delete_file(audio_file.name) ai_response_text = chat_response.text.strip() return original_text, ai_response_text except Exception as e: if audio_file: try: GEMINI_CLIENT.delete_file(audio_file.name) except: pass return "Түпнұсқа мәтін қатесі", f"ЖИ Сөйлесу Қатесі: {e.__class__.__name__}. Gemini-ге аудио жіберу қатесі." # 2.5. Vision (Image Analysis) def analyze_vision_gemini(img): if img is None or GEMINI_CLIENT is None or API_STATUSES.get('Gemini') != "✅": df = pd.DataFrame({"Нәтиже": ["Қате"], "Сенімділік": ["-"]}) return df, "Gemini API Қатесі ❌" vision_file = None temp_img_path = None try: with tempfile.NamedTemporaryFile(suffix=".jpeg", delete=False) as tmp: img.save(tmp.name, format="JPEG") temp_img_path = tmp.name vision_file = GEMINI_CLIENT.upload_file(file=temp_img_path) prompt = "Analyze this image in detail and provide ONLY the analysis in fluent Kazakh. Start with a short, encouraging greeting." response = GEMINI_CLIENT.models.generate_content( model='gemini-2.5-flash', contents=[prompt, vision_file] ) GEMINI_CLIENT.delete_file(vision_file.name) os.unlink(temp_img_path) full_text = response.text.strip() df = pd.DataFrame([["Жасанды Интеллект Талдауы", "Жоғары"]], columns=["Нәтиже", "Сенімділік"]) return df, f"Талдау Ескертпесі: {full_text}" except Exception as e: if vision_file: try: GEMINI_CLIENT.delete_file(vision_file.name) except: pass if temp_img_path and os.path.exists(temp_img_path): os.unlink(temp_img_path) df = pd.DataFrame({"Нәтиже": ["Қате"], "Сенімділік": ["-"]}) return df, f"Gemini Vision Қатесі: {e.__class__.__name__}" # 2.6. News and Moderation def get_latest_news(query): if not NEWS_API_KEY or API_STATUSES.get('News') != "✅": return "NewsAPI Key орнатылмаған немесе қате ❌." if not query: query = "Kazakhstan OR Mongolia OR Kazakh Diaspora" url = f"https://newsapi.org/v2/everything?q={query}&language=en&sortBy=publishedAt&apiKey={NEWS_API_KEY}&pageSize=5" try: response = requests.get(url, timeout=10) response.raise_for_status() data = response.json() articles = data.get('articles', []) if not articles: return f"Табылған жаңалықтар жоқ: '{query}'" article_summaries = "\n---\n".join([f"Атауы: {a.get('title')}. Қысқаша: {a.get('description') or 'N/A'}" for a in articles]) prompt = f""" Төмендегі 5 жаңалықтың атаулары мен қысқаша сипаттамалары берілген. Оларды 1-ден 5-ке дейін нөмірлеп, әрқайсысын 1 сөйлеммен толығымен қазақ тілінде түсіндіріп беріңіз. Жауапты '🚨 Соңғы Жаңалықтар:' деген сөзбен бастаңыз. Мәтін: {article_summaries} """ if GEMINI_CLIENT and API_STATUSES.get('Gemini') == "✅": summary_response = GEMINI_CLIENT.models.generate_content( model='gemini-2.5-flash', contents=prompt ) return summary_response.text.strip() else: output = "🚨 Соңғы Жаңалықтар (Gemini қорытындысы жоқ. Ағылшын тіліндегі деректер):\n\n" for i, article in enumerate(articles): title = article.get('title', 'Атауы жоқ') source = article.get('source', {}).get('name', 'Белгісіз') output += f"{i+1}. **{title}**\n(Дереккөз: {source})\n\n" return output.strip() except Exception as e: return f"Жаңалықтар Қатесі: {e.__class__.__name__}" def add_post_with_moderation(current_state, new_message): if not new_message: return current_state, "\n\n---\n\n".join(current_state), time.time(), "Модерация ✅" is_toxic, ai_note = False, "Модерация ✅" if OPENAI_KEY and API_STATUSES.get('OpenAI Mod') == "✅": try: mod_response = requests.post( "https://api.openai.com/v1/moderations", headers={"Authorization": f"Bearer {OPENAI_KEY}", "Content-Type": "application/json"}, json={"input": new_message} ) mod_response.raise_for_status() data = mod_response.json() if data["results"][0]["flagged"]: is_toxic = True ai_note = "🛡️ Қабылданбады (Токсикалық Контент)" except Exception: ai_note = "Модерация Қатесі ⚠️" if is_toxic: return current_state, "\n\n---\n\n".join(current_state), time.time(), ai_note now = datetime.datetime.now(datetime.timezone(datetime.timedelta(hours=8))).strftime('%H:%M') formatted_msg = "[{}] Хабарлама: {}".format(now, new_message) updated_state = [formatted_msg] + current_state return updated_state[:10], "\n\n---\n\n".join(updated_state[:10]), time.time(), ai_note def get_local_time_and_holiday(last_active): now = datetime.datetime.now(datetime.timezone(datetime.timedelta(hours=8))) time_str = now.strftime('%Y жылғы %B айының %d күні, сағат %H:%M') holiday_note = "" # Example holidays for Bayan-Olgii/Mongolia if now.month == 7 and now.day in [11, 12, 13, 14, 15]: holiday_note = "Бүгін - Наадам Мерекесі (Ұлттық Мереке) 🇲🇳" elif now.month == 12 and now.day == 29: holiday_note = "Бүгін - Тәуелсіздік Күні 🇲🇳" return f"⏱️ Өлгий (Моңғолия) Уақыты:\n\n**{time_str}**\n\n{holiday_note}" # ----------------------------------------------------------------- # 3. GRADIO INTERFACE (V59.0 - Starry Night Theme & Modern Chat) # ----------------------------------------------------------------- # (V59.0) ҚАЙТАРУ: Сізге ұнаған 'Жұлдызды Түн' тақырыбы (Dark Mode) # Бұл CSS 'style.css' файлын қажет етпейді, кодтың өзіне енгізілген. css_style = """ /* V59.0: Vincent Van Gogh Starry Night Theme (Fixed) */ body { background-image: url('https://upload.wikimedia.org/wikipedia/commons/thumb/e/ea/Van_Gogh_Starry_Night_-_Restored.jpg/2560px-Van_Gogh_Starry_Night_-_Restored.jpg') !important; background-size: cover !important; background-attachment: fixed !important; background-position: center !important; color: #f0f0f0 !important; } .gradio-container { background: rgba(0, 0, 0, 0.8) !important; /* Қараңғы фон (80%) */ border-radius: 20px !important; padding: 20px !important; color: #f0f0f0 !important; box-shadow: 0 0 40px rgba(255, 215, 0, 0.5); /* Gold shadow for contrast */ } h1, h3 { color: #ffd700 !important; font-family: sans-serif; } /* Input and text boxes styling */ .gradio-box, .gr-box, .gr-form, .gr-input, .gr-textarea { background: rgba(40, 40, 40, 0.9) !important; color: #f0f0f0 !important; border: 1px solid #1a4d95 !important; border-radius: 8px !important; } /* Button styling (Gold/Navy contrast) */ .gr-button { background: #ffd700 !important; color: #1a4d95 !important; font-weight: bold !important; border-radius: 12px !important; transition: all 0.2s; } .gr-button:hover { background: #ffc400 !important; transform: translateY(-2px); box-shadow: 0 4px 10px rgba(255, 215, 0, 0.5); } /* Active Tab styling */ .gr-tab-button.selected { background: #1a4d95 !important; color: #ffd700 !important; border-top-left-radius: 10px; border-top-right-radius: 10px; } /* Chatbot styling (Dark mode) */ .gradio-chatbot { border: 1px solid #ffd700 !important; border-radius: 10px !important; background-color: rgba(0, 0, 0, 0.5) !important; padding: 10px; } /* Custom class for the status note */ .status-note { font-size: 1.1em; font-weight: bold; padding: 10px; border-radius: 10px; background-color: rgba(255, 243, 205, 0.9); /* Ашық сары фон */ color: #856404; /* Қою сары мәтін */ text-align: center; border: 1px solid #ffeeba; } """ with gr.Blocks(theme=gr.themes.Soft(), css=css_style, title="SQG Quantum Leap AI V59.0") as interface: # State variables message_state = gr.State([]) last_active = gr.State(0) # Header gr.Markdown(f"