Spaces:
Runtime error
Runtime error
Download bot.py from WladlenVK/evolution-prometheus: direct link, hf CLI and curl.
- Browser
- Download file 10.6 kB
-
https://huggingface.co/spaces/WladlenVK/evolution-prometheus/resolve/main/bot.py
- Command line
-
hf download hf://spaces/WladlenVK/evolution-prometheus/bot.py
-
curl -L -o bot.py https://huggingface.co/spaces/WladlenVK/evolution-prometheus/resolve/main/bot.py
10.6 kB
| import os | |
| import logging | |
| import threading | |
| import signal | |
| import time | |
| from flask import Flask, request, jsonify, send_from_directory | |
| from telegram import Update | |
| from telegram.ext import ApplicationBuilder, ContextTypes, CommandHandler, MessageHandler, filters | |
| from telegram.request import HTTPXRequest # Добавлено для управления таймаутами | |
| from supabase.client import create_client | |
| import google.generativeai as genai | |
| from groq import Groq | |
| # --- 0. КОНТРОЛЬ ВЕРСИИ --- | |
| VERSION = "20260417.01" | |
| # --- 1. НАСТРОЙКА ЛОГИРОВАНИЯ --- | |
| logging.basicConfig( | |
| format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', | |
| level=logging.INFO | |
| ) | |
| # --- 2. ИНИЦИАЛИЗАЦИЯ КЛЮЧЕЙ --- | |
| TELEGRAM_TOKEN = os.environ.get("TELEGRAM_BOT_TOKEN") | |
| SUPABASE_URL = os.environ.get("SUPABASE_URL") | |
| SUPABASE_KEY = os.environ.get("SUPABASE_SERVICE_ROLE_KEY") | |
| GEMINI_KEY = os.environ.get("GEMINI_API_KEY") | |
| GROQ_KEY = os.environ.get("GROQ_API_KEY") | |
| ADMIN_ID = os.environ.get("MY_TELEGRAM_ID") | |
| WEB_PASSWORD = os.environ.get("WEB_PASSWORD", "admin") | |
| # --- 3. ИНИЦИАЛИЗАЦИЯ СЕРВИСОВ --- | |
| supabase = create_client(SUPABASE_URL, SUPABASE_KEY) | |
| genai.configure(api_key=GEMINI_KEY) | |
| groq_client = Groq(api_key=GROQ_KEY) | |
| # --- 4. ВЕБ-СЕРВЕР (FLASK) --- | |
| app = Flask(__name__) | |
| api_usage_counter = 0 | |
| telegram_usage_counter = 0 | |
| def send_static(path): | |
| """Раздача логотипа и других файлов.""" | |
| return send_from_directory('static', path) | |
| def ai_plugin(): | |
| """Манифест для внешних ИИ (ChatGPT и др.).""" | |
| return jsonify({ | |
| "schema_version": "v1", | |
| "name_for_human": "Evolution Prometheus", | |
| "name_for_model": "evolution_prometheus", | |
| "description_for_human": f"База знаний по книге 'Концепция развития'. Версия {VERSION}", | |
| "description_for_model": "Доступ к философским и научным концепциям книги 'Концепция развития'.", | |
| "auth": {"type": "none"}, | |
| "api": {"type": "openapi", "url": "https://wladlenvk-evolution-prometheus.hf.space/openapi.json"}, | |
| "logo_url": "https://i.ibb.co/C5Z95Jqs/logo.png", | |
| "contact_email": "wladlenvk@gmail.com" | |
| }) | |
| def admin_panel(): | |
| """Главная страница и панель управления.""" | |
| global api_usage_counter, telegram_usage_counter | |
| pwd = request.args.get('pwd') | |
| if pwd != WEB_PASSWORD: | |
| return ( | |
| f"<h1>Система 'Прометей'</h1>" | |
| f"<p>Вер. {VERSION}</p>" | |
| f"<p>Telegram сообщений: {telegram_usage_counter}</p>" | |
| f"<p>API запросов: {api_usage_counter}</p>" | |
| f"<p><i>Для доступа к управлению введите пароль в URL.</i></p>" | |
| ) | |
| return f""" | |
| <h1>Панель управления Прометей</h1> | |
| <p>Версия: <b>{VERSION}</b></p> | |
| <hr> | |
| <p>Статистика: ТГ: {telegram_usage_counter} | API: {api_usage_counter}</p> | |
| <form action="/action" method="get"> | |
| <input type="hidden" name="pwd" value="{WEB_PASSWORD}"> | |
| <button name="do" value="restart" style="padding:10px; background:red; color:white; border:none; cursor:pointer;">ПЕРЕЗАГРУЗИТЬ БОТА</button> | |
| <button name="do" value="clear_history" style="padding:10px; margin-left:10px; cursor:pointer;">ОЧИСТИТЬ ВСЮ ИСТОРИЮ ЧАТОВ</button> | |
| </form> | |
| """ | |
| def handle_action(): | |
| pwd = request.args.get('pwd') | |
| do = request.args.get('do') | |
| if pwd != WEB_PASSWORD: return "Доступ запрещен", 403 | |
| if do == "restart": | |
| threading.Thread(target=lambda: (time.sleep(1), os.kill(os.getpid(), signal.SIGTERM))).start() | |
| return "<h1>Перезагрузка...</h1>" | |
| if do == "clear_history": | |
| try: | |
| supabase.table("chat_history").delete().neq("user_id", "0").execute() | |
| return "<h1>История очищена!</h1><a href='/?pwd=" + WEB_PASSWORD + "'>Назад</a>" | |
| except Exception as e: | |
| return f"Ошибка БД: {str(e)}" | |
| return "Неизвестное действие" | |
| def openapi_spec(): | |
| """Техпаспорт API.""" | |
| return jsonify({ | |
| "openapi": "3.0.0", | |
| "info": {"title": "Prometheus Knowledge Base", "version": VERSION}, | |
| "servers": [{"url": "https://wladlenvk-evolution-prometheus.hf.space"}], | |
| "paths": { | |
| "/api/knowledge": { | |
| "post": { | |
| "summary": "Поиск в книге", | |
| "operationId": "searchKnowledge", | |
| "requestBody": { | |
| "content": {"application/json": {"schema": {"type": "object", "properties": {"query": {"type": "string"}}, "required": ["query"]}}} | |
| }, | |
| "responses": {"200": {"description": "OK"}} | |
| } | |
| } | |
| } | |
| }) | |
| def external_ai_request(): | |
| global api_usage_counter | |
| try: | |
| data = request.json | |
| user_query = data.get("query", "") | |
| if not user_query: return jsonify({"error": "Empty query"}), 400 | |
| query_vector = get_embedding(user_query) | |
| relevant_docs = search_docs(query_vector) | |
| api_usage_counter += 1 | |
| return jsonify({ | |
| "source": "Концепция развития. Нетривиальный взгляд на эволюцию.", | |
| "content": [doc['content'] for doc in relevant_docs] if relevant_docs else [], | |
| "version": VERSION | |
| }) | |
| except Exception as e: | |
| return jsonify({"error": str(e)}), 500 | |
| def run_flask(): | |
| port = int(os.environ.get("PORT", 7860)) | |
| app.run(host='0.0.0.0', port=port) | |
| # --- 5. ФУНКЦИИ ПАМЯТИ И RAG (БЕЗ ИЗМЕНЕНИЙ) --- | |
| def save_chat_message(user_id, role, content): | |
| try: | |
| supabase.table("chat_history").insert({"user_id": user_id, "role": role, "content": content}).execute() | |
| except Exception as e: | |
| logging.error(f"Error saving history: {e}") | |
| def get_chat_history(user_id, limit=10): | |
| try: | |
| res = supabase.table("chat_history").select("role", "content").eq("user_id", user_id).order("created_at", desc=True).limit(limit).execute() | |
| return res.data[::-1] | |
| except Exception as e: | |
| logging.error(f"Error getting history: {e}") | |
| return [] | |
| def get_embedding(text): | |
| try: | |
| result = genai.embed_content(model="models/gemini-embedding-001", content=text, task_type="retrieval_query") | |
| return result['embedding'] | |
| except Exception as e: | |
| raise Exception(f"Gemini Error: {str(e)}") | |
| def search_docs(query_embedding): | |
| try: | |
| rpc_params = {'query_embedding': query_embedding, 'match_threshold': 0.5, 'match_count': 5} | |
| response = supabase.rpc('match_documents', rpc_params).execute() | |
| return response.data | |
| except Exception as e: | |
| raise Exception(f"Supabase Error: {str(e)}") | |
| def split_text(text, max_length=4000): | |
| return [text[i:i+max_length] for i in range(0, len(text), max_length)] | |
| # --- 7. ОБРАБОТЧИКИ ТЕЛЕГРАМ (БЕЗ ИЗМЕНЕНИЙ) --- | |
| async def start(update: Update, context: ContextTypes.DEFAULT_TYPE): | |
| await update.message.reply_text(f"Бот-консультант системы 'Прометей' (v{VERSION}) готов.") | |
| async def restart_bot(update: Update, context: ContextTypes.DEFAULT_TYPE): | |
| if str(update.message.from_user.id) == ADMIN_ID: | |
| await update.message.reply_text("♻️ Перезагрузка...") | |
| os.kill(os.getpid(), signal.SIGTERM) | |
| async def handle_message(update: Update, context: ContextTypes.DEFAULT_TYPE): | |
| global telegram_usage_counter | |
| telegram_usage_counter += 1 | |
| user_id = update.message.from_user.id | |
| user_text = update.message.text | |
| status_msg = await update.message.reply_text("🔍 Анализирую...") | |
| try: | |
| save_chat_message(user_id, "user", user_text) | |
| query_vector = get_embedding(user_text) | |
| relevant_docs = search_docs(query_vector) | |
| context_text = "\n\n".join([doc['content'] for doc in relevant_docs]) if relevant_docs else "Контекст не найден." | |
| history = get_chat_history(user_id) | |
| messages = [{"role": "system", "content": f"Ты тех-ассистент. Отвечай строго по контексту.\n\nКОНТЕКСТ:\n{context_text}"}] | |
| for msg in history: | |
| messages.append({"role": msg["role"], "content": msg["content"]}) | |
| completion = groq_client.chat.completions.create(model="llama-3.3-70b-versatile", messages=messages, temperature=0.1) | |
| final_answer = completion.choices[0].message.content | |
| save_chat_message(user_id, "assistant", final_answer) | |
| if len(final_answer) <= 4096: | |
| await status_msg.edit_text(final_answer) | |
| else: | |
| await status_msg.delete() | |
| for chunk in split_text(final_answer): | |
| await update.message.reply_text(chunk) | |
| except Exception as e: | |
| logging.error(f"Error: {e}") | |
| await update.message.reply_text(f"⚠️ Ошибка: {str(e)}") | |
| if __name__ == '__main__': | |
| if not TELEGRAM_TOKEN: | |
| print("CRITICAL ERROR: No Token") | |
| else: | |
| # Увеличенные таймауты оставляем — это полезная практика для облачных хостингов | |
| t_request = HTTPXRequest( | |
| connect_timeout=45, | |
| read_timeout=45 | |
| ) | |
| # Запуск Flask в отдельном потоке | |
| threading.Thread(target=run_flask, daemon=True).start() | |
| # Сборка и запуск бота | |
| application = ApplicationBuilder().token(TELEGRAM_TOKEN).request(t_request).build() | |
| application.add_handler(CommandHandler('start', start)) | |
| application.add_handler(CommandHandler('restart', restart_bot)) | |
| application.add_handler(MessageHandler(filters.TEXT & (~filters.COMMAND), handle_message)) | |
| print(f"--- СИСТЕМА ПРОМЕТЕЙ {VERSION} ЗАПУЩЕНА ---") | |
| application.run_polling() | |