2026-10-02 19:30:49 +07:00

192 lines
8.5 KiB
C++
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include <iostream>
#include <chrono>
#include <thread>
#include <vector>
#include <string>
#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<std::string> 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<std::string> 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;
}