uncloseai.com/languages/cpp/boost-beast/uncloseai.cpp
Russell Ballestrini 9e3eccb1b9 modified: .gitignore
modified:   CLAUDE.md
	modified:   Makefile
	new file:   languages/awk/Dockerfile
	new file:   languages/awk/uncloseai.awk
	modified:   languages/bash/Dockerfile
	deleted:    languages/bash/examples.sh
	new file:   languages/bash/uncloseai.sh
	new file:   languages/c/curl/Dockerfile
	new file:   languages/c/curl/Makefile
	new file:   languages/c/curl/index.html
	new file:   languages/c/curl/uncloseai.c
	new file:   languages/c/libh2o/Dockerfile
	new file:   languages/c/libh2o/Makefile
	new file:   languages/c/libh2o/uncloseai.c
	new file:   languages/c/nghttp2/Dockerfile
	new file:   languages/c/nghttp2/Makefile
	new file:   languages/c/nghttp2/uncloseai.c
	new file:   languages/clojure/Dockerfile
	new file:   languages/clojure/deps.edn
	new file:   languages/clojure/uncloseai.clj
	new file:   languages/cobol/Dockerfile
	new file:   languages/cobol/discover.sh
	new file:   languages/cobol/hermes.sh
	new file:   languages/cobol/qwen.sh
	new file:   languages/cobol/tts.sh
	new file:   languages/cobol/uncloseai.cob
	new file:   languages/cpp/boost-beast/Dockerfile
	new file:   languages/cpp/boost-beast/Makefile
	new file:   languages/cpp/boost-beast/uncloseai.cpp
	new file:   languages/cpp/cpp-httplib/Dockerfile
	new file:   languages/cpp/cpp-httplib/Makefile
	new file:   languages/cpp/cpp-httplib/uncloseai.cpp
	new file:   languages/cpp/libcurl/Dockerfile
	new file:   languages/cpp/libcurl/Makefile
	new file:   languages/cpp/libcurl/uncloseai.cpp
	new file:   languages/crystal/Dockerfile
	new file:   languages/crystal/uncloseai.cr
	new file:   languages/csharp/Dockerfile
	new file:   languages/csharp/Uncloseai.cs
	new file:   languages/csharp/csharp.csproj
	new file:   languages/dart/Dockerfile
	new file:   languages/dart/bin/uncloseai.dart
	new file:   languages/dart/pubspec.yaml
	new file:   languages/deno/Dockerfile
	new file:   languages/deno/uncloseai.ts
	new file:   languages/elixir/Dockerfile
	new file:   languages/elixir/lib/uncloseai.ex
	new file:   languages/elixir/mix.exs
	new file:   languages/elixir/run.exs
	new file:   languages/erlang/Dockerfile
	new file:   languages/erlang/rebar.config
	new file:   languages/erlang/src/uncloseai.app.src
	new file:   languages/erlang/src/uncloseai.erl
	new file:   languages/fortran/Dockerfile
	new file:   languages/fortran/uncloseai.f90
	new file:   languages/fsharp/Dockerfile
	new file:   languages/fsharp/Uncloseai.fs
	new file:   languages/fsharp/fsharp.fsproj
	new file:   languages/go/Dockerfile
	new file:   languages/go/README.md
	new file:   languages/go/examples/basic.go
	new file:   languages/go/go.mod
	new file:   languages/go/uncloseai.go
	new file:   languages/go/uncloseai/uncloseai.go
	new file:   languages/haskell/Dockerfile
	new file:   languages/haskell/UncloseAI.hs
	new file:   languages/haskell/uncloseai.cabal
	new file:   languages/java/Dockerfile
	new file:   languages/java/UncloseAI.java
	new file:   languages/javascript/bun/Dockerfile
	new file:   languages/javascript/bun/uncloseai.ts
	new file:   languages/javascript/nodejs/Dockerfile
	new file:   languages/javascript/nodejs/README.md
	new file:   languages/javascript/nodejs/package.json
	new file:   languages/javascript/nodejs/uncloseai.js
	new file:   languages/javascript/typescript/Dockerfile
	new file:   languages/javascript/typescript/package.json
	new file:   languages/javascript/typescript/tsconfig.json
	new file:   languages/javascript/typescript/uncloseai.ts
	new file:   languages/javascript/vanilla/Dockerfile
	new file:   languages/javascript/vanilla/uncloseai.html
	new file:   languages/julia/Dockerfile
	new file:   languages/julia/Project.toml
	new file:   languages/julia/src/uncloseai.jl
	new file:   languages/kotlin/Dockerfile
	new file:   languages/kotlin/build.gradle.kts
	new file:   languages/kotlin/src/main/kotlin/UncloseAI.kt
	new file:   languages/kotlin/uncloseai.kt
	new file:   languages/lua/Dockerfile
	new file:   languages/lua/uncloseai.lua
	new file:   languages/nim/Dockerfile
	new file:   languages/nim/uncloseai.nim
	new file:   languages/ocaml/Dockerfile
	new file:   languages/ocaml/dune
	new file:   languages/ocaml/dune-project
	new file:   languages/ocaml/uncloseai.ml
	new file:   languages/odin/Dockerfile
	new file:   languages/odin/uncloseai.odin
	new file:   languages/perl/Dockerfile
	new file:   languages/perl/uncloseai.pl
	new file:   languages/php/Dockerfile
	new file:   languages/php/uncloseai.php
	new file:   languages/powershell/Dockerfile
	new file:   languages/powershell/uncloseai.ps1
	new file:   languages/prolog/Dockerfile
	new file:   languages/prolog/uncloseai.pl
	new file:   languages/python/aiohttp/Dockerfile
	new file:   languages/python/aiohttp/requirements.txt
	new file:   languages/python/aiohttp/uncloseai.py
	modified:   languages/python/httpx-async/Dockerfile
	deleted:    languages/python/httpx-async/examples.py
	new file:   languages/python/httpx-async/uncloseai.py
	modified:   languages/python/openai-client/Dockerfile
	deleted:    languages/python/openai-client/examples.py
	new file:   languages/python/openai-client/uncloseai.py
	modified:   languages/python/requests/Dockerfile
	deleted:    languages/python/requests/examples.py
	new file:   languages/python/requests/uncloseai.py
	new file:   languages/r/Dockerfile
	new file:   languages/r/uncloseai.R
	new file:   languages/ruby/Dockerfile
	new file:   languages/ruby/README.md
	new file:   languages/ruby/uncloseai.rb
	new file:   languages/rust/Cargo.toml
	new file:   languages/rust/Dockerfile
	new file:   languages/rust/README.md
	new file:   languages/rust/examples/basic.rs
	new file:   languages/rust/src/lib.rs
	new file:   languages/rust/src/uncloseai.rs
	new file:   languages/scala/Dockerfile
	new file:   languages/scala/build.sbt
	new file:   languages/scala/project/build.properties
	new file:   languages/scala/project/plugins.sbt
	new file:   languages/scala/src/main/scala/UncloseAI.scala
	new file:   languages/tcl/Dockerfile
	new file:   languages/tcl/uncloseai.tcl
	new file:   languages/v/Dockerfile
	new file:   languages/v/uncloseai.v
	new file:   languages/v/v.mod
	new file:   languages/vbnet/Dockerfile
	new file:   languages/vbnet/UncloseAI.vb
	new file:   languages/vbnet/UncloseAI.vbproj
	modified:   languages/zig/Dockerfile
	modified:   languages/zig/build.zig
	deleted:    languages/zig/src/main.zig
	new file:   languages/zig/src/uncloseai.zig
2025-10-14 10:24:16 -04:00

345 lines
11 KiB
C++

/*
* UncloseAI C++ Library
* OpenAI-compatible API client with streaming support
* Compatible with vLLM, Ollama, and OpenAI-compatible endpoints
*/
#include <iostream>
#include <string>
#include <vector>
#include <memory>
#include <fstream>
#include <sstream>
#include <cstdlib>
#include <cstring>
#include <curl/curl.h>
// Model info structure
struct ModelInfo {
std::string id;
std::string endpoint;
int max_tokens;
};
// UncloseAI Client class
class UncloseAIClient {
private:
std::vector<ModelInfo> models;
std::vector<std::string> tts_endpoints;
int timeout;
public:
UncloseAIClient(int timeout_sec = 30) : timeout(timeout_sec) {}
std::vector<ModelInfo> get_models() const { return models; }
std::vector<std::string> get_tts_endpoints() const { return tts_endpoints; }
// Initialize client with model discovery
void init();
// Non-streaming chat
int chat(int model_idx, const std::string& prompt, std::string& response);
// Streaming chat
int chat_stream(int model_idx, const std::string& prompt);
// Text-to-speech
int tts(const std::string& text, const std::string& voice, const std::string& output_file);
private:
void discover_models_from_endpoint(const std::string& endpoint);
};
// Memory callback struct for capturing response data
struct MemoryStruct {
std::string data;
};
// Callback function to capture response data
static size_t WriteMemoryCallback(void *contents, size_t size, size_t nmemb, void *userp) {
size_t realsize = size * nmemb;
MemoryStruct *mem = static_cast<MemoryStruct*>(userp);
mem->data.append(static_cast<char*>(contents), realsize);
return realsize;
}
// Helper to extract model IDs from JSON (simple string search)
void extract_model_ids(const std::string& json, const std::string& endpoint, std::vector<ModelInfo>& models) {
size_t pos = 0;
std::string id_marker = "\"id\":\"";
while((pos = json.find(id_marker, pos)) != std::string::npos) {
pos += id_marker.length();
size_t end = json.find("\"", pos);
if(end != std::string::npos) {
std::string model_id = json.substr(pos, end - pos);
// Filter out modelperm-* entries
if(model_id.find("modelperm-") == 0) {
pos = end + 1;
continue;
}
ModelInfo info;
info.id = model_id;
info.endpoint = endpoint;
info.max_tokens = 8192;
models.push_back(info);
}
pos = end + 1;
}
}
void UncloseAIClient::discover_models_from_endpoint(const std::string& endpoint) {
CURL *curl = curl_easy_init();
if(!curl) return;
std::string url = endpoint + "/models";
MemoryStruct chunk;
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &chunk);
curl_easy_setopt(curl, CURLOPT_TIMEOUT, 10L);
CURLcode res = curl_easy_perform(curl);
if(res == CURLE_OK) {
extract_model_ids(chunk.data, endpoint, models);
}
curl_easy_cleanup(curl);
}
void UncloseAIClient::init() {
std::cout << "Initializing UncloseAI client..." << std::endl;
// Discover chat/code models
for(int i = 1; i < 10000; i++) {
std::string var_name = "MODEL_ENDPOINT_" + std::to_string(i);
const char* endpoint = std::getenv(var_name.c_str());
if(!endpoint) break;
std::cout << "Endpoint " << i << ": " << endpoint << std::endl;
discover_models_from_endpoint(endpoint);
}
// Discover TTS endpoints
for(int i = 1; i < 10000; i++) {
std::string var_name = "TTS_ENDPOINT_" + std::to_string(i);
const char* endpoint = std::getenv(var_name.c_str());
if(!endpoint) break;
tts_endpoints.push_back(endpoint);
}
std::cout << "Discovered " << models.size() << " models, "
<< tts_endpoints.size() << " TTS endpoints\n" << std::endl;
}
int UncloseAIClient::chat(int model_idx, const std::string& prompt, std::string& response) {
if(model_idx >= static_cast<int>(models.size())) return -1;
ModelInfo model = models[model_idx];
std::string url = model.endpoint + "/chat/completions";
std::string json = "{\"model\":\"" + model.id + "\","
"\"messages\":[{\"role\":\"user\",\"content\":\"" + prompt + "\"}],"
"\"stream\":false,\"max_tokens\":100,\"temperature\":0.7}";
CURL *curl = curl_easy_init();
if(!curl) return -1;
struct curl_slist *headers = nullptr;
headers = curl_slist_append(headers, "Content-Type: application/json");
MemoryStruct chunk;
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, json.c_str());
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &chunk);
curl_easy_setopt(curl, CURLOPT_TIMEOUT, timeout);
CURLcode res = curl_easy_perform(curl);
curl_slist_free_all(headers);
curl_easy_cleanup(curl);
if(res == CURLE_OK) {
response = chunk.data;
return 0;
}
return -1;
}
// Streaming context
struct StreamContext {
std::string buffer;
};
static size_t StreamWriteCallback(void *contents, size_t size, size_t nmemb, void *userp) {
size_t realsize = size * nmemb;
StreamContext *ctx = static_cast<StreamContext*>(userp);
std::string data(static_cast<char*>(contents), realsize);
ctx->buffer += data;
size_t pos;
while((pos = ctx->buffer.find('\n')) != std::string::npos) {
std::string line = ctx->buffer.substr(0, pos);
ctx->buffer.erase(0, pos + 1);
if(line.find("data: ") == 0) {
std::string json_data = line.substr(6);
if(json_data == "[DONE]") return 0;
size_t content_pos = json_data.find("\"content\":\"");
if(content_pos != std::string::npos) {
content_pos += 11;
size_t end_pos = json_data.find("\"", content_pos);
if(end_pos != std::string::npos) {
std::string content = json_data.substr(content_pos, end_pos - content_pos);
if(!content.empty()) {
std::cout << content << std::flush;
}
}
}
}
}
return realsize;
}
int UncloseAIClient::chat_stream(int model_idx, const std::string& prompt) {
if(model_idx >= static_cast<int>(models.size())) return -1;
ModelInfo model = models[model_idx];
std::string url = model.endpoint + "/chat/completions";
std::string json = "{\"model\":\"" + model.id + "\","
"\"messages\":[{\"role\":\"user\",\"content\":\"" + prompt + "\"}],"
"\"stream\":true,\"max_tokens\":500,\"temperature\":0.7}";
CURL *curl = curl_easy_init();
if(!curl) return -1;
struct curl_slist *headers = nullptr;
headers = curl_slist_append(headers, "Content-Type: application/json");
StreamContext ctx;
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, json.c_str());
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, StreamWriteCallback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &ctx);
curl_easy_setopt(curl, CURLOPT_TIMEOUT, timeout);
CURLcode res = curl_easy_perform(curl);
curl_slist_free_all(headers);
curl_easy_cleanup(curl);
return (res == CURLE_OK) ? 0 : -1;
}
int UncloseAIClient::tts(const std::string& text, const std::string& voice, const std::string& output_file) {
if(tts_endpoints.empty()) return -1;
std::string url = tts_endpoints[0] + "/audio/speech";
std::string json = "{\"model\":\"tts-1\",\"voice\":\"" + voice + "\",\"input\":\"" + text + "\"}";
CURL *curl = curl_easy_init();
if(!curl) return -1;
struct curl_slist *headers = nullptr;
headers = curl_slist_append(headers, "Content-Type: application/json");
MemoryStruct chunk;
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, json.c_str());
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &chunk);
curl_easy_setopt(curl, CURLOPT_TIMEOUT, timeout);
CURLcode res = curl_easy_perform(curl);
curl_slist_free_all(headers);
curl_easy_cleanup(curl);
if(res == CURLE_OK) {
std::ofstream output(output_file, std::ios::binary);
if(output.is_open()) {
output.write(chunk.data.c_str(), chunk.data.size());
output.close();
return 0;
}
}
return -1;
}
// Demo program showing library usage
int main() {
std::cout << "=== UncloseAI C++ Client (with Streaming) ===\n" << std::endl;
curl_global_init(CURL_GLOBAL_ALL);
// Initialize client
UncloseAIClient client(30);
client.init();
if(client.get_models().empty()) {
std::cout << "ERROR: No models discovered" << std::endl;
curl_global_cleanup();
return 1;
}
// Non-streaming chat example
std::cout << "=== Non-Streaming Chat ===" << std::endl;
std::cout << "Model: " << client.get_models()[0].id << std::endl;
std::string response;
if(client.chat(0, "Explain quantum computing in one sentence", response) == 0) {
std::cout << "Response received (" << response.size() << " bytes)" << std::endl;
std::cout << "(Full response requires JSON parsing library)" << std::endl;
} else {
std::cout << "Request failed" << std::endl;
}
std::cout << std::endl;
// Streaming chat example
int model_idx = client.get_models().size() >= 2 ? 1 : 0;
std::cout << "=== Streaming Chat ===" << std::endl;
std::cout << "Model: " << client.get_models()[model_idx].id << std::endl;
std::cout << "Response: ";
if(client.chat_stream(model_idx, "Write a hello world program in C++") != 0) {
std::cout << std::endl << "Streaming request failed" << std::endl;
}
std::cout << "\n" << std::endl;
// TTS example
if(!client.get_tts_endpoints().empty()) {
std::cout << "=== TTS Speech Generation ===" << std::endl;
std::cout << "Model: tts-1" << std::endl;
if(client.tts("Hello from UncloseAI C++ client!", "alloy", "/tmp/speech.mp3") == 0) {
std::cout << "Audio saved to /tmp/speech.mp3" << std::endl;
} else {
std::cout << "TTS failed" << std::endl;
}
}
std::cout << "\n=== Examples Complete ===" << std::endl;
curl_global_cleanup();
return 0;
}