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