#include #include #include #include #include #include "./xenith/libs/cpp-httplib-0.57.1/httplib.hpp" #include "./xenith/libs/json-3.12.0/nlohmann/json.hpp" using json = nlohmann::json; void log_ollama_options(const std::string& request_body) { try { auto j = json::parse(request_body); if (j.contains("options") && j["options"].is_object()) { std::cout << "\n🎛️ [НАСТРОЙКИ МОДЕЛИ] Получены пользовательские параметры:\n"; auto options = j["options"]; for (auto& [key, value] : options.items()) { std::cout << " • " << key << " = " << value << "\n"; } std::cout << "-----------------------------------\n\n"; } else { std::cout << "ℹ️ [НАСТРОЙКИ МОДЕЛИ] Параметры не переданы (используются дефолтные).\n\n"; } } catch (const std::exception& e) { std::cout << "⚠️ [ОШИБКА JSON] Не удалось распарсить тело запроса: " << e.what() << "\n"; } } int main_() { httplib::Server svr; svr.set_logger([](const httplib::Request& req, const httplib::Response& res) { std::cout << "[ЛОГ] " << req.method << " " << req.path << " -> Статус: " << res.status << "\n"; if (!req.body.empty() && req.path != "/api/generate" && req.path != "/api/chat") { std::cout << "[ЛОГ] Тело запроса: " << req.body << "\n"; } std::cout << "-----------------------------------\n"; }); svr.Get("/api/tags", [](const httplib::Request& req, httplib::Response& res) { std::string json_response = R"({ "models": [ { "name": "BiPy:latest", "model": "BiPy:latest", "modified_at": "2026-09-25T16:45:00.1234567Z", "size": 4661224679, "digest": "365c0bd3c000a256fbf3c88083a2166cb9e931444146a7826317d745d0b4b2cb", "details": { "parent_model": "", "format": "gguf", "family": "llama", "families": ["llama"], "parameter_size": "8.0B", "quantization_level": "FP16" } } ] })"; res.set_content(json_response, "application/json"); }); // 2. Максимальный список запущенных моделей svr.Get("/api/ps", [](const httplib::Request& req, httplib::Response& res) { std::string json_response = R"({ "models": [ { "name": "BiPy:latest", "model": "BiPy:latest", "size": 4661224679, "digest": "365c0bd3c000a256fbf3c88083a2166cb9e931444146a7826317d745d0b4b2cb", "details": { "parent_model": "", "format": "gguf", "family": "llama", "families": ["llama"], "parameter_size": "8.0B", "quantization_level": "FP16" }, "expires_at": "2026-09-25T17:15:00Z", "size_vram": 4661224679 } ] })"; res.set_content(json_response, "application/json"); }); // 3. Версия сервера svr.Get("/api/version", [](const httplib::Request& req, httplib::Response& res) { res.set_content(R"({"version": "0.1.48"})", "application/json"); }); // 4. Полноценный эндпоинт генерации текста (POST /api/generate) svr.Post("/api/generate", [](const httplib::Request& req, httplib::Response& res) { std::cout << "-> Сработал POST /api/generate. Нейросеть BiPy генерирует ответ...\n"; // Разбираем и выводим опции в консоль log_ollama_options(req.body); bool is_stream = (req.body.find("\"stream\": false") == std::string::npos); if (is_stream) { std::vector tokens = { "Привет!", " Я", " полнофункциональная", " тестовая", " модель", " BiPy.", " Настройки", " успешно", " выведены." }; res.set_chunked_content_provider("application/x-ndjson", [tokens](size_t offset, httplib::DataSink &sink) { if (offset < tokens.size()) { std::this_thread::sleep_for(std::chrono::milliseconds(100)); bool is_done = (offset == tokens.size() - 1); std::string chunk; if (!is_done) { chunk = "{\"model\":\"BiPy:latest\",\"created_at\":\"2026-09-25T16:45:10.123456Z\",\"response\":\"" + tokens[offset] + "\",\"done\":false}\n"; } else { chunk = "{\"model\":\"BiPy:latest\",\"created_at\":\"2026-09-25T16:45:11.123456Z\",\"response\":\"" + tokens[offset] + "\",\"done\":true," "\"context\":[1,2,3]," "\"total_duration\":1500000000,\"load_duration\":5000000,\"prompt_eval_count\":12," "\"prompt_eval_duration\":120000000,\"eval_count\":9,\"eval_duration\":880000000}\n"; } sink.write(chunk.c_str(), chunk.size()); return true; } sink.done(); return true; } ); } else { // ИСПРАВЛЕНО: Заменено `"context":,` на `"context":[]` std::string full_response = R"({ "model": "BiPy:latest", "created_at": "2026-09-25T16:45:10.123456Z", "response": "Привет! Я полнофункциональная тестовая модель BiPy. Настройки прочитаны!", "done": true, "context": [], "total_duration": 1500000000, "load_duration": 4500000, "prompt_eval_count": 10, "prompt_eval_duration": 100000000, "eval_count": 15, "eval_duration": 1300000000 })"; res.set_content(full_response, "application/x-ndjson"); } }); // 5. Полноценный эндпоинт чата (POST /api/chat) svr.Post("/api/chat", [](const httplib::Request& req, httplib::Response& res) { std::cout << "-> Сработал POST /api/chat. Имитируем продвинутый диалог...\n"; // Разбираем и выводим опции в консоль log_ollama_options(req.body); std::vector tokens = { "Привет", " в", " режиме", " полноценного", " чата!", " Все", " ползунки", " зафиксированы." }; res.set_chunked_content_provider("application/x-ndjson", [tokens](size_t offset, httplib::DataSink &sink) { if (offset < tokens.size()) { std::this_thread::sleep_for(std::chrono::milliseconds(100)); bool is_done = (offset == tokens.size() - 1); std::string chunk; if (!is_done) { chunk = "{\"model\":\"BiPy:latest\",\"created_at\":\"2026-09-25T16:45:20.123456Z\",\"message\":{\"role\":\"assistant\",\"content\":\"" + tokens[offset] + "\"},\"done\":false}\n"; } else { chunk = "{\"model\":\"BiPy:latest\",\"created_at\":\"2026-09-25T16:45:21.123456Z\",\"message\":{\"role\":\"assistant\",\"content\":\"" + tokens[offset] + "\"},\"done\":true," "\"total_duration\":1200000000,\"load_duration\":4000000,\"prompt_eval_count\":15," "\"prompt_eval_duration\":150000000,\"eval_count\":8,\"eval_duration\":750000000}\n"; } sink.write(chunk.c_str(), chunk.size()); return true; } sink.done(); return true; } ); }); std::cout << "Сервер запущен на http://localhost:11423\n"; if (!svr.listen("0.0.0.0", 11423)) { std::cerr << "Ошибка: Не удалось запустить сервер! Проверьте порт.\n"; return 1; } return 0; }