llm-proxy / client.cpp
trung.le
feat: add C++ client and refresh client features
57ce88d
Raw
History Blame Contribute Delete
8.01 kB
/*
* LLM Proxy Client - C++ Version (Linux/WSL using libcurl)
* Dependencies: sudo apt install g++ libcurl4-openssl-dev
* Compile: g++ -std=c++11 -O2 -Wall -Wextra -pedantic client.cpp -lcurl -o llm_client_cpp
* Usage: ./llm_client_cpp
*/
#include <curl/curl.h>
#include <iostream>
#include <limits>
#include <sstream>
#include <stdexcept>
#include <string>
struct LLMModel {
const char* name;
const char* id;
};
const LLMModel MODELS[] = {
{"Laguna S 2.1 (Free)", "poolside/laguna-s-2.1:free"},
{"Gemini 3.6 Flash (Paid)", "google/gemini-3.6-flash"},
{"Claude Opus 5 (Paid)", "anthropic/claude-opus-5"},
{"Claude Fable 5 (Paid)", "anthropic/claude-fable-5"},
{"GPT-5.6 Sol (Paid)", "openai/gpt-5.6-sol"},
{"GPT-5.6 Luna (Paid)", "openai/gpt-5.6-luna"},
{"Nemotron 3 Ultra (Free)", "nvidia/nemotron-3-ultra-550b-a55b:free"}
};
const std::size_t MODEL_COUNT = sizeof(MODELS) / sizeof(MODELS[0]);
class CurlGlobal {
public:
CurlGlobal() {
const CURLcode result = curl_global_init(CURL_GLOBAL_DEFAULT);
if (result != CURLE_OK) {
throw std::runtime_error(std::string("Failed to initialize libcurl: ") +
curl_easy_strerror(result));
}
}
~CurlGlobal() { curl_global_cleanup(); }
CurlGlobal(const CurlGlobal&) = delete;
CurlGlobal& operator=(const CurlGlobal&) = delete;
};
class CurlHandle {
public:
CurlHandle() : handle_(curl_easy_init()) {
if (!handle_) {
throw std::runtime_error("Failed to create libcurl handle");
}
}
~CurlHandle() { curl_easy_cleanup(handle_); }
CurlHandle(const CurlHandle&) = delete;
CurlHandle& operator=(const CurlHandle&) = delete;
CURL* get() const { return handle_; }
private:
CURL* handle_;
};
class CurlHeaders {
public:
~CurlHeaders() { curl_slist_free_all(headers_); }
void append(const std::string& header) {
curl_slist* updated = curl_slist_append(headers_, header.c_str());
if (!updated) {
throw std::runtime_error("Failed to allocate HTTP headers");
}
headers_ = updated;
}
curl_slist* get() const { return headers_; }
CurlHeaders(const CurlHeaders&) = delete;
CurlHeaders& operator=(const CurlHeaders&) = delete;
CurlHeaders() = default;
private:
curl_slist* headers_ = nullptr;
};
std::size_t writeResponse(char* data, std::size_t size, std::size_t count, void* output) {
const std::size_t bytes = size * count;
static_cast<std::string*>(output)->append(data, bytes);
return bytes;
}
void requireCurlOption(CURLcode result, const char* option) {
if (result != CURLE_OK) {
throw std::runtime_error(std::string("Failed to set ") + option + ": " +
curl_easy_strerror(result));
}
}
std::string askLLM(const std::string& prompt, const LLMModel& model) {
CurlHandle curl;
CurlHeaders headers;
std::string response;
headers.append("Content-Type: text/plain; charset=utf-8");
headers.append(std::string("X-Model: ") + model.id);
requireCurlOption(curl_easy_setopt(curl.get(), CURLOPT_URL,
"https://ttl0110-llm-proxy.hf.space/ask"),
"request URL");
requireCurlOption(curl_easy_setopt(curl.get(), CURLOPT_HTTPHEADER, headers.get()),
"request headers");
requireCurlOption(curl_easy_setopt(curl.get(), CURLOPT_POST, 1L), "POST method");
requireCurlOption(curl_easy_setopt(curl.get(), CURLOPT_POSTFIELDS, prompt.c_str()),
"request body");
requireCurlOption(curl_easy_setopt(curl.get(), CURLOPT_POSTFIELDSIZE_LARGE,
static_cast<curl_off_t>(prompt.size())),
"request body size");
requireCurlOption(curl_easy_setopt(curl.get(), CURLOPT_WRITEFUNCTION, writeResponse),
"response callback");
requireCurlOption(curl_easy_setopt(curl.get(), CURLOPT_WRITEDATA, &response),
"response destination");
requireCurlOption(curl_easy_setopt(curl.get(), CURLOPT_CONNECTTIMEOUT, 30L),
"connection timeout");
requireCurlOption(curl_easy_setopt(curl.get(), CURLOPT_TIMEOUT, 120L),
"request timeout");
requireCurlOption(curl_easy_setopt(curl.get(), CURLOPT_USERAGENT,
"LLM-Proxy-Cpp-Client/1.0"),
"user agent");
const CURLcode result = curl_easy_perform(curl.get());
if (result != CURLE_OK) {
throw std::runtime_error(std::string("Request failed: ") + curl_easy_strerror(result));
}
long statusCode = 0;
const CURLcode statusResult = curl_easy_getinfo(curl.get(), CURLINFO_RESPONSE_CODE, &statusCode);
if (statusResult != CURLE_OK) {
throw std::runtime_error(std::string("Failed to read HTTP status: ") +
curl_easy_strerror(statusResult));
}
if (statusCode < 200 || statusCode >= 300) {
throw std::runtime_error("Server returned HTTP " + std::to_string(statusCode) +
(response.empty() ? "" : ": " + response));
}
return response;
}
std::size_t selectModel() {
std::cout << "Chon Model LLM:\n";
for (std::size_t i = 0; i < MODEL_COUNT; ++i) {
std::cout << i + 1 << ". " << MODELS[i].name << '\n';
}
int choice = 0;
while (choice < 1 || choice > static_cast<int>(MODEL_COUNT)) {
std::cout << "Nhap lua chon (1-" << MODEL_COUNT << "): ";
if (!(std::cin >> choice)) {
std::cin.clear();
}
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
}
return static_cast<std::size_t>(choice - 1);
}
int main() {
try {
CurlGlobal curlGlobal;
std::cout << "=== C++ LLM Assistant (HuggingFace Proxy) ===\n\n";
const LLMModel& selectedModel = MODELS[selectModel()];
std::cout << "Ban da chon: " << selectedModel.name << "\n\n";
std::cout << "Nhap prompt prefix (bo trong neu khong dung): ";
std::string promptPrefix;
std::getline(std::cin, promptPrefix);
std::cout << "Huong dan: Nhap prompt cua ban. Nhap 'SUBMIT' o dong cuoi de gui.\n";
std::cout << "Nhap 'EXIT' o mot dong rieng de thoat.\n";
while (true) {
std::cout << "\nBan [" << selectedModel.name << "]:\n";
std::ostringstream prompt;
std::string line;
bool exitRequested = false;
while (std::getline(std::cin, line)) {
if (line == "EXIT") {
exitRequested = true;
break;
}
if (line == "SUBMIT") {
break;
}
if (prompt.tellp() > 0) {
prompt << '\n';
}
prompt << line;
}
if (exitRequested || !std::cin) {
break;
}
const std::string promptText = prompt.str();
if (promptText.empty()) {
std::cout << "Prompt trong, vui long nhap lai.\n";
continue;
}
const std::string fullPrompt = promptPrefix.empty()
? promptText
: promptPrefix + "\n\n" + promptText;
std::cout << "Thinking...\n";
try {
std::cout << "\n[LLM]: " << askLLM(fullPrompt, selectedModel) << '\n';
} catch (const std::exception& error) {
std::cout << "\nError: " << error.what() << '\n';
}
std::cout << "-------------------------------------------\n";
}
std::cout << "Tam biet!\n";
return 0;
} catch (const std::exception& error) {
std::cerr << "Fatal error: " << error.what() << '\n';
return 1;
}
}