| |
| |
| |
| |
| |
| |
| |
|
|
| #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); |
| } |
| } |
|
|