/* * LLM Proxy Client - C Version (Windows using curl.exe) * Compile: gcc -std=c11 -O2 -Wall -Wextra -o llm_client_curl.exe client_curl.c * Usage: llm_client_curl.exe * Requires curl.exe, included with current Windows 10 and Windows 11. */ #include #include #include #include #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 int create_temp_file(char *path) { char temp_dir[MAX_PATH]; DWORD length = GetTempPathA(sizeof(temp_dir), temp_dir); if (length == 0 || length >= sizeof(temp_dir)) { return 0; } return GetTempFileNameA(temp_dir, "llm", 0, path) != 0; } static int write_binary_file(const char *path, const char *data, size_t size) { FILE *file = fopen(path, "wb"); if (file == NULL) { return 0; } int success = fwrite(data, 1, size, file) == size; if (fclose(file) != 0) { success = 0; } return success; } static char *read_response_file(const char *path) { FILE *file = fopen(path, "rb"); if (file == NULL) { return copy_string("Error: Failed to read curl response"); } char *response = (char *)malloc(MAX_RESPONSE_SIZE); if (response == NULL) { fclose(file); return copy_string("Error: Memory allocation failed"); } size_t total_size = fread(response, 1, MAX_RESPONSE_SIZE - 1, file); if (ferror(file)) { free(response); fclose(file); return copy_string("Error: Failed while reading curl response"); } response[total_size] = '\0'; fclose(file); return response; } char *ask_llm(const char *prompt, const LLMModel *model) { char request_path[MAX_PATH] = {0}; char response_path[MAX_PATH] = {0}; char command[2048]; STARTUPINFOA startup_info; PROCESS_INFORMATION process_info; DWORD exit_code = 0; char *response = NULL; if (!create_temp_file(request_path) || !create_temp_file(response_path)) { response = error_message("Failed to create temporary files", GetLastError()); goto cleanup; } if (!write_binary_file(request_path, prompt, strlen(prompt))) { response = copy_string("Error: Failed to write temporary request file"); 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 \"@%s\" --output \"%s\" " "https://ttl0110-llm-proxy.hf.space/ask", model->id, request_path, response_path); 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); if (!CreateProcessA( NULL, command, NULL, NULL, FALSE, CREATE_NO_WINDOW, NULL, NULL, &startup_info, &process_info)) { response = error_message("Failed to start curl.exe", GetLastError()); goto cleanup; } WaitForSingleObject(process_info.hProcess, INFINITE); if (!GetExitCodeProcess(process_info.hProcess, &exit_code)) { response = error_message("Failed to read curl exit code", GetLastError()); } CloseHandle(process_info.hThread); CloseHandle(process_info.hProcess); if (response == NULL) { response = read_response_file(response_path); } if (exit_code != 0 && response != NULL && response[0] == '\0') { free(response); response = error_message("curl.exe request failed", exit_code); } cleanup: if (request_path[0] != '\0') { DeleteFileA(request_path); } if (response_path[0] != '\0') { DeleteFileA(response_path); } 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) ===\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); } }