false, 'error' => 'Шаг 1: Папка uploads не найдена']); exit; } $files = array_filter(scandir($uploads_dir), function($f) use ($uploads_dir) { return is_file($uploads_dir . '/' . $f); }); $projectContext = ""; foreach ($files as $f) { $projectContext .= "=== НАЧАЛО ФАЙЛА: $f ===\n" . file_get_contents($uploads_dir . '/' . $f) . "\n=== КОНЕЦ ФАЙЛА: $f ===\n\n"; } $systemPrompt = "Ты веб-разработчик. Задача: $prompt\nТекущие файлы:\n$projectContext\nИзмени код и верни файлы ПОЛНОСТЬЮ. Строго используй теги:\n[START_FILE: имя]\nкод\n[END_FILE: имя]"; $postData = json_encode([ 'model' => 'qwen2.5-coder:14b', 'prompt' => $systemPrompt, 'stream' => false, 'options' => [ 'temperature' => 0, 'num_ctx' => 8192 ] ]); $fp = @fsockopen('127.0.0.1', 11434, $errno, $errstr, 10); if (!$fp) { echo json_encode(['success' => false, 'error' => "Не удалось подключиться к Ollama на 127.0.0.1:11434. Ошибка: $errstr ($errno)"]); exit; } // Запрашиваем через HTTP/1.0, чтобы исключить chunked transfer encoding на бэкенде $out = "POST /api/generate HTTP/1.0\r\n"; $out .= "Host: 127.0.0.1\r\n"; $out .= "Content-Type: application/json\r\n"; $out .= "Content-Length: " . strlen($postData) . "\r\n"; $out .= "Connection: close\r\n\r\n"; $out .= $postData; fwrite($fp, $out); $response = ''; while (!feof($fp)) { $response .= fgets($fp, 4096); } fclose($fp); // Гарантированно отрезаем заголовки по двойному переносу строки $parts = explode("\r\n\r\n", $response, 2); $body = isset($parts[1]) ? trim($parts[1]) : ''; if (empty($body)) { // Пробуем отрезать по одиночным \n на случай Unix-формата заголовков сервера $parts = explode("\n\n", $response, 2); $body = isset($parts[1]) ? trim($parts[1]) : ''; } // Разбор JSON $responseData = json_decode($body, true); $aiText = isset($responseData['response']) ? $responseData['response'] : ''; if (empty($aiText)) { echo json_encode([ 'success' => false, 'error' => 'Шаг 4: Не удалось распарсить JSON от Ollama.', 'debug_raw_body' => substr($body, 0, 300) ]); exit; } // Шаг 5: Парсинг маркеров [START_FILE] $parsedFiles = []; preg_match_all('/\[START_FILE:\s*([^\]\s]+)\]([\s\S]*?)\[END_FILE:\s*\1\]/', $aiText, $matches, PREG_SET_ORDER); foreach ($matches as $match) { $parsedFiles[] = [ 'name' => trim($match[1]), 'content' => trim($match[2]) ]; } if (empty($parsedFiles)) { echo json_encode([ 'success' => false, 'error' => 'Шаг 5: Модель проигнорировала маркеры [START_FILE] и выдала обычный текст.', 'ai_raw_text' => substr($aiText, 0, 300) ]); exit; } echo json_encode(['success' => true, 'files' => $parsedFiles]); exit; } if ($action === 'save') { $filesToSave = isset($input['files']) ? $input['files'] : []; foreach ($filesToSave as $f) { $fileName = basename(trim($f['name'])); file_put_contents($uploads_dir . '/' . $fileName, $f['content']); } echo json_encode(['success' => true]); exit; } echo json_encode(['success' => false, 'error' => 'Неверное действие']);