// Written by retoor@molodetz.nl // This code defines functions for HTTP requests and structuring tool descriptions and executions in JSON format using the JSON-C library. It uses cURL for HTTP requests. // Imports not part of the language itself: JSON-C (json-c/json.h, json-c/json_object.h) and a custom header "http_curl.h" // MIT License #ifndef R_TOOLS_H #define R_TOOLS_H #include <json-c/json.h> #include <json-c/json_object.h> #include <string.h> #include "http_curl.h" #include <dirent.h> #include <sys/stat.h> #include <time.h> #include <glob.h> struct json_object *tool_description_http_get(); struct json_object *tool_description_linux_terminal(); struct json_object *tool_description_directory_glob(); struct json_object *tool_description_read_file(); struct json_object *tool_description_write_file(); struct json_object *tool_description_directory_rglob(); struct json_object *tools_descriptions() { struct json_object *root = json_object_new_array(); json_object_array_add(root, tool_description_http_get()); json_object_array_add(root, tool_description_linux_terminal()); json_object_array_add(root, tool_description_directory_glob()); json_object_array_add(root, tool_description_read_file()); json_object_array_add(root, tool_description_write_file()); json_object_array_add(root, tool_description_directory_rglob()); return root; } char *tool_function_http_get(char *url) { fprintf(stderr, "Tool http_get: %s\n", url); return curl_get(url); } char * tool_function_linux_terminal(char * command){ fprintf(stderr, "Tool linux_terminal: %s\n", command); FILE *fp; char buffer[1024]; size_t total_size = 0; char *output = NULL; // Open the command for reading fp = popen(command, "r"); if (fp == NULL) { perror("popen failed"); return strdup("Popen failed!"); } // Read output in chunks while (fgets(buffer, sizeof(buffer), fp) != NULL) { size_t chunk_size = strlen(buffer); char *new_output = realloc(output, total_size + chunk_size + 1); if (new_output == NULL) { perror("realloc failed"); free(output); pclose(fp); return strdup("Failed to allocate memory!"); } output = new_output; strcpy(output + total_size, buffer); total_size += chunk_size; } pclose(fp); return output ? output : strdup(""); } struct json_object *tool_description_directory_rglob() { struct json_object *root = json_object_new_object(); json_object_object_add(root, "type", json_object_new_string("function")); struct json_object *function = json_object_new_object(); json_object_object_add(function, "name", json_object_new_string("directory_rglob")); json_object_object_add(function, "description", json_object_new_string("Recursively list the contents of a specified directory in glob format. " "Result is a json array containing objects with keys: name, modification_date(iso), creation_date(iso), type and size_bytes.")); struct json_object *parameters = json_object_new_object(); json_object_object_add(parameters, "type", json_object_new_string("object")); struct json_object *properties = json_object_new_object(); struct json_object *directory = json_object_new_object(); json_object_object_add(directory, "type", json_object_new_string("string")); json_object_object_add(directory, "description", json_object_new_string("Path to the directory to list in glob format.")); json_object_object_add(properties, "path", directory); json_object_object_add(parameters, "properties", properties); struct json_object *required = json_object_new_array(); json_object_array_add(required, json_object_new_string("path")); json_object_object_add(parameters, "required", required); json_object_object_add(parameters, "additionalProperties", json_object_new_boolean(0)); json_object_object_add(function, "parameters", parameters); json_object_object_add(function, "strict", json_object_new_boolean(1)); json_object_object_add(root, "function", function); return root; } struct json_object *tool_description_read_file() { struct json_object *root = json_object_new_object(); json_object_object_add(root, "type", json_object_new_string("function")); struct json_object *function = json_object_new_object(); json_object_object_add(function, "name", json_object_new_string("read_file")); json_object_object_add(function, "description", json_object_new_string("Reads / opens / loads a file and returns its contents as a string.")); struct json_object *parameters = json_object_new_object(); json_object_object_add(parameters, "type", json_object_new_string("object")); struct json_object *properties = json_object_new_object(); struct json_object *path = json_object_new_object(); json_object_object_add(path, "type", json_object_new_string("string")); json_object_object_add(path, "description", json_object_new_string("Path to the file to read / open / load.")); json_object_object_add(properties, "path", path); json_object_object_add(parameters, "properties", properties); struct json_object *required = json_object_new_array(); json_object_array_add(required, json_object_new_string("path")); json_object_object_add(parameters, "required", required); json_object_object_add(parameters, "additionalProperties", json_object_new_boolean(0)); json_object_object_add(function, "parameters", parameters); json_object_object_add(function, "strict", json_object_new_boolean(1)); json_object_object_add(root, "function", function); return root; } // Write File Description struct json_object *tool_description_write_file() { struct json_object *root = json_object_new_object(); json_object_object_add(root, "type", json_object_new_string("function")); struct json_object *function = json_object_new_object(); json_object_object_add(function, "name", json_object_new_string("write_file")); json_object_object_add(function, "description", json_object_new_string("Writes / saves / stores content to a file.")); struct json_object *parameters = json_object_new_object(); json_object_object_add(parameters, "type", json_object_new_string("object")); struct json_object *properties = json_object_new_object(); struct json_object *path = json_object_new_object(); json_object_object_add(path, "type", json_object_new_string("string")); json_object_object_add(path, "description", json_object_new_string("Path to the file to write / save / store.")); json_object_object_add(properties, "path", path); struct json_object *content = json_object_new_object(); json_object_object_add(content, "type", json_object_new_string("string")); json_object_object_add(content, "description", json_object_new_string("Content to write / save / store into the file.")); json_object_object_add(properties, "content", content); json_object_object_add(parameters, "properties", properties); struct json_object *required = json_object_new_array(); json_object_array_add(required, json_object_new_string("path")); json_object_array_add(required, json_object_new_string("content")); json_object_object_add(parameters, "required", required); json_object_object_add(parameters, "additionalProperties", json_object_new_boolean(0)); json_object_object_add(function, "parameters", parameters); json_object_object_add(function, "strict", json_object_new_boolean(1)); json_object_object_add(root, "function", function); return root; } char *tool_function_read_file(char *path) { fprintf(stderr, "Tools read_file: %s\n", path); FILE *fp = fopen(path, "r"); if (fp == NULL) { perror("fopen failed"); return strdup("Failed to open file for reading!"); } fseek(fp, 0, SEEK_END); long size = ftell(fp); rewind(fp); char *content = (char *)malloc(size + 1); if (content == NULL) { fclose(fp); return strdup("Memory allocation failed!"); } ssize_t read_size = fread(content, 1, size, fp); (void)read_size; content[size] = '\0'; fclose(fp); return content; } // Write content to a file char *tool_function_write_file(char *path, char *content) { fprintf(stderr, "Tools write_file with %zu bytes: %s\n", strlen(content), path); FILE *fp = fopen(path, "w"); if (fp == NULL) { perror("fopen failed"); return strdup("Failed to open file for writing!"); } fwrite(content, 1, strlen(content), fp); fclose(fp); return strdup("File written successfully."); } const char *get_file_type(struct stat *st) { if (S_ISREG(st->st_mode)) return "file"; if (S_ISDIR(st->st_mode)) return "directory"; return "other"; } // Function to convert timestamp to human-readable format void format_time(time_t raw_time, char *buffer, size_t size) { struct tm *time_info = localtime(&raw_time); strftime(buffer, size, "%Y-%m-%d %H:%M:%S", time_info); } void recursive_glob(const char *pattern, glob_t *results) { // First, find all matching files in the current scope glob_t current_matches; int ret = glob(pattern, GLOB_NOSORT | GLOB_TILDE, NULL, ¤t_matches); // Handle errors if (ret != 0 && ret != GLOB_NOMATCH) { // Copy error state to results results->gl_pathc = 0; results->gl_pathv = NULL; results->gl_offs = 0; return; } // Initialize results if this is the first call if (results->gl_pathv == NULL) { memset(results, 0, sizeof(glob_t)); } // Add found paths to results for (size_t i = 0; i < current_matches.gl_pathc; i++) { char *path = current_matches.gl_pathv[i]; // Add the path to results if (results->gl_pathc == 0) { // First result results->gl_pathc = 1; results->gl_pathv = malloc(sizeof(char *)); results->gl_pathv[0] = strdup(path); } else { // Additional result results->gl_pathc++; results->gl_pathv = realloc(results->gl_pathv, results->gl_pathc * sizeof(char *)); results->gl_pathv[results->gl_pathc - 1] = strdup(path); } } // Now look for directories to recurse into DIR *dir; struct dirent *entry; struct stat statbuf; // Extract directory part from pattern char *pattern_copy = strdup(pattern); char *last_slash = strrchr(pattern_copy, '/'); char *dir_path; char *file_pattern; if (last_slash) { *last_slash = '\0'; dir_path = pattern_copy; file_pattern = last_slash + 1; } else { // No directory part in the pattern dir_path = "."; file_pattern = pattern_copy; } if ((dir = opendir(dir_path)) != NULL) { while ((entry = readdir(dir)) != NULL) { // Skip . and .. if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) { continue; } // Construct full path - ensure sufficient buffer space char full_path[PATH_MAX]; int path_len; if (strcmp(dir_path, ".") == 0) { path_len = snprintf(full_path, PATH_MAX, "%s", entry->d_name); } else { path_len = snprintf(full_path, PATH_MAX, "%s/%s", dir_path, entry->d_name); } // Check if snprintf truncated the output if (path_len >= PATH_MAX) { // Path too long, skip this entry continue; } // Check if it's a directory if (stat(full_path, &statbuf) == 0 && S_ISDIR(statbuf.st_mode)) { // Create new pattern for recursion - ensure sufficient buffer space char new_pattern[PATH_MAX]; int new_pattern_len = snprintf(new_pattern, PATH_MAX, "%s/%s", full_path, file_pattern); // Check if snprintf truncated the output if (new_pattern_len >= PATH_MAX) { // Pattern too long, skip this recursion continue; } // Recurse recursive_glob(new_pattern, results); } } closedir(dir); } // Free temporary resources free(pattern_copy); globfree(¤t_matches); } char *tool_function_directory_rglob(char *target_dir) { fprintf(stderr, "Tools directory_rglob: %s\n", target_dir); glob_t results; results.gl_pathc = 0; struct stat file_stat; char mod_time[20], create_time[20]; // Perform glob search recursively recursive_glob(target_dir, &results); // Create a JSON array to store results json_object *json_array = json_object_new_array(); // Iterate through the matched files for (size_t i = 0; i < results.gl_pathc; i++) { const char *file_path = results.gl_pathv[i]; // Get file stats if (stat(file_path, &file_stat) == -1) { perror("stat failed"); continue; } // Format timestamps format_time(file_stat.st_mtime, mod_time, sizeof(mod_time)); format_time(file_stat.st_ctime, create_time, sizeof(create_time)); // Creation time is unreliable on Linux // Create JSON object for each file json_object *json_entry = json_object_new_object(); json_object_object_add(json_entry, "name", json_object_new_string(file_path)); json_object_object_add(json_entry, "modification_date", json_object_new_string(mod_time)); json_object_object_add(json_entry, "creation_date", json_object_new_string(create_time)); json_object_object_add(json_entry, "type", json_object_new_string(get_file_type(&file_stat))); json_object_object_add(json_entry, "size_bytes", json_object_new_int64(file_stat.st_size)); // Add to JSON array json_object_array_add(json_array, json_entry); } // Free glob results globfree(&results); char *result = strdup(json_object_to_json_string_ext(json_array, JSON_C_TO_STRING_PRETTY)); // Cleanup json_object_put(json_array); return result; } char * tool_function_directory_glob(char *target_dir) { fprintf(stderr, "Tools directory_glob: %s\n", target_dir); glob_t results; struct stat file_stat; char mod_time[20], create_time[20]; if(!strcmp(target_dir, ".")) { target_dir[0] = '*'; } // Perform glob search if (glob(target_dir, GLOB_TILDE, NULL, &results) != 0) { perror("glob failed"); return NULL; } // Create a JSON array to store results json_object *json_array = json_object_new_array(); // Iterate through the matched files for (size_t i = 0; i < results.gl_pathc; i++) { const char *file_path = results.gl_pathv[i]; // Get file stats if (stat(file_path, &file_stat) == -1) { perror("stat failed"); continue; } // Format timestamps format_time(file_stat.st_mtime, mod_time, sizeof(mod_time)); format_time(file_stat.st_ctime, create_time, sizeof(create_time)); // Creation time is unreliable on Linux // Create JSON object for each file json_object *json_entry = json_object_new_object(); json_object_object_add(json_entry, "name", json_object_new_string(file_path)); json_object_object_add(json_entry, "modification_date", json_object_new_string(mod_time)); json_object_object_add(json_entry, "creation_date", json_object_new_string(create_time)); json_object_object_add(json_entry, "type", json_object_new_string(get_file_type(&file_stat))); json_object_object_add(json_entry, "size_bytes", json_object_new_int64(file_stat.st_size)); // Add to JSON array json_object_array_add(json_array, json_entry); } // Free glob results globfree(&results); char * result = strdup(json_object_to_json_string_ext(json_array, JSON_C_TO_STRING_PRETTY)); // Cleanup json_object_put(json_array); return result; } struct json_object *tool_description_http_get() { struct json_object *root = json_object_new_object(); json_object_object_add(root, "type", json_object_new_string("function")); struct json_object *function = json_object_new_object(); json_object_object_add(function, "name", json_object_new_string("http_fetch")); json_object_object_add(function, "description", json_object_new_string("Get the contents of a URL.")); struct json_object *parameters = json_object_new_object(); json_object_object_add(parameters, "type", json_object_new_string("object")); struct json_object *properties = json_object_new_object(); struct json_object *url = json_object_new_object(); json_object_object_add(url, "type", json_object_new_string("string")); json_object_object_add(url, "description", json_object_new_string("Fetch URL contents.")); json_object_object_add(properties, "url", url); json_object_object_add(parameters, "properties", properties); struct json_object *required = json_object_new_array(); json_object_array_add(required, json_object_new_string("url")); json_object_object_add(parameters, "required", required); json_object_object_add(parameters, "additionalProperties", json_object_new_boolean(0)); json_object_object_add(function, "parameters", parameters); json_object_object_add(function, "strict", json_object_new_boolean(1)); json_object_object_add(root, "function", function); return root; } struct json_object *tool_description_directory_glob() { struct json_object *root = json_object_new_object(); json_object_object_add(root, "type", json_object_new_string("function")); struct json_object *function = json_object_new_object(); json_object_object_add(function, "name", json_object_new_string("directory_glob")); json_object_object_add(function, "description", json_object_new_string("List the contents of a specified directory in glob format. " "Result is a json array containing objects with keys: name, modification_date(iso), creation_date(iso), type and size_bytes.")); struct json_object *parameters = json_object_new_object(); json_object_object_add(parameters, "type", json_object_new_string("object")); struct json_object *properties = json_object_new_object(); struct json_object *directory = json_object_new_object(); json_object_object_add(directory, "type", json_object_new_string("string")); json_object_object_add(directory, "description", json_object_new_string("Path to the directory to list in glob format.")); json_object_object_add(properties, "path", directory); json_object_object_add(parameters, "properties", properties); struct json_object *required = json_object_new_array(); json_object_array_add(required, json_object_new_string("path")); json_object_object_add(parameters, "required", required); json_object_object_add(parameters, "additionalProperties", json_object_new_boolean(0)); json_object_object_add(function, "parameters", parameters); json_object_object_add(function, "strict", json_object_new_boolean(1)); json_object_object_add(root, "function", function); return root; } struct json_object *tool_description_linux_terminal() { struct json_object *root = json_object_new_object(); json_object_object_add(root, "type", json_object_new_string("function")); struct json_object *function = json_object_new_object(); json_object_object_add(function, "name", json_object_new_string("linux_terminal_execute")); json_object_object_add(function, "description", json_object_new_string("Execute a linux_terminal command on user terminal.")); struct json_object *parameters = json_object_new_object(); json_object_object_add(parameters, "type", json_object_new_string("object")); struct json_object *properties = json_object_new_object(); struct json_object *url = json_object_new_object(); json_object_object_add(url, "type", json_object_new_string("string")); json_object_object_add(url, "description", json_object_new_string("Bash command to execute.")); json_object_object_add(properties, "command", url); json_object_object_add(parameters, "properties", properties); struct json_object *required = json_object_new_array(); json_object_array_add(required, json_object_new_string("command")); json_object_object_add(parameters, "required", required); json_object_object_add(parameters, "additionalProperties", json_object_new_boolean(0)); json_object_object_add(function, "parameters", parameters); json_object_object_add(function, "strict", json_object_new_boolean(1)); json_object_object_add(root, "function", function); return root; } struct json_object *tools_execute(struct json_object *tools_array) { struct json_object *tools_result_messages = json_object_new_array(); int array_len = json_object_array_length(tools_array); for (int i = 0; i < array_len; i++) { struct json_object *obj = json_object_array_get_idx(tools_array, i); struct json_object *tool_result = json_object_new_object(); json_object_object_add(tool_result, "tool_call_id", json_object_new_string(json_object_get_string( json_object_object_get(obj, "id")))); json_object_object_add(tool_result, "role", json_object_new_string("tool")); struct json_object *type_obj; if (json_object_object_get_ex(obj, "type", &type_obj)) { if (strcmp(json_object_get_string(type_obj), "function")) { continue; } } struct json_object *function_obj; if (json_object_object_get_ex(obj, "function", &function_obj)) { struct json_object *name_obj; char *function_name = NULL; if (json_object_object_get_ex(function_obj, "name", &name_obj)) { function_name = (char *)json_object_get_string(name_obj); } if (!strcmp(function_name, "linux_terminal_execute")) { struct json_object *arguments_obj; if (json_object_object_get_ex(function_obj, "arguments", &arguments_obj)) { struct json_object *arguments = json_tokener_parse(json_object_get_string(arguments_obj)); struct json_object *url_obj; if (json_object_object_get_ex(arguments, "command", &url_obj)) { char *command = (char *)json_object_get_string(url_obj); char *http_result = tool_function_linux_terminal(command); json_object_object_add(tool_result, "content", json_object_new_string(http_result)); } } } if (!strcmp(function_name, "directory_glob")) { struct json_object *arguments_obj; if (json_object_object_get_ex(function_obj, "arguments", &arguments_obj)) { struct json_object *arguments = json_tokener_parse(json_object_get_string(arguments_obj)); struct json_object *path_obj; if (json_object_object_get_ex(arguments, "path", &path_obj)) { char *path = (char *)json_object_get_string(path_obj); char *listing_result = tool_function_directory_glob(path); json_object_object_add(tool_result, "content", json_object_new_string(listing_result)); free(listing_result); } } } if (!strcmp(function_name, "http_fetch")) { struct json_object *arguments_obj; if (json_object_object_get_ex(function_obj, "arguments", &arguments_obj)) { struct json_object *arguments = json_tokener_parse(json_object_get_string(arguments_obj)); struct json_object *url_obj; if (json_object_object_get_ex(arguments, "url", &url_obj)) { char *url = (char *)json_object_get_string(url_obj); char *http_result = tool_function_http_get(url); json_object_object_add(tool_result, "content", json_object_new_string(http_result)); } } } if (!strcmp(function_name, "read_file")) { struct json_object *arguments_obj; if (json_object_object_get_ex(function_obj, "arguments", &arguments_obj)) { struct json_object *path_obj; struct json_object *arguments = json_tokener_parse(json_object_get_string(arguments_obj)); if (json_object_object_get_ex(arguments, "path", &path_obj)) { char *path = (char *)json_object_get_string(path_obj); char *file_content = tool_function_read_file(path); json_object_object_add(tool_result, "content", json_object_new_string(file_content)); free(file_content); } } } if (!strcmp(function_name, "write_file")) { struct json_object *arguments_obj; if (json_object_object_get_ex(function_obj, "arguments", &arguments_obj)) { struct json_object *path_obj, *content_obj; struct json_object *arguments = json_tokener_parse(json_object_get_string(arguments_obj)); if (json_object_object_get_ex(arguments, "path", &path_obj) && json_object_object_get_ex(arguments, "content", &content_obj)) { char *path = (char *)json_object_get_string(path_obj); char *content = (char *)json_object_get_string(content_obj); char *write_result = tool_function_write_file(path, content); json_object_object_add(tool_result, "content", json_object_new_string(write_result)); free(write_result); } } } if (!strcmp(function_name, "directory_rglob")) { struct json_object *arguments_obj; if (json_object_object_get_ex(function_obj, "arguments", &arguments_obj)) { struct json_object *arguments = json_tokener_parse(json_object_get_string(arguments_obj)); struct json_object *path_obj; if (json_object_object_get_ex(arguments, "path", &path_obj)) { char *path = (char *)json_object_get_string(path_obj); char *listing_result = tool_function_directory_rglob(path); json_object_object_add(tool_result, "content", json_object_new_string(listing_result)); free(listing_result); } } } json_object_array_add(tools_result_messages, tool_result); } } return tools_result_messages; } #endif