llm-proxy / client_curl_pipe.c
trung.le
feat: add curl.exe C clients
2af870d
Raw
History Blame Contribute Delete
10.3 kB
/*
* LLM Proxy Client - C Version (Windows using curl.exe and anonymous pipes)
* Compile: gcc -std=c11 -O2 -Wall -Wextra -o llm_client_curl_pipe.exe client_curl_pipe.c
* Usage: llm_client_curl_pipe.exe
* Requires curl.exe, included with current Windows 10 and Windows 11.
* Prompt and response stay in memory; this client does not create temporary files.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <windows.h>
#define MAX_PROMPT_SIZE 4096
#define MAX_RESPONSE_SIZE 65536
typedef struct {
const char *name;
const char *id;
} LLMModel;
static 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"}
};
static const int MODEL_COUNT = sizeof(MODELS) / sizeof(MODELS[0]);
static char *copy_string(const char *text) {
size_t size = strlen(text) + 1;
char *copy = (char *)malloc(size);
if (copy != NULL) {
memcpy(copy, text, size);
}
return copy;
}
static char *error_message(const char *message, DWORD code) {
char buffer[256];
snprintf(buffer, sizeof(buffer), "Error: %s (code: %lu)", message, (unsigned long)code);
return copy_string(buffer);
}
static void close_handle(HANDLE *handle) {
if (*handle != NULL && *handle != INVALID_HANDLE_VALUE) {
CloseHandle(*handle);
*handle = NULL;
}
}
static int write_all(HANDLE pipe, const char *data, size_t size) {
size_t total_written = 0;
while (total_written < size) {
DWORD chunk_size = (DWORD)(size - total_written);
DWORD written = 0;
if (!WriteFile(pipe, data + total_written, chunk_size, &written, NULL)) {
return 0;
}
if (written == 0) {
return 0;
}
total_written += written;
}
return 1;
}
static char *read_all(HANDLE pipe) {
char *response = (char *)malloc(MAX_RESPONSE_SIZE);
char buffer[4096];
if (response == NULL) {
return copy_string("Error: Memory allocation failed");
}
size_t total_size = 0;
while (1) {
DWORD bytes_read = 0;
if (!ReadFile(pipe, buffer, sizeof(buffer), &bytes_read, NULL)) {
DWORD error = GetLastError();
if (error == ERROR_BROKEN_PIPE) {
break;
}
free(response);
return error_message("Failed to read curl output", error);
}
if (bytes_read == 0) {
break;
}
size_t remaining = MAX_RESPONSE_SIZE - 1 - total_size;
size_t bytes_to_copy = bytes_read < remaining ? bytes_read : remaining;
if (bytes_to_copy > 0) {
memcpy(response + total_size, buffer, bytes_to_copy);
total_size += bytes_to_copy;
}
}
response[total_size] = '\0';
return response;
}
char *ask_llm(const char *prompt, const LLMModel *model) {
SECURITY_ATTRIBUTES security_attributes;
STARTUPINFOA startup_info;
PROCESS_INFORMATION process_info;
HANDLE stdin_read = NULL;
HANDLE stdin_write = NULL;
HANDLE stdout_read = NULL;
HANDLE stdout_write = NULL;
DWORD exit_code = 0;
char curl_path[MAX_PATH];
char command[1024];
char *response = NULL;
ZeroMemory(&security_attributes, sizeof(security_attributes));
ZeroMemory(&process_info, sizeof(process_info));
security_attributes.nLength = sizeof(security_attributes);
security_attributes.bInheritHandle = TRUE;
UINT system_path_length = GetSystemDirectoryA(curl_path, sizeof(curl_path));
if (system_path_length == 0 || system_path_length >= sizeof(curl_path) - 9) {
return error_message("Failed to locate the Windows system directory", GetLastError());
}
strcat(curl_path, "\\curl.exe");
if (!CreatePipe(&stdin_read, &stdin_write, &security_attributes, 0)) {
return error_message("Failed to create curl stdin pipe", GetLastError());
}
if (!SetHandleInformation(stdin_write, HANDLE_FLAG_INHERIT, 0)) {
response = error_message("Failed to configure curl stdin pipe", GetLastError());
goto cleanup;
}
if (!CreatePipe(&stdout_read, &stdout_write, &security_attributes, 0)) {
response = error_message("Failed to create curl output pipe", GetLastError());
goto cleanup;
}
if (!SetHandleInformation(stdout_read, HANDLE_FLAG_INHERIT, 0)) {
response = error_message("Failed to configure curl output pipe", GetLastError());
goto cleanup;
}
int command_length = snprintf(
command,
sizeof(command),
"curl.exe --silent --show-error --fail-with-body --connect-timeout 30 "
"--max-time 120 --request POST --header \"Content-Type: text/plain; charset=utf-8\" "
"--header \"X-Model: %s\" --data-binary @- "
"https://ttl0110-llm-proxy.hf.space/ask",
model->id);
if (command_length < 0 || command_length >= (int)sizeof(command)) {
response = copy_string("Error: curl command is too long");
goto cleanup;
}
ZeroMemory(&startup_info, sizeof(startup_info));
ZeroMemory(&process_info, sizeof(process_info));
startup_info.cb = sizeof(startup_info);
startup_info.dwFlags = STARTF_USESTDHANDLES;
startup_info.hStdInput = stdin_read;
startup_info.hStdOutput = stdout_write;
startup_info.hStdError = stdout_write;
if (!CreateProcessA(
curl_path,
command,
NULL,
NULL,
TRUE,
CREATE_NO_WINDOW,
NULL,
NULL,
&startup_info,
&process_info)) {
response = error_message("Failed to start curl.exe", GetLastError());
goto cleanup;
}
close_handle(&stdin_read);
close_handle(&stdout_write);
if (!write_all(stdin_write, prompt, strlen(prompt))) {
response = error_message("Failed to write prompt to curl.exe", GetLastError());
}
close_handle(&stdin_write);
char *curl_output = read_all(stdout_read);
if (response == NULL) {
response = curl_output;
} else {
free(curl_output);
}
close_handle(&stdout_read);
WaitForSingleObject(process_info.hProcess, INFINITE);
if (!GetExitCodeProcess(process_info.hProcess, &exit_code) && response == NULL) {
response = error_message("Failed to read curl exit code", GetLastError());
}
close_handle(&process_info.hThread);
close_handle(&process_info.hProcess);
if (exit_code != 0 && response != NULL && response[0] == '\0') {
free(response);
response = error_message("curl.exe request failed", exit_code);
}
cleanup:
close_handle(&stdin_read);
close_handle(&stdin_write);
close_handle(&stdout_read);
close_handle(&stdout_write);
close_handle(&process_info.hThread);
close_handle(&process_info.hProcess);
return response != NULL ? response : copy_string("Error: Unknown request failure");
}
void clear_input_buffer(void) {
int c;
while ((c = getchar()) != '\n' && c != EOF);
}
int main(void) {
char prompt[MAX_PROMPT_SIZE];
char prompt_prefix[MAX_PROMPT_SIZE] = {0};
int choice = 0;
SetConsoleOutputCP(CP_UTF8);
SetConsoleCP(CP_UTF8);
printf("=== C LLM Assistant (curl.exe pipes) ===\n\n");
printf("Chon Model LLM:\n");
for (int i = 0; i < MODEL_COUNT; i++) {
printf("%d. %s\n", i + 1, MODELS[i].name);
}
while (choice < 1 || choice > MODEL_COUNT) {
printf("Nhap lua chon (1-%d): ", MODEL_COUNT);
if (scanf("%d", &choice) != 1) {
clear_input_buffer();
choice = 0;
}
}
clear_input_buffer();
const LLMModel *selected_model = &MODELS[choice - 1];
printf("Ban da chon: %s\n\n", selected_model->name);
printf("Nhap prompt prefix (bo trong neu khong dung): ");
if (fgets(prompt_prefix, sizeof(prompt_prefix), stdin) != NULL) {
prompt_prefix[strcspn(prompt_prefix, "\r\n")] = '\0';
}
printf("Huong dan: Nhap prompt cua ban. Nhap dong trong de gui.\n");
printf("Nhap 'EXIT' de thoat.\n");
while (1) {
char full_prompt[MAX_PROMPT_SIZE] = {0};
size_t total_len = 0;
printf("\nBan [%s]:\n", selected_model->name);
while (1) {
if (fgets(prompt, sizeof(prompt), stdin) == NULL) {
printf("Tam biet!\n");
return 0;
}
if (_strnicmp(prompt, "EXIT", 4) == 0 &&
(prompt[4] == '\n' || prompt[4] == '\r' || prompt[4] == '\0')) {
printf("Tam biet!\n");
return 0;
}
if (prompt[0] == '\n' || (prompt[0] == '\r' && prompt[1] == '\n')) {
break;
}
size_t line_len = strlen(prompt);
if (total_len + line_len < MAX_PROMPT_SIZE - 1) {
strcat(full_prompt, prompt);
total_len += line_len;
}
}
if (total_len == 0) {
printf("Prompt trong, vui long nhap lai.\n");
continue;
}
while (total_len > 0 &&
(full_prompt[total_len - 1] == '\n' || full_prompt[total_len - 1] == '\r')) {
full_prompt[--total_len] = '\0';
}
char request_prompt[MAX_PROMPT_SIZE] = {0};
if (prompt_prefix[0] != '\0') {
int written = snprintf(request_prompt, sizeof(request_prompt), "%s\n\n%s",
prompt_prefix, full_prompt);
if (written < 0 || written >= (int)sizeof(request_prompt)) {
printf("Prompt va prefix qua dai, vui long nhap lai.\n");
continue;
}
} else {
strcpy(request_prompt, full_prompt);
}
printf("Thinking...\n");
char *response = ask_llm(request_prompt, selected_model);
printf("\n[LLM]: %s\n", response);
printf("-------------------------------------------\n");
free(response);
}
}