225 lines
6.5 KiB
Markdown
225 lines
6.5 KiB
Markdown
# 🔧 Исправление: Экранирование HTML тегов в комментариях
|
||
|
||
## 🐛 Проблема
|
||
|
||
AI комментарии содержали упоминания JSX/HTML тегов, которые **исчезали** при отображении в Gitea/GitHub:
|
||
|
||
**До исправления:**
|
||
```
|
||
Неправильное использование key: key должен быть на элементе , а не на
|
||
↑ ↑
|
||
<CharacterItem> исчез <img> исчез
|
||
```
|
||
|
||
**Причина:** Markdown интерпретирует `<CharacterItem>` как HTML тег и пытается его отрендерить, но т.к. такого тега не существует, он просто исчезает.
|
||
|
||
---
|
||
|
||
## ✅ Решение
|
||
|
||
Добавлена функция **`_escape_html_in_text()`**, которая **оборачивает HTML-подобные теги** в backticks:
|
||
|
||
```python
|
||
def _escape_html_in_text(self, text: str) -> str:
|
||
"""Escape HTML tags in text to prevent Markdown from hiding them"""
|
||
import re
|
||
|
||
def replace_tag(match):
|
||
tag = match.group(0)
|
||
return f"`{tag}`" # Оборачиваем в backticks
|
||
|
||
# Находим все <...> паттерны
|
||
text = re.sub(r'<[^>]+>', replace_tag, text)
|
||
return text
|
||
```
|
||
|
||
---
|
||
|
||
## 🎯 Как работает
|
||
|
||
### Шаг 1: AI генерирует комментарий
|
||
|
||
```
|
||
"key должен быть на элементе <CharacterItem>, а не на <img>"
|
||
```
|
||
|
||
### Шаг 2: Функция экранирования
|
||
|
||
```python
|
||
text = _escape_html_in_text(text)
|
||
# Результат:
|
||
"key должен быть на элементе `<CharacterItem>`, а не на `<img>`"
|
||
```
|
||
|
||
### Шаг 3: В Gitea/GitHub отображается
|
||
|
||
```
|
||
key должен быть на элементе `<CharacterItem>`, а не на `<img>`
|
||
↑ ↑ ↑ ↑
|
||
backticks делают теги видимыми
|
||
```
|
||
|
||
---
|
||
|
||
## 📊 Примеры
|
||
|
||
### Пример 1: JSX элементы
|
||
|
||
**Входной текст:**
|
||
```
|
||
Неправильное использование key: key должен быть на <CharacterItem>, а не на <img>
|
||
```
|
||
|
||
**После обработки:**
|
||
```
|
||
Неправильное использование key: key должен быть на `<CharacterItem>`, а не на `<img>`
|
||
```
|
||
|
||
**В Gitea видно:**
|
||
```
|
||
Неправильное использование key: key должен быть на `<CharacterItem>`, а не на `<img>`
|
||
```
|
||
|
||
---
|
||
|
||
### Пример 2: HTML теги
|
||
|
||
**Входной текст:**
|
||
```
|
||
Используйте <div> вместо <span> для обертки
|
||
```
|
||
|
||
**После обработки:**
|
||
```
|
||
Используйте `<div>` вместо `<span>` для обертки
|
||
```
|
||
|
||
**В Gitea видно:**
|
||
```
|
||
Используйте `<div>` вместо `<span>` для обертки
|
||
```
|
||
|
||
---
|
||
|
||
### Пример 3: Без HTML тегов
|
||
|
||
**Входной текст:**
|
||
```
|
||
Опечатка в строке: 'shmapplication/json' должно быть 'application/json'
|
||
```
|
||
|
||
**После обработки:**
|
||
```
|
||
Опечатка в строке: 'shmapplication/json' должно быть 'application/json'
|
||
```
|
||
|
||
**В Gitea видно:**
|
||
```
|
||
Опечатка в строке: 'shmapplication/json' должно быть 'application/json'
|
||
```
|
||
|
||
*Без изменений - теги не найдены*
|
||
|
||
---
|
||
|
||
## 🔄 Где применяется
|
||
|
||
Функция вызывается **дважды** для каждого ревью:
|
||
|
||
### 1. Для каждого комментария
|
||
|
||
```python
|
||
for comment_data in state["comments"]:
|
||
message = comment_data.get("message", "")
|
||
message = self._remove_think_blocks(message)
|
||
message = self._escape_html_in_text(message) # ← Экранируем
|
||
|
||
comment = Comment(content=message, ...)
|
||
```
|
||
|
||
### 2. Для общего summary
|
||
|
||
```python
|
||
summary = await self.analyzer.generate_summary(...)
|
||
summary = self._remove_think_blocks(summary)
|
||
summary = self._escape_html_in_text(summary) # ← Экранируем
|
||
|
||
await git_service.create_review(body=summary, ...)
|
||
```
|
||
|
||
---
|
||
|
||
## 🧪 Тестирование
|
||
|
||
Создан тест для проверки:
|
||
|
||
```python
|
||
test_texts = [
|
||
"key должен быть на элементе <CharacterItem>, а не на <img>",
|
||
"Используйте <div> вместо <span> здесь"
|
||
]
|
||
|
||
for text in test_texts:
|
||
escaped = escape_html_in_text(text)
|
||
print(f"Original: {text}")
|
||
print(f"Escaped: {escaped}")
|
||
```
|
||
|
||
**Результат:**
|
||
```
|
||
Original: key должен быть на элементе <CharacterItem>, а не на <img>
|
||
Escaped: key должен быть на элементе `<CharacterItem>`, а не на `<img>`
|
||
|
||
Original: Используйте <div> вместо <span> здесь
|
||
Escaped: Используйте `<div>` вместо `<span>` здесь
|
||
```
|
||
|
||
✅ **Работает как ожидалось!**
|
||
|
||
---
|
||
|
||
## 🎨 Визуальное сравнение
|
||
|
||
### ❌ До исправления (в Gitea):
|
||
|
||
```
|
||
❌ src/pages/search-character.tsx:105
|
||
ERROR: Неправильное использование key: key должен быть на элементе , а не на
|
||
↑ теги исчезли, непонятно о чем речь
|
||
```
|
||
|
||
### ✅ После исправления (в Gitea):
|
||
|
||
```
|
||
❌ src/pages/search-character.tsx:105
|
||
ERROR: Неправильное использование key: key должен быть на элементе `<CharacterItem>`, а не на `<img>`
|
||
↑ теги видны и кликабельны, все понятно
|
||
```
|
||
|
||
---
|
||
|
||
## 📝 Измененные файлы
|
||
|
||
- **`backend/app/agents/reviewer.py`**:
|
||
- Добавлена функция `_escape_html_in_text()`
|
||
- Вызов функции для комментариев
|
||
- Вызов функции для summary
|
||
|
||
---
|
||
|
||
## 🚀 Как попробовать
|
||
|
||
1. Backend уже подхватил изменения (`--reload`)
|
||
2. Нажмите **🔄 Повторить ревью**
|
||
3. Откройте PR в Gitea
|
||
4. Проверьте что теги теперь видны: `<CharacterItem>`, `<img>`, и т.д.
|
||
|
||
---
|
||
|
||
## ✅ Готово!
|
||
|
||
Теперь все HTML/JSX теги в комментариях **отображаются корректно** и код понятен! 🎉
|
||
|
||
**Попробуйте прямо сейчас!** 🧪
|
||
|