fix: исправлены критические ошибки в коде
- GigaChatProvider: добавлено наследование от BaseAIProvider и методы - Компактификация: исправлен парсинг JSON-ответа Qwen - Compactor: добавлена проверка на None - Векторный поиск: исправлена структура результатов ChromaDB - extract_facts_with_ai: добавлена проверка авторизации Qwen - SSH сессия: исправлена логика буфера вывода - Cron job: добавлено обновление next_run после выполнения
This commit is contained in:
parent
107685771c
commit
719dfa2015
6
bot.py
6
bot.py
|
|
@ -376,8 +376,10 @@ async def handle_ai_task(update: Update, text: str):
|
||||||
# Формируем контекст с историей + памятью + summary
|
# Формируем контекст с историей + памятью + summary
|
||||||
# Получаем summary и последние сообщения из ChromaDB
|
# Получаем summary и последние сообщения из ChromaDB
|
||||||
summary = None
|
summary = None
|
||||||
|
recent_messages = []
|
||||||
try:
|
try:
|
||||||
summary, recent_messages = compactor.get_context_with_summary(limit=20)
|
if compactor is not None:
|
||||||
|
summary, recent_messages = compactor.get_context_with_summary(limit=20)
|
||||||
# Формируем историю из последних сообщений
|
# Формируем историю из последних сообщений
|
||||||
history_context = "\n".join([f"{msg['role']}: {msg['content']}" for msg in recent_messages])
|
history_context = "\n".join([f"{msg['role']}: {msg['content']}" for msg in recent_messages])
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|
@ -859,7 +861,7 @@ async def handle_ssh_session_input(update: Update, text: str, session: SSHSessio
|
||||||
while not is_done:
|
while not is_done:
|
||||||
more_output, is_done = await read_ssh_output(session.process, timeout=5.0)
|
more_output, is_done = await read_ssh_output(session.process, timeout=5.0)
|
||||||
output += more_output
|
output += more_output
|
||||||
session.output_buffer += output
|
session.output_buffer += more_output
|
||||||
session.last_activity = datetime.now()
|
session.last_activity = datetime.now()
|
||||||
|
|
||||||
new_input_type = detect_input_type(output)
|
new_input_type = detect_input_type(output)
|
||||||
|
|
|
||||||
|
|
@ -341,7 +341,7 @@ class DialogueCompactor:
|
||||||
logger.info("Запуск Qwen Code для сжатия...")
|
logger.info("Запуск Qwen Code для сжатия...")
|
||||||
|
|
||||||
# Запускаем задачу
|
# Запускаем задачу
|
||||||
await self.qwen_manager.run_task(
|
result = await self.qwen_manager.run_task(
|
||||||
user_id=999, # Системный user_id для компактификации
|
user_id=999, # Системный user_id для компактификации
|
||||||
task=prompt,
|
task=prompt,
|
||||||
on_output=on_output,
|
on_output=on_output,
|
||||||
|
|
@ -349,9 +349,30 @@ class DialogueCompactor:
|
||||||
use_system_prompt=False # Не добавляем системный промпт бота
|
use_system_prompt=False # Не добавляем системный промпт бота
|
||||||
)
|
)
|
||||||
|
|
||||||
summary = "".join(output_parts)
|
# Парсим результат - извлекаем текст из JSON как в основном коде
|
||||||
|
import re
|
||||||
|
summary = "".join(output_parts).strip()
|
||||||
|
|
||||||
|
# Извлекаем текст из JSON ответа (как в bot.py)
|
||||||
|
text_matches = re.findall(r'"text":"([^"]+)"', summary)
|
||||||
|
if text_matches:
|
||||||
|
summary = " ".join(text_matches).replace("\\n", "\n")
|
||||||
|
else:
|
||||||
|
# Fallback: пробуем найти result поле
|
||||||
|
try:
|
||||||
|
import json
|
||||||
|
for line in summary.split('\n'):
|
||||||
|
line = line.strip()
|
||||||
|
if line.startswith('{'):
|
||||||
|
data = json.loads(line)
|
||||||
|
if data.get('type') == 'result':
|
||||||
|
summary = data.get('result', summary)
|
||||||
|
break
|
||||||
|
if data.get('result'):
|
||||||
|
summary = data.get('result', summary)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
# Очищаем summary от служебных символов
|
|
||||||
summary = summary.strip()
|
summary = summary.strip()
|
||||||
|
|
||||||
if not summary:
|
if not summary:
|
||||||
|
|
|
||||||
|
|
@ -501,6 +501,9 @@ class CronTool(BaseTool):
|
||||||
conn.commit()
|
conn.commit()
|
||||||
conn.close()
|
conn.close()
|
||||||
|
|
||||||
|
# Обновляем next_run после успешного выполнения
|
||||||
|
await self.update_next_run(job_id)
|
||||||
|
|
||||||
return ToolResult(
|
return ToolResult(
|
||||||
success=True,
|
success=True,
|
||||||
data=result_data,
|
data=result_data,
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,14 @@ from datetime import datetime, timedelta
|
||||||
from typing import Optional, Dict, Callable, Any, List, Union
|
from typing import Optional, Dict, Callable, Any, List, Union
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
|
|
||||||
|
from bot.base_ai_provider import (
|
||||||
|
BaseAIProvider,
|
||||||
|
ProviderResponse,
|
||||||
|
AIMessage,
|
||||||
|
ToolCall,
|
||||||
|
ToolCallStatus,
|
||||||
|
)
|
||||||
|
|
||||||
# Импортируем OAuth модуль и константы
|
# Импортируем OAuth модуль и константы
|
||||||
from bot.utils.qwen_oauth import (
|
from bot.utils.qwen_oauth import (
|
||||||
get_authorization_url,
|
get_authorization_url,
|
||||||
|
|
@ -596,7 +604,7 @@ class QwenCodeManager:
|
||||||
return '\n'.join(cleaned) if cleaned else output
|
return '\n'.join(cleaned) if cleaned else output
|
||||||
|
|
||||||
|
|
||||||
class GigaChatProvider:
|
class GigaChatProvider(BaseAIProvider):
|
||||||
"""
|
"""
|
||||||
AI-провайдер для работы с GigaChat API.
|
AI-провайдер для работы с GigaChat API.
|
||||||
|
|
||||||
|
|
@ -605,10 +613,23 @@ class GigaChatProvider:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
super().__init__()
|
||||||
self._tool = None
|
self._tool = None
|
||||||
self._initialized = False
|
self._initialized = False
|
||||||
self._config_error: Optional[str] = None
|
self._config_error: Optional[str] = None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def provider_name(self) -> str:
|
||||||
|
return "GigaChat"
|
||||||
|
|
||||||
|
@property
|
||||||
|
def supports_tools(self) -> bool:
|
||||||
|
return False
|
||||||
|
|
||||||
|
@property
|
||||||
|
def supports_streaming(self) -> bool:
|
||||||
|
return False
|
||||||
|
|
||||||
def _ensure_initialized(self):
|
def _ensure_initialized(self):
|
||||||
"""Ленивая инициализация инструмента"""
|
"""Ленивая инициализация инструмента"""
|
||||||
if self._initialized:
|
if self._initialized:
|
||||||
|
|
@ -729,6 +750,94 @@ class GigaChatProvider:
|
||||||
self._ensure_initialized()
|
self._ensure_initialized()
|
||||||
return self._config_error
|
return self._config_error
|
||||||
|
|
||||||
|
async def chat(
|
||||||
|
self,
|
||||||
|
prompt: str,
|
||||||
|
system_prompt: Optional[str] = None,
|
||||||
|
context: Optional[List[Dict[str, str]]] = None,
|
||||||
|
tools: Optional[List[Dict[str, Any]]] = None,
|
||||||
|
on_chunk: Optional[Callable[[str], Any]] = None,
|
||||||
|
user_id: Optional[int] = None,
|
||||||
|
**kwargs
|
||||||
|
) -> ProviderResponse:
|
||||||
|
"""Реализация метода chat для интерфейса BaseAIProvider."""
|
||||||
|
result = await self.chat(
|
||||||
|
prompt=prompt,
|
||||||
|
system_prompt=system_prompt,
|
||||||
|
on_chunk=on_chunk,
|
||||||
|
)
|
||||||
|
|
||||||
|
if result.get("success"):
|
||||||
|
return ProviderResponse(
|
||||||
|
success=True,
|
||||||
|
message=AIMessage(
|
||||||
|
content=result.get("content", ""),
|
||||||
|
metadata={"model": result.get("model")}
|
||||||
|
),
|
||||||
|
provider_name=self.provider_name,
|
||||||
|
usage=result.get("usage")
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
return ProviderResponse(
|
||||||
|
success=False,
|
||||||
|
error=result.get("error", "Unknown error"),
|
||||||
|
provider_name=self.provider_name
|
||||||
|
)
|
||||||
|
|
||||||
|
async def execute_tool(
|
||||||
|
self,
|
||||||
|
tool_name: str,
|
||||||
|
tool_args: Dict[str, Any],
|
||||||
|
tool_call_id: Optional[str] = None,
|
||||||
|
**kwargs
|
||||||
|
) -> ToolCall:
|
||||||
|
"""GigaChat не поддерживает инструменты нативно."""
|
||||||
|
return ToolCall(
|
||||||
|
tool_name=tool_name,
|
||||||
|
tool_args=tool_args,
|
||||||
|
tool_call_id=tool_call_id,
|
||||||
|
status=ToolCallStatus.PENDING
|
||||||
|
)
|
||||||
|
|
||||||
|
async def process_with_tools(
|
||||||
|
self,
|
||||||
|
prompt: str,
|
||||||
|
system_prompt: Optional[str] = None,
|
||||||
|
context: Optional[List[Dict[str, str]]] = None,
|
||||||
|
tools_registry: Optional[Dict[str, Any]] = None,
|
||||||
|
on_chunk: Optional[Callable[[str], Any]] = None,
|
||||||
|
max_iterations: int = 5,
|
||||||
|
**kwargs
|
||||||
|
) -> ProviderResponse:
|
||||||
|
"""Обработка запроса с инструментами для GigaChat.
|
||||||
|
|
||||||
|
GigaChat не поддерживает инструменты нативно, поэтому просто
|
||||||
|
выполняем запрос без инструментов.
|
||||||
|
"""
|
||||||
|
# GigaChat не поддерживает инструменты - выполняем обычный запрос
|
||||||
|
result = await self.chat(
|
||||||
|
prompt=prompt,
|
||||||
|
system_prompt=system_prompt,
|
||||||
|
on_chunk=on_chunk,
|
||||||
|
)
|
||||||
|
|
||||||
|
if result.get("success"):
|
||||||
|
return ProviderResponse(
|
||||||
|
success=True,
|
||||||
|
message=AIMessage(
|
||||||
|
content=result.get("content", ""),
|
||||||
|
metadata={"model": result.get("model")}
|
||||||
|
),
|
||||||
|
provider_name=self.provider_name,
|
||||||
|
usage=result.get("usage")
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
return ProviderResponse(
|
||||||
|
success=False,
|
||||||
|
error=result.get("error", "Unknown error"),
|
||||||
|
provider_name=self.provider_name
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
# Глобальный менеджер
|
# Глобальный менеджер
|
||||||
qwen_manager = QwenCodeManager()
|
qwen_manager = QwenCodeManager()
|
||||||
|
|
|
||||||
|
|
@ -180,18 +180,22 @@ class VectorMemoryStorage:
|
||||||
# Преобразуем результаты
|
# Преобразуем результаты
|
||||||
found_messages = []
|
found_messages = []
|
||||||
|
|
||||||
if results and results['ids'] and results['ids'][0]:
|
if results and results.get('ids') and results['ids']:
|
||||||
|
docs = results.get('documents', [[]])[0]
|
||||||
|
metas = results.get('metadatas', [[]])[0]
|
||||||
|
dists = results.get('distances', [[]])[0] if results.get('distances') else []
|
||||||
|
|
||||||
for i, doc_id in enumerate(results['ids'][0]):
|
for i, doc_id in enumerate(results['ids'][0]):
|
||||||
doc_text = results['documents'][0][i]
|
doc_text = docs[i] if i < len(docs) else ""
|
||||||
metadata = results['metadatas'][0][i]
|
metadata = metas[i] if i < len(metas) else {}
|
||||||
distance = results['distances'][0][i] if results['distances'] else 0.0
|
distance = dists[i] if i < len(dists) else 0.0
|
||||||
|
|
||||||
message = Message(
|
message = Message(
|
||||||
id=None,
|
id=None,
|
||||||
user_id=int(metadata['user_id']),
|
user_id=int(metadata.get('user_id', 0)),
|
||||||
role=metadata['role'],
|
role=metadata.get('role', 'user'),
|
||||||
content=doc_text,
|
content=doc_text,
|
||||||
timestamp=datetime.fromisoformat(metadata['timestamp']),
|
timestamp=datetime.fromisoformat(metadata.get('timestamp', datetime.now().isoformat())),
|
||||||
session_id=metadata.get('session_id')
|
session_id=metadata.get('session_id')
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -226,10 +230,13 @@ class VectorMemoryStorage:
|
||||||
)
|
)
|
||||||
|
|
||||||
messages = []
|
messages = []
|
||||||
if results and results.get('ids') and results['ids'][0]:
|
if results and results.get('ids') and results['ids']:
|
||||||
|
docs = results.get('documents', [[]])[0] if results.get('documents') else []
|
||||||
|
metas = results.get('metadatas', [[]])[0] if results.get('metadatas') else []
|
||||||
|
|
||||||
for i, doc_id in enumerate(results['ids'][0]):
|
for i, doc_id in enumerate(results['ids'][0]):
|
||||||
doc_text = results['documents'][0][i] if 'documents' in results else ""
|
doc_text = docs[i] if i < len(docs) else ""
|
||||||
metadata = results['metadatas'][0][i] if 'metadatas' in results else {}
|
metadata = metas[i] if i < len(metas) else {}
|
||||||
|
|
||||||
message = Message(
|
message = Message(
|
||||||
id=None,
|
id=None,
|
||||||
|
|
@ -480,8 +487,14 @@ class HybridMemoryManager:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Импортируем qwen_manager
|
# Импортируем qwen_manager и проверяем авторизацию
|
||||||
from qwen_integration import qwen_manager
|
from qwen_integration import qwen_manager
|
||||||
|
from bot.utils.qwen_oauth import is_authorized
|
||||||
|
|
||||||
|
# Проверяем авторизацию перед выполнением
|
||||||
|
if not await is_authorized():
|
||||||
|
logger.warning(f"Qwen не авторизован, пропускаем извлечение фактов для пользователя {user_id}")
|
||||||
|
return []
|
||||||
|
|
||||||
output_buffer = []
|
output_buffer = []
|
||||||
def on_output(text):
|
def on_output(text):
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue