# -*- coding: utf-8 -*- """Генерация полного проекта по ТЗ (№2): структура, файлы, тесты, прогон. Модель генерирует проект как JSON {"files": {"имя.py": "код", ...}}, пишет файлы в папку, затем гоняет unittest-тесты и при падении чинит. Запуск: venv/Scripts/python.exe project.py "FastAPI-приложение для заметок с SQLite" --out myapp [--code deepseek|lmstudio|local] """ import json import os import re import subprocess import sys from brain import pick, generate def parse_json(text): m = re.search(r"\{.*\}", text, re.DOTALL) if not m: return None try: d = json.loads(m.group(0)) return d if isinstance(d, dict) else None except Exception: return None def run_project_tests(path): try: r = subprocess.run([sys.executable, "-m", "unittest", "discover", "-s", path], capture_output=True, text=True, timeout=120) return r.returncode == 0, (r.stdout or "") + (r.stderr or "") except Exception as e: return False, f"тесты: {e}" def main(): args = sys.argv[1:] outdir = "generated_project" backend = None parts = [] i = 0 while i < len(args): if args[i] == "--out" and i + 1 < len(args): outdir = args[i + 1]; i += 2; continue if args[i] == "--code" and i + 1 < len(args): backend = args[i + 1]; i += 2; continue parts.append(args[i]); i += 1 prompt = " ".join(parts) or "Консольный TODO-менеджер с сохранением в JSON" backend = pick(backend) msgs = [{"role": "system", "content": "Ты — senior-разработчик. Проектируй и пиши рабочий код без заглушек."}, {"role": "user", "content": f"Сгенерируй проект по ТЗ: {prompt}\n\nВерни СТРОГО JSON: " f"{{\"files\": {{\"<имя.py>\": \"<полный код>\", ...}}}}. Включи " f"основной модуль, вспомогательные модули (при нужде) и файл " f"test_*.py с unittest-тестами. Без пояснений вне JSON."}] data = None for _ in range(3): raw = generate(backend, msgs, 3072) data = parse_json(raw) if data and data.get("files"): break if not data or not data.get("files"): print("[!] не удалось получить валидный JSON-проект. Попробуй ещё раз.") sys.exit(1) os.makedirs(outdir, exist_ok=True) for name, content in data["files"].items(): safe = os.path.basename(name) # защита от путей вроде ../../x with open(os.path.join(outdir, safe), "w", encoding="utf-8") as f: f.write(content) print(f"[*] проект создан в {outdir}: {len(data['files'])} файлов") ok, out = run_project_tests(outdir) if ok: print("[OK] тесты проекта прошли") else: print(f"[!] тесты падают:\n{out[-600:]}") if __name__ == "__main__": main()