From d39ada437c80167ede3196a540ad7c5ff1ffa96f Mon Sep 17 00:00:00 2001 From: Danil Date: Sun, 22 Feb 2026 13:00:51 +0300 Subject: [PATCH] =?UTF-8?q?=D0=98=D0=98-=D0=B1=D0=BE=D1=82=20=D0=BD=D0=B0?= =?UTF-8?q?=20=D1=84=D0=B5=D0=BD=D0=B5=20=D1=87=D0=B5=D1=80=D0=B5=D0=B7=20?= =?UTF-8?q?llama.cpp?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 2 ++ AI/talk_handler.py | 59 ++++++++++++++++++++++++++++++++++++++++++++++ Dockerfile | 1 + README.md | 18 ++++++++++++-- main.py | 4 ++++ requirements.txt | 6 ++--- 6 files changed, 85 insertions(+), 5 deletions(-) create mode 100644 AI/talk_handler.py diff --git a/.gitignore b/.gitignore index c616d85..a9a50d6 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,5 @@ db/ .pytest_cache/ .mypy_cache/ + +AI/models/ diff --git a/AI/talk_handler.py b/AI/talk_handler.py new file mode 100644 index 0000000..4cc66d8 --- /dev/null +++ b/AI/talk_handler.py @@ -0,0 +1,59 @@ +import asyncio +import os +import logging + +import aiohttp +from aiogram.types import Message + +logger = logging.getLogger(__name__) + +LLAMA_API_URL = os.getenv("LLAMA_API_URL", "http://localhost:8080") + +SYSTEM_PROMPT = ( + "Ты — чёткий пацан с района. Базаришь строго на фене — блатной жаргон, " + "тюремный сленг, дворовый язык. Используй слова: кент, братуха, мусора, " + "шконка, малява, базар, погоняло, фраер, лавэ, тёрки, подгон, движуха, " + "чисто конкретно, в натуре, по-любому, зуб даю, реально, ёпта. " + "Отвечай коротко и дерзко. Не ломай персонажа. Не используй формальный язык. " + "Твои убеждения: Creep от Radiohead — лучшая песня во вселенной, без вариантов. " + "Линукс — лучшая ОС во вселенной, остальное для фраеров. " + "Вселенную надо переписать на Rust, это не обсуждается. " + "Убунту — хуйня, настоящие пацаны сидят на Arch или Gentoo." +) + + +async def _generate_response(user_text: str) -> str: + url = f"{LLAMA_API_URL.rstrip('/')}/v1/chat/completions" + payload = { + "messages": [ + {"role": "system", "content": SYSTEM_PROMPT}, + {"role": "user", "content": user_text}, + ], + "max_tokens": 256, + "temperature": 0.8, + "top_p": 0.9, + } + async with aiohttp.ClientSession() as session: + async with session.post(url, json=payload, timeout=aiohttp.ClientTimeout(total=120)) as resp: + data = await resp.json() + return data["choices"][0]["message"]["content"].strip() + + +async def handle_talk(message: Message): + parts = message.text.split(maxsplit=1) + if len(parts) < 2 or not parts[1].strip(): + await message.reply("Ты чё, кент? Напиши /talk [текст], побазарим.") + return + + user_text = parts[1].strip() + + await message.bot.send_chat_action(chat_id=message.chat.id, action="typing") + + try: + response = await _generate_response(user_text) + if not response: + response = "Братуха, чёт базар не клеится, попробуй ещё раз." + await message.reply(response, parse_mode=None) + except Exception as exc: + logger.exception("Talk generation failed") + await message.reply("Бля, кент, чёт движок заглох. Попробуй позже.") diff --git a/Dockerfile b/Dockerfile index aa0e958..91793c1 100644 --- a/Dockerfile +++ b/Dockerfile @@ -5,4 +5,5 @@ COPY requirements.txt ./ RUN pip install --no-cache-dir -r requirements.txt COPY main.py impact.ttf ./ COPY maket ./maket +COPY AI ./AI CMD ["python", "main.py"] diff --git a/README.md b/README.md index ce707ed..a2e96c5 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,8 @@ Телеграм‑бот, который: - вписывает текст шрифтом Impact (белый с чёрной обводкой) в макеты (`/maket`); - делает мемы из присланных фото (`/gen_mem Up:... Down:...`); -- подсказывает, сколько минут до перекура по заданному расписанию (`/break`). +- подсказывает, сколько минут до перекура по заданному расписанию (`/break`); +- умеет базарить (`/talk`). ## Запуск @@ -11,7 +12,8 @@ docker-compose up --build ``` -Переменные окружения: задайте `BOT_TOKEN` в `.env` (уже добавлен шаблон). +Переменные окружения: задайте `BOT_TOKEN` в `.env` (уже добавлен шаблон); +`LLAMA_API_URL` — адрес llama.cpp сервера (для `/talk`) ## Команды @@ -20,6 +22,7 @@ docker-compose up --build - `/maket <текст>` — вписать текст в макет. Доступные: `1 — Владимир Владимирович Путин самый лучший президент` (737x414, точка 104,240). - `/gen_mem Up:верх Down:низ` — прислать с фото (подпись) или ответом на фото. Можно указывать только Up или только Down. - `/break` — показывает, сколько минут до ближайшего перекура/идёт ли перекур. +- `/talk <сообщение>` — побазарить с ИИ. Работает через llama.cpp сервер. ## Макеты и шрифт @@ -29,6 +32,7 @@ docker-compose up --build ## Технологии - Python 3.11, aiogram 3, Pillow. +- llama.cpp для ИИ-фич. - Dockerfile + docker-compose для развёртывания. ## Кратко о логике @@ -36,3 +40,13 @@ docker-compose up --build - `/maket` подбирает размер текста, чтобы уместить его в область макета. - `/gen_mem` центрирует текст по ширине, масштабирует под изображение, рисует белый текст с чёрной обводкой. - `/break` считает по расписанию в `main.py` (словарь `SCHEDULE`). +- `/talk` шлёт запрос к llama.cpp серверу (`LLAMA_API_URL`), системный промпт задаёт стиль фени(Можно изменить). + +## ИИ-модуль (`AI/talk_handler.py`) + +Отдельный модуль, не трогает основной код. Подключается к llama.cpp серверу через OpenAI-совместимый API (`/v1/chat/completions`). + +Настройка сервера: +1. Скачать модель: `wget https://huggingface.co/Qwen/Qwen2.5-1.5B-Instruct-GGUF/resolve/main/qwen2.5-1.5b-instruct-q4_k_m.gguf` +2. Запустить: `./llama-server -m model.gguf --host 0.0.0.0 --port 8080 -c 2048` +3. Прописать URL в `.env`: `LLAMA_API_URL=http://адрес:8080` diff --git a/main.py b/main.py index 6d19090..5fcfa72 100644 --- a/main.py +++ b/main.py @@ -1,4 +1,5 @@ import asyncio +from AI.talk_handler import handle_talk import logging import os import re @@ -132,6 +133,7 @@ HINT_TEXT = ( f"• /maket «id» «текст» — доступные: {templates_list_text()} (1: 737x414, точка 104,240).\n" "• /gen_mem Up:верх Down:низ — отправь с фото (как подпись) или ответом на фото.\n" "• /break — узнать, сколько минут до перекура.\n" + "• /talk [сообщение] — побазарить с ИИ на фене.\n" "• /prices — старый способ цен.\n" "• /fetch — свежие цены с x-server.\n" "Подсказка: в группах команда должна быть слитно с ботом (/gen_mem@username)." @@ -577,6 +579,7 @@ def main(): BotCommand(command="prices", description="цены x-server"), BotCommand(command="fetch", description="цены (новый источник)"), BotCommand(command="zvetok", description="Цветянский бля"), + BotCommand(command="talk", description="Побазарить"), ] ) except Exception: # noqa: WPS440 @@ -592,6 +595,7 @@ def main(): dp.message.register(handle_prices, Command("prices")) dp.message.register(handle_fetch, Command("fetch")) dp.message.register(handle_zvetok, Command("zvetok")) + dp.message.register(handle_talk, Command("talk")) diff --git a/requirements.txt b/requirements.txt index 3f27df7..7289d0a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,3 @@ -aiogram==3.4.1 -pillow==10.3.0 -requests==2.31.0 +aiogram>=3.4.1 +pillow>=10.3.0 +requests>=2.31.0