/* * UncloseAI C++ Library using Boost.Beast * OpenAI-compatible API client with HTTP/HTTPS support * Compatible with vLLM, Ollama, and OpenAI-compatible endpoints */ #include #include #include #include #include #include #include #include #include #include #include #include #include namespace beast = boost::beast; namespace http = beast::http; namespace net = boost::asio; namespace ssl = net::ssl; using tcp = net::ip::tcp; struct ModelInfo { std::string id; std::string host; std::string port; std::string base_path; int max_tokens; }; class UncloseAI { private: std::vector models; std::vector> tts_endpoints; // host, port int timeout; void parse_url(const std::string& url, std::string& host, std::string& port, std::string& path) { // Parse https://host:port/path size_t proto_end = url.find("://"); if (proto_end == std::string::npos) return; std::string rest = url.substr(proto_end + 3); size_t slash = rest.find("/"); std::string host_port = (slash != std::string::npos) ? rest.substr(0, slash) : rest; path = (slash != std::string::npos) ? rest.substr(slash) : "/v1"; size_t colon = host_port.find(":"); if (colon != std::string::npos) { host = host_port.substr(0, colon); port = host_port.substr(colon + 1); } else { host = host_port; port = (url.find("https://") == 0) ? "443" : "80"; } } std::string http_get(const std::string& host, const std::string& port, const std::string& target) { try { net::io_context ioc; tcp::resolver resolver(ioc); beast::tcp_stream stream(ioc); auto const results = resolver.resolve(host, port); stream.connect(results); http::request req{http::verb::get, target, 11}; req.set(http::field::host, host); req.set(http::field::user_agent, "UncloseAI-Beast"); http::write(stream, req); beast::flat_buffer buffer; http::response res; http::read(stream, buffer, res); beast::error_code ec; stream.socket().shutdown(tcp::socket::shutdown_both, ec); return res.body(); } catch (...) { return ""; } } std::string http_post(const std::string& host, const std::string& port, const std::string& target, const std::string& body) { try { net::io_context ioc; tcp::resolver resolver(ioc); beast::tcp_stream stream(ioc); auto const results = resolver.resolve(host, port); stream.connect(results); http::request req{http::verb::post, target, 11}; req.set(http::field::host, host); req.set(http::field::user_agent, "UncloseAI-Beast"); req.set(http::field::content_type, "application/json"); req.body() = body; req.prepare_payload(); http::write(stream, req); beast::flat_buffer buffer; http::response res; http::read(stream, buffer, res); beast::error_code ec; stream.socket().shutdown(tcp::socket::shutdown_both, ec); return res.body(); } catch (...) { return ""; } } void discover_models_from_endpoint(const std::string& endpoint) { std::string host, port, base_path; parse_url(endpoint, host, port, base_path); std::string target = base_path + (base_path.back() == '/' ? "models" : "/models"); std::string response = http_get(host, port, target); if (!response.empty()) { // Simple JSON parsing for model IDs size_t pos = 0; while ((pos = response.find("\"id\":\"", pos)) != std::string::npos) { pos += 6; size_t end = response.find("\"", pos); if (end != std::string::npos) { std::string model_id = response.substr(pos, end - pos); // Filter out modelperm-* and chatcmpl-* if (model_id.substr(0, 10) != "modelperm-" && model_id.substr(0, 9) != "chatcmpl-") { ModelInfo info; info.id = model_id; info.host = host; info.port = port; info.base_path = base_path; info.max_tokens = 8192; models.push_back(info); } } pos = end + 1; } } } public: UncloseAI(int timeout_sec = 30) : timeout(timeout_sec) { std::vector endpoints, tts_eps; // Discover endpoints from environment for (int i = 1; i < 10000; i++) { std::string var = "MODEL_ENDPOINT_" + std::to_string(i); const char* ep = std::getenv(var.c_str()); if (!ep) break; endpoints.push_back(ep); } for (int i = 1; i < 10000; i++) { std::string var = "TTS_ENDPOINT_" + std::to_string(i); const char* ep = std::getenv(var.c_str()); if (!ep) break; tts_eps.push_back(ep); } for (const auto& ep : endpoints) { std::cout << "Discovering from: " << ep << std::endl; discover_models_from_endpoint(ep); } for (const auto& ep : tts_eps) { std::string host, port, path; parse_url(ep, host, port, path); tts_endpoints.push_back({host, port}); } std::cout << "\nDiscovered " << models.size() << " model(s)\n" << std::endl; } const std::vector& list_models() const { return models; } int chat(const std::string& prompt, std::string& response, int model_idx = 0, int max_tokens = 100) { if (model_idx >= static_cast(models.size())) return -1; const ModelInfo& model = models[model_idx]; std::string target = model.base_path + (model.base_path.back() == '/' ? "chat/completions" : "/chat/completions"); std::ostringstream json; json << "{\"model\":\"" << model.id << "\"," << "\"messages\":[{\"role\":\"user\",\"content\":\"" << prompt << "\"}]," << "\"stream\":false," << "\"max_tokens\":" << max_tokens << "," << "\"temperature\":0.7}"; response = http_post(model.host, model.port, target, json.str()); return response.empty() ? -1 : 0; } int tts(const std::string& text, const std::string& voice, const std::string& output_file) { if (tts_endpoints.empty()) return -1; auto [host, port] = tts_endpoints[0]; std::ostringstream json; json << "{\"model\":\"tts-1\"," << "\"voice\":\"" << voice << "\"," << "\"input\":\"" << text << "\"}"; std::string response = http_post(host, port, "/audio/speech", json.str()); if (!response.empty()) { std::ofstream file(output_file, std::ios::binary); if (file.is_open()) { file.write(response.c_str(), response.size()); file.close(); return 0; } } return -1; } }; int main() { std::cout << "=== UncloseAI C++ Client (Boost.Beast) ===\n\n"; UncloseAI client(30); if (client.list_models().empty()) { std::cout << "ERROR: No models discovered. Set environment variables:\n"; std::cout << " MODEL_ENDPOINT_1, MODEL_ENDPOINT_2, etc.\n"; return 1; } auto models = client.list_models(); for (const auto& m : models) { std::cout << " - " << m.id << " (max_tokens: " << m.max_tokens << ")\n"; } std::cout << "\n"; // Non-streaming chat std::cout << "=== Non-Streaming Chat ===\n"; std::string response; if (client.chat("Explain quantum computing in one sentence", response) == 0) { std::cout << "Response received (" << response.size() << " bytes)\n\n"; } else { std::cout << "Request failed\n\n"; } // TTS std::cout << "=== TTS Speech Generation ===\n"; if (client.tts("Hello from Boost.Beast!", "alloy", "/tmp/speech.mp3") == 0) { std::cout << "Audio saved to /tmp/speech.mp3\n"; } else { std::cout << "TTS failed\n"; } std::cout << "\n=== Examples Complete ===\n"; return 0; }