Update 86 files
- /rag/commands/expert.py - /core/services/__init__.py - /core/services/embedding_service.py - /core/services/file_service.py - /core/services/giga_client.py - /core/services/kb_service.py - /core/services/postgres_service.py - /core/services/qdrant_service.py - /core/services/reranker_service.py - /core/functions/__init__.py - /core/functions/check_consistency.py - /core/functions/check_spelling.py - /core/functions/critique_answer.py - /core/functions/expand_query.py - /core/functions/extract_metrics.py - /core/functions/file_processor.py - /core/functions/generate_document.py - /core/functions/intent_classify.py - /core/functions/rerank_context.py - /core/functions/summarize_document.py - /core/utils/__init__.py - /core/utils/arg_parser.py - /core/utils/config_loader.py - /core/utils/layout_converter.py - /core/utils/logger.py - /core/utils/text_utils.py - /core/utils/web_utils.py - /bots/commands/__init__.py - /bots/commands/base.py - /bots/commands/create.py - /core/commands/global_remove.py - /core/commands/help.py - /core/commands/info.py - /core/commands/kb.py - /core/commands/learn.py - /core/commands/other.py - /core/commands/registry.py - /core/commands/stats.py - /core/commands/template.py - /core/handlers/__init__.py - /core/handlers/message_handler.py - /core/workers/__init__.py - /core/workers/indexing_worker.py - /core/xmpp/__init__.py - /core/xmpp/client.py - /bots/commands/global_remove.py - /bots/commands/help.py - /bots/commands/info.py - /bots/commands/kb.py - /bots/commands/registry.py - /bots/commands/other.py - /bots/commands/template.py - /bots/commands/learn.py - /bots/commands/stats.py - /bots/handlers/message_handler.py - /bots/handlers/__init__.py - /bots/workers/__init__.py - /bots/workers/indexing_worker.py - /bots/xmpp/__init__.py - /bots/xmpp/client.py - /rag/services/qdrant_service.py - /rag/services/giga_client.py - /rag/services/embedding_service.py - /rag/services/reranker_service.py - /rag/services/__init__.py - /rag/services/file_service.py - /rag/services/postgres_service.py - /rag/services/kb_service.py - /rag/functions/check_spelling.py - /rag/functions/__init__.py - /rag/functions/extract_metrics.py - /rag/functions/generate_document.py - /rag/functions/expand_query.py - /rag/functions/critique_answer.py - /rag/functions/file_processor.py - /rag/functions/check_consistency.py - /rag/functions/summarize_document.py - /rag/functions/rerank_context.py - /rag/functions/intent_classify.py - /rag/utils/__init__.py - /rag/utils/layout_converter.py - /rag/utils/text_utils.py - /rag/utils/arg_parser.py - /rag/utils/config_loader.py - /rag/utils/logger.py - /rag/utils/web_utils.py
This commit is contained in:
@@ -1,7 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
"""
|
|
||||||
Пакет commands – команды XMPP-ботов (каждая команда — отдельный класс).
|
|
||||||
Команды реализуют пользовательский интерфейс:
|
|
||||||
- info, help, learn, kb, stats, expert, reset, template, create, global_remove.
|
|
||||||
Регистрация команд осуществляется через registry.py.
|
|
||||||
"""
|
|
||||||
@@ -1,77 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
"""
|
|
||||||
Базовый класс для всех команд бота.
|
|
||||||
Реализует:
|
|
||||||
- алиасы (альтернативные имена команд)
|
|
||||||
- проверку прав доступа (admin)
|
|
||||||
- парсинг аргументов (через core.utils.arg_parser)
|
|
||||||
- метод справки get_help()
|
|
||||||
- кэширование результатов (опционально)
|
|
||||||
|
|
||||||
Изменения для групповой работы:
|
|
||||||
- метод execute теперь принимает дополнительный параметр room_jid (None для личных чатов).
|
|
||||||
- Команды, которым нужен контекст комнаты, могут его использовать.
|
|
||||||
"""
|
|
||||||
|
|
||||||
from abc import ABC, abstractmethod
|
|
||||||
from typing import TYPE_CHECKING, Dict, Any, Tuple, List, Optional
|
|
||||||
import time
|
|
||||||
|
|
||||||
from core.utils.arg_parser import parse_args # путь изменён: arg_parser перенесён в utils
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
|
||||||
from core.xmpp.client import Bot # обновлённый импорт
|
|
||||||
|
|
||||||
|
|
||||||
class Command(ABC):
|
|
||||||
"""Абстрактный базовый класс команды. Все команды должны наследоваться от него."""
|
|
||||||
|
|
||||||
# Основное имя команды (например "!info")
|
|
||||||
name: str = None
|
|
||||||
# Альтернативные имена (например ["!help", "!справка"])
|
|
||||||
aliases: List[str] = []
|
|
||||||
# Необходимые права: None – любой, "admin" – только администраторы
|
|
||||||
permission: Optional[str] = None
|
|
||||||
# Время жизни кэша в секундах (0 – не кэшировать)
|
|
||||||
cache_ttl: int = 0
|
|
||||||
|
|
||||||
def __init__(self, bot: 'Bot'):
|
|
||||||
self.bot = bot
|
|
||||||
# Кэш: ключ -> (время, результат)
|
|
||||||
self._cache: Dict[str, Tuple[float, Any]] = {}
|
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
async def execute(self, msg, args: str, user_jid: str, room_jid: str = None):
|
|
||||||
"""
|
|
||||||
Выполнить команду.
|
|
||||||
|
|
||||||
:param msg: объект сообщения slixmpp (для ответа)
|
|
||||||
:param args: полная строка аргументов (включая имя команды)
|
|
||||||
:param user_jid: JID пользователя (без ресурса)
|
|
||||||
:param room_jid: JID комнаты, если команда вызвана в групповом чате; иначе None
|
|
||||||
"""
|
|
||||||
pass
|
|
||||||
|
|
||||||
def get_help(self) -> str:
|
|
||||||
"""Возвращает строку справки для команды. Переопределяется в дочерних классах."""
|
|
||||||
return "Нет описания."
|
|
||||||
|
|
||||||
def check_permission(self, user_jid: str) -> bool:
|
|
||||||
"""Проверяет, имеет ли пользователь право на выполнение команды."""
|
|
||||||
if self.permission is None:
|
|
||||||
return True
|
|
||||||
if self.permission == "admin":
|
|
||||||
return user_jid in self.bot.config.admin_jids
|
|
||||||
return False
|
|
||||||
|
|
||||||
def get_cache_key(self, user_jid: str, args: str) -> str:
|
|
||||||
"""Формирует ключ для кэширования (по умолчанию user_jid)."""
|
|
||||||
return user_jid
|
|
||||||
|
|
||||||
def parse_args(self, args_str: str) -> Tuple[str, Dict[str, Any], List[str]]:
|
|
||||||
"""Разбирает строку аргументов на команду, флаги и позиционные аргументы."""
|
|
||||||
return parse_args(args_str)
|
|
||||||
|
|
||||||
def _cache_result(self, key: str, result: str):
|
|
||||||
"""Сохраняет результат в кэш (вызывается из команды)."""
|
|
||||||
self._cache[key] = (time.time(), result)
|
|
||||||
@@ -1,60 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
from core.commands.base import Command
|
|
||||||
from core.functions.generate_document import generate_document_from_template
|
|
||||||
import asyncio
|
|
||||||
import logging
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
class CreateCommand(Command):
|
|
||||||
name = "!create"
|
|
||||||
aliases = ["/create"]
|
|
||||||
|
|
||||||
async def execute(self, msg, args: str, user_jid: str, room_jid: str = None):
|
|
||||||
if not room_jid:
|
|
||||||
reply = msg.reply("⚠️ Команда !create работает только в комнатах.")
|
|
||||||
if reply:
|
|
||||||
reply.send()
|
|
||||||
return
|
|
||||||
|
|
||||||
clean_args = args.strip()
|
|
||||||
if clean_args.startswith(self.name):
|
|
||||||
clean_args = clean_args[len(self.name):].lstrip()
|
|
||||||
name = clean_args.strip()
|
|
||||||
if name.startswith('"') and name.endswith('"'):
|
|
||||||
name = name[1:-1]
|
|
||||||
|
|
||||||
if not name:
|
|
||||||
reply = msg.reply("Укажите название шаблона: !create \"Название\"")
|
|
||||||
if reply:
|
|
||||||
reply.send()
|
|
||||||
return
|
|
||||||
|
|
||||||
template_info = await self.bot.db.get_template(room_jid, name)
|
|
||||||
if not template_info:
|
|
||||||
reply = msg.reply(f"❌ Шаблон '{name}' не найден.")
|
|
||||||
if reply:
|
|
||||||
reply.send()
|
|
||||||
return
|
|
||||||
|
|
||||||
reply = msg.reply("🔄 Начинаю генерацию документа. Это может занять несколько минут. Я сообщу, когда будет готово.")
|
|
||||||
if reply:
|
|
||||||
reply.send()
|
|
||||||
|
|
||||||
asyncio.create_task(
|
|
||||||
generate_document_from_template(
|
|
||||||
bot=self.bot,
|
|
||||||
msg=msg,
|
|
||||||
room_jid=room_jid,
|
|
||||||
template_path=template_info['file_path'],
|
|
||||||
template_name=name,
|
|
||||||
user_jid=user_jid
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
def get_help(self) -> str:
|
|
||||||
return (
|
|
||||||
"Генерирует документ по шаблону из базы знаний. "
|
|
||||||
"Использование: `!create \"Название шаблона\"` (только в комнате)"
|
|
||||||
)
|
|
||||||
@@ -1,121 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
"""
|
|
||||||
Экспертные команды: !summary, !metrics.
|
|
||||||
Не зависят от комнаты (используют последний загруженный пользователем файл или общую БЗ).
|
|
||||||
"""
|
|
||||||
|
|
||||||
import time
|
|
||||||
import logging
|
|
||||||
from core.commands.base import Command
|
|
||||||
from core.functions.summarize_document import summarize_document
|
|
||||||
from core.functions.extract_metrics import extract_metrics
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
class SummaryCommand(Command):
|
|
||||||
name = "!summary"
|
|
||||||
aliases = ["/summary", "!суммаризация", "/суммаризация"]
|
|
||||||
|
|
||||||
async def execute(self, msg, args: str, user_jid: str, room_jid: str = None):
|
|
||||||
# Используем текст последнего загруженного файла (личный или комнатный – он хранится в last_file_texts)
|
|
||||||
last_text = self.bot.last_file_texts.get(user_jid, "")
|
|
||||||
if not last_text:
|
|
||||||
reply = msg.reply("Нет документа для суммаризации.")
|
|
||||||
if reply:
|
|
||||||
reply.send() # исправлено
|
|
||||||
else:
|
|
||||||
logger.error("Не удалось отправить сообщение об отсутствии документа для суммаризации")
|
|
||||||
return
|
|
||||||
reply = msg.reply("📝 Создаю суммаризацию...")
|
|
||||||
if reply:
|
|
||||||
reply.send() # исправлено
|
|
||||||
else:
|
|
||||||
logger.error("Не удалось отправить сообщение о начале суммаризации")
|
|
||||||
# Исправленный вызов: используем функцию из core.functions
|
|
||||||
summary = await summarize_document(
|
|
||||||
giga=self.bot.giga,
|
|
||||||
text=last_text,
|
|
||||||
title="Ваш документ",
|
|
||||||
prompt_text=self.bot.ai_prompts.get('summary', ''),
|
|
||||||
bot_config=self.bot.config
|
|
||||||
)
|
|
||||||
reply = msg.reply(f"📄 **Суммаризация:**\n\n{summary}")
|
|
||||||
if reply:
|
|
||||||
reply.send() # исправлено
|
|
||||||
else:
|
|
||||||
logger.error("Не удалось отправить суммаризацию")
|
|
||||||
|
|
||||||
def get_help(self) -> str:
|
|
||||||
return "Создаёт суммаризацию последнего загруженного документа."
|
|
||||||
|
|
||||||
|
|
||||||
class MetricsCommand(Command):
|
|
||||||
name = "!metrics"
|
|
||||||
aliases = ["/metrics", "!метрики", "/метрики"]
|
|
||||||
cache_ttl = 300 # 5 минут
|
|
||||||
|
|
||||||
async def execute(self, msg, args: str, user_jid: str, room_jid: str = None):
|
|
||||||
cache_key = self.get_cache_key(user_jid, args)
|
|
||||||
now = time.time()
|
|
||||||
if cache_key in self._cache:
|
|
||||||
cached_time, cached_result = self._cache[cache_key]
|
|
||||||
if now - cached_time < self.cache_ttl:
|
|
||||||
reply = msg.reply(cached_result)
|
|
||||||
if reply:
|
|
||||||
reply.send() # исправлено
|
|
||||||
else:
|
|
||||||
logger.error("Не удалось отправить кэшированный результат метрик")
|
|
||||||
return
|
|
||||||
|
|
||||||
reply = msg.reply("🔍 Ищу числовые показатели в ваших документах...")
|
|
||||||
if reply:
|
|
||||||
reply.send() # исправлено
|
|
||||||
else:
|
|
||||||
logger.error("Не удалось отправить сообщение о поиске метрик")
|
|
||||||
query = "числовые показатели KPI метрики статистика проценты суммы количество"
|
|
||||||
# Ищем по всей доступной БЗ (личной + глобальной или комнатной + глобальной)
|
|
||||||
context = await self.bot.kb.find_relevant_info(query, user_jid, room_jid, top_k=30)
|
|
||||||
if not context:
|
|
||||||
reply = msg.reply("📭 В вашей базе знаний нет документов с числовыми показателями.")
|
|
||||||
if reply:
|
|
||||||
reply.send() # исправлено
|
|
||||||
else:
|
|
||||||
logger.error("Не удалось отправить сообщение об отсутствии метрик")
|
|
||||||
return
|
|
||||||
# Исправленный вызов: используем функцию из core.functions
|
|
||||||
metrics = await extract_metrics(
|
|
||||||
giga=self.bot.giga,
|
|
||||||
context=context,
|
|
||||||
prompt_text=self.bot.ai_prompts.get('metrics', ''),
|
|
||||||
bot_config=self.bot.config
|
|
||||||
)
|
|
||||||
if metrics:
|
|
||||||
lines = []
|
|
||||||
for m in metrics[:20]:
|
|
||||||
name = m.get('metric_name', 'Показатель')
|
|
||||||
value = m.get('value', '?')
|
|
||||||
unit = m.get('unit', '')
|
|
||||||
period = m.get('period', '')
|
|
||||||
line = f"- **{name}**: {value}"
|
|
||||||
if unit:
|
|
||||||
line += f" {unit}"
|
|
||||||
if period:
|
|
||||||
line += f" ({period})"
|
|
||||||
lines.append(line)
|
|
||||||
reply = "📊 **Извлечённые метрики из ваших документов:**\n" + "\n".join(lines)
|
|
||||||
self._cache[cache_key] = (time.time(), reply)
|
|
||||||
reply_obj = msg.reply(reply)
|
|
||||||
if reply_obj:
|
|
||||||
reply_obj.send() # исправлено
|
|
||||||
else:
|
|
||||||
logger.error("Не удалось отправить список метрик")
|
|
||||||
else:
|
|
||||||
reply_obj = msg.reply("⚠️ Не удалось извлечь структурированные метрики.")
|
|
||||||
if reply_obj:
|
|
||||||
reply_obj.send() # исправлено
|
|
||||||
else:
|
|
||||||
logger.error("Не удалось отправить сообщение об ошибке извлечения метрик")
|
|
||||||
|
|
||||||
def get_help(self) -> str:
|
|
||||||
return "Извлекает KPI из базы знаний (с кэшем на 5 минут)."
|
|
||||||
Reference in New Issue
Block a user