diff --git a/agents/config.yml b/agents/config.yml
index dd07082af49db4df96e7fb678e26a8db220e5d2c..493703fcd970b7aef8e95e11466d91277cef5959 100644
--- a/agents/config.yml
+++ b/agents/config.yml
@@ -61,6 +61,7 @@ ui_port: 8765
# === Данные пользователя ===
default_user:
username: "user"
+ badges: "📌"
email: "user@example.com"
password: "password" # пусто при инициализации, будет установлен при регистрации
diff --git a/agents/examples/config.yml b/agents/examples/config.yml
index dd07082af49db4df96e7fb678e26a8db220e5d2c..493703fcd970b7aef8e95e11466d91277cef5959 100644
--- a/agents/examples/config.yml
+++ b/agents/examples/config.yml
@@ -61,6 +61,7 @@ ui_port: 8765
# === Данные пользователя ===
default_user:
username: "user"
+ badges: "📌"
email: "user@example.com"
password: "password" # пусто при инициализации, будет установлен при регистрации
diff --git a/agents/init.py b/agents/init.py
index 3b0b7d0b45d3636b50154d553359dabd95e04312..2479adbc962abdea8adb31cbaf4133683094f93d 100644
--- a/agents/init.py
+++ b/agents/init.py
@@ -64,6 +64,7 @@ def init_user(storage, config):
did = generate_did()
user_entry = {
"username": user.get("username", "user"),
+ "badges": user.get("badges", ""),
"mail": user["email"],
"password_hash": password_hash,
"did": did,
diff --git a/agents/tools/storage.py b/agents/tools/storage.py
index 72fb5ef1ee97acd7a502f3e4473b51c00a5a7ecc..856bfa44da13bbd0ffec4a03c1cd0ed2ff250c7f 100644
--- a/agents/tools/storage.py
+++ b/agents/tools/storage.py
@@ -653,11 +653,12 @@ class Storage:
cursor = self.conn.cursor()
cursor.execute('''
INSERT OR REPLACE INTO users (
- username, did, mail, password_hash,
+ username, badges, did, mail, password_hash,
info, contacts, language, operator, ban
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
''', (
user.get('username'),
+ user.get('badges'),
user.get('did'),
user.get('mail'),
user.get('password_hash'),
diff --git a/hf_repo/agents/notebook/views.py b/hf_repo/agents/notebook/views.py
index 82c9ffe5cba4f65504222ddcda004401f5312ac3..07b085ae71d59fd240339461c1232d5a56b7f936 100644
--- a/hf_repo/agents/notebook/views.py
+++ b/hf_repo/agents/notebook/views.py
@@ -7,6 +7,7 @@ import uuid
from fastapi import APIRouter, Request, Form, UploadFile, File
from fastapi.responses import RedirectResponse, HTMLResponse, StreamingResponse
from fastapi.templating import Jinja2Templates
+from datetime import datetime
from starlette.status import HTTP_303_SEE_OTHER
from typing import List
from tools.storage import Storage
@@ -20,6 +21,16 @@ allowed_attributes = {
'a': ['href', 'title']
}
+# Обработка даты и времени
+def format_timestamp(value):
+ try:
+ dt = datetime.fromtimestamp(float(value))
+ return dt.strftime("%Y-%m-%d %H:%M:%S")
+ except Exception:
+ return str(value)
+
+templates.env.filters['format_timestamp'] = format_timestamp
+
# Очистка сообщений
def sanitize_html(text: str) -> str:
# 1. Сначала очищаем HTML
diff --git a/hf_repo/agents/web_ui.py b/hf_repo/agents/web_ui.py
index dc9d4b51f5630cffe68ae1fa7f0a161f7058e93c..86875cc6bafe065d6e1b556ebc186a6cbe679220 100644
--- a/hf_repo/agents/web_ui.py
+++ b/hf_repo/agents/web_ui.py
@@ -20,7 +20,6 @@ storage = Storage()
app = FastAPI()
app.add_middleware(SessionMiddleware, secret_key="очень_секретный_ключ")
-
app.mount("/static", StaticFiles(directory=os.path.join(os.path.dirname(__file__), "notebook/static")), name="static")
templates = Jinja2Templates(directory=os.path.join(os.path.dirname(__file__), "notebook/templates"))
diff --git a/hf_repo/hf_repo/agents/notebook/templates/messages.html b/hf_repo/hf_repo/agents/notebook/templates/messages.html
index 4d5577d6d228ce2b415b331dab5796c19313cf48..57ece18424eb52f889587d9445a28d692ae7b3f6 100644
--- a/hf_repo/hf_repo/agents/notebook/templates/messages.html
+++ b/hf_repo/hf_repo/agents/notebook/templates/messages.html
@@ -3,7 +3,7 @@
Сообщения
@@ -23,8 +29,10 @@
Сообщения
-
@@ -50,6 +58,22 @@
{{ msg.text|safe }}
+
+ {% if msg.code %}
+
+ Код:
+ {{ msg.code }}
+ {% endif %}
+
+ {% if msg.attachments %}
+
+ Файлы:
+
+ {% for file in msg.attachments %}
+ - {{ file.filename }} ({{ file.size }} байт)
+ {% endfor %}
+
+ {% endif %}
{% endfor %}
diff --git a/hf_repo/hf_repo/hf_repo/hf_repo/agents/notebook/views.py b/hf_repo/hf_repo/hf_repo/hf_repo/agents/notebook/views.py
index 1757af34596db894229de40b4671ad19743a9535..82c9ffe5cba4f65504222ddcda004401f5312ac3 100644
--- a/hf_repo/hf_repo/hf_repo/hf_repo/agents/notebook/views.py
+++ b/hf_repo/hf_repo/hf_repo/hf_repo/agents/notebook/views.py
@@ -2,11 +2,13 @@
import re
import bleach
+import uuid
-from fastapi import APIRouter, Request, Form
-from fastapi.responses import RedirectResponse, HTMLResponse
+from fastapi import APIRouter, Request, Form, UploadFile, File
+from fastapi.responses import RedirectResponse, HTMLResponse, StreamingResponse
from fastapi.templating import Jinja2Templates
from starlette.status import HTTP_303_SEE_OTHER
+from typing import List
from tools.storage import Storage
router = APIRouter()
@@ -76,27 +78,59 @@ def show_messages(request: Request, only_personal: bool = False):
})
@router.post("/messages")
-def post_message(
+async def post_message(
request: Request,
text: str = Form(...),
- hidden: str = Form(default="false")
+ code: str = Form(None),
+ hidden: str = Form(default="false"),
+ binary_files: List[UploadFile] = File(default=[])
):
did = request.session.get("did", "anon")
is_hidden = 1 if hidden.lower() == "true" else 0
- # Проверка на бан
if storage.is_banned(did):
return HTMLResponse(content="Вы забанены и не можете отправлять сообщения.", status_code=403)
- if text.strip():
- storage.write_note(
- content=sanitize_html(text.strip()),
+ if text.strip() or code or binary_files:
+ # Очистка текста
+ safe_text = sanitize_html(text.strip()) if text else ""
+
+ # Сохраняем сообщение и получаем message_id
+ message_id = storage.write_note_returning_id(
+ content=safe_text,
user_did=did,
source="user",
- hidden=is_hidden
+ hidden=is_hidden,
+ code=code.strip() if code else None
)
+
+ # Сохраняем файлы
+ for upload in binary_files:
+ data = await upload.read()
+ if data:
+ storage.save_attachment(
+ message_id=message_id,
+ filename=upload.filename,
+ mime_type=upload.content_type,
+ content=data
+ )
+
return RedirectResponse(url="/messages", status_code=303)
+@router.get("/download/{file_id}")
+def download_file(file_id: int):
+ file = storage.get_attachment_by_id(file_id)
+ if not file:
+ raise HTTPException(status_code=404, detail="Файл не найден")
+
+ return StreamingResponse(
+ iter([file["binary"]]),
+ media_type=file["mime_type"],
+ headers={
+ "Content-Disposition": f'attachment; filename="{file["filename"]}"'
+ }
+ )
+
@router.get("/login")
def login_page(request: Request):
return templates.TemplateResponse("login.html", {"request": request})
diff --git a/hf_repo/hf_repo/hf_repo/hf_repo/agents/tools/storage.py b/hf_repo/hf_repo/hf_repo/hf_repo/agents/tools/storage.py
index e21e261807fa9f5c43099792cef5014bba8eb51e..72fb5ef1ee97acd7a502f3e4473b51c00a5a7ecc 100644
--- a/hf_repo/hf_repo/hf_repo/hf_repo/agents/tools/storage.py
+++ b/hf_repo/hf_repo/hf_repo/hf_repo/agents/tools/storage.py
@@ -5,6 +5,7 @@ import sqlite3
import os
import json
import uuid
+import time
from datetime import datetime, timedelta, UTC
from werkzeug.security import generate_password_hash, check_password_hash
@@ -714,13 +715,40 @@ class Storage:
""", (content, user_did, source, timestamp, hidden))
self.conn.commit()
+ def write_note_returning_id(self, content, user_did, source="user", hidden=False, code=None):
+ cursor = self.conn.cursor()
+ cursor.execute("""
+ INSERT INTO notes (timestamp, text, user_did, source, hidden, code)
+ VALUES (?, ?, ?, ?, ?, ?)
+ """, (time.time(), content, user_did, source, int(hidden), code))
+ self.conn.commit()
+ return cursor.lastrowid
+
+ def save_attachment(self, message_id, filename, mime_type, content):
+ cursor = self.conn.cursor()
+ cursor.execute("""
+ INSERT INTO attachments (message_id, filename, mime_type, size, binary)
+ VALUES (?, ?, ?, ?, ?)
+ """, (message_id, filename, mime_type, len(content), content))
+ self.conn.commit()
+
+ def get_attachment_by_id(self, file_id):
+ cursor = self.conn.cursor()
+ cursor.execute("""
+ SELECT id, filename, mime_type, size, binary
+ FROM attachments
+ WHERE id = ?
+ """, (file_id,))
+ row = cursor.fetchone()
+ return dict(row) if row else None
+
def get_notes(self, limit=50, user_did="anon", only_personal=False):
cursor = self.conn.cursor()
if only_personal:
# Только личные (скрытые) сообщения пользователя
query = """
- SELECT n.id, n.text, n.source, n.user_did, u.username, n.timestamp, n.hidden
+ SELECT n.id, n.text, n.code, n.source, n.user_did, u.username, n.timestamp, n.hidden
FROM notes n
LEFT JOIN users u ON n.user_did = u.did
WHERE n.user_did = ? AND n.hidden = 1
@@ -731,7 +759,7 @@ class Storage:
else:
# Личные сообщения + публичные от user/llm, которые не скрыты
query = """
- SELECT n.id, n.text, n.source, n.user_did, u.username, u.badges, n.timestamp, n.hidden
+ SELECT n.id, n.text, n.code, n.source, n.user_did, u.username, u.badges, n.timestamp, n.hidden
FROM notes n
LEFT JOIN users u ON n.user_did = u.did
WHERE n.user_did = ?
@@ -741,6 +769,19 @@ class Storage:
"""
cursor.execute(query, (user_did, limit))
+ result = [dict(row) for row in cursor.fetchall()]
+
+ for note in result:
+ note["attachments"] = self.get_attachments_for_note(note["id"])
+
+ return result
+
+ def get_attachments_for_note(self, message_id):
+ cursor = self.conn.cursor()
+ cursor.execute("""
+ SELECT id, filename, mime_type, size FROM attachments
+ WHERE message_id = ?
+ """, (message_id,))
return [dict(row) for row in cursor.fetchall()]
# Пользователи
diff --git a/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/agents/tools/db_structure.sql b/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/agents/tools/db_structure.sql
index 1541b92ecce06930792cb91b8f47eeeb791db957..5d9a57aac072034328f97b22957aedfb40eb6cde 100644
--- a/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/agents/tools/db_structure.sql
+++ b/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/agents/tools/db_structure.sql
@@ -42,17 +42,29 @@ CREATE TABLE IF NOT EXISTS diary_graph_index (
-- Заметки, подсказки, сообщения пользователя и LLM
CREATE TABLE IF NOT EXISTS notes (
- id INTEGER PRIMARY KEY AUTOINCREMENT, -- Уникальный идентификатор заметки
- text TEXT NOT NULL, -- Текст заметки
- tags TEXT, -- Теги (например: "idea", "instruction")
- user_did TEXT DEFAULT 'ALL', -- DID пользователя (или 'ALL' — для всех)
- source TEXT DEFAULT 'user', -- Источник заметки: user | cli | llm | system
- links TEXT DEFAULT '', -- Ссылки или связи с другими объектами
- read INTEGER DEFAULT 0, -- Статус прочтения LLM: 0 = нет, 1 = да
- hidden INTEGER DEFAULT 0, -- Скрыта ли от пользователя: 0 = нет, 1 = да
- priority INTEGER DEFAULT 0, -- Приоритет заметки
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ text TEXT NOT NULL, -- Основной текст заметки/сообщения
+ code TEXT, -- Прикреплённый код (Python, JS и т.п.)
+ tags TEXT, -- Теги (устанавливаются агентом, например: "idea", "instruction")
+ user_did TEXT DEFAULT 'ALL', -- Идентификатор пользователя (или 'ALL')
+ source TEXT DEFAULT 'user', -- Источник: user | cli | llm | system
+ links TEXT DEFAULT '', -- Ссылки на другие объекты (например, JSON со связями)
+ read INTEGER DEFAULT 0, -- Агент прочитал: 0 = нет, 1 = да
+ hidden INTEGER DEFAULT 0, -- Скрыто от UI (например, технические записи)
+ priority INTEGER DEFAULT 0, -- Приоритет обработки (>0: срочность/важность, задается вручную или агентом)
timestamp TEXT DEFAULT CURRENT_TIMESTAMP, -- Время создания
- llm_id TEXT -- Идентификатор LLM
+ llm_id TEXT -- Идентификатор агента, добавившего сообщение
+);
+
+-- Вложения (может быть несколько к одной заметке)
+CREATE TABLE IF NOT EXISTS attachments (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ message_id INTEGER NOT NULL, -- Связь с notes.id
+ filename TEXT, -- Имя файла
+ mime_type TEXT, -- Тип (например, image/png, application/zip)
+ size INTEGER, -- Размер файла
+ binary BLOB NOT NULL, -- Сами данные
+ FOREIGN KEY (message_id) REFERENCES notes(id) ON DELETE CASCADE
);
-- Лог процессов: задачи, ошибки, события
diff --git a/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/agents/add_message.py b/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/agents/add_message.py
index 6948a46b2b26b8beb814773504b9bf6a5d11f51a..ddcebdc259679e707bc6a9207b59e9701394f0ed 100644
--- a/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/agents/add_message.py
+++ b/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/agents/add_message.py
@@ -5,11 +5,12 @@ from tools.storage import Storage
storage = Storage()
-def add_message(content, source="cli", user_did="anon"):
+def add_message(content, source="cli", user_did="anon", hidden=1):
storage.write_note(
content,
source=source,
- user_did=user_did
+ user_did=user_did,
+ hidden=hidden
)
print(f"[+] Сообщение от {source} ({user_did}) добавлено: {content}")
@@ -19,6 +20,7 @@ if __name__ == "__main__":
parser.add_argument("--content", required=True)
parser.add_argument("--source", default="cli")
parser.add_argument("--user_did", default="anon")
+ parser.add_argument("--hidden", default=1)
args = parser.parse_args()
- add_message(args.content, args.source, args.user_did)
+ add_message(args.content, args.source, args.user_did, args.hidden)
diff --git a/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/agents/tools/storage.py b/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/agents/tools/storage.py
index abc788acd749ff19c248fcdafda15c563860b24e..e21e261807fa9f5c43099792cef5014bba8eb51e 100644
--- a/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/agents/tools/storage.py
+++ b/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/agents/tools/storage.py
@@ -735,7 +735,7 @@ class Storage:
FROM notes n
LEFT JOIN users u ON n.user_did = u.did
WHERE n.user_did = ?
- OR ((n.source = 'user' OR n.source = 'llm') AND n.hidden = 0)
+ OR ((n.source = 'user' OR n.source = 'llm' OR n.source = 'cli') AND n.hidden = 0)
ORDER BY n.timestamp DESC
LIMIT ?
"""
diff --git a/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/agents/notebook/views.py b/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/agents/notebook/views.py
index 442d9bd7bc73217f9ab8a1e60dbe7f63a9120737..1757af34596db894229de40b4671ad19743a9535 100644
--- a/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/agents/notebook/views.py
+++ b/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/agents/notebook/views.py
@@ -1,5 +1,6 @@
# agents/notebook/views.py
+import re
import bleach
from fastapi import APIRouter, Request, Form
@@ -12,13 +13,20 @@ router = APIRouter()
templates = Jinja2Templates(directory="notebook/templates")
storage = Storage()
-allowed_tags = ['b', 'i', 's', 'u', 'a', 'ol', 'ul', 'li', 'dl', 'dt', 'dd', 'table', 'caption', 'tr', 'th', 'td']
+allowed_tags = ['b', 'i', 's', 'u', 'a', 'ol', 'ul', 'li', 'dl', 'dt', 'dd', 'table', 'caption', 'tr', 'th', 'td', 'code', 'pre', 'blockquote', 'br', 'hr']
allowed_attributes = {
'a': ['href', 'title']
}
-def sanitize_html(text):
- return bleach.clean(text, tags=allowed_tags, attributes=allowed_attributes, strip=True)
+# Очистка сообщений
+def sanitize_html(text: str) -> str:
+ # 1. Сначала очищаем HTML
+ cleaned = bleach.clean(text, tags=allowed_tags, attributes=allowed_attributes, strip=True)
+
+ # 2. Заменяем 3 и более
подряд на ровно два
+ cleaned = re.sub(r'(
\s*){3,}', '
', cleaned, flags=re.IGNORECASE)
+
+ return cleaned
@router.get("/chat")
def chat_page(request: Request):
diff --git a/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/agents/tools/__init__.py b/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/agents/tools/__init__.py
index 49652b8f9694110cfa87f3d1903c256af66e9be1..c1623bfec92d28925597e271faa5e7d700376cc7 100644
--- a/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/agents/tools/__init__.py
+++ b/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/agents/tools/__init__.py
@@ -1 +1 @@
-from agents.tools.storage import Storage
+from tools.storage import Storage
diff --git a/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/agents/notebook/templates/messages.html b/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/agents/notebook/templates/messages.html
index 3df3b2d777024accdd96f1d91091cc41e7609143..b6ba764292e7ba72b64207941e1faa65ebd21505 100644
--- a/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/agents/notebook/templates/messages.html
+++ b/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/agents/notebook/templates/messages.html
@@ -25,8 +25,8 @@
diff --git a/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/agents/notebook/views.py b/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/agents/notebook/views.py
index 7779f71798add544d2953b5df5bc4e7c41aa9c60..442d9bd7bc73217f9ab8a1e60dbe7f63a9120737 100644
--- a/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/agents/notebook/views.py
+++ b/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/agents/notebook/views.py
@@ -12,7 +12,7 @@ router = APIRouter()
templates = Jinja2Templates(directory="notebook/templates")
storage = Storage()
-allowed_tags = ['b', 'i', 'a', 'ol', 'ul', 'li', 'dl', 'dt', 'dd', 'table', 'caption', 'tr', 'th', 'td']
+allowed_tags = ['b', 'i', 's', 'u', 'a', 'ol', 'ul', 'li', 'dl', 'dt', 'dd', 'table', 'caption', 'tr', 'th', 'td']
allowed_attributes = {
'a': ['href', 'title']
}
diff --git a/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/agents/notebook/views.py b/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/agents/notebook/views.py
index 2976e44700e87ab3a4f7b0de4303cbfc54ee6fa5..7779f71798add544d2953b5df5bc4e7c41aa9c60 100644
--- a/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/agents/notebook/views.py
+++ b/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/agents/notebook/views.py
@@ -1,5 +1,7 @@
# agents/notebook/views.py
+import bleach
+
from fastapi import APIRouter, Request, Form
from fastapi.responses import RedirectResponse, HTMLResponse
from fastapi.templating import Jinja2Templates
@@ -10,6 +12,14 @@ router = APIRouter()
templates = Jinja2Templates(directory="notebook/templates")
storage = Storage()
+allowed_tags = ['b', 'i', 'a', 'ol', 'ul', 'li', 'dl', 'dt', 'dd', 'table', 'caption', 'tr', 'th', 'td']
+allowed_attributes = {
+ 'a': ['href', 'title']
+}
+
+def sanitize_html(text):
+ return bleach.clean(text, tags=allowed_tags, attributes=allowed_attributes, strip=True)
+
@router.get("/chat")
def chat_page(request: Request):
did = request.session.get("did")
@@ -72,7 +82,7 @@ def post_message(
if text.strip():
storage.write_note(
- content=text.strip(),
+ content=sanitize_html(text.strip()),
user_did=did,
source="user",
hidden=is_hidden
diff --git a/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/agents/requirements.txt b/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/agents/requirements.txt
index a63403550b2c5653989211469ee0a08a48065867..c515e968c47a5ffa70ce17857ce182ab4c5295e8 100644
--- a/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/agents/requirements.txt
+++ b/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/agents/requirements.txt
@@ -12,4 +12,5 @@ jinja2
python-multipart
passlib[bcrypt]
werkzeug
-itsdangerous
\ No newline at end of file
+itsdangerous
+bleach
\ No newline at end of file
diff --git a/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/agents/notebook/templates/messages.html b/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/agents/notebook/templates/messages.html
index 8b6c076dad77c38f5846dd71252100836e2c4379..3df3b2d777024accdd96f1d91091cc41e7609143 100644
--- a/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/agents/notebook/templates/messages.html
+++ b/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/agents/notebook/templates/messages.html
@@ -46,10 +46,10 @@
Источник: {{ msg.source }} — {{ msg.timestamp[:19].replace('T', ' ') }}
- {{ msg.badges }}Пользователь: {% if msg.username %}{{ msg.username }}{% endif %} {% if msg.user_did !="" %}({{ msg.user_did }}){% endif %}
+ {% if msg.badges %}{{ msg.badges }}{% endif %}Пользователь: {% if msg.username %}{{ msg.username }}{% endif %} {% if msg.user_did %}({{ msg.user_did }}){% endif %}
- {{ msg.text }}
+ {{ msg.text|safe }}
{% endfor %}