fix(ai-review): max_tokens=16000, fallback на reasoning_content (20.09.2026)
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
name: AI Code Review
|
||||
|
||||
on:
|
||||
# Кнопка "Run workflow" в UI Gitea
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
|
||||
+20
-9
@@ -8,7 +8,8 @@ AI Code Review for Filwords PL Backend
|
||||
Запускается через Gitea Actions (workflow_dispatch — вручную из UI).
|
||||
|
||||
(20.09.2026) Первая версия — конвейер без tool-calling, предсказуемо по токенам.
|
||||
(20.09.2026, вечер) Фикс: tool_choice=none, безопасное извлечение content.
|
||||
(20.09.2026, вечер) Фикс: max_tokens=16000, fallback на reasoning_content
|
||||
(DeepSeek V4.1 Flash — reasoning-модель, тратит много токенов на размышления).
|
||||
"""
|
||||
|
||||
import os
|
||||
@@ -27,7 +28,10 @@ GITEA_INSTANCE = os.environ.get("GITEA_INSTANCE", "https://git.filwordspl.com")
|
||||
GITEA_REPO = os.environ.get("GITEA_REPO", "Drobysh/filwordspl-backend")
|
||||
|
||||
MODEL = "deepseek-ai/DeepSeek-V4.1-Flash"
|
||||
MAX_OUTPUT_TOKENS = 4000
|
||||
|
||||
# 🔧 ФИКС (20.09.2026): было 4000 — DeepSeek V4.1 Flash тратит ~4000 на reasoning,
|
||||
# не оставляя токенов на финальный ответ. 16000 хватает с запасом.
|
||||
MAX_OUTPUT_TOKENS = 16000
|
||||
|
||||
# Что игнорировать при сборе кода
|
||||
IGNORE_DIRS = {
|
||||
@@ -71,7 +75,6 @@ def collect_code(repo_root: Path) -> str:
|
||||
if path.suffix not in INCLUDE_EXTENSIONS:
|
||||
continue
|
||||
|
||||
# Пропускаем игнорируемые папки
|
||||
rel = path.relative_to(repo_root)
|
||||
if any(part in IGNORE_DIRS for part in rel.parts):
|
||||
continue
|
||||
@@ -84,7 +87,6 @@ def collect_code(repo_root: Path) -> str:
|
||||
print(f"⚠️ Не прочитал {rel}: {e}", file=sys.stderr)
|
||||
continue
|
||||
|
||||
# Пропускаем слишком большие файлы
|
||||
if len(content) > MAX_FILE_SIZE:
|
||||
print(f"⚠️ Пропущен большой файл: {rel} ({len(content)} символов)", file=sys.stderr)
|
||||
continue
|
||||
@@ -176,7 +178,6 @@ def call_together(code: str) -> str:
|
||||
],
|
||||
"max_tokens": MAX_OUTPUT_TOKENS,
|
||||
"temperature": 0.3,
|
||||
"tool_choice": "none",
|
||||
},
|
||||
timeout=300.0,
|
||||
)
|
||||
@@ -189,18 +190,28 @@ def call_together(code: str) -> str:
|
||||
data = response.json()
|
||||
|
||||
try:
|
||||
result = data["choices"][0]["message"]["content"]
|
||||
if result is None:
|
||||
raise KeyError("content is None")
|
||||
message = data["choices"][0]["message"]
|
||||
result = message.get("content")
|
||||
|
||||
# 🔧 ФИКС (20.09.2026): если content пустой — используем reasoning_content.
|
||||
# DeepSeek V4.1 Flash — reasoning-модель, может уйти в размышления
|
||||
# и не выдать финальный ответ. Тогда берём reasoning.
|
||||
if not result:
|
||||
result = message.get("reasoning_content")
|
||||
|
||||
if not result:
|
||||
raise KeyError("и content, и reasoning_content пустые")
|
||||
except (KeyError, IndexError, TypeError) as e:
|
||||
print(f"❌ Неожиданный формат ответа: {e}", file=sys.stderr)
|
||||
print(f"Полный ответ: {data}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
usage = data.get("usage", {})
|
||||
finish = data["choices"][0].get("finish_reason", "unknown")
|
||||
print(
|
||||
f"✅ Ответ получен. Токены: вход={usage.get('prompt_tokens', 0)}, "
|
||||
f"выход={usage.get('completion_tokens', 0)}"
|
||||
f"выход={usage.get('completion_tokens', 0)}, "
|
||||
f"finish={finish}"
|
||||
)
|
||||
|
||||
return result
|
||||
|
||||
Reference in New Issue
Block a user