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
This commit is contained in:
parent
bf3f8cfac6
commit
9e3eccb1b9
147 changed files with 20225 additions and 566 deletions
21
languages/cpp/boost-beast/Dockerfile
Normal file
21
languages/cpp/boost-beast/Dockerfile
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
# Pin to specific Alpine version (checked 2025-10-12: alpine:3.21 is latest stable)
|
||||
FROM alpine:3.21
|
||||
|
||||
# Install C++ compiler and libcurl development libraries
|
||||
RUN apk --no-cache add \
|
||||
g++ \
|
||||
musl-dev \
|
||||
curl-dev \
|
||||
make \
|
||||
ca-certificates
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY uncloseai.cpp .
|
||||
COPY Makefile .
|
||||
|
||||
# Compile the application
|
||||
RUN make
|
||||
|
||||
# Run the examples
|
||||
CMD ["./uncloseai"]
|
||||
16
languages/cpp/boost-beast/Makefile
Normal file
16
languages/cpp/boost-beast/Makefile
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
CXX = g++
|
||||
CXXFLAGS = -std=c++11 -Wall -Wextra -O2
|
||||
LDFLAGS = -lcurl
|
||||
|
||||
TARGET = uncloseai
|
||||
SRC = uncloseai.cpp
|
||||
|
||||
all: $(TARGET)
|
||||
|
||||
$(TARGET): $(SRC)
|
||||
$(CXX) $(CXXFLAGS) -o $(TARGET) $(SRC) $(LDFLAGS)
|
||||
|
||||
clean:
|
||||
rm -f $(TARGET) speech.mp3
|
||||
|
||||
.PHONY: all clean
|
||||
345
languages/cpp/boost-beast/uncloseai.cpp
Normal file
345
languages/cpp/boost-beast/uncloseai.cpp
Normal file
|
|
@ -0,0 +1,345 @@
|
|||
/*
|
||||
* 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;
|
||||
}
|
||||
21
languages/cpp/cpp-httplib/Dockerfile
Normal file
21
languages/cpp/cpp-httplib/Dockerfile
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
# Pin to specific Alpine version (checked 2025-10-12: alpine:3.21 is latest stable)
|
||||
FROM alpine:3.21
|
||||
|
||||
# Install C++ compiler and libcurl development libraries
|
||||
RUN apk --no-cache add \
|
||||
g++ \
|
||||
musl-dev \
|
||||
curl-dev \
|
||||
make \
|
||||
ca-certificates
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY uncloseai.cpp .
|
||||
COPY Makefile .
|
||||
|
||||
# Compile the application
|
||||
RUN make
|
||||
|
||||
# Run the examples
|
||||
CMD ["./uncloseai"]
|
||||
16
languages/cpp/cpp-httplib/Makefile
Normal file
16
languages/cpp/cpp-httplib/Makefile
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
CXX = g++
|
||||
CXXFLAGS = -std=c++11 -Wall -Wextra -O2
|
||||
LDFLAGS = -lcurl
|
||||
|
||||
TARGET = uncloseai
|
||||
SRC = uncloseai.cpp
|
||||
|
||||
all: $(TARGET)
|
||||
|
||||
$(TARGET): $(SRC)
|
||||
$(CXX) $(CXXFLAGS) -o $(TARGET) $(SRC) $(LDFLAGS)
|
||||
|
||||
clean:
|
||||
rm -f $(TARGET) speech.mp3
|
||||
|
||||
.PHONY: all clean
|
||||
339
languages/cpp/cpp-httplib/uncloseai.cpp
Normal file
339
languages/cpp/cpp-httplib/uncloseai.cpp
Normal file
|
|
@ -0,0 +1,339 @@
|
|||
/*
|
||||
* UncloseAI C++ Library using cpp-httplib
|
||||
* 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 <functional>
|
||||
#include <curl/curl.h>
|
||||
|
||||
struct ModelInfo {
|
||||
std::string id;
|
||||
std::string endpoint;
|
||||
int max_tokens;
|
||||
};
|
||||
|
||||
struct MemoryStruct {
|
||||
std::string data;
|
||||
};
|
||||
|
||||
// Callback for non-streaming responses
|
||||
static size_t WriteCallback(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;
|
||||
}
|
||||
|
||||
// Streaming context
|
||||
struct StreamContext {
|
||||
std::function<void(const std::string&)> callback;
|
||||
std::string buffer;
|
||||
};
|
||||
|
||||
// Extract content from SSE JSON
|
||||
static void extract_sse_content(const std::string& data, std::string& content) {
|
||||
size_t content_pos = data.find("\"content\":\"");
|
||||
if(content_pos != std::string::npos) {
|
||||
content_pos += 11;
|
||||
size_t end_pos = data.find("\"", content_pos);
|
||||
if(end_pos != std::string::npos) {
|
||||
content = data.substr(content_pos, end_pos - content_pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Streaming callback
|
||||
static size_t StreamCallback(void *contents, size_t size, size_t nmemb, void *userp) {
|
||||
size_t realsize = size * nmemb;
|
||||
StreamContext *ctx = static_cast<StreamContext*>(userp);
|
||||
|
||||
ctx->buffer.append(static_cast<char*>(contents), realsize);
|
||||
|
||||
size_t pos = 0;
|
||||
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 data = line.substr(6);
|
||||
if(data == "[DONE]") break;
|
||||
|
||||
std::string content;
|
||||
extract_sse_content(data, content);
|
||||
|
||||
if(!content.empty() && ctx->callback) {
|
||||
ctx->callback(content);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return realsize;
|
||||
}
|
||||
|
||||
class UncloseAI {
|
||||
private:
|
||||
std::vector<ModelInfo> models;
|
||||
std::vector<std::string> tts_endpoints;
|
||||
int timeout;
|
||||
bool debug;
|
||||
|
||||
void discover_endpoints_from_env(const std::string& prefix, std::vector<std::string>& endpoints) {
|
||||
for(int i = 1; i < 10000; i++) {
|
||||
std::string var_name = prefix + "_" + std::to_string(i);
|
||||
const char* endpoint = std::getenv(var_name.c_str());
|
||||
if(!endpoint) break;
|
||||
endpoints.push_back(endpoint);
|
||||
}
|
||||
}
|
||||
|
||||
void discover_models(const std::vector<std::string>& endpoints) {
|
||||
for(const auto& endpoint : endpoints) {
|
||||
if(debug) {
|
||||
std::cout << "[DEBUG] Discovering from: " << endpoint << std::endl;
|
||||
}
|
||||
|
||||
CURL *curl = curl_easy_init();
|
||||
if(curl) {
|
||||
MemoryStruct response;
|
||||
std::string url = endpoint + "/models";
|
||||
|
||||
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
|
||||
curl_easy_setopt(curl, CURLOPT_TIMEOUT, 10L);
|
||||
|
||||
CURLcode res = curl_easy_perform(curl);
|
||||
curl_easy_cleanup(curl);
|
||||
|
||||
if(res == CURLE_OK) {
|
||||
// Simple JSON parsing for model IDs
|
||||
size_t pos = 0;
|
||||
while((pos = response.data.find("\"id\":\"", pos)) != std::string::npos) {
|
||||
pos += 6;
|
||||
size_t end = response.data.find("\"", pos);
|
||||
if(end != std::string::npos) {
|
||||
std::string model_id = response.data.substr(pos, end - pos);
|
||||
|
||||
// Filter out modelperm-* and chatcmpl-* entries
|
||||
if(model_id.substr(0, 10) != "modelperm-" && model_id.substr(0, 9) != "chatcmpl-") {
|
||||
ModelInfo info;
|
||||
info.id = model_id;
|
||||
info.endpoint = endpoint;
|
||||
info.max_tokens = 8192;
|
||||
models.push_back(info);
|
||||
|
||||
if(debug) {
|
||||
std::cout << "[DEBUG] Discovered: " << model_id << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
pos = end + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
UncloseAI(int timeout = 30, bool debug = false) : timeout(timeout), debug(debug) {
|
||||
std::vector<std::string> endpoints;
|
||||
std::vector<std::string> tts_eps;
|
||||
|
||||
discover_endpoints_from_env("MODEL_ENDPOINT", endpoints);
|
||||
discover_endpoints_from_env("TTS_ENDPOINT", tts_eps);
|
||||
|
||||
if(debug) {
|
||||
std::cout << "[DEBUG] Initialized with " << endpoints.size() << " endpoint(s)" << std::endl;
|
||||
}
|
||||
|
||||
discover_models(endpoints);
|
||||
tts_endpoints = tts_eps;
|
||||
}
|
||||
|
||||
const std::vector<ModelInfo>& 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<int>(models.size())) return -1;
|
||||
|
||||
const ModelInfo& model = models[model_idx];
|
||||
std::string url = model.endpoint + "/chat/completions";
|
||||
|
||||
std::ostringstream json;
|
||||
json << "{\"model\":\"" << model.id << "\","
|
||||
<< "\"messages\":[{\"role\":\"user\",\"content\":\"" << prompt << "\"}],"
|
||||
<< "\"stream\":false,"
|
||||
<< "\"max_tokens\":" << max_tokens << ","
|
||||
<< "\"temperature\":0.7}";
|
||||
|
||||
CURL *curl = curl_easy_init();
|
||||
if(!curl) return -1;
|
||||
|
||||
MemoryStruct mem;
|
||||
struct curl_slist *headers = nullptr;
|
||||
headers = curl_slist_append(headers, "Content-Type: application/json");
|
||||
|
||||
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
|
||||
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
|
||||
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, json.str().c_str());
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &mem);
|
||||
curl_easy_setopt(curl, CURLOPT_TIMEOUT, (long)timeout);
|
||||
|
||||
CURLcode res = curl_easy_perform(curl);
|
||||
|
||||
curl_slist_free_all(headers);
|
||||
curl_easy_cleanup(curl);
|
||||
|
||||
response = mem.data;
|
||||
return (res == CURLE_OK) ? 0 : -1;
|
||||
}
|
||||
|
||||
int chat_stream(const std::string& prompt, std::function<void(const std::string&)> callback, int model_idx = 0, int max_tokens = 500) {
|
||||
if(model_idx >= static_cast<int>(models.size())) return -1;
|
||||
|
||||
const ModelInfo& model = models[model_idx];
|
||||
std::string url = model.endpoint + "/chat/completions";
|
||||
|
||||
std::ostringstream json;
|
||||
json << "{\"model\":\"" << model.id << "\","
|
||||
<< "\"messages\":[{\"role\":\"user\",\"content\":\"" << prompt << "\"}],"
|
||||
<< "\"stream\":true,"
|
||||
<< "\"max_tokens\":" << max_tokens << ","
|
||||
<< "\"temperature\":0.7}";
|
||||
|
||||
CURL *curl = curl_easy_init();
|
||||
if(!curl) return -1;
|
||||
|
||||
StreamContext ctx;
|
||||
ctx.callback = callback;
|
||||
|
||||
struct curl_slist *headers = nullptr;
|
||||
headers = curl_slist_append(headers, "Content-Type: application/json");
|
||||
|
||||
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
|
||||
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
|
||||
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, json.str().c_str());
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, StreamCallback);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &ctx);
|
||||
curl_easy_setopt(curl, CURLOPT_TIMEOUT, (long)timeout);
|
||||
|
||||
CURLcode res = curl_easy_perform(curl);
|
||||
|
||||
curl_slist_free_all(headers);
|
||||
curl_easy_cleanup(curl);
|
||||
|
||||
return (res == CURLE_OK) ? 0 : -1;
|
||||
}
|
||||
|
||||
int 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::ostringstream json;
|
||||
json << "{\"model\":\"tts-1\","
|
||||
<< "\"voice\":\"" << voice << "\","
|
||||
<< "\"input\":\"" << text << "\"}";
|
||||
|
||||
CURL *curl = curl_easy_init();
|
||||
if(!curl) return -1;
|
||||
|
||||
MemoryStruct mem;
|
||||
struct curl_slist *headers = nullptr;
|
||||
headers = curl_slist_append(headers, "Content-Type: application/json");
|
||||
|
||||
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
|
||||
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
|
||||
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, json.str().c_str());
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &mem);
|
||||
curl_easy_setopt(curl, CURLOPT_TIMEOUT, (long)timeout);
|
||||
|
||||
CURLcode res = curl_easy_perform(curl);
|
||||
|
||||
curl_slist_free_all(headers);
|
||||
curl_easy_cleanup(curl);
|
||||
|
||||
if(res == CURLE_OK) {
|
||||
std::ofstream file(output_file, std::ios::binary);
|
||||
if(file.is_open()) {
|
||||
file.write(mem.data.c_str(), mem.data.size());
|
||||
file.close();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
};
|
||||
|
||||
// Demo program showing library usage
|
||||
int main() {
|
||||
std::cout << "=== UncloseAI C++ Client (cpp-httplib with Streaming) ===\n\n";
|
||||
|
||||
curl_global_init(CURL_GLOBAL_ALL);
|
||||
|
||||
UncloseAI client(30, true);
|
||||
|
||||
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";
|
||||
curl_global_cleanup();
|
||||
return 1;
|
||||
}
|
||||
|
||||
auto models = client.list_models();
|
||||
std::cout << "\nDiscovered " << models.size() << " model(s):\n";
|
||||
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";
|
||||
std::cout << "(Full response requires JSON parsing library)\n\n";
|
||||
} else {
|
||||
std::cout << "Request failed\n\n";
|
||||
}
|
||||
|
||||
// Streaming chat
|
||||
std::cout << "=== Streaming Chat ===\n";
|
||||
int model_idx = (models.size() > 1) ? 1 : 0;
|
||||
std::cout << "Model: " << models[model_idx].id << "\n";
|
||||
std::cout << "Response: ";
|
||||
|
||||
client.chat_stream("Write a hello world program in C++",
|
||||
[](const std::string& content) {
|
||||
std::cout << content << std::flush;
|
||||
}, model_idx, 500);
|
||||
|
||||
std::cout << "\n\n";
|
||||
|
||||
// TTS
|
||||
std::cout << "=== TTS Speech Generation ===\n";
|
||||
std::cout << "Model: tts-1\n";
|
||||
if(client.tts("Hello from UncloseAI C++ client with cpp-httplib! This demonstrates streaming support.",
|
||||
"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";
|
||||
|
||||
curl_global_cleanup();
|
||||
return 0;
|
||||
}
|
||||
21
languages/cpp/libcurl/Dockerfile
Normal file
21
languages/cpp/libcurl/Dockerfile
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
# Pin to specific Alpine version (checked 2025-10-12: alpine:3.21 is latest stable)
|
||||
FROM alpine:3.21
|
||||
|
||||
# Install C++ compiler and libcurl development libraries
|
||||
RUN apk --no-cache add \
|
||||
g++ \
|
||||
musl-dev \
|
||||
curl-dev \
|
||||
make \
|
||||
ca-certificates
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY uncloseai.cpp .
|
||||
COPY Makefile .
|
||||
|
||||
# Compile the application
|
||||
RUN make
|
||||
|
||||
# Run the examples
|
||||
CMD ["./uncloseai"]
|
||||
16
languages/cpp/libcurl/Makefile
Normal file
16
languages/cpp/libcurl/Makefile
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
CXX = g++
|
||||
CXXFLAGS = -std=c++11 -Wall -Wextra -O2
|
||||
LDFLAGS = -lcurl
|
||||
|
||||
TARGET = uncloseai
|
||||
SRC = uncloseai.cpp
|
||||
|
||||
all: $(TARGET)
|
||||
|
||||
$(TARGET): $(SRC)
|
||||
$(CXX) $(CXXFLAGS) -o $(TARGET) $(SRC) $(LDFLAGS)
|
||||
|
||||
clean:
|
||||
rm -f $(TARGET) speech.mp3
|
||||
|
||||
.PHONY: all clean
|
||||
336
languages/cpp/libcurl/uncloseai.cpp
Normal file
336
languages/cpp/libcurl/uncloseai.cpp
Normal file
|
|
@ -0,0 +1,336 @@
|
|||
/*
|
||||
* UncloseAI C++ Library using libcurl
|
||||
* OpenAI-compatible API client with streaming support
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
#include <cstring>
|
||||
#include <cstdlib>
|
||||
#include <fstream>
|
||||
#include <curl/curl.h>
|
||||
|
||||
#define MAX_CONTENT_LEN 1024
|
||||
|
||||
struct ModelInfo {
|
||||
std::string id;
|
||||
std::string endpoint;
|
||||
int max_tokens;
|
||||
};
|
||||
|
||||
struct MemoryStruct {
|
||||
std::string data;
|
||||
};
|
||||
|
||||
// Callback for non-streaming responses
|
||||
static size_t WriteCallback(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;
|
||||
}
|
||||
|
||||
// Streaming context
|
||||
struct StreamContext {
|
||||
std::function<void(const std::string&)> callback;
|
||||
std::string buffer;
|
||||
};
|
||||
|
||||
// Extract content from SSE JSON
|
||||
static void extract_sse_content(const std::string& data, std::string& content) {
|
||||
const char *content_marker = "\"content\":\"";
|
||||
size_t start = data.find(content_marker);
|
||||
if(start == std::string::npos) return;
|
||||
|
||||
start += strlen(content_marker);
|
||||
size_t end = start;
|
||||
|
||||
while(end < data.size() && data[end] != '"') {
|
||||
if(data[end] == '\\' && end + 1 < data.size()) {
|
||||
end += 2;
|
||||
} else {
|
||||
end++;
|
||||
}
|
||||
}
|
||||
|
||||
content = data.substr(start, end - start);
|
||||
}
|
||||
|
||||
// Streaming callback
|
||||
static size_t StreamCallback(void *contents, size_t size, size_t nmemb, void *userp) {
|
||||
size_t realsize = size * nmemb;
|
||||
StreamContext *ctx = static_cast<StreamContext*>(userp);
|
||||
|
||||
ctx->buffer.append(static_cast<char*>(contents), realsize);
|
||||
|
||||
size_t pos = 0;
|
||||
while((pos = ctx->buffer.find("\n\n")) != std::string::npos) {
|
||||
std::string line = ctx->buffer.substr(0, pos);
|
||||
ctx->buffer.erase(0, pos + 2);
|
||||
|
||||
if(line.substr(0, 6) == "data: ") {
|
||||
std::string data = line.substr(6);
|
||||
|
||||
if(data == "[DONE]") break;
|
||||
|
||||
std::string content;
|
||||
extract_sse_content(data, content);
|
||||
|
||||
if(!content.empty() && ctx->callback) {
|
||||
ctx->callback(content);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return realsize;
|
||||
}
|
||||
|
||||
class UncloseAI {
|
||||
private:
|
||||
std::vector<ModelInfo> models;
|
||||
std::vector<std::string> tts_endpoints;
|
||||
int timeout;
|
||||
bool debug;
|
||||
|
||||
void discover_endpoints_from_env(const std::string& prefix, std::vector<std::string>& endpoints) {
|
||||
for(int i = 1; i < 10000; i++) {
|
||||
std::string var_name = prefix + "_" + std::to_string(i);
|
||||
const char* endpoint = std::getenv(var_name.c_str());
|
||||
if(!endpoint) break;
|
||||
endpoints.push_back(endpoint);
|
||||
}
|
||||
}
|
||||
|
||||
void discover_models(const std::vector<std::string>& endpoints) {
|
||||
for(const auto& endpoint : endpoints) {
|
||||
if(debug) {
|
||||
std::cout << "[DEBUG] Discovering from: " << endpoint << std::endl;
|
||||
}
|
||||
|
||||
CURL *curl = curl_easy_init();
|
||||
if(curl) {
|
||||
MemoryStruct response;
|
||||
std::string url = endpoint + "/models";
|
||||
|
||||
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
|
||||
curl_easy_setopt(curl, CURLOPT_TIMEOUT, 10L);
|
||||
|
||||
CURLcode res = curl_easy_perform(curl);
|
||||
curl_easy_cleanup(curl);
|
||||
|
||||
if(res == CURLE_OK) {
|
||||
// Simple JSON parsing for model IDs
|
||||
size_t pos = 0;
|
||||
while((pos = response.data.find("\"id\":\"", pos)) != std::string::npos) {
|
||||
pos += 6;
|
||||
size_t end = response.data.find("\"", pos);
|
||||
if(end != std::string::npos) {
|
||||
std::string model_id = response.data.substr(pos, end - pos);
|
||||
|
||||
if(model_id.substr(0, 10) != "modelperm-") {
|
||||
ModelInfo info;
|
||||
info.id = model_id;
|
||||
info.endpoint = endpoint;
|
||||
info.max_tokens = 8192;
|
||||
models.push_back(info);
|
||||
|
||||
if(debug) {
|
||||
std::cout << "[DEBUG] Discovered: " << model_id << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
pos = end + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
UncloseAI(int timeout = 30, bool debug = false) : timeout(timeout), debug(debug) {
|
||||
std::vector<std::string> endpoints;
|
||||
std::vector<std::string> tts_eps;
|
||||
|
||||
discover_endpoints_from_env("MODEL_ENDPOINT", endpoints);
|
||||
discover_endpoints_from_env("TTS_ENDPOINT", tts_eps);
|
||||
|
||||
if(debug) {
|
||||
std::cout << "[DEBUG] Initialized with " << endpoints.size() << " endpoint(s)" << std::endl;
|
||||
}
|
||||
|
||||
discover_models(endpoints);
|
||||
tts_endpoints = tts_eps;
|
||||
}
|
||||
|
||||
const std::vector<ModelInfo>& list_models() const {
|
||||
return models;
|
||||
}
|
||||
|
||||
int chat(const std::string& prompt, std::string& response, int model_idx = 0) {
|
||||
if(model_idx >= static_cast<int>(models.size())) return -1;
|
||||
|
||||
const ModelInfo& model = models[model_idx];
|
||||
std::string url = model.endpoint + "/chat/completions";
|
||||
|
||||
std::string json = "{\"model\":\"" + model.id + "\","
|
||||
"\"messages\":[{\"role\":\"user\",\"content\":\"" + prompt + "\"}],"
|
||||
"\"max_tokens\":100}";
|
||||
|
||||
CURL *curl = curl_easy_init();
|
||||
if(!curl) return -1;
|
||||
|
||||
MemoryStruct mem;
|
||||
struct curl_slist *headers = nullptr;
|
||||
headers = curl_slist_append(headers, "Content-Type: application/json");
|
||||
|
||||
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, WriteCallback);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &mem);
|
||||
curl_easy_setopt(curl, CURLOPT_TIMEOUT, (long)timeout);
|
||||
|
||||
CURLcode res = curl_easy_perform(curl);
|
||||
|
||||
curl_slist_free_all(headers);
|
||||
curl_easy_cleanup(curl);
|
||||
|
||||
response = mem.data;
|
||||
return (res == CURLE_OK) ? 0 : -1;
|
||||
}
|
||||
|
||||
int chat_stream(const std::string& prompt, std::function<void(const std::string&)> callback, int model_idx = 0) {
|
||||
if(model_idx >= static_cast<int>(models.size())) return -1;
|
||||
|
||||
const 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}";
|
||||
|
||||
CURL *curl = curl_easy_init();
|
||||
if(!curl) return -1;
|
||||
|
||||
StreamContext ctx;
|
||||
ctx.callback = callback;
|
||||
|
||||
struct curl_slist *headers = nullptr;
|
||||
headers = curl_slist_append(headers, "Content-Type: application/json");
|
||||
|
||||
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, StreamCallback);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &ctx);
|
||||
curl_easy_setopt(curl, CURLOPT_TIMEOUT, (long)timeout);
|
||||
|
||||
CURLcode res = curl_easy_perform(curl);
|
||||
|
||||
curl_slist_free_all(headers);
|
||||
curl_easy_cleanup(curl);
|
||||
|
||||
return (res == CURLE_OK) ? 0 : -1;
|
||||
}
|
||||
|
||||
int 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;
|
||||
|
||||
MemoryStruct mem;
|
||||
struct curl_slist *headers = nullptr;
|
||||
headers = curl_slist_append(headers, "Content-Type: application/json");
|
||||
|
||||
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, WriteCallback);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &mem);
|
||||
curl_easy_setopt(curl, CURLOPT_TIMEOUT, (long)timeout);
|
||||
|
||||
CURLcode res = curl_easy_perform(curl);
|
||||
|
||||
curl_slist_free_all(headers);
|
||||
curl_easy_cleanup(curl);
|
||||
|
||||
if(res == CURLE_OK) {
|
||||
std::ofstream file(output_file, std::ios::binary);
|
||||
if(file.is_open()) {
|
||||
file.write(mem.data.c_str(), mem.data.size());
|
||||
file.close();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
};
|
||||
|
||||
// Demo
|
||||
int main() {
|
||||
std::cout << "=== UncloseAI C++ Client (with Streaming) ===\n\n";
|
||||
|
||||
curl_global_init(CURL_GLOBAL_ALL);
|
||||
|
||||
UncloseAI client(30, true);
|
||||
|
||||
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";
|
||||
curl_global_cleanup();
|
||||
return 1;
|
||||
}
|
||||
|
||||
auto models = client.list_models();
|
||||
std::cout << "\nDiscovered " << models.size() << " model(s):\n";
|
||||
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: (" << response.size() << " bytes received)\n\n";
|
||||
}
|
||||
|
||||
// Streaming chat
|
||||
std::cout << "=== Streaming Chat ===\n";
|
||||
int model_idx = (models.size() > 1) ? 1 : 0;
|
||||
std::cout << "Model: " << models[model_idx].id << "\n";
|
||||
std::cout << "Response: ";
|
||||
|
||||
client.chat_stream("Write a C++ function to check if a number is prime",
|
||||
[](const std::string& content) {
|
||||
std::cout << content << std::flush;
|
||||
}, model_idx);
|
||||
|
||||
std::cout << "\n\n";
|
||||
|
||||
// TTS
|
||||
std::cout << "=== TTS Speech Generation ===\n";
|
||||
if(client.tts("Hello from UncloseAI C++ client! This demonstrates streaming support.",
|
||||
"alloy", "speech.mp3") == 0) {
|
||||
std::cout << "✓ Speech file created: speech.mp3\n\n";
|
||||
} else {
|
||||
std::cout << "✗ TTS Error\n\n";
|
||||
}
|
||||
|
||||
std::cout << "=== Examples Complete ===\n";
|
||||
|
||||
curl_global_cleanup();
|
||||
return 0;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue