diff --git a/agent.h b/agent.h deleted file mode 100755 index 649d443..0000000 --- a/agent.h +++ /dev/null @@ -1,381 +0,0 @@ -// retoor - -#ifndef R_AGENT_H -#define R_AGENT_H - -#include "chat.h" -#include "http_curl.h" -#include "messages.h" -#include "r.h" -#include "tools.h" -#include -#include -#include - -#define AGENT_MAX_ITERATIONS 300 -#define AGENT_MAX_TOOL_RETRIES 3 - -typedef enum { - AGENT_STATUS_RUNNING, - AGENT_STATUS_COMPLETED, - AGENT_STATUS_MAX_ITERATIONS, - AGENT_STATUS_ERROR, - AGENT_STATUS_TOOL_ERROR -} agent_status_t; - -typedef struct { - char *goal; - int iteration_count; - int max_iterations; - int tool_retry_count; - int max_tool_retries; - agent_status_t status; - time_t start_time; - char *last_error; - bool verbose; -} agent_state_t; - -static agent_state_t *current_agent = NULL; - -agent_state_t *agent_create(const char *goal) { - agent_state_t *agent = (agent_state_t *)malloc(sizeof(agent_state_t)); - if (agent == NULL) { - return NULL; - } - - agent->goal = goal ? strdup(goal) : NULL; - agent->iteration_count = 0; - agent->max_iterations = AGENT_MAX_ITERATIONS; - agent->tool_retry_count = 0; - agent->max_tool_retries = AGENT_MAX_TOOL_RETRIES; - agent->status = AGENT_STATUS_RUNNING; - agent->start_time = time(NULL); - agent->last_error = NULL; - agent->verbose = is_verbose; - - return agent; -} - -void agent_destroy(agent_state_t *agent) { - if (agent == NULL) { - return; - } - if (agent->goal) { - free(agent->goal); - } - if (agent->last_error) { - free(agent->last_error); - } - free(agent); -} - -void agent_set_error(agent_state_t *agent, const char *error) { - if (agent == NULL) { - return; - } - if (agent->last_error) { - free(agent->last_error); - } - agent->last_error = error ? strdup(error) : NULL; -} - -static struct json_object *agent_process_response(const char *api_url, - const char *json_data) { - char *response = curl_post(api_url, json_data); - if (!response) { - return NULL; - } - - struct json_object *parsed_json = json_tokener_parse(response); - free(response); - - if (!parsed_json) { - return NULL; - } - - struct json_object *error_object; - if (json_object_object_get_ex(parsed_json, "error", &error_object)) { - const char *err_str = json_object_to_json_string(error_object); - fprintf(stderr, "API Error: %s\n", err_str); - json_object_put(parsed_json); - return NULL; - } - - struct json_object *choices_array; - if (!json_object_object_get_ex(parsed_json, "choices", &choices_array)) { - json_object_put(parsed_json); - return NULL; - } - - struct json_object *first_choice = json_object_array_get_idx(choices_array, 0); - if (!first_choice) { - json_object_put(parsed_json); - return NULL; - } - - return first_choice; -} - -static bool agent_has_tool_calls(struct json_object *choice) { - struct json_object *message_obj; - if (!json_object_object_get_ex(choice, "message", &message_obj)) { - return false; - } - - struct json_object *tool_calls; - if (!json_object_object_get_ex(message_obj, "tool_calls", &tool_calls)) { - return false; - } - - return json_object_array_length(tool_calls) > 0; -} - -static const char *agent_get_finish_reason(struct json_object *choice) { - struct json_object *finish_reason_obj; - if (json_object_object_get_ex(choice, "finish_reason", &finish_reason_obj)) { - return json_object_get_string(finish_reason_obj); - } - return NULL; -} - -static char *agent_get_content(struct json_object *choice) { - struct json_object *message_obj; - if (!json_object_object_get_ex(choice, "message", &message_obj)) { - return NULL; - } - - struct json_object *content_obj; - if (!json_object_object_get_ex(message_obj, "content", &content_obj)) { - return NULL; - } - - const char *content = json_object_get_string(content_obj); - return content ? strdup(content) : NULL; -} - -static struct json_object *agent_get_tool_calls(struct json_object *choice) { - struct json_object *message_obj; - if (!json_object_object_get_ex(choice, "message", &message_obj)) { - return NULL; - } - - struct json_object *tool_calls; - if (!json_object_object_get_ex(message_obj, "tool_calls", &tool_calls)) { - return NULL; - } - - return tool_calls; -} - -static struct json_object *agent_get_message(struct json_object *choice) { - struct json_object *message_obj; - if (json_object_object_get_ex(choice, "message", &message_obj)) { - return message_obj; - } - return NULL; -} - -static bool agent_response_indicates_incomplete(const char *content) { - if (content == NULL) { - return false; - } - - const char *incomplete_phrases[] = { - "I'll ", "I will ", "Let me ", "I'm going to ", - "Next, I", "Now I'll", "Now I will", "I'll now", - "I need to", "I should", "I can ", "Going to ", - "Will now", "Proceeding", "Starting to", "About to ", - "First, I", "Then I", "After that", "Following that", - NULL - }; - - for (int i = 0; incomplete_phrases[i] != NULL; i++) { - if (strstr(content, incomplete_phrases[i]) != NULL) { - return true; - } - } - - const char *incomplete_endings[] = { - "...", ":", "files:", "content:", "implementation:", - NULL - }; - - size_t len = strlen(content); - if (len > 3) { - for (int i = 0; incomplete_endings[i] != NULL; i++) { - size_t end_len = strlen(incomplete_endings[i]); - if (len >= end_len && strcmp(content + len - end_len, incomplete_endings[i]) == 0) { - return true; - } - } - } - - return false; -} - -char *agent_run(agent_state_t *agent, const char *user_message) { - if (agent == NULL) { - return NULL; - } - - current_agent = agent; - agent->status = AGENT_STATUS_RUNNING; - agent->iteration_count = 0; - agent->tool_retry_count = 0; - - if (user_message == NULL || *user_message == '\0') { - agent->status = AGENT_STATUS_ERROR; - agent_set_error(agent, "Empty user message"); - return NULL; - } - - char *json_data = chat_json("user", user_message); - if (json_data == NULL) { - agent->status = AGENT_STATUS_ERROR; - agent_set_error(agent, "Failed to create chat JSON"); - return NULL; - } - - char *final_response = NULL; - - while (agent->status == AGENT_STATUS_RUNNING) { - agent->iteration_count++; - - if (agent->iteration_count > agent->max_iterations) { - agent->status = AGENT_STATUS_MAX_ITERATIONS; - agent_set_error(agent, "Maximum iterations reached"); - if (agent->verbose) { - fprintf(stderr, "[Agent] Max iterations (%d) reached\n", - agent->max_iterations); - } - break; - } - - if (agent->verbose) { - fprintf(stderr, "[Agent] Iteration %d/%d\n", agent->iteration_count, - agent->max_iterations); - } - - struct json_object *choice = - agent_process_response(get_completions_api_url(), json_data); - - if (choice == NULL) { - agent->tool_retry_count++; - if (agent->tool_retry_count >= agent->max_tool_retries) { - agent->status = AGENT_STATUS_ERROR; - agent_set_error(agent, "API request failed after retries"); - break; - } - if (agent->verbose) { - fprintf(stderr, "[Agent] API error, retry %d/%d\n", - agent->tool_retry_count, agent->max_tool_retries); - } - continue; - } - - agent->tool_retry_count = 0; - - struct json_object *message_obj = agent_get_message(choice); - if (message_obj) { - message_add_object(json_object_get(message_obj)); - } - - const char *finish_reason = agent_get_finish_reason(choice); - bool has_tools = agent_has_tool_calls(choice); - - if (agent->verbose) { - fprintf(stderr, "[Agent] finish_reason=%s, has_tool_calls=%s\n", - finish_reason ? finish_reason : "null", - has_tools ? "true" : "false"); - } - - if (has_tools) { - struct json_object *tool_calls = agent_get_tool_calls(choice); - - if (agent->verbose) { - int num_tools = json_object_array_length(tool_calls); - fprintf(stderr, "[Agent] Executing %d tool(s)\n", num_tools); - } - - struct json_object *tool_results = tools_execute(tool_calls); - - int results_count = json_object_array_length(tool_results); - for (int i = 0; i < results_count; i++) { - struct json_object *tool_result = - json_object_array_get_idx(tool_results, i); - message_add_tool_call(json_object_get(tool_result)); - } - - json_data = chat_json(NULL, NULL); - if (json_data == NULL) { - agent->status = AGENT_STATUS_ERROR; - agent_set_error(agent, "Failed to create follow-up JSON"); - break; - } - - } else { - char *content = agent_get_content(choice); - - if (content && agent_response_indicates_incomplete(content)) { - if (agent->verbose) { - fprintf(stderr, "[Agent] Response indicates incomplete work, auto-continuing\n"); - } - - free(content); - json_data = chat_json("user", "Continue. Execute the necessary actions to complete the task."); - if (json_data == NULL) { - agent->status = AGENT_STATUS_ERROR; - agent_set_error(agent, "Failed to create continue JSON"); - break; - } - - } else { - final_response = content; - agent->status = AGENT_STATUS_COMPLETED; - - if (agent->verbose) { - fprintf(stderr, "[Agent] Completed in %d iteration(s)\n", - agent->iteration_count); - } - } - } - } - - current_agent = NULL; - return final_response; -} - -char *agent_chat(const char *user_message) { - agent_state_t *agent = agent_create(user_message); - if (agent == NULL) { - return NULL; - } - - char *response = agent_run(agent, user_message); - - if (agent->verbose && agent->status != AGENT_STATUS_COMPLETED && agent->last_error) { - fprintf(stderr, "[Agent] Error: %s\n", agent->last_error); - } - - agent_destroy(agent); - return response; -} - -char *agent_chat_with_limit(const char *user_message, int max_iterations) { - agent_state_t *agent = agent_create(user_message); - if (agent == NULL) { - return NULL; - } - - agent->max_iterations = max_iterations; - char *response = agent_run(agent, user_message); - - if (agent->verbose && agent->status != AGENT_STATUS_COMPLETED && agent->last_error) { - fprintf(stderr, "[Agent] Error: %s\n", agent->last_error); - } - - agent_destroy(agent); - return response; -} - -#endif diff --git a/auth.h b/auth.h deleted file mode 100755 index 33763e1..0000000 --- a/auth.h +++ /dev/null @@ -1,75 +0,0 @@ -// Written by retoor@molodetz.nl - -// This source code retrieves an API key from environment variables or defaults -// to a hardcoded key if none are found. - -// Uses the C standard library functions from stdlib.h for environment -// management and stdio.h for error handling. - -// MIT License - -#ifndef R_AUTH_H -#define R_AUTH_H - -#include -#include - -enum AUTH_TYPE { AUTH_TYPE_NONE, AUTH_TYPE_API_KEY, AUTH_TYPE_FREE }; - -int auth_type = AUTH_TYPE_NONE; - -void auth_free() { auth_type = AUTH_TYPE_FREE; } - -void auth_init() { - - char *api_key = NULL; - if (auth_type != AUTH_TYPE_FREE) { - - api_key = getenv("R_KEY"); - if (api_key) { - auth_type = AUTH_TYPE_API_KEY; - return; - } - api_key = getenv("OPENROUTER_API_KEY"); - if (api_key) { - auth_type = AUTH_TYPE_API_KEY; - return; - } - api_key = getenv("OPENAI_API_KEY"); - if (api_key) { - auth_type = AUTH_TYPE_API_KEY; - return; - } - } - auth_type = AUTH_TYPE_FREE; - return; -} - -const char *resolve_api_key() { - static char *api_key = NULL; - if (auth_type != AUTH_TYPE_FREE) { - - api_key = getenv("R_KEY"); - if (api_key) { - auth_type = AUTH_TYPE_API_KEY; - return api_key; - } - api_key = getenv("OPENROUTER_API_KEY"); - if (api_key) { - auth_type = AUTH_TYPE_API_KEY; - return api_key; - } - api_key = getenv("OPENAI_API_KEY"); - if (api_key) { - auth_type = AUTH_TYPE_API_KEY; - return api_key; - } - } - auth_type = AUTH_TYPE_FREE; - api_key = "sk-proj-d798HLfWYBeB9HT_o7isaY0s88631IaYhhOR5IVAd4D_fF-" - "SQ5z46BCr8iDi1ang1rUmlagw55T3BlbkFJ6IOsqhAxNN9Zt6ERDBnv2p2HCc2fDgc" - "5DsNhPxdOzYb009J6CNd4wILPsFGEoUdWo4QrZ1eOkA"; - return api_key; -} - -#endif diff --git a/browse.c b/browse.c deleted file mode 100755 index f8c547f..0000000 --- a/browse.c +++ /dev/null @@ -1,17 +0,0 @@ -#include "browse.h" -#include -#include - -int main() { - char *query = "example"; - char *result = get_news(query); - - if (result) { - printf("Result: %s\n", result); - free(result); // Free the allocated memory for the result - } else { - fprintf(stderr, "Error: Failed to fetch news.\n"); - } - - return 0; -} \ No newline at end of file diff --git a/browse.h b/browse.h deleted file mode 100755 index c10011e..0000000 --- a/browse.h +++ /dev/null @@ -1,23 +0,0 @@ -#include "http_curl.h" -#include -#include -#include -#include - -char *url_encode(char *s) { return curl_easy_escape(NULL, s, 0); } - -char *web_search(char *q) { - char url[4096]; - char *q_encoded = url_encode(q); - if (!q_encoded) { - return NULL; - } - snprintf(url, sizeof(url), "https://static.molodetz.nl/search.cgi?query=%s", q_encoded); - free(q_encoded); - char *ret = curl_get(url); - return ret; -} - -char *web_search_news(char *q) { - return web_search(q); -} diff --git a/chat.h b/chat.h deleted file mode 100755 index c3b2d23..0000000 --- a/chat.h +++ /dev/null @@ -1,71 +0,0 @@ -// Written by retoor@molodetz.nl - -// This code defines functionality for creating and managing JSON-based chat -// prompts using a specific AI model configuration, providing easy integration -// with message handling and HTTP communication for dynamic applications. - -// Non-standard imports: json-c library for handling JSON objects. - -// MIT License -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#ifndef R_PROMPT_H -#define R_PROMPT_H - -#include "auth.h" -#include "messages.h" -#include "r.h" -#include "tools.h" -#include -static json_object *_prompt = NULL; - -void chat_free() { - if (_prompt == NULL) - return; - json_object_put(_prompt); - _prompt = NULL; -} - -char *chat_json(const char *role, const char *message) { - chat_free(); - json_object *root_object = json_object_new_object(); - if (root_object == NULL) { - return NULL; - } - json_object_object_add(root_object, "model", - json_object_new_string(get_prompt_model())); - - if (role != NULL && message != NULL) { - message_add(role, message); - if (use_tools()) { - json_object_object_add(root_object, "tools", tools_descriptions()); - } - } - - json_object_object_add(root_object, "messages", json_object_get(message_list())); - json_object_object_add(root_object, "temperature", - json_object_new_double(PROMPT_TEMPERATURE)); - - _prompt = root_object; - return (char *)json_object_to_json_string_ext(root_object, - JSON_C_TO_STRING_PRETTY); -} - -#endif diff --git a/db_utils.c b/db_utils.c deleted file mode 100755 index f156374..0000000 --- a/db_utils.c +++ /dev/null @@ -1,64 +0,0 @@ -#include "db_utils.h" -#include -#include -#include - -void db_initialize() { - sqlite3 *db; - char *err_msg = 0; - int rc = sqlite3_open("database.db", &db); - - if (rc != SQLITE_OK) { - fprintf(stderr, "Error: Cannot open database: %s\n", sqlite3_errmsg(db)); - return; - } - - const char *sql = - "CREATE TABLE IF NOT EXISTS kv_store (key TEXT PRIMARY KEY, value TEXT);"; - rc = sqlite3_exec(db, sql, 0, 0, &err_msg); - - if (rc != SQLITE_OK) { - fprintf(stderr, "SQL error: %s\n", err_msg); - sqlite3_free(err_msg); - } - - sqlite3_close(db); -} - -void test_db_set() { - json_object *result = db_set("test_key", "test_value"); - if (result) { - printf("db_set: %s\n", json_object_get_string(result)); - json_object_put(result); - } else { - printf("db_set failed\n"); - } -} - -void test_db_get() { - json_object *result = db_get("test_key"); - if (result) { - printf("db_get: %s\n", json_object_to_json_string(result)); - json_object_put(result); - } else { - printf("db_get failed\n"); - } -} - -void test_db_query() { - json_object *result = db_query("SELECT * FROM kv_store"); - if (result) { - printf("db_query: %s\n", json_object_to_json_string(result)); - json_object_put(result); - } else { - printf("db_query failed\n"); - } -} - -int main() { - db_initialize(); - test_db_set(); - test_db_get(); - test_db_query(); - return 0; -} \ No newline at end of file diff --git a/db_utils.h b/db_utils.h deleted file mode 100755 index f94af07..0000000 --- a/db_utils.h +++ /dev/null @@ -1,236 +0,0 @@ -#ifndef DB_UTILS_H -#define DB_UTILS_H - -#include "r.h" -#include "utils.h" -#include -#include - -json_object *db_execute(const char *query); - -char *db_file_expanded() { - char *expanded = expand_home_directory(DB_FILE); - static char result[4096]; - result[0] = 0; - strcpy(result, expanded); - free(expanded); - return result; -} - -void db_initialize(); -json_object *db_set(const char *key, const char *value); -json_object *db_get(const char *key); -json_object *db_query(const char *query); - -void db_initialize() { - sqlite3 *db; - int rc = sqlite3_open(db_file_expanded(), &db); - - if (rc != SQLITE_OK) { - return; - } - - db_execute("CREATE TABLE IF NOT EXISTS kv_store (key TEXT PRIMARY KEY, value " - "TEXT);"); - db_execute("CREATE TABLE IF NOT EXISTS file_version_history ( id INTEGER " - "PRIMARY KEY AUTOINCREMENT," - "path TEXT NOT NULL," - "content TEXT," - "date DATETIME DEFAULT CURRENT_TIMESTAMP" - ");"); - - sqlite3_close(db); -} - -json_object *db_set(const char *key, const char *value) { - sqlite3 *db; - char *err_msg = 0; - int rc = sqlite3_open(db_file_expanded(), &db); - - if (rc != SQLITE_OK) { - return NULL; - } - - char *sql = - sqlite3_mprintf("INSERT INTO kv_store (key, value) VALUES (%Q, %Q) ON " - "CONFLICT(key) DO UPDATE SET value = %Q WHERE key = %Q", - key, value, value, key); - rc = sqlite3_exec(db, sql, 0, 0, &err_msg); - sqlite3_free(sql); - - if (rc != SQLITE_OK) { - sqlite3_free(err_msg); - sqlite3_close(db); - return NULL; - } - - sqlite3_close(db); - return json_object_new_string("Success"); -} - -json_object *db_get(const char *key) { - sqlite3 *db; - sqlite3_stmt *stmt; - json_object *result = json_object_new_object(); - if (result == NULL) { - return NULL; - } - - int rc = sqlite3_open(db_file_expanded(), &db); - if (rc != SQLITE_OK) { - json_object_put(result); - return NULL; - } - - const char *sql = "SELECT value FROM kv_store WHERE key = ?"; - rc = sqlite3_prepare_v2(db, sql, -1, &stmt, NULL); - if (rc != SQLITE_OK) { - sqlite3_close(db); - json_object_put(result); - return NULL; - } - sqlite3_bind_text(stmt, 1, key, -1, SQLITE_STATIC); - - if (sqlite3_step(stmt) == SQLITE_ROW) { - const char *value = (const char *)sqlite3_column_text(stmt, 0); - if (value) { - json_object_object_add(result, "value", json_object_new_string(value)); - } else { - json_object_object_add(result, "error", - json_object_new_string("Key not found")); - } - } else { - json_object_object_add(result, "error", - json_object_new_string("Key not found")); - } - - sqlite3_finalize(stmt); - sqlite3_close(db); - return result; -} -json_object *db_query(const char *query) { - sqlite3 *db; - sqlite3_stmt *stmt; - - if (strncmp(query, "SELECT", 6)) { - return db_execute(query); - } - - json_object *result = json_object_new_array(); - if (result == NULL) { - return NULL; - } - - int rc = sqlite3_open(db_file_expanded(), &db); - if (rc != SQLITE_OK) { - json_object_put(result); - return NULL; - } - - rc = sqlite3_prepare_v2(db, query, -1, &stmt, NULL); - if (rc != SQLITE_OK) { - sqlite3_close(db); - json_object_put(result); - return NULL; - } - while (sqlite3_step(stmt) == SQLITE_ROW) { - json_object *row = json_object_new_object(); - for (int i = 0; i < sqlite3_column_count(stmt); i++) { - const char *col_name = sqlite3_column_name(stmt, i); - switch (sqlite3_column_type(stmt, i)) { - case SQLITE_INTEGER: - json_object_object_add( - row, col_name, - json_object_new_int64(sqlite3_column_int64(stmt, i))); - break; - case SQLITE_FLOAT: - json_object_object_add( - row, col_name, - json_object_new_double(sqlite3_column_double(stmt, i))); - break; - case SQLITE_TEXT: - json_object_object_add( - row, col_name, - json_object_new_string((const char *)sqlite3_column_text(stmt, i))); - break; - case SQLITE_BLOB: - json_object_object_add(row, col_name, - json_object_new_string_len( - (const char *)sqlite3_column_blob(stmt, i), - sqlite3_column_bytes(stmt, i))); - break; - case SQLITE_NULL: - default: - json_object_object_add(row, col_name, json_object_new_string("NULL")); - break; - } - } - json_object_array_add(result, row); - } - - sqlite3_finalize(stmt); - sqlite3_close(db); - - return result; -} - -json_object *db_execute(const char *query) { - sqlite3 *db; - char *err_msg = 0; - int rc = sqlite3_open(db_file_expanded(), &db); - json_object *result = json_object_new_object(); - - if (rc != SQLITE_OK) { - json_object_object_add(result, "error", - json_object_new_string("Cannot open database")); - return result; - } - - rc = sqlite3_exec(db, query, 0, 0, &err_msg); - if (rc != SQLITE_OK) { - json_object_object_add(result, "error", json_object_new_string(err_msg)); - sqlite3_free(err_msg); - } else { - json_object_object_add( - result, "success", - json_object_new_string("Query executed successfully")); - } - - sqlite3_close(db); - return result; -} -void db_store_file_version(const char *path) { - char *expanded = expand_home_directory(path); - if (expanded == NULL) { - return; - } - - char *content = read_file(expanded); - if (!content) { - free(expanded); - return; - } - fprintf(stderr, "Creating backup:: %s\n", expanded); - char *formatted = sqlite3_mprintf( - "INSERT INTO file_version_history (path, content) VALUES (%Q, %Q)", - expanded, content); - if (formatted) { - db_execute(formatted); - sqlite3_free(formatted); - } - free(content); - free(expanded); -} -char *db_get_schema() { - json_object *tables = - db_query("SELECT * FROM sqlite_master WHERE type='table'"); - if (tables == NULL) { - return strdup("[]"); - } - const char *str = json_object_to_json_string(tables); - char *result = str ? strdup(str) : strdup("[]"); - json_object_put(tables); - return result; -} - -#endif diff --git a/http_curl.h b/http_curl.h deleted file mode 100755 index 3ef7eed..0000000 --- a/http_curl.h +++ /dev/null @@ -1,232 +0,0 @@ -// Written by retoor@molodetz.nl - -// This code defines a simple HTTP client using libcurl in C. It provides -// functions for executing POST and GET HTTP requests with JSON data, including -// authorization via a bearer token. The functions `curl_post` and `curl_get` -// handle these operations and return the server's response as a string. - -// Uses libcurl for HTTP requests and includes a custom "auth.h" for API key -// resolution. - -// MIT License -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: the above copyright -// notice and this permission notice shall be included in all copies or -// substantial portions of the Software. The Software is provided "as is", -// without warranty of any kind, express or implied, including but not limited -// to the warranties of merchantability, fitness for a particular purpose and -// noninfringement. In no event shall the authors or copyright holders be liable -// for any claim, damages or other liability, whether in an action of contract, -// tort or otherwise, arising from, out of or in connection with the software or -// the use or other dealings in the Software. - -#ifndef HTTP_CURL -#define HTTP_CURL - -#include "auth.h" -#include -#include -#include -#include -#include -#include -#include -#include - -struct ResponseBuffer { - char *data; - size_t size; -}; - -static size_t WriteCallback(void *contents, size_t size, size_t nmemb, - void *userp) { - size_t total_size = size * nmemb; - struct ResponseBuffer *response = (struct ResponseBuffer *)userp; - - if (total_size > SIZE_MAX - response->size - 1) { - fprintf(stderr, "Response too large\n"); - return 0; - } - - char *ptr = realloc(response->data, response->size + total_size + 1); - if (ptr == NULL) { - fprintf(stderr, "Failed to allocate memory for response\n"); - return 0; - } - response->data = ptr; - memcpy(&(response->data[response->size]), contents, total_size); - response->size += total_size; - response->data[response->size] = '\0'; - return total_size; -} - -#define HTTP_MAX_RETRIES 3 -#define HTTP_RETRY_DELAY_MS 2000 - -static struct timespec spinner_start_time = {0, 0}; -static volatile int spinner_running = 0; - -static double get_elapsed_seconds() { - struct timespec now; - clock_gettime(CLOCK_MONOTONIC, &now); - return (now.tv_sec - spinner_start_time.tv_sec) + - (now.tv_nsec - spinner_start_time.tv_nsec) / 1e9; -} - -static void *spinner_thread(void *arg) { - (void)arg; - const char *frames[] = {"⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"}; - int frame = 0; - while (spinner_running) { - double elapsed = get_elapsed_seconds(); - fprintf(stderr, "\r%s Querying AI... (%.1fs) ", frames[frame % 10], elapsed); - fflush(stderr); - frame++; - usleep(80000); - } - return NULL; -} - -char *curl_post(const char *url, const char *data) { - CURL *curl; - CURLcode res; - struct ResponseBuffer response = {NULL, 0}; - int retry_count = 0; - pthread_t spinner_tid; - - clock_gettime(CLOCK_MONOTONIC, &spinner_start_time); - spinner_running = 1; - pthread_create(&spinner_tid, NULL, spinner_thread, NULL); - - while (retry_count < HTTP_MAX_RETRIES) { - if (response.data) { - free(response.data); - } - response.data = malloc(1); - response.size = 0; - if (!response.data) { - spinner_running = 0; - pthread_join(spinner_tid, NULL); - return NULL; - } - response.data[0] = '\0'; - - curl = curl_easy_init(); - if (!curl) { - free(response.data); - spinner_running = 0; - pthread_join(spinner_tid, NULL); - return NULL; - } - - struct curl_slist *headers = NULL; - curl_easy_setopt(curl, CURLOPT_URL, url); - curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 10L); - curl_easy_setopt(curl, CURLOPT_TIMEOUT, 300L); - headers = curl_slist_append(headers, "Content-Type: application/json"); - char bearer_header[1337]; - snprintf(bearer_header, sizeof(bearer_header), "Authorization: Bearer %s", - resolve_api_key()); - headers = curl_slist_append(headers, bearer_header); - curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); - curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data); - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback); - curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&response); - - res = curl_easy_perform(curl); - curl_slist_free_all(headers); - curl_easy_cleanup(curl); - - if (res == CURLE_OK) { - spinner_running = 0; - pthread_join(spinner_tid, NULL); - fprintf(stderr, "\r \r"); - fflush(stderr); - return response.data; - } - - retry_count++; - spinner_running = 0; - pthread_join(spinner_tid, NULL); - fprintf(stderr, "\r \r"); - fprintf(stderr, "Network error: %s (attempt %d/%d)\n", - curl_easy_strerror(res), retry_count, HTTP_MAX_RETRIES); - - if (retry_count < HTTP_MAX_RETRIES) { - fprintf(stderr, "Retrying in %d seconds...\n", HTTP_RETRY_DELAY_MS / 1000); - usleep(HTTP_RETRY_DELAY_MS * 1000); - clock_gettime(CLOCK_MONOTONIC, &spinner_start_time); - spinner_running = 1; - pthread_create(&spinner_tid, NULL, spinner_thread, NULL); - } - } - - fprintf(stderr, "Failed after %d attempts.\n", HTTP_MAX_RETRIES); - free(response.data); - return NULL; -} - -char *curl_get(const char *url) { - CURL *curl; - CURLcode res; - struct ResponseBuffer response = {NULL, 0}; - int retry_count = 0; - - while (retry_count < HTTP_MAX_RETRIES) { - if (response.data) { - free(response.data); - } - response.data = malloc(1); - response.size = 0; - if (!response.data) { - return NULL; - } - response.data[0] = '\0'; - - curl = curl_easy_init(); - if (!curl) { - free(response.data); - return NULL; - } - - struct curl_slist *headers = NULL; - curl_easy_setopt(curl, CURLOPT_URL, url); - curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 10L); - curl_easy_setopt(curl, CURLOPT_TIMEOUT, 60L); - headers = curl_slist_append(headers, "Content-Type: application/json"); - char bearer_header[1337]; - snprintf(bearer_header, sizeof(bearer_header), "Authorization: Bearer %s", - resolve_api_key()); - headers = curl_slist_append(headers, bearer_header); - curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback); - curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&response); - - res = curl_easy_perform(curl); - curl_slist_free_all(headers); - curl_easy_cleanup(curl); - - if (res == CURLE_OK) { - return response.data; - } - - retry_count++; - fprintf(stderr, "Network error: %s (attempt %d/%d)\n", - curl_easy_strerror(res), retry_count, HTTP_MAX_RETRIES); - - if (retry_count < HTTP_MAX_RETRIES) { - fprintf(stderr, "Retrying in %d seconds...\n", HTTP_RETRY_DELAY_MS / 1000); - usleep(HTTP_RETRY_DELAY_MS * 1000); - } - } - - fprintf(stderr, "Failed after %d attempts.\n", HTTP_MAX_RETRIES); - free(response.data); - return NULL; -} - -#endif \ No newline at end of file diff --git a/indexer.h b/indexer.h deleted file mode 100755 index d38c9d1..0000000 --- a/indexer.h +++ /dev/null @@ -1,147 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define MAX_FILES 20000 -#define MAX_PATH 4096 - -static const char *extensions[] = { - ".c", ".cpp", ".h", ".py", ".java", ".js", - ".mk", ".html", "Makefile", ".css", ".json", ".cs", - ".csproj", ".sln", ".toml", ".rs", ".go", ".rb", - ".swift", ".php", ".pl", ".sh", ".bash", ".sql", - ".xml", ".yaml", ".yml", ".kt", ".dart", ".scala", - ".clj", ".asm", ".m", ".r", ".lua", ".groovy", - ".v", ".pas", ".d", ".f90", ".f95", ".for", - ".s", ".tcl", ".vhdl", ".verilog", ".coffee", ".less", - ".scss", ".ps1", ".psm1", ".cmd", ".bat", ".json5", - ".cxx", ".cc", ".hpp", ".hxx", ".inc", ".nsi", - ".ninja", ".cmake", ".cmake.in", ".mk.in", ".make", ".makefile", - ".gyp", ".gypi", ".pro", ".qml", ".ui", ".wxs", - ".wxl", ".wxi", ".wxl", ".wxs", ".wxi", ".wxl", - ".wxs", ".wxi"}; -static const size_t ext_count = sizeof(extensions) / sizeof(extensions[0]); - -typedef struct { - char name[MAX_PATH]; - char modification_date[20]; - char creation_date[20]; - char type[10]; - size_t size_bytes; -} FileInfo; - -static FileInfo file_list[MAX_FILES]; -static size_t file_count = 0; - -static int is_valid_extension(const char *filename) { - const char *dot = strrchr(filename, '.'); - if (!dot) - dot = filename; - for (size_t i = 0; i < ext_count; i++) { - if (strcmp(dot, extensions[i]) == 0) - return 1; - } - return 0; -} - -static int is_ignored_directory(const char *dir_name) { - const char *ignored_dirs[] = {"env", ".venv", "node_modules", "venv", - "virtualenv"}; - for (size_t i = 0; i < sizeof(ignored_dirs) / sizeof(ignored_dirs[0]); i++) { - if (strcmp(dir_name, ignored_dirs[i]) == 0) - return 1; - } - return 0; -} - -static int get_file_info(const char *path) { - if (file_count >= MAX_FILES) { - return -1; - } - struct stat file_stat; - if (stat(path, &file_stat) == 0) { - FileInfo info; - strncpy(info.name, path, MAX_PATH - 1); - info.name[MAX_PATH - 1] = '\0'; - strftime(info.modification_date, sizeof(info.modification_date), - "%Y-%m-%d %H:%M:%S", localtime(&file_stat.st_mtime)); - strftime(info.creation_date, sizeof(info.creation_date), - "%Y-%m-%d %H:%M:%S", localtime(&file_stat.st_ctime)); - strncpy(info.type, S_ISDIR(file_stat.st_mode) ? "directory" : "file", - sizeof(info.type) - 1); - info.type[sizeof(info.type) - 1] = '\0'; - info.size_bytes = file_stat.st_size; - file_list[file_count++] = info; - return 0; - } - return -1; -} - -char *index_directory(const char *dir_path) { - DIR *dir = opendir(dir_path); - if (!dir) { - perror("Failed to open directory"); - return NULL; - } - - struct dirent *entry; - json_object *jarray = json_object_new_array(); - if (jarray == NULL) { - closedir(dir); - return NULL; - } - - while ((entry = readdir(dir)) != NULL) { - if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) - continue; - if (entry->d_name[0] == '.' || is_ignored_directory(entry->d_name)) - continue; - - char full_path[MAX_PATH]; - snprintf(full_path, sizeof(full_path), "%s/%s", dir_path, entry->d_name); - - if (entry->d_type == DT_DIR) { - char *subdir_json = index_directory(full_path); - if (subdir_json) { - json_object *jsubdir = json_object_new_string(subdir_json); - json_object_array_add(jarray, jsubdir); - free(subdir_json); - } - } else if (is_valid_extension(entry->d_name)) { - if (get_file_info(full_path) == 0 && file_count > 0) { - json_object *jfile = json_object_new_object(); - if (jfile == NULL) { - continue; - } - json_object_object_add( - jfile, "file_name", - json_object_new_string(file_list[file_count - 1].name)); - json_object_object_add( - jfile, "modification_date", - json_object_new_string(file_list[file_count - 1].modification_date)); - json_object_object_add( - jfile, "creation_date", - json_object_new_string(file_list[file_count - 1].creation_date)); - json_object_object_add( - jfile, "type", - json_object_new_string(file_list[file_count - 1].type)); - json_object_object_add( - jfile, "size_bytes", - json_object_new_int64(file_list[file_count - 1].size_bytes)); - - json_object_array_add(jarray, jfile); - } - } - } - closedir(dir); - - char *result = strdup(json_object_to_json_string(jarray)); - json_object_put(jarray); - return result; -} \ No newline at end of file diff --git a/line.h b/line.h deleted file mode 100755 index da22ee0..0000000 --- a/line.h +++ /dev/null @@ -1,123 +0,0 @@ -// Written by retoor@molodetz.nl - -// This source code provides command-line input functionalities with -// autocomplete and history features using the readline library. It allows users -// to complete commands and manage input history. - -// External includes: -// - -// - -// - - -// MIT License: Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction. - -#include "utils.h" -#include -#include -#include -#include -#include -#define HISTORY_FILE "~/.r_history" - -bool line_initialized = false; - -char *get_history_file() { - static char result[4096]; - result[0] = '\0'; - - char *expanded = expand_home_directory(HISTORY_FILE); - if (expanded == NULL) { - return result; - } - strncpy(result, expanded, sizeof(result) - 1); - result[sizeof(result) - 1] = '\0'; - free(expanded); - return result; -} - -char *line_command_generator(const char *text, int state) { - static int list_index, len = 0; - const char *commands[] = {"help", "exit", "list", "review", - "refactor", "obfuscate", "!verbose", "!dump", - "!model", "!debug", NULL}; - - if (!state) { - list_index = 0; - len = strlen(text); - } - - while (commands[list_index]) { - const char *command = commands[list_index++]; - if (strncmp(command, text, len) == 0) { - return strdup(command); - } - } - - return NULL; -} - -char *line_file_generator(const char *text, int state) { - static int list_index; - static glob_t glob_result; - static int glob_valid = 0; - char pattern[1024]; - - if (!state) { - if (glob_valid) { - globfree(&glob_result); - glob_valid = 0; - } - list_index = 0; - snprintf(pattern, sizeof(pattern), "%s*", text); - if (glob(pattern, GLOB_NOSORT, NULL, &glob_result) == 0) { - glob_valid = 1; - } else { - return NULL; - } - } - - if (glob_valid && (size_t)list_index < glob_result.gl_pathc) { - return strdup(glob_result.gl_pathv[list_index++]); - } - - if (glob_valid) { - globfree(&glob_result); - glob_valid = 0; - } - return NULL; -} - -char **line_command_completion(const char *text, int start, int end) { - rl_attempted_completion_over = 1; - - // Check if the input is a file path - if (start > 0 && text[0] != ' ') { - return rl_completion_matches(text, line_file_generator); - } - - return rl_completion_matches(text, line_command_generator); -} - -void line_init() { - if (!line_initialized) { - rl_attempted_completion_function = line_command_completion; - line_initialized = true; - read_history(get_history_file()); - } -} - -char *line_read(char *prefix) { - char *data = readline(prefix); - if (!(data && *data)) { - free(data); - return NULL; - } - return data; -} - -void line_add_history(char *data) { - add_history(data); - write_history(get_history_file()); -} diff --git a/main.c b/main.c deleted file mode 100755 index 974f0c1..0000000 --- a/main.c +++ /dev/null @@ -1,387 +0,0 @@ -#include "agent.h" -#include "db_utils.h" -#include "line.h" -#include "markdown.h" -#include "openai.h" -#include "r.h" -#include "tools.h" -#include "utils.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -volatile sig_atomic_t sigint_count = 0; -time_t first_sigint_time = 0; -bool SYNTAX_HIGHLIGHT_ENABLED = true; -bool API_MODE = false; - -static void render(const char *content); -static bool openai_include(const char *path); -static char *get_prompt_from_stdin(char *prompt); -static char *get_prompt_from_args(int argc, char **argv); -static bool try_prompt(int argc, char *argv[]); -static void repl(void); -static void init(void); -static void handle_sigint(int sig); - -char *get_env_string() { - FILE *fp = popen("env", "r"); - if (fp == NULL) { - perror("popen failed"); - return NULL; - } - - size_t buffer_size = 1024; - size_t total_size = 0; - char *output = malloc(buffer_size); - if (output == NULL) { - perror("malloc failed"); - pclose(fp); - return NULL; - } - - size_t bytes_read; - while ((bytes_read = fread(output + total_size, 1, buffer_size - total_size, - fp)) > 0) { - total_size += bytes_read; - if (total_size >= buffer_size) { - buffer_size *= 2; - char *temp = realloc(output, buffer_size); - if (temp == NULL) { - perror("realloc failed"); - free(output); - pclose(fp); - return NULL; - } - output = temp; - } - } - - // Null-terminate the output - output[total_size] = '\0'; - - pclose(fp); - return output; -} - -static char *get_prompt_from_stdin(char *prompt) { - int index = 0; - int c; - while ((c = getchar()) != EOF) { - prompt[index++] = (char)c; - } - prompt[index] = '\0'; - return prompt; -} - -static char *get_prompt_from_args(int argc, char **argv) { - char *prompt = malloc(10 * 1024 * 1024 + 1); - char *system = malloc(1024 * 1024); - if (!prompt || !system) { - fprintf(stderr, "Error: Memory allocation failed.\n"); - free(prompt); - free(system); - return NULL; - } - - bool get_from_std_in = false; - - for (int i = 1; i < argc; i++) { - if (strcmp(argv[i], "--stdin") == 0) { - fprintf(stderr, "Reading from stdin.\n"); - get_from_std_in = true; - } else if (strcmp(argv[i], "--verbose") == 0) { - is_verbose = true; - } else if (strcmp(argv[i], "--py") == 0 && i + 1 < argc) { - char *py_file_path = expand_home_directory(argv[++i]); - fprintf(stderr, "Including \"%s\".\n", py_file_path); - openai_include(py_file_path); - free(py_file_path); - } else if (strcmp(argv[i], "--free") == 0) { - auth_free(); - } else if (strcmp(argv[i], "--context") == 0 && i + 1 < argc) { - char *context_file_path = argv[++i]; - fprintf(stderr, "Including \"%s\".\n", context_file_path); - openai_include(context_file_path); - } else if (strcmp(argv[i], "--api") == 0) { - API_MODE = true; - } else if (strcmp(argv[i], "--nh") == 0) { - SYNTAX_HIGHLIGHT_ENABLED = false; - fprintf(stderr, "Syntax highlighting disabled.\n"); - } else if (strncmp(argv[i], "--session=", 10) == 0) { - continue; - } else if (strcmp(argv[i], "-s") == 0 || strcmp(argv[i], "--session") == 0) { - i++; - continue; - } else { - strcat(system, argv[i]); - strcat(system, (i < argc - 1) ? " " : "."); - } - } - - if (get_from_std_in) { - if (*system) - openai_system(system); - prompt = get_prompt_from_stdin(prompt); - } else { - free(prompt); - prompt = system; - } - - if (!*prompt) { - free(prompt); - return NULL; - } - return prompt; -} - -static bool try_prompt(int argc, char *argv[]) { - char *prompt = get_prompt_from_args(argc, argv); - if (prompt) { - char *response = agent_chat(prompt); - if (!response) { - printf("Could not get response from server\n"); - free(prompt); - return false; - } - render(response); - free(response); - free(prompt); - return true; - } - return false; -} - -static bool openai_include(const char *path) { - char *file_content = read_file(path); - if (!file_content) - return false; - - openai_system(file_content); - free(file_content); - return true; -} - -static void render(const char *content) { - if (SYNTAX_HIGHLIGHT_ENABLED) { - parse_markdown_to_ansi(content); - } else { - printf("%s", content); - } -} - -static void repl(void) { - line_init(); - char *line = NULL; - - while (true) { - line = line_read("> "); - if (!line || !*line) - continue; - - if (!strncmp(line, "!dump", 5)) { - printf("%s\n", message_json()); - continue; - } - if(!strncmp(line,"!debug",6)) - { - printf("%s\n", R_BASE_URL); - continue; - } - if (!strncmp(line, "!clear", 6)) { - messages_remove(); - fprintf(stderr, "Session cleared.\n"); - continue; - } - if (!strncmp(line, "!session", 8)) { - init_session_id(); - printf("Session: %s\n", get_session_id()); - printf("DB Key: %s\n", get_session_db_key()); - continue; - } - if (!strncmp(line, "!verbose", 8)) { - is_verbose = !is_verbose; - fprintf(stderr, "%s\n", - is_verbose ? "Verbose mode enabled" : "Verbose mode disabled"); - continue; - } - if (line && *line != '\n') - line_add_history(line); - if (!strncmp(line, "!tools", 6)) { - printf("Available tools: %s\n", - json_object_to_json_string(tools_descriptions())); - continue; - } - - if (!strncmp(line, "!models", 7)) { - printf("Current model: %s\n", openai_fetch_models()); - continue; - } - if (!strncmp(line, "!model", 6)) { - if (line[6] == ' ') { - set_prompt_model(line + 7); - } - printf("Current model: %s\n", get_prompt_model()); - continue; - } - if (!strncmp(line, "exit", 4)) - exit(0); - - while (line && *line != '\n') { - char *response = agent_chat(line); - if (response) { - render(response); - printf("\n"); - if (strstr(response, "_STEP_")) { - line = "continue"; - } else { - line = NULL; - } - free(response); - } else { - fprintf(stderr, "Agent returned no response\n"); - line = NULL; - } - } - } -} - -static void init(void) { - setbuf(stdout, NULL); - line_init(); - auth_init(); - db_initialize(); - - char *schema = db_get_schema(); - char payload[1024 * 1024] = {0}; - - time_t now = time(NULL); - struct tm *tm_info = localtime(&now); - char datetime[64]; - strftime(datetime, sizeof(datetime), "%Y-%m-%d %H:%M:%S %Z", tm_info); - - char cwd[4096]; - if (!getcwd(cwd, sizeof(cwd))) { - strcpy(cwd, "unknown"); - } - - snprintf( - payload, sizeof(payload), - "# AUTONOMOUS AGENT INSTRUCTIONS\n" - "Current date/time: %s\n" - "Working directory: %s\n\n" - "You are an autonomous AI agent. You operate in a loop: reason about the task, " - "select and execute tools when needed, observe results, and continue until the goal is achieved.\n\n" - "## Reasoning Pattern (ReAct)\n" - "For complex tasks, think step-by-step:\n" - "1. Thought: What do I need to accomplish? What information do I have?\n" - "2. Action: Which tool should I use? With what parameters?\n" - "3. Observation: What did the tool return? What does this tell me?\n" - "4. Repeat until the goal is complete.\n\n" - "## Tool Usage\n" - "- Use tools proactively to gather information and take actions\n" - "- If a tool fails, analyze the error and try a different approach\n" - "- You can call multiple tools in sequence to accomplish complex tasks\n\n" - "## CRITICAL OUTPUT RULES\n" - "- You MUST include the actual content/data from tool results in your response\n" - "- When you search the web, QUOTE the relevant information found\n" - "- When you run a command, SHOW the output\n" - "- NEVER say 'I found information' without showing what you found\n" - "- NEVER say 'task complete' or 'report provided' - SHOW THE ACTUAL DATA\n" - "- The user cannot see tool results - only YOUR response. Include everything relevant.\n\n" - "## Local Database\n" - "You have a local SQLite database accessible via db_query, db_get, and db_set tools.\n" - "Use stemmed, lowercase keys to prevent duplicates.\n" - "Schema: %s\n\n" - "## Response Format\n" - "Your response IS the only thing the user sees. Tool outputs are hidden from them.\n" - "You MUST copy/paste relevant data from tool results into your response.\n" - "Bad: 'I searched and found information about X.'\n" - "Good: 'Here is what I found: [actual content from search results]'\n", - datetime, cwd, schema); - - free(schema); - fprintf(stderr, "Loading..."); - openai_system(payload); - - char *env_system_message = get_env_system_message(); - if (env_system_message && *env_system_message) { - openai_system(env_system_message); - free(env_system_message); - } - - if (!openai_include(".rcontext.txt")) { - openai_include("~/.rcontext.txt"); - } - - fprintf(stderr, "\r \r"); -} - -static void handle_sigint(int sig) { - time_t current_time = time(NULL); - printf("\n"); - if (sigint_count == 0) { - first_sigint_time = current_time; - sigint_count++; - } else { - if (difftime(current_time, first_sigint_time) <= 1) { - exit(0); - } else { - sigint_count = 1; - first_sigint_time = current_time; - } - } -} - -static void parse_session_arg(int argc, char *argv[]) { - for (int i = 1; i < argc; i++) { - if (strncmp(argv[i], "--session=", 10) == 0) { - const char *name = argv[i] + 10; - if (!set_session_id(name)) { - fprintf(stderr, "Error: Invalid session name '%s'\n", name); - exit(1); - } - return; - } - if ((strcmp(argv[i], "-s") == 0 || strcmp(argv[i], "--session") == 0) && i + 1 < argc) { - const char *name = argv[++i]; - if (!set_session_id(name)) { - fprintf(stderr, "Error: Invalid session name '%s'\n", name); - exit(1); - } - return; - } - } - char *env_session = get_env_session(); - if (env_session) { - if (!set_session_id(env_session)) { - fprintf(stderr, "Warning: Invalid R_SESSION '%s', using default\n", env_session); - } - } -} - -int main(int argc, char *argv[]) { - signal(SIGINT, handle_sigint); - - parse_session_arg(argc, argv); - init(); - char *env_string = get_env_string(); - if (env_string && *env_string) { - openai_system(env_string); - free(env_string); - } - - messages_load_conversation(); - - if (try_prompt(argc, argv)) - return 0; - - repl(); - return 0; -} diff --git a/markdown.h b/markdown.h deleted file mode 100755 index 46ec120..0000000 --- a/markdown.h +++ /dev/null @@ -1,351 +0,0 @@ -#include -#include -#include -#include - -// --- ANSI Escape Codes --- -#define RESET "\033[0m" -#define BOLD "\033[1m" -#define ITALIC "\033[3m" -#define STRIKETHROUGH "\033[9m" - -#define FG_YELLOW "\033[33m" -#define FG_BLUE "\033[34m" -#define FG_CYAN "\033[36m" -#define FG_MAGENTA "\033[35m" - -#define BG_YELLOW_FG_BLACK "\033[43;30m" - -/** - * @brief Checks if a given word is a programming language keyword. - * * @param word The word to check. - * @return int 1 if it's a keyword, 0 otherwise. - */ -int is_keyword(const char *word) { - // A comprehensive list of keywords from various popular languages. - const char *keywords[] = { - // C keywords - "int", "float", "double", "char", "void", "if", "else", "while", "for", - "return", "struct", "printf", - // Rust keywords - "let", "fn", "impl", "match", "enum", "trait", "use", "mod", "pub", - "const", "static", - // Python keywords - "def", "class", "import", "from", "as", "with", "try", "except", - "finally", "lambda", "async", "await", - // Java keywords - "public", "private", "protected", "class", "interface", "extends", - "implements", "new", "static", "final", "synchronized", - // JavaScript keywords - "var", "let", "const", "function", "async", "await", "if", "else", - "switch", "case", "break", "continue", "return", - // C++ keywords - "namespace", "template", "typename", "class", "public", "private", - "protected", "virtual", "override", "friend", "new", - // Go keywords - "package", "import", "func", "var", "const", "type", "interface", - "struct", "go", "defer", "select", - // Bash keywords - "if", "then", "else", "elif", "fi", "case", "esac", "for", "while", - "until", "do", "done", "function", - // C# keywords - "namespace", "using", "class", "interface", "public", "private", - "protected", "static", "void", "new", "override"}; - - for (size_t i = 0; i < sizeof(keywords) / sizeof(keywords[0]); i++) { - if (strcmp(word, keywords[i]) == 0) { - return 1; - } - } - return 0; -} - -/** - * @brief Applies basic syntax highlighting to a string of code. - * * @param code The code string to highlight. - */ -void highlight_code(const char *code) { - const char *ptr = code; - char buffer[4096]; - size_t index = 0; - - while (*ptr) { - // Highlight keywords - if (isalpha((unsigned char)*ptr) || *ptr == '_') { - while (isalnum((unsigned char)*ptr) || *ptr == '_') { - buffer[index++] = *ptr++; - } - buffer[index] = '\0'; - - if (is_keyword(buffer)) { - printf(FG_BLUE "%s" RESET FG_YELLOW, buffer); - } else { - printf("%s", buffer); - } - index = 0; - // Highlight numbers - } else if (isdigit((unsigned char)*ptr)) { - while (isdigit((unsigned char)*ptr)) { - buffer[index++] = *ptr++; - } - buffer[index] = '\0'; - printf(FG_CYAN "%s" RESET FG_YELLOW, buffer); - index = 0; - // Print other characters as-is - } else { - putchar(*ptr); - ptr++; - } - } -} - -/** - * @brief Parses a Markdown string and prints it to the console with ANSI color - * codes. - * - * This version supports a wide range of Markdown features, including: - * - Headers (H1-H6) - * - Bold (**, __) and Italic (*, _) text - * - Strikethrough (~~) and Highlight (==) - * - Blockquotes (>), Nested Ordered (1.) and Unordered lists (*, -, +) - * - Inline code (`) and full code blocks (```) with syntax highlighting - * - Links ([text](url)) and Horizontal rules (---, ***) - * * @param markdown The raw Markdown string to parse. - */ -void parse_markdown_to_ansi(const char *markdown) { - const char *ptr = markdown; - bool is_start_of_line = true; - - while (*ptr) { - // --- Code Blocks (```) --- - if (is_start_of_line && strncmp(ptr, "```", 3) == 0) { - ptr += 3; - while (*ptr && *ptr != '\n') - ptr++; - if (*ptr) - ptr++; - - const char *code_start = ptr; - const char *code_end = strstr(code_start, "```"); - - if (code_end) { - char block_buffer[code_end - code_start + 1]; - strncpy(block_buffer, code_start, code_end - code_start); - block_buffer[code_end - code_start] = '\0'; - - printf(FG_YELLOW); - highlight_code(block_buffer); - printf(RESET); - - ptr = code_end + 3; - if (*ptr == '\n') - ptr++; - is_start_of_line = true; - continue; - } else { - printf(FG_YELLOW); - highlight_code(code_start); - printf(RESET); - break; - } - } - - // --- Block-level Elements (checked at the start of a line) --- - if (is_start_of_line) { - const char *line_start_ptr = ptr; - int indent_level = 0; - while (*ptr == ' ') { - indent_level++; - ptr++; - } - - bool block_processed = true; - if (strncmp(ptr, "###### ", 7) == 0) { - printf(BOLD FG_YELLOW); - ptr += 7; - } else if (strncmp(ptr, "##### ", 6) == 0) { - printf(BOLD FG_YELLOW); - ptr += 6; - } else if (strncmp(ptr, "#### ", 5) == 0) { - printf(BOLD FG_YELLOW); - ptr += 5; - } else if (strncmp(ptr, "### ", 4) == 0) { - printf(BOLD FG_YELLOW); - ptr += 4; - } else if (strncmp(ptr, "## ", 3) == 0) { - printf(BOLD FG_YELLOW); - ptr += 3; - } else if (strncmp(ptr, "# ", 2) == 0) { - printf(BOLD FG_YELLOW); - ptr += 2; - } else if ((strncmp(ptr, "---", 3) == 0 || strncmp(ptr, "***", 3) == 0) && - (*(ptr + 3) == '\n' || *(ptr + 3) == '\0')) { - printf(FG_CYAN "───────────────────────────────────────────────────────" - "──────────" RESET "\n"); - ptr += 3; - if (*ptr == '\n') - ptr++; - is_start_of_line = true; - continue; - } else if (strncmp(ptr, "> ", 2) == 0) { - for (int i = 0; i < indent_level; i++) - putchar(' '); - printf(ITALIC FG_CYAN "▎ " RESET); - ptr += 2; - is_start_of_line = false; - continue; - } else if ((*ptr == '*' || *ptr == '-' || *ptr == '+') && - *(ptr + 1) == ' ') { - for (int i = 0; i < indent_level; i++) - putchar(' '); - printf(FG_MAGENTA "• " RESET); - ptr += 2; - is_start_of_line = false; - continue; - } else { - const char *temp_ptr = ptr; - while (isdigit((unsigned char)*temp_ptr)) - temp_ptr++; - if (temp_ptr > ptr && *temp_ptr == '.' && *(temp_ptr + 1) == ' ') { - for (int i = 0; i < indent_level; i++) - putchar(' '); - printf(FG_MAGENTA); - fwrite(ptr, 1, (temp_ptr - ptr) + 1, stdout); - printf(" " RESET); - ptr = temp_ptr + 2; - is_start_of_line = false; - continue; - } else { - block_processed = false; - ptr = line_start_ptr; - } - } - if (block_processed) { - while (*ptr && *ptr != '\n') - putchar(*ptr++); - printf(RESET "\n"); - if (*ptr == '\n') - ptr++; - is_start_of_line = true; - continue; - } - } - - // --- Inline Elements (order is important) --- - if (strncmp(ptr, "***", 3) == 0 || strncmp(ptr, "___", 3) == 0) { - const char *marker = strncmp(ptr, "***", 3) == 0 ? "***" : "___"; - printf(BOLD ITALIC); - ptr += 3; - const char *end = strstr(ptr, marker); - if (end) { - fwrite(ptr, 1, end - ptr, stdout); - ptr = end + 3; - } else { - fputs(ptr, stdout); - ptr += strlen(ptr); - } - printf(RESET); - continue; - } - if (strncmp(ptr, "**", 2) == 0 || strncmp(ptr, "__", 2) == 0) { - const char *marker = strncmp(ptr, "**", 2) == 0 ? "**" : "__"; - printf(BOLD); - ptr += 2; - const char *end = strstr(ptr, marker); - if (end) { - fwrite(ptr, 1, end - ptr, stdout); - ptr = end + 2; - } else { - fputs(ptr, stdout); - ptr += strlen(ptr); - } - printf(RESET); - continue; - } - if ((*ptr == '*' || *ptr == '_') && !isspace(*(ptr - 1)) && - !isspace(*(ptr + 1))) { - char marker = *ptr; - printf(ITALIC); - ptr++; - const char *start = ptr; - while (*ptr && *ptr != marker) - ptr++; - if (*ptr == marker) { - fwrite(start, 1, ptr - start, stdout); - ptr++; - } else { - putchar(marker); - ptr = start; - } - printf(RESET); - continue; - } - if (strncmp(ptr, "~~", 2) == 0) { - printf(STRIKETHROUGH); - ptr += 2; - const char *end = strstr(ptr, "~~"); - if (end) { - fwrite(ptr, 1, end - ptr, stdout); - ptr = end + 2; - } else { - fputs(ptr, stdout); - ptr += strlen(ptr); - } - printf(RESET); - continue; - } - if (strncmp(ptr, "==", 2) == 0) { - printf(BG_YELLOW_FG_BLACK); - ptr += 2; - const char *end = strstr(ptr, "=="); - if (end) { - fwrite(ptr, 1, end - ptr, stdout); - ptr = end + 2; - } else { - fputs(ptr, stdout); - ptr += strlen(ptr); - } - printf(RESET); - continue; - } - if (*ptr == '`' && *(ptr + 1) != '`') { - printf(FG_YELLOW); - ptr++; - const char *start = ptr; - while (*ptr && *ptr != '`') - ptr++; - fwrite(start, 1, ptr - start, stdout); - if (*ptr == '`') - ptr++; - printf(RESET); - continue; - } - if (*ptr == '[') { - const char *text_start = ptr + 1; - const char *text_end = strchr(text_start, ']'); - if (text_end && *(text_end + 1) == '(') { - const char *url_start = text_end + 2; - const char *url_end = strchr(url_start, ')'); - if (url_end) { - printf(FG_BLUE); - fwrite(text_start, 1, text_end - text_start, stdout); - printf(RESET " ("); - printf(ITALIC FG_CYAN); - fwrite(url_start, 1, url_end - url_start, stdout); - printf(RESET ")"); - ptr = url_end + 1; - continue; - } - } - } - - // --- Default Character --- - if (*ptr == '\n') { - is_start_of_line = true; - } else if (!isspace((unsigned char)*ptr)) { - is_start_of_line = false; - } - putchar(*ptr); - ptr++; - } -} diff --git a/messages.h b/messages.h deleted file mode 100755 index ac05484..0000000 --- a/messages.h +++ /dev/null @@ -1,260 +0,0 @@ -// Written by retoor@molodetz.nl - -// This code manages a collection of messages using JSON objects. It provides -// functions to retrieve all messages as a JSON array, add a new message with a -// specified role and content, and free the allocated resources. - -// Uses the external library for JSON manipulation - -// MIT License -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: The above copyright -// notice and this permission notice shall be included in all copies or -// substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", -// WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED -// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE -// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR -// THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -#ifndef R_MESSAGES_H -#define R_MESSAGES_H - -#include "db_utils.h" -#include "json-c/json.h" -#include "tools.h" -#include "utils.h" -#include -#include - -struct json_object *message_array = NULL; -static char session_id[256] = {0}; -static char override_session_id[256] = {0}; - -struct json_object *message_list(); - -bool set_session_id(const char *name) { - if (name == NULL || *name == '\0') { - return false; - } - size_t len = strlen(name); - if (len > 200) { - return false; - } - for (const char *p = name; *p; p++) { - if (*p == '/' || *p == '\\') { - return false; - } - } - strncpy(override_session_id, name, sizeof(override_session_id) - 1); - override_session_id[sizeof(override_session_id) - 1] = '\0'; - return true; -} - -const char *get_session_id() { - if (session_id[0] == '\0') { - if (override_session_id[0] != '\0') { - strncpy(session_id, override_session_id, sizeof(session_id) - 1); - session_id[sizeof(session_id) - 1] = '\0'; - } - } - return session_id; -} - -void init_session_id() { - if (session_id[0] != '\0') { - return; - } - if (override_session_id[0] != '\0') { - strncpy(session_id, override_session_id, sizeof(session_id) - 1); - session_id[sizeof(session_id) - 1] = '\0'; - return; - } - char *tty = ttyname(STDIN_FILENO); - if (tty) { - unsigned long h = 5381; - for (char *p = tty; *p; p++) { - h = ((h << 5) + h) + (unsigned char)*p; - } - snprintf(session_id, sizeof(session_id), "%lu", h); - } else { - snprintf(session_id, sizeof(session_id), "%d", getppid()); - } -} - -char *get_session_db_key() { - static char key[512]; - init_session_id(); - snprintf(key, sizeof(key), "session:%s", session_id); - return key; -} - -bool messages_save() { - char *key = get_session_db_key(); - const char *json_str = json_object_to_json_string_ext(message_list(), JSON_C_TO_STRING_PLAIN); - if (!json_str) { - return false; - } - json_object *result = db_set(key, json_str); - if (result) { - json_object_put(result); - return true; - } - return false; -} - -bool messages_load_conversation() { - char *key = get_session_db_key(); - json_object *result = db_get(key); - if (!result) { - return false; - } - json_object *value_obj; - if (!json_object_object_get_ex(result, "value", &value_obj)) { - json_object_put(result); - return false; - } - const char *json_str = json_object_get_string(value_obj); - if (!json_str) { - json_object_put(result); - return false; - } - struct json_object *loaded = json_tokener_parse(json_str); - json_object_put(result); - if (!loaded || !json_object_is_type(loaded, json_type_array)) { - if (loaded) { - json_object_put(loaded); - } - return false; - } - int len = json_object_array_length(loaded); - int added = 0; - for (int i = 0; i < len; i++) { - struct json_object *msg = json_object_array_get_idx(loaded, i); - struct json_object *role_obj; - if (json_object_object_get_ex(msg, "role", &role_obj)) { - const char *role = json_object_get_string(role_obj); - if (role && strcmp(role, "system") != 0) { - json_object_array_add(message_list(), json_object_get(msg)); - added++; - } - } else { - json_object_array_add(message_list(), json_object_get(msg)); - added++; - } - } - json_object_put(loaded); - return added > 0; -} - -struct json_object *message_list() { - if (!message_array) { - message_array = json_object_new_array(); - } - return message_array; -} -bool messages_remove_last() { - struct json_object *messages = message_list(); - int size = json_object_array_length(messages); - if (size) { - json_object_array_del_idx(messages, size - 1, 1); - messages_save(); - return true; - } - return false; -} - -void messages_remove() { - if (message_array) { - json_object_put(message_array); - message_array = NULL; - } - message_list(); - messages_save(); -} - -struct json_object *message_add_tool_call(struct json_object *message) { - struct json_object *messages = message_list(); - json_object_array_add(messages, message); - messages_save(); - return message; -} - -struct json_object *message_add_tool_result(const char *tool_call_id, - char *tool_result) { - struct json_object *messages = message_list(); - struct json_object *message = json_object_new_object(); - if (message == NULL) { - return NULL; - } - - json_object_object_add(message, "tool_call_id", - json_object_new_string(tool_call_id)); - - size_t result_len = strlen(tool_result); - if (result_len > 104000) { - result_len = 104000; - } - json_object_object_add(message, "tool_result", - json_object_new_string_len(tool_result, (int)result_len)); - - json_object_array_add(messages, message); - messages_save(); - return message; -} - -void message_add_object(json_object *message) { - struct json_object *messages = message_list(); - json_object_array_add(messages, message); - messages_save(); -} - -struct json_object *message_add(const char *role, const char *content); - -struct json_object *message_add(const char *role, const char *content) { - struct json_object *messages = message_list(); - struct json_object *message = json_object_new_object(); - if (message == NULL) { - return NULL; - } - json_object_object_add(message, "role", json_object_new_string(role)); - - if (content) { - size_t content_len = strlen(content); - if (content_len > 1048570) { - content_len = 1048570; - } - json_object_object_add(message, "content", - json_object_new_string_len(content, (int)content_len)); - } - if (!strcmp(role, "user")) { - json_object_object_add(message, "tools", tools_descriptions()); - json_object_object_add(message, "parallel_tool_calls", - json_object_new_boolean(true)); - } - - json_object_array_add(messages, message); - if (strcmp(role, "system") != 0) { - messages_save(); - } - return message; -} - -char *message_json() { - return (char *)json_object_to_json_string_ext(message_list(), - JSON_C_TO_STRING_PRETTY); -} - -void message_free() { - if (message_array) { - json_object_put(message_array); - message_array = NULL; - } -} - -#endif diff --git a/openai.h b/openai.h deleted file mode 100755 index 5d0bb88..0000000 --- a/openai.h +++ /dev/null @@ -1,144 +0,0 @@ -// Written by retoor@molodetz.nl - -// This code interacts with OpenAI's API to perform various tasks such as -// fetching models, sending chat messages, and processing responses. - -// Uncommon imports include "http.h", "chat.h", and "http_curl.h". These may be -// internal or external libraries providing HTTP and JSON communication -// capabilities required to interact with APIs. - -// MIT License -// -// Copyright (c) 2023 -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#ifndef R_OPENAI_H -#define R_OPENAI_H -#include "chat.h" -#include "http_curl.h" -#include "r.h" -#include -#include - -char *openai_fetch_models() { return curl_get(get_models_api_url()); } - -bool openai_system(char *message_content) { - chat_json("system", message_content); - return true; -} - -struct json_object *openai_process_chat_message(const char *api_url, - const char *json_data) { - char *response = curl_post(api_url, json_data); - if (!response) { - fprintf(stderr, "Failed to get response.\n"); - return NULL; - } - struct json_object *parsed_json = json_tokener_parse(response); - if (!parsed_json) { - fprintf(stderr, "Failed to parse JSON.\nContent: \"%s\"\n", response); - return NULL; - } - struct json_object *error_object; - if (json_object_object_get_ex(parsed_json, "error", &error_object) && - message_array) { - - const char *all_messages = json_object_to_json_string(message_array); - - fprintf(stderr, "Messages: "); - if (all_messages) { - fwrite(all_messages, strlen(all_messages), 1, stderr); - } - fprintf(stderr, "\n"); - - fprintf(stderr, "%s\n", json_object_to_json_string(parsed_json)); - - json_object_put(parsed_json); - messages_remove_last(); - messages_remove_last(); - - return NULL; - } - - struct json_object *choices_array; - if (!json_object_object_get_ex(parsed_json, "choices", &choices_array)) { - fprintf(stderr, "Failed to get 'choices' array.\n%s\n", response); - fprintf(stderr, "%s\n", json_object_to_json_string(parsed_json)); - json_object_put(parsed_json); - return NULL; - } - struct json_object *first_choice = - json_object_array_get_idx(choices_array, 0); - if (!first_choice) { - fprintf(stderr, "Failed to get the first element of 'choices'.\n"); - json_object_put(parsed_json); - return NULL; - } - struct json_object *message_object; - if (!json_object_object_get_ex(first_choice, "message", &message_object)) { - fprintf(stderr, "Failed to get 'message' object.\n"); - json_object_put(parsed_json); - return NULL; - } - return message_object; -} - -char *openai_chat(const char *user_role, const char *message_content) { - if (message_content == NULL || *message_content == '\0' || - *message_content == '\n') { - return NULL; - } - - char *json_data = chat_json(user_role, message_content); - - struct json_object *message_object = - openai_process_chat_message(get_completions_api_url(), json_data); - - if (message_object == NULL) { - return NULL; - } - - message_add_object(message_object); - struct json_object *tool_calls; - json_object_object_get_ex(message_object, "tool_calls", &tool_calls); - if (tool_calls) { - // message_add_tool_call(message_object); - struct json_object *tool_call_results = tools_execute(tool_calls); - int results_count = json_object_array_length(tool_call_results); - for (int i = 0; i < results_count; i++) { - struct json_object *tool_call_result = - json_object_array_get_idx(tool_call_results, i); - message_add_tool_call(tool_call_result); - } - char *tool_calls_result_str = chat_json(NULL, NULL); - message_object = openai_process_chat_message(get_completions_api_url(), - tool_calls_result_str); - if (message_object == NULL) { - return NULL; - } - message_add_object(message_object); - // message_add_tool_call(message_object); - } - const char *content_str = - json_object_get_string(json_object_object_get(message_object, "content")); - return strdup(content_str); -} - -#endif diff --git a/plugin.h b/plugin.h deleted file mode 100755 index 8200caf..0000000 --- a/plugin.h +++ /dev/null @@ -1,62 +0,0 @@ -// Written by retoor@molodetz.nl - -// This source code initializes a Python interpreter within a plugin, executes a -// provided Python script with some basic imports, and finalizes the Python -// environment when done. - -// This code does not use any non-standard imports or includes aside from -// Python.h and structmember.h which are part of Python's C API. - -// MIT License - -#include -#include -#include -#include -#include -#include - -bool plugin_initialized = false; - -bool plugin_construct() { - if (plugin_initialized) - return true; - - Py_Initialize(); - if (!Py_IsInitialized()) { - fprintf(stderr, "Failed to initialize the Python interpreter\n"); - return false; - } - plugin_initialized = true; - return true; -} - -void plugin_run(char *src) { - plugin_construct(); - /*const char *basics = - "import sys\n" - "import os\n" - "from os import *\n" - "import math\n" - "import pathlib\n" - "from pathlib import Path\n" - "import re\n" - "import subprocess\n" - "from subprocess import *\n" - "import time\n" - "from datetime import datetime\n" - "%s"; - */ - const char *basics = "\n\n"; - size_t length = strlen(basics) + strlen(src); - char *script = malloc(length + 1); - sprintf(script, basics, src); - script[length] = '\0'; - PyRun_SimpleString(script); - free(script); -} - -void plugin_destruct() { - if (plugin_initialized) - Py_Finalize(); -} diff --git a/r.h b/r.h deleted file mode 100755 index 464a31d..0000000 --- a/r.h +++ /dev/null @@ -1,126 +0,0 @@ -#ifndef R_H -#define R_H -#include "auth.h" -#include "utils.h" -#include -#include -bool is_verbose = false; - -char *models_api_url = "https://api.openai.com/v1/models"; -char *completions_api_url = "https://api.openai.com/v1/chat/completions"; -char *advanced_model = "gpt-4o-mini"; -char *fast_model = "gpt-3.5-turbo"; - -// char *models_api_url = "https://api.openai.com/v1/models"; -// char *completions_api_url = "https://api.anthropic.com/v1/chat/completions"; -// char *advanced_model = "claude-3-5-haiku-20241022"; -// char *advanced_model = "meta-llama/Meta-Llama-3.1-8B-Instruct"; -// char *advanced_model = "google/gemini-1.5-flash"; -// char *fast_model = "claude-3-5-haiku-20241022"; - -// #endif -// #ifdef OLLAMA -// char *models_api_url = "https://ollama.molodetz.nl/v1/models"; -// char *completions_api_url = "https://ollama.molodetz.nl/v1/chat/completions"; -// char *advanced_model = "qwen2.5:3b"; -// char *advanced_model = "qwen2.5-coder:0.5b"; -// char *fast_model = "qwen2.5:0.5b"; -// #endif - -char *_model = NULL; - -#define DB_FILE "~/.r.db" -#define PROMPT_TEMPERATURE 0.1 - -bool use_tools() { - if (getenv("R_USE_TOOLS") != NULL) { - const char *value = getenv("R_USE_TOOLS"); - if (!strcmp(value, "true")) { - return true; - } - if (!strcmp(value, "false")) { - return false; - } - if (!strcmp(value, "1")) { - return true; - } - if (!strcmp(value, "0")) { - return false; - } - } - return true; -} - -char *get_env_system_message() { - if (getenv("R_SYSTEM_MESSAGE") != NULL) { - return strdup(getenv("R_SYSTEM_MESSAGE")); - } - return NULL; -} - -char *get_env_session() { - char *session = getenv("R_SESSION"); - return (session && *session) ? session : NULL; -} - -bool get_use_strict() { - if (getenv("R_USE_STRICT") != NULL) { - const char *value = getenv("R_USE_STRICT"); - if (!strcmp(value, "true")) { - return true; - } - if (!strcmp(value, "false")) { - return false; - } - if (!strcmp(value, "1")) { - return true; - } - if (!strcmp(value, "0")) { - return false; - } - } - return true; -} - -char *get_completions_api_url() { - if (getenv("R_BASE_URL") != NULL) { - char * path = joinpath(getenv("R_BASE_URL"), "v1/chat/completions"); - printf("%s\n",path); - - return path; - } - printf("%s\n",completions_api_url); - return completions_api_url; -} -char *get_models_api_url() { - if (getenv("R_BASE_URL") != NULL) { - return joinpath(getenv("R_BASE_URL"), "v1/models"); - } - return models_api_url; -} - -void set_prompt_model(const char *model) { - if (_model != NULL) { - free(_model); - } - _model = strdup(model); -} - -const char *get_prompt_model() { - if (_model == NULL && getenv("R_MODEL") != NULL) { - _model = strdup(getenv("R_MODEL")); - } - if (_model) { - return _model; - } - if (auth_type != AUTH_TYPE_API_KEY) { - if (_model == NULL) { - _model = strdup(fast_model); - } - } else if (_model == NULL) { - _model = strdup(advanced_model); - } - return _model; -} - -#endif diff --git a/rpylib.c b/rpylib.c deleted file mode 100755 index b75609c..0000000 --- a/rpylib.c +++ /dev/null @@ -1,112 +0,0 @@ -/* Written by retoor@molodetz.nl */ - -/* -This C extension for Python provides a simple API for communication with an -OpenAI service. It includes functions to return a "Hello World" string, conduct -a chat session through OpenAI, and reset the message history. -*/ - -/* -Summary of used imports: -- : Includes necessary Python headers to create a C extension. -- "openai.h": Assumes an external library for OpenAI interaction. -*/ - -/* -MIT License - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ - -#define PY_SSIZE_T_CLEAN -#include "auth.h" -#include "openai.h" -#include - -static PyObject *rpylib_reset(PyObject *self, PyObject *args) { - return PyUnicode_FromString("True"); -} - -static PyObject *rpylib_chat(PyObject *self, PyObject *args) { - const char *role, *message; - - if (!PyArg_ParseTuple(args, "ss", &role, &message)) { - return NULL; - } - - char *result = openai_chat(role, message); - if (!result) { - PyErr_SetString(PyExc_RuntimeError, "Failed to get response from OpenAI."); - return NULL; - } - - PyObject *py_result = PyUnicode_FromString(result); - free(result); - return py_result; -} - -static PyObject *rpylib_prompt(PyObject *self, PyObject *args) { - const char *role = "user"; - const char *message; - if (!PyArg_ParseTuple(args, "s", &message)) { - return NULL; - } - - char *result = openai_chat(role, message); - if (!result) { - PyErr_SetString(PyExc_RuntimeError, "Failed to get response from OpenAI."); - return NULL; - } - - PyObject *py_result = PyUnicode_FromString(result); - free(result); - return py_result; -} - -static PyObject *rpylib_system(PyObject *self, PyObject *args) { - const char *role = "system"; - const char *message; - if (!PyArg_ParseTuple(args, "s", &message)) { - return NULL; - } - - char *result = openai_chat(role, message); - if (!result) { - PyErr_SetString(PyExc_RuntimeError, "Failed to get response from OpenAI."); - return NULL; - } - - PyObject *py_result = PyUnicode_FromString(result); - free(result); - return py_result; -} - -static PyMethodDef MyModuleMethods[] = { - {"chat", rpylib_chat, METH_VARARGS, "Chat with OpenAI."}, - {"reset", rpylib_reset, METH_NOARGS, "Reset message history."}, - {NULL, NULL, 0, NULL}}; - -static struct PyModuleDef rpylib = { - PyModuleDef_HEAD_INIT, "rpylib", - "R - the power of R in Python, made by retoor.", -1, MyModuleMethods}; - -PyMODINIT_FUNC PyInit_rpylib(void) { - auth_init(); - return PyModule_Create(&rpylib); -} \ No newline at end of file diff --git a/src/agent.c b/src/agent.c index 56eedbe..5c766cb 100755 --- a/src/agent.c +++ b/src/agent.c @@ -112,6 +112,10 @@ void agent_set_verbose(agent_handle agent, bool verbose) { if (agent) agent->verbose = verbose; } +void agent_set_tool_registry(agent_handle agent, tool_registry_t *registry) { + if (agent && registry) agent->tools = registry; +} + agent_state_t agent_get_state(agent_handle agent) { return agent ? agent->state : AGENT_STATE_ERROR; } diff --git a/src/context_summarizer.c b/src/context_summarizer.c new file mode 100644 index 0000000..1db834d --- /dev/null +++ b/src/context_summarizer.c @@ -0,0 +1,46 @@ +#include "context_summarizer.h" +#include +#include +#include + +// Placeholder for LLM API call +// In a real implementation, this function would call the LLM API to get the summary. +static char* call_llm_to_summarize(const char* messages_concatenated) { + // For demonstration, just return a dummy summary. + const char* dummy_summary = "This is a summary of the oldest 20 messages."; + char* result = malloc(strlen(dummy_summary) + 1); + if (result) { + strcpy(result, dummy_summary); + } + return result; +} + +char* summarize_oldest_messages(const char** messages, size_t message_count) { + // Concatenate the oldest 20 messages + size_t total_length = 0; + size_t start_index = 0; + if (message_count > 20) { + start_index = message_count - 20; + } + for (size_t i = start_index; i < message_count; ++i) { + total_length += strlen(messages[i]) + 1; // +1 for separator + } + + char* concatenated = malloc(total_length + 1); + if (!concatenated) { + return NULL; + } + concatenated[0] = '\0'; + + for (size_t i = start_index; i < message_count; ++i) { + strcat(concatenated, messages[i]); + if (i < message_count - 1) { + strcat(concatenated, " "); // separator + } + } + + // Call the LLM API to get the summary + char* summary = call_llm_to_summarize(concatenated); + free(concatenated); + return summary; +} diff --git a/src/context_summarizer.h b/src/context_summarizer.h new file mode 100644 index 0000000..02b5398 --- /dev/null +++ b/src/context_summarizer.h @@ -0,0 +1,12 @@ +#ifndef CONTEXT_SUMMARIZER_H +#define CONTEXT_SUMMARIZER_H + +#include + +// Summarizes the oldest 20 messages into a single summary. +// messages: array of message strings +// message_count: number of messages in the array +// Returns a newly allocated string containing the summary. +char* summarize_oldest_messages(const char** messages, size_t message_count); + +#endif // CONTEXT_SUMMARIZER_H \ No newline at end of file diff --git a/src/core/buffer.c b/src/core/buffer.c new file mode 100644 index 0000000..cf2a94b --- /dev/null +++ b/src/core/buffer.c @@ -0,0 +1,146 @@ +// retoor + +#include "buffer.h" +#include +#include +#include +#include + +struct buffer_t { + char *data; + size_t size; + size_t capacity; +}; + +static const size_t DEFAULT_CAPACITY = 256; + +buffer_handle buffer_create(size_t initial_capacity) { + if (initial_capacity == 0) initial_capacity = DEFAULT_CAPACITY; + + struct buffer_t *b = malloc(sizeof(struct buffer_t)); + if (!b) return NULL; + + b->data = malloc(initial_capacity); + if (!b->data) { + free(b); + return NULL; + } + + b->size = 0; + b->capacity = initial_capacity; + return b; +} + +buffer_handle buffer_create_empty(void) { + return buffer_create(DEFAULT_CAPACITY); +} + +void buffer_write(buffer_handle b, const void *data, size_t size) { + if (!b || !data || size == 0) return; + + if (b->size + size > b->capacity) { + size_t new_capacity = b->capacity; + while (new_capacity < b->size + size) new_capacity *= 2; + + char *new_data = realloc(b->data, new_capacity); + if (!new_data) return; + + b->data = new_data; + b->capacity = new_capacity; + } + + memcpy(b->data + b->size, data, size); + b->size += size; +} + +void buffer_write_string(buffer_handle b, const char *str) { + if (!b || !str) return; + buffer_write(b, str, strlen(str)); +} + +void buffer_write_stringf(buffer_handle b, const char *fmt, ...) { + if (!b || !fmt) return; + + va_list args, args_copy; + va_start(args, fmt); + va_copy(args_copy, args); + + int len = vsnprintf(NULL, 0, fmt, args_copy); + va_end(args_copy); + + if (len < 0) { + va_end(args); + return; + } + + if ((size_t)len > 0) buffer_write(b, NULL, (size_t)len); + + if (b->size + (size_t)len > b->capacity) { + size_t new_capacity = b->capacity; + while (new_capacity < b->size + (size_t)len) new_capacity *= 2; + + char *new_data = realloc(b->data, new_capacity); + if (!new_data) { + va_end(args); + return; + } + + b->data = new_data; + b->capacity = new_capacity; + } + + vsnprintf(b->data + b->size, b->capacity - b->size, fmt, args); + b->size += (size_t)len; + va_end(args); +} + +void buffer_write_buffer(buffer_handle dest, buffer_handle src) { + if (!dest || !src) return; + buffer_write(dest, src->data, src->size); +} + +size_t buffer_size(buffer_handle b) { + return b ? b->size : 0; +} + +size_t buffer_capacity(buffer_handle b) { + return b ? b->capacity : 0; +} + +const void *buffer_data(buffer_handle b) { + return b ? b->data : NULL; +} + +char *buffer_to_string(buffer_handle b) { + if (!b) return NULL; + + char *str = malloc(b->size + 1); + if (!str) return NULL; + + if (b->size > 0) memcpy(str, b->data, b->size); + str[b->size] = '\0'; + + return str; +} + +void *buffer_release_data(buffer_handle b) { + if (!b) return NULL; + + void *data = b->data; + b->data = NULL; + b->size = 0; + b->capacity = 0; + + return data; +} + +void buffer_clear(buffer_handle b) { + if (!b) return; + b->size = 0; +} + +void buffer_destroy(buffer_handle b) { + if (!b) return; + if (b->data) free(b->data); + free(b); +} diff --git a/src/core/buffer.h b/src/core/buffer.h new file mode 100644 index 0000000..7cfa216 --- /dev/null +++ b/src/core/buffer.h @@ -0,0 +1,28 @@ +// retoor + +#ifndef R_BUFFER_H +#define R_BUFFER_H + +#include "r_error.h" +#include + +typedef struct buffer_t *buffer_handle; + +buffer_handle buffer_create(size_t initial_capacity); +buffer_handle buffer_create_empty(void); + +void buffer_write(buffer_handle b, const void *data, size_t size); +void buffer_write_string(buffer_handle b, const char *str); +void buffer_write_stringf(buffer_handle b, const char *fmt, ...); +void buffer_write_buffer(buffer_handle dest, buffer_handle src); + +size_t buffer_size(buffer_handle b); +size_t buffer_capacity(buffer_handle b); +const void *buffer_data(buffer_handle b); +char *buffer_to_string(buffer_handle b); +void *buffer_release_data(buffer_handle b); + +void buffer_clear(buffer_handle b); +void buffer_destroy(buffer_handle b); + +#endif diff --git a/src/core/memory.c b/src/core/memory.c new file mode 100644 index 0000000..10ffe3e --- /dev/null +++ b/src/core/memory.c @@ -0,0 +1,107 @@ +// retoor + +#include "memory.h" +#include +#include + +arena_t *arena_create(size_t chunk_size) { + if (chunk_size == 0) chunk_size = 4096; + + arena_t *arena = calloc(1, sizeof(arena_t)); + if (!arena) return NULL; + + arena->block_size = chunk_size; + arena->current_block = malloc(chunk_size); + if (!arena->current_block) { + free(arena); + return NULL; + } + + arena_block_t *block_info = calloc(1, sizeof(arena_block_t)); + if (!block_info) { + free(arena->current_block); + free(arena); + return NULL; + } + + block_info->data = arena->current_block; + block_info->size = chunk_size; + arena->blocks = block_info; + arena->block_count = 1; + + return arena; +} + +void *arena_alloc(arena_t *arena, size_t size) { + if (!arena || size == 0) return NULL; + + size_t aligned_size = (size + 7) & ~7; + + if (arena->block_used + aligned_size > arena->block_size) { + arena_block_t *new_block = calloc(1, sizeof(arena_block_t)); + if (!new_block) return NULL; + + char *new_data = malloc(arena->block_size); + if (!new_data) { + free(new_block); + return NULL; + } + + new_block->data = new_data; + new_block->size = arena->block_size; + new_block->next = arena->blocks; + arena->blocks = new_block; + arena->current_block = new_data; + arena->block_used = 0; + arena->block_count++; + } + + void *ptr = arena->current_block + arena->block_used; + arena->block_used += aligned_size; + + return ptr; +} + +char *arena_strdup(arena_t *arena, const char *str) { + if (!arena || !str) return NULL; + + size_t len = strlen(str); + char *copy = arena_alloc(arena, len + 1); + if (!copy) return NULL; + + memcpy(copy, str, len + 1); + return copy; +} + +void arena_reset(arena_t *arena) { + if (!arena) return; + arena->block_used = 0; + + arena_block_t *block = arena->blocks; + while (block) { + arena_block_t *next = block->next; + if (block != arena->blocks) { + free(block->data); + free(block); + } + block = next; + } + + arena->blocks->next = NULL; + arena->current_block = arena->blocks->data; + arena->block_count = 1; +} + +void arena_destroy(arena_t *arena) { + if (!arena) return; + + arena_block_t *block = arena->blocks; + while (block) { + arena_block_t *next = block->next; + free(block->data); + free(block); + block = next; + } + + free(arena); +} diff --git a/src/core/memory.h b/src/core/memory.h new file mode 100644 index 0000000..9600497 --- /dev/null +++ b/src/core/memory.h @@ -0,0 +1,31 @@ +// retoor + +#ifndef R_MEMORY_H +#define R_MEMORY_H + +#include + +typedef struct arena_t { + char *current_block; + size_t block_size; + size_t block_used; + size_t block_count; + struct arena_block *blocks; +} arena_t; + +typedef struct arena_block { + char *data; + size_t size; + struct arena_block *next; +} arena_block_t; + +arena_t *arena_create(size_t chunk_size); +void *arena_alloc(arena_t *arena, size_t size); +char *arena_strdup(arena_t *arena, const char *str); +void arena_reset(arena_t *arena); +void arena_destroy(arena_t *arena); + +#define ARENA_ALLOC(arena, type) ((type *)arena_alloc(arena, sizeof(type))) +#define ARENA_ARRAY(arena, type, count) ((type *)arena_alloc(arena, sizeof(type) * (count))) + +#endif diff --git a/src/core/string.c b/src/core/string.c new file mode 100644 index 0000000..4ac392d --- /dev/null +++ b/src/core/string.c @@ -0,0 +1,259 @@ +// retoor + +#include "string.h" +#include +#include +#include +#include + +struct string_t { + char *data; + size_t length; + size_t capacity; +}; + +static const size_t DEFAULT_CAPACITY = 32; + +string_handle string_create(const char *str) { + if (!str) return string_create_empty(); + return string_create_n(str, strlen(str)); +} + +string_handle string_create_empty(void) { + struct string_t *s = calloc(1, sizeof(struct string_t)); + if (!s) return NULL; + + s->data = malloc(DEFAULT_CAPACITY); + if (!s->data) { + free(s); + return NULL; + } + + s->data[0] = '\0'; + s->length = 0; + s->capacity = DEFAULT_CAPACITY; + return s; +} + +string_handle string_create_n(const char *str, size_t len) { + if (!str) return string_create_empty(); + if (len == 0) return string_create_empty(); + + struct string_t *s = malloc(sizeof(struct string_t)); + if (!s) return NULL; + + s->capacity = len + 1; + if (s->capacity < DEFAULT_CAPACITY) s->capacity = DEFAULT_CAPACITY; + + s->data = malloc(s->capacity); + if (!s->data) { + free(s); + return NULL; + } + + memcpy(s->data, str, len); + s->data[len] = '\0'; + s->length = len; + return s; +} + +string_handle string_format(const char *fmt, ...) { + if (!fmt) return string_create_empty(); + + va_list args, args_copy; + va_start(args, fmt); + va_copy(args_copy, args); + + int len = vsnprintf(NULL, 0, fmt, args_copy); + va_end(args_copy); + + if (len < 0) { + va_end(args); + return string_create_empty(); + } + + struct string_t *s = malloc(sizeof(struct string_t)); + if (!s) { + va_end(args); + return NULL; + } + + s->capacity = (size_t)len + 1; + s->data = malloc(s->capacity); + if (!s->data) { + free(s); + va_end(args); + return NULL; + } + + vsnprintf(s->data, s->capacity, fmt, args); + s->length = (size_t)len; + va_end(args); + + return s; +} + +string_handle string_clone(const char *str) { + return string_create(str); +} + +string_handle string_clone_handle(string_handle s) { + if (!s) return NULL; + return string_create_n(s->data, s->length); +} + +string_handle string_concat(const char *a, const char *b) { + if (!a) return string_create(b); + if (!b) return string_create(a); + + size_t len_a = strlen(a); + size_t len_b = strlen(b); + + struct string_t *s = malloc(sizeof(struct string_t)); + if (!s) return NULL; + + s->length = len_a + len_b; + s->capacity = s->length + 1; + s->data = malloc(s->capacity); + if (!s->data) { + free(s); + return NULL; + } + + memcpy(s->data, a, len_a); + memcpy(s->data + len_a, b, len_b); + s->data[s->length] = '\0'; + + return s; +} + +string_handle string_concat_handle(string_handle a, const char *b) { + if (!a) return string_create(b); + if (!b) return string_clone_handle(a); + + size_t len_b = strlen(b); + size_t new_len = a->length + len_b; + + if (new_len + 1 > a->capacity) { + size_t new_capacity = a->capacity * 2; + while (new_capacity < new_len + 1) new_capacity *= 2; + + char *new_data = realloc(a->data, new_capacity); + if (!new_data) return NULL; + + a->data = new_data; + a->capacity = new_capacity; + } + + memcpy(a->data + a->length, b, len_b); + a->length = new_len; + a->data[a->length] = '\0'; + + return a; +} + +void string_append(string_handle s, const char *str) { + if (!s || !str) return; + string_append_n(s, str, strlen(str)); +} + +void string_append_n(string_handle s, const char *str, size_t len) { + if (!s || !str || len == 0) return; + + if (s->length + len + 1 > s->capacity) { + size_t new_capacity = s->capacity; + while (new_capacity < s->length + len + 1) new_capacity *= 2; + + char *new_data = realloc(s->data, new_capacity); + if (!new_data) return; + + s->data = new_data; + s->capacity = new_capacity; + } + + memcpy(s->data + s->length, str, len); + s->length += len; + s->data[s->length] = '\0'; +} + +void string_appendf(string_handle s, const char *fmt, ...) { + if (!s || !fmt) return; + + va_list args; + va_start(args, fmt); + + int len = vsnprintf(NULL, 0, fmt, args); + va_end(args); + + if (len < 0) return; + + if (s->length + (size_t)len + 1 > s->capacity) { + size_t new_capacity = s->capacity; + while (new_capacity < s->length + (size_t)len + 1) new_capacity *= 2; + + char *new_data = realloc(s->data, new_capacity); + if (!new_data) return; + + s->data = new_data; + s->capacity = new_capacity; + } + + va_start(args, fmt); + vsnprintf(s->data + s->length, s->capacity - s->length, fmt, args); + s->length += (size_t)len; + va_end(args); +} + +void string_append_handle(string_handle dest, string_handle src) { + if (!dest || !src) return; + string_append_n(dest, src->data, src->length); +} + +size_t string_length(string_handle s) { + return s ? s->length : 0; +} + +size_t string_capacity(string_handle s) { + return s ? s->capacity : 0; +} + +const char *string_c_str(string_handle s) { + return s ? s->data : ""; +} + +char *string_release(string_handle s) { + if (!s) return NULL; + + char *data = s->data; + free(s); + return data; +} + +bool string_equals(string_handle a, string_handle b) { + if (!a && !b) return true; + if (!a || !b) return false; + if (a->length != b->length) return false; + return memcmp(a->data, b->data, a->length) == 0; +} + +bool string_equals_c_str(string_handle s, const char *str) { + if (!s && !str) return true; + if (!s || !str) return false; + return strcmp(s->data, str) == 0; +} + +bool string_is_empty(string_handle s) { + return !s || s->length == 0; +} + +void string_clear(string_handle s) { + if (!s) return; + s->length = 0; + if (s->data) s->data[0] = '\0'; +} + +void string_destroy(string_handle s) { + if (!s) return; + if (s->data) free(s->data); + free(s); +} diff --git a/src/core/string.h b/src/core/string.h new file mode 100644 index 0000000..17dacf4 --- /dev/null +++ b/src/core/string.h @@ -0,0 +1,37 @@ +// retoor + +#ifndef R_STRING_H +#define R_STRING_H + +#include "../include/r_error.h" +#include +#include + +typedef struct string_t *string_handle; + +string_handle string_create(const char *str); +string_handle string_create_empty(void); +string_handle string_create_n(const char *str, size_t len); +string_handle string_format(const char *fmt, ...); +string_handle string_clone(const char *str); +string_handle string_clone_handle(string_handle s); +string_handle string_concat(const char *a, const char *b); +string_handle string_concat_handle(string_handle a, const char *b); + +void string_append(string_handle s, const char *str); +void string_append_n(string_handle s, const char *str, size_t len); +void string_appendf(string_handle s, const char *fmt, ...); +void string_append_handle(string_handle dest, string_handle src); + +size_t string_length(string_handle s); +size_t string_capacity(string_handle s); +const char *string_c_str(string_handle s); +char *string_release(string_handle s); +bool string_equals(string_handle a, string_handle b); +bool string_equals_c_str(string_handle s, const char *str); +bool string_is_empty(string_handle s); + +void string_clear(string_handle s); +void string_destroy(string_handle s); + +#endif diff --git a/src/db.c b/src/db.c index 5e39a9b..4cd291d 100755 --- a/src/db.c +++ b/src/db.c @@ -83,6 +83,16 @@ r_status_t db_init(db_handle db) { " session_key TEXT PRIMARY KEY," " data TEXT NOT NULL," " updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP" + ");" + "CREATE TABLE IF NOT EXISTS blackboard (" + " id INTEGER PRIMARY KEY AUTOINCREMENT," + " sender TEXT NOT NULL," + " recipient TEXT," + " topic TEXT NOT NULL," + " message TEXT NOT NULL," + " status TEXT DEFAULT 'pending'," + " created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP," + " updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP" ");"; char *err_msg = NULL; diff --git a/src/impl/db_sqlite.c b/src/impl/db_sqlite.c new file mode 100644 index 0000000..a3e7190 --- /dev/null +++ b/src/impl/db_sqlite.c @@ -0,0 +1,167 @@ +#include "database.h" +#include "util/path.h" +#include +#include +#include + +struct database_t { + sqlite3 *conn; + char *path; +}; + +database_handle database_open(const char *path) { + struct database_t *db = calloc(1, sizeof(struct database_t)); + if (!db) return NULL; + + char *expanded_path = path_expand_home(path); + if (!expanded_path) { + free(db); + return NULL; + } + + if (sqlite3_open(expanded_path, &db->conn) != SQLITE_OK) { + free(expanded_path); + free(db); + return NULL; + } + + db->path = expanded_path; + database_init(db); + + return db; +} + +void database_close(database_handle db) { + if (!db) return; + + if (db->conn) sqlite3_close(db->conn); + if (db->path) free(db->path); + free(db); +} + +r_status_t database_init(database_handle db) { + if (!db || !db->conn) return R_ERROR_INVALID_ARG; + + const char *sql = + "CREATE TABLE IF NOT EXISTS kv (" + " key TEXT PRIMARY KEY," + " value TEXT NOT NULL," + " created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP," + " updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP" + ");" + "CREATE TABLE IF NOT EXISTS file_versions (" + " id INTEGER PRIMARY KEY AUTOINCREMENT," + " path TEXT NOT NULL," + " content TEXT NOT NULL," + " created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP" + ");" + "CREATE TABLE IF NOT EXISTS conversations (" + " session_key TEXT PRIMARY KEY," + " data TEXT NOT NULL," + " updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP" + ");" + "CREATE TABLE IF NOT EXISTS blackboard (" + " id INTEGER PRIMARY KEY AUTOINCREMENT," + " sender TEXT NOT NULL," + " recipient TEXT," + " topic TEXT NOT NULL," + " message TEXT NOT NULL," + " status TEXT DEFAULT 'pending'," + " created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP," + " updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP" + ");"; + + char *err_msg = NULL; + if (sqlite3_exec(db->conn, sql, NULL, NULL, &err_msg) != SQLITE_OK) { + sqlite3_free(err_msg); + return R_ERROR_DB_CONNECTION; + } + + return R_SUCCESS; +} + +r_status_t database_execute(database_handle db, const char *sql, char **error) { + if (!db || !db->conn || !sql) return R_ERROR_INVALID_ARG; + + char *err_msg = NULL; + if (sqlite3_exec(db->conn, sql, NULL, NULL, &err_msg) != SQLITE_OK) { + if (error) *error = err_msg ? strdup(err_msg) : NULL; + return R_ERROR_DB_QUERY; + } + + return R_SUCCESS; +} + +r_status_t database_query(database_handle db, const char *sql, + database_callback_t callback, void *ctx) { + if (!db || !db->conn || !sql) return R_ERROR_INVALID_ARG; + + char *err_msg = NULL; + if (sqlite3_exec(db->conn, sql, + (void (*)(void *, int, char **, char **))callback, + ctx, &err_msg) != SQLITE_OK) { + return R_ERROR_DB_QUERY; + } + + return R_SUCCESS; +} + +r_status_t database_set(database_handle db, const char *key, const char *value) { + if (!db || !key) return R_ERROR_INVALID_ARG; + + const char *sql = "INSERT OR REPLACE INTO kv (key, value) VALUES (?, ?)"; + sqlite3_stmt *stmt; + + if (sqlite3_prepare_v2(db->conn, sql, -1, &stmt, NULL) != SQLITE_OK) { + return R_ERROR_DB_QUERY; + } + + sqlite3_bind_text(stmt, 1, key, -1, SQLITE_STATIC); + sqlite3_bind_text(stmt, 2, value ? value : "", -1, SQLITE_TRANSIENT); + + int result = sqlite3_step(stmt); + sqlite3_finalize(stmt); + + return result == SQLITE_DONE ? R_SUCCESS : R_ERROR_DB_QUERY; +} + +r_status_t database_get(database_handle db, const char *key, char **value) { + if (!db || !key) return R_ERROR_INVALID_ARG; + + const char *sql = "SELECT value FROM kv WHERE key = ?"; + sqlite3_stmt *stmt; + + if (sqlite3_prepare_v2(db->conn, sql, -1, &stmt, NULL) != SQLITE_OK) { + return R_ERROR_DB_QUERY; + } + + sqlite3_bind_text(stmt, 1, key, -1, SQLITE_STATIC); + + if (sqlite3_step(stmt) == SQLITE_ROW) { + const char *result = (const char *)sqlite3_column_text(stmt, 0); + if (result) *value = strdup(result); + sqlite3_finalize(stmt); + return R_SUCCESS; + } + + sqlite3_finalize(stmt); + return R_ERROR_DB_NOT_FOUND; +} + +r_status_t database_delete(database_handle db, const char *key) { + if (!db || !key) return R_ERROR_INVALID_ARG; + + const char *sql = "DELETE FROM kv WHERE key = ?"; + sqlite3_stmt *stmt; + + if (sqlite3_prepare_v2(db->conn, sql, -1, &stmt, NULL) != SQLITE_OK) { + return R_ERROR_DB_QUERY; + } + + sqlite3_bind_text(stmt, 1, key, -1, SQLITE_STATIC); + + int result = sqlite3_step(stmt); + sqlite3_finalize(stmt); + + return result == SQLITE_DONE ? R_SUCCESS : R_ERROR_DB_QUERY; +} diff --git a/src/impl/http_curl.c b/src/impl/http_curl.c new file mode 100644 index 0000000..0d306d0 --- /dev/null +++ b/src/impl/http_curl.c @@ -0,0 +1,164 @@ +#include "http.h" +#include +#include +#include + +struct http_client_t { + CURL *curl; + char *base_url; + char *bearer_token; + long timeout; + long connect_timeout; +}; + +static size_t write_callback(void *contents, size_t size, size_t nmemb, void *userp) { + size_t total_size = size * nmemb; + struct { + char *data; + size_t size; + } *buffer = userp; + + char *ptr = realloc(buffer->data, buffer->size + total_size + 1); + if (!ptr) return 0; + + buffer->data = ptr; + memcpy(&(buffer->data[buffer->size]), contents, total_size); + buffer->size += total_size; + buffer->data[buffer->size] = '\0'; + + return total_size; +} + +http_client_handle http_create(const char *base_url) { + struct http_client_t *client = calloc(1, sizeof(struct http_client_t)); + if (!client) return NULL; + + client->curl = curl_easy_init(); + if (!client->curl) { + free(client); + return NULL; + } + + client->base_url = base_url ? strdup(base_url) : NULL; + client->bearer_token = NULL; + client->timeout = 0; + client->connect_timeout = 0; + + curl_easy_setopt(client->curl, CURLOPT_WRITEFUNCTION, write_callback); + + return client; +} + +void http_destroy(http_client_handle client) { + if (!client) return; + + if (client->curl) curl_easy_cleanup(client->curl); + if (client->base_url) free(client->base_url); + if (client->bearer_token) free(client->bearer_token); + free(client); +} + +void http_set_bearer_token(http_client_handle client, const char *token) { + if (!client) return; + + if (client->bearer_token) free(client->bearer_token); + client->bearer_token = token ? strdup(token) : NULL; +} + +void http_set_timeout(http_client_handle client, long timeout_seconds) { + if (!client) return; + client->timeout = timeout_seconds; +} + +void http_set_connect_timeout(http_client_handle client, long timeout_seconds) { + if (!client) return; + client->connect_timeout = timeout_seconds; +} + +static const char *method_string(http_method_t method) { + switch (method) { + case HTTP_METHOD_GET: return "GET"; + case HTTP_METHOD_POST: return "POST"; + case HTTP_METHOD_PUT: return "PUT"; + case HTTP_METHOD_DELETE: return "DELETE"; + default: return "GET"; + } +} + +r_status_t http_request(http_client_handle client, http_method_t method, + const char *path, const void *body, size_t body_size, + char **response, int *response_code) { + if (!client || !client->curl) return R_ERROR_INVALID_ARG; + + struct { + char *data; + size_t size; + } buffer = {NULL, 0}; + + curl_easy_setopt(client->curl, CURLOPT_WRITEDATA, &buffer); + + char url[4096]; + if (client->base_url) { + snprintf(url, sizeof(url), "%s%s", client->base_url, path); + } else { + strncpy(url, path, sizeof(url) - 1); + url[sizeof(url) - 1] = '\0'; + } + + curl_easy_setopt(client->curl, CURLOPT_URL, url); + curl_easy_setopt(client->curl, CURLOPT_CUSTOMREQUEST, method_string(method)); + + if (client->bearer_token) { + char auth_header[512]; + snprintf(auth_header, sizeof(auth_header), "Authorization: Bearer %s", client->bearer_token); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, auth_header); + curl_easy_setopt(client->curl, CURLOPT_HTTPHEADER, headers); + } + + if (client->timeout > 0) { + curl_easy_setopt(client->curl, CURLOPT_TIMEOUT, client->timeout); + } + if (client->connect_timeout > 0) { + curl_easy_setopt(client->curl, CURLOPT_CONNECTTIMEOUT, client->connect_timeout); + } + + if (body && body_size > 0) { + curl_easy_setopt(client->curl, CURLOPT_POSTFIELDSIZE, (long)body_size); + curl_easy_setopt(client->curl, CURLOPT_POSTFIELDS, body); + } + + CURLcode res = curl_easy_perform(client->curl); + + if (res != CURLE_OK) { + if (buffer.data) free(buffer.data); + return R_ERROR_HTTP_CONNECTION; + } + + long code; + curl_easy_getinfo(client->curl, CURLINFO_RESPONSE_CODE, &code); + if (response_code) *response_code = (int)code; + + if (response) { + *response = buffer.data ? buffer.data : strdup(""); + } else { + if (buffer.data) free(buffer.data); + } + + return R_SUCCESS; +} + +r_status_t http_get(http_client_handle client, const char *path, char **response) { + int code; + return http_request(client, HTTP_METHOD_GET, path, NULL, 0, response, &code); +} + +r_status_t http_post(http_client_handle client, const char *path, const char *body, char **response) { + int code; + size_t body_size = body ? strlen(body) : 0; + return http_request(client, HTTP_METHOD_POST, path, body, body_size, response, &code); +} + +r_status_t http_post_json(http_client_handle client, const char *path, const char *json, char **response) { + return http_post(client, path, json, response); +} diff --git a/src/interfaces/config.c b/src/interfaces/config.c new file mode 100644 index 0000000..abbf2ca --- /dev/null +++ b/src/interfaces/config.c @@ -0,0 +1,188 @@ +#include "config.h" +#include "util/path.h" +#include +#include +#include +#include + +struct config_t { + char *api_url; + char *models_url; + char *model; + char *api_key; + char *db_path; + char *session_id; + char *system_message; + double temperature; + bool use_tools; + bool use_strict; + bool verbose; +}; + +static struct config_t *instance = NULL; + +static char *strdup_safe(const char *s) { + return s ? strdup(s) : NULL; +} + +static bool resolve_env_bool(const char *env_name, bool default_val) { + const char *val = getenv(env_name); + if (!val) return default_val; + if (!strcmp(val, "true") || !strcmp(val, "1")) return true; + if (!strcmp(val, "false") || !strcmp(val, "0")) return false; + return default_val; +} + +static const char *resolve_api_key(void) { + const char *key = getenv("OPENROUTER_API_KEY"); + if (key && *key) return key; + + key = getenv("R_KEY"); + if (key && *key) return key; + + key = getenv("OPENAI_API_KEY"); + if (key && *key) return key; + + return "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"; +} + +static bool is_valid_session_id(const char *session_id) { + if (!session_id || !*session_id) return false; + if (strlen(session_id) > 255) return false; + + for (const char *p = session_id; *p; p++) { + if (!isalnum((unsigned char)*p) && *p != '-' && *p != '_' && *p != '.') { + return false; + } + } + return true; +} + +config_handle config_create(void) { + if (instance) return instance; + + struct config_t *cfg = calloc(1, sizeof(struct config_t)); + if (!cfg) return NULL; + + const char *base_url = getenv("R_BASE_URL"); + if (base_url && *base_url) { + size_t len = strlen(base_url); + cfg->api_url = malloc(len + 32); + cfg->models_url = malloc(len + 32); + if (cfg->api_url && cfg->models_url) { + snprintf(cfg->api_url, len + 32, "%s/v1/chat/completions", base_url); + snprintf(cfg->models_url, len + 32, "%s/v1/models", base_url); + } + } else { + cfg->api_url = strdup("https://api.openai.com/v1/chat/completions"); + cfg->models_url = strdup("https://api.openai.com/v1/models"); + } + + const char *model = getenv("R_MODEL"); + cfg->model = strdup(model && *model ? model : "gpt-4o-mini"); + + cfg->api_key = strdup(resolve_api_key()); + cfg->db_path = strdup("~/.r.db"); + cfg->temperature = 0.1; + cfg->use_tools = resolve_env_bool("R_USE_TOOLS", true); + cfg->use_strict = resolve_env_bool("R_USE_STRICT", true); + cfg->verbose = false; + + const char *session = getenv("R_SESSION"); + if (session && is_valid_session_id(session)) { + cfg->session_id = strdup(session); + } else { + cfg->session_id = strdup("default"); + } + + const char *system_msg = getenv("R_SYSTEM_MESSAGE"); + cfg->system_message = strdup_safe(system_msg); + + instance = cfg; + return cfg; +} + +void config_destroy(config_handle cfg) { + if (!cfg) return; + + if (cfg != instance) { + if (cfg->api_url) free(cfg->api_url); + if (cfg->models_url) free(cfg->models_url); + if (cfg->model) free(cfg->model); + if (cfg->api_key) free(cfg->api_key); + if (cfg->db_path) free(cfg->db_path); + if (cfg->session_id) free(cfg->session_id); + if (cfg->system_message) free(cfg->system_message); + free(cfg); + } +} + +const char *config_get_api_url(config_handle cfg) { + return cfg ? cfg->api_url : NULL; +} + +const char *config_get_models_url(config_handle cfg) { + return cfg ? cfg->models_url : NULL; +} + +const char *config_get_api_key(config_handle cfg) { + return cfg ? cfg->api_key : NULL; +} + +const char *config_get_model(config_handle cfg) { + return cfg ? cfg->model : NULL; +} + +const char *config_get_db_path(config_handle cfg) { + return cfg ? cfg->db_path : NULL; +} + +const char *config_get_session_id(config_handle cfg) { + return cfg ? cfg->session_id : NULL; +} + +const char *config_get_system_message(config_handle cfg) { + return cfg ? cfg->system_message : NULL; +} + +double config_get_temperature(config_handle cfg) { + return cfg ? cfg->temperature : 0.0; +} + +bool config_use_tools(config_handle cfg) { + return cfg ? cfg->use_tools : true; +} + +bool config_use_strict(config_handle cfg) { + return cfg ? cfg->use_strict : true; +} + +bool config_is_verbose(config_handle cfg) { + return cfg ? cfg->verbose : false; +} + +r_status_t config_set_model(config_handle cfg, const char *model) { + if (!cfg || !model) return R_ERROR_INVALID_ARG; + + if (cfg->model) free(cfg->model); + cfg->model = strdup(model); + + return R_SUCCESS; +} + +r_status_t config_set_session_id(config_handle cfg, const char *session_id) { + if (!cfg || !session_id) return R_ERROR_INVALID_ARG; + + if (!is_valid_session_id(session_id)) { + return R_ERROR_SESSION_INVALID; + } + + if (cfg->session_id) free(cfg->session_id); + cfg->session_id = strdup(session_id); + + return R_SUCCESS; +} + +void config_set_verbose(config_handle cfg, bool verbose) { + if (cfg) cfg->verbose = verbose; +} diff --git a/src/interfaces/config.h b/src/interfaces/config.h new file mode 100644 index 0000000..fef674c --- /dev/null +++ b/src/interfaces/config.h @@ -0,0 +1,30 @@ +// retoor + +#ifndef R_INTERFACES_CONFIG_H +#define R_INTERFACES_CONFIG_H + +#include + +typedef struct config_t *config_handle; + +config_handle config_create(void); +void config_destroy(config_handle cfg); + +const char *config_get_api_url(config_handle cfg); +const char *config_get_models_url(config_handle cfg); +const char *config_get_api_key(config_handle cfg); +const char *config_get_model(config_handle cfg); +const char *config_get_db_path(config_handle cfg); +const char *config_get_session_id(config_handle cfg); +const char *config_get_system_message(config_handle cfg); + +double config_get_temperature(config_handle cfg); +bool config_use_tools(config_handle cfg); +bool config_use_strict(config_handle cfg); +bool config_is_verbose(config_handle cfg); + +r_status_t config_set_model(config_handle cfg, const char *model); +r_status_t config_set_session_id(config_handle cfg, const char *session_id); +void config_set_verbose(config_handle cfg, bool verbose); + +#endif diff --git a/src/interfaces/database.h b/src/interfaces/database.h new file mode 100644 index 0000000..6d529ca --- /dev/null +++ b/src/interfaces/database.h @@ -0,0 +1,27 @@ +// retoor + +#ifndef R_INTERFACES_DATABASE_H +#define R_INTERFACES_DATABASE_H + +#include "r_error.h" +#include +#include + +typedef struct database_t *database_handle; + +database_handle database_open(const char *path); +void database_close(database_handle db); + +r_status_t database_init(database_handle db); +r_status_t database_execute(database_handle db, const char *sql, char **error); + +typedef void (*database_callback_t)(void *ctx, int col_count, char **values, char **names); + +r_status_t database_query(database_handle db, const char *sql, + database_callback_t callback, void *ctx); + +r_status_t database_set(database_handle db, const char *key, const char *value); +r_status_t database_get(database_handle db, const char *key, char **value); +r_status_t database_delete(database_handle db, const char *key); + +#endif diff --git a/src/interfaces/http.h b/src/interfaces/http.h new file mode 100644 index 0000000..036ee6e --- /dev/null +++ b/src/interfaces/http.h @@ -0,0 +1,40 @@ +// retoor + +#ifndef R_INTERFACES_HTTP_H +#define R_INTERFACES_HTTP_H + +#include "r_error.h" +#include +#include + +typedef struct http_client_t *http_client_handle; + +typedef enum { + HTTP_METHOD_GET, + HTTP_METHOD_POST, + HTTP_METHOD_PUT, + HTTP_METHOD_DELETE +} http_method_t; + +http_client_handle http_create(const char *base_url); +void http_destroy(http_client_handle client); + +void http_set_bearer_token(http_client_handle client, const char *token); +void http_set_timeout(http_client_handle client, long timeout_seconds); +void http_set_connect_timeout(http_client_handle client, long timeout_seconds); + +r_status_t http_request( + http_client_handle client, + http_method_t method, + const char *path, + const void *body, + size_t body_size, + char **response, + int *response_code +); + +r_status_t http_get(http_client_handle client, const char *path, char **response); +r_status_t http_post(http_client_handle client, const char *path, const char *body, char **response); +r_status_t http_post_json(http_client_handle client, const char *path, const char *json, char **response); + +#endif diff --git a/src/interfaces/logger.c b/src/interfaces/logger.c new file mode 100644 index 0000000..360d2e4 --- /dev/null +++ b/src/interfaces/logger.c @@ -0,0 +1,66 @@ +// retoor + +#include "logger.h" +#include +#include +#include +#include +#include + +struct logger_t { + log_level_t level; +}; + +static const char *level_strings[] = { + "TRACE", "DEBUG", "INFO", "WARN", "ERROR", "FATAL" +}; +static const char *level_colors[] = { + "\033[90m", "\033[36m", "\033[32m", "\033[33m", "\033[31m", "\033[35m" +}; +static const char *color_reset = "\033[0m"; + +logger_handle logger_create(log_level_t level) { + struct logger_t *logger = malloc(sizeof(struct logger_t)); + if (!logger) return NULL; + + logger->level = level; + return logger; +} + +void logger_destroy(logger_handle logger) { + if (logger) free(logger); +} + +void logger_set_level(logger_handle logger, log_level_t level) { + if (logger) logger->level = level; +} + +log_level_t logger_get_level(logger_handle logger) { + return logger ? logger->level : LOG_LEVEL_INFO; +} + +void logger_log(logger_handle logger, log_level_t level, + const char *file, int line, const char *fmt, ...) { + if (!logger || !fmt) return; + + if (level < logger->level) return; + + va_list args; + va_start(args, fmt); + + time_t now = time(NULL); + struct tm *tm_info = localtime(&now); + char time_buf[32]; + strftime(time_buf, sizeof(time_buf), "%Y-%m-%d %H:%M:%S", tm_info); + + const char *basename = strrchr(file, '/'); + basename = basename ? basename + 1 : file; + + fprintf(stderr, "%s[%s%s%s %s:%d] ", + level_colors[level], level_strings[level], color_reset, + basename, line); + vfprintf(stderr, fmt, args); + fprintf(stderr, "%s\n", color_reset); + + va_end(args); +} diff --git a/src/interfaces/logger.h b/src/interfaces/logger.h new file mode 100644 index 0000000..363027a --- /dev/null +++ b/src/interfaces/logger.h @@ -0,0 +1,41 @@ +// retoor + +#ifndef R_INTERFACES_LOGGER_H +#define R_INTERFACES_LOGGER_H + +#include + +typedef enum { + LOG_LEVEL_TRACE, + LOG_LEVEL_DEBUG, + LOG_LEVEL_INFO, + LOG_LEVEL_WARN, + LOG_LEVEL_ERROR, + LOG_LEVEL_FATAL +} log_level_t; + +typedef struct logger_t *logger_handle; + +logger_handle logger_create(log_level_t level); +void logger_destroy(logger_handle logger); + +void logger_set_level(logger_handle logger, log_level_t level); +log_level_t logger_get_level(logger_handle logger); + +void logger_log(logger_handle logger, log_level_t level, + const char *file, int line, const char *fmt, ...); + +#define LOG_TRACE(logger, fmt, ...) \ + logger_log(logger, LOG_LEVEL_TRACE, __FILE__, __LINE__, fmt, ##__VA_ARGS__) +#define LOG_DEBUG(logger, fmt, ...) \ + logger_log(logger, LOG_LEVEL_DEBUG, __FILE__, __LINE__, fmt, ##__VA_ARGS__) +#define LOG_INFO(logger, fmt, ...) \ + logger_log(logger, LOG_LEVEL_INFO, __FILE__, __LINE__, fmt, ##__VA_ARGS__) +#define LOG_WARN(logger, fmt, ...) \ + logger_log(logger, LOG_LEVEL_WARN, __FILE__, __LINE__, fmt, ##__VA_ARGS__) +#define LOG_ERROR(logger, fmt, ...) \ + logger_log(logger, LOG_LEVEL_ERROR, __FILE__, __LINE__, fmt, ##__VA_ARGS__) +#define LOG_FATAL(logger, fmt, ...) \ + logger_log(logger, LOG_LEVEL_FATAL, __FILE__, __LINE__, fmt, ##__VA_ARGS__) + +#endif diff --git a/src/main.c b/src/main.c index 3577807..4ee2f04 100755 --- a/src/main.c +++ b/src/main.c @@ -305,6 +305,13 @@ static void init(void) { "- You can call multiple tools in sequence to accomplish complex " "tasks\n\n" "- Take decissions based on tool output yourself autonomously." + "## Multi-Agent Orchestration\n" + "You can delegate tasks to specialized agents using the `spawn_agent` tool.\n" + "Personas:\n" + "- researcher: Best for web search and data gathering.\n" + "- developer: Best for writing and testing code.\n" + "- security: Best for security audits and vulnerability analysis.\n\n" + "Use the `blackboard` table in the database to share state and pass detailed instructions or results between agents if they exceed tool parameter limits.\n\n" "## CRITICAL OUTPUT RULES\n" "- You MUST include the actual content/data from tool results in your " "response\n" diff --git a/src/tools/tool_agent.c b/src/tools/tool_agent.c new file mode 100644 index 0000000..df7a483 --- /dev/null +++ b/src/tools/tool_agent.c @@ -0,0 +1,146 @@ +// retoor + +#include "tool.h" +#include "agent.h" +#include "messages.h" +#include "r_config.h" +#include +#include +#include +#include + +typedef struct { + tool_t tool; +} tool_agent_t; + +static struct json_object *tool_spawn_agent_get_description(void) { + struct json_object *obj = json_object_new_object(); + json_object_object_add(obj, "name", json_object_new_string("spawn_agent")); + json_object_object_add(obj, "description", json_object_new_string("Spawn a specialized sub-agent to handle a specific task.")); + + struct json_object *params = json_object_new_object(); + json_object_object_add(params, "type", json_object_new_string("object")); + + struct json_object *props = json_object_new_object(); + + struct json_object *persona = json_object_new_object(); + json_object_object_add(persona, "type", json_object_new_string("string")); + json_object_object_add(persona, "description", json_object_new_string("The persona of the agent (researcher, developer, security).")); + struct json_object *persona_enum = json_object_new_array(); + json_object_array_add(persona_enum, json_object_new_string("researcher")); + json_object_array_add(persona_enum, json_object_new_string("developer")); + json_object_array_add(persona_enum, json_object_new_string("security")); + json_object_object_add(persona, "enum", persona_enum); + json_object_object_add(props, "persona", persona); + + struct json_object *goal = json_object_new_object(); + json_object_object_add(goal, "type", json_object_new_string("string")); + json_object_object_add(goal, "description", json_object_new_string("The specific task or goal for the sub-agent.")); + json_object_object_add(props, "goal", goal); + + json_object_object_add(params, "properties", props); + + struct json_object *required = json_object_new_array(); + json_object_array_add(required, json_object_new_string("persona")); + json_object_array_add(required, json_object_new_string("goal")); + json_object_object_add(params, "required", required); + + json_object_object_add(obj, "parameters", params); + + struct json_object *full_obj = json_object_new_object(); + json_object_object_add(full_obj, "type", json_object_new_string("function")); + json_object_object_add(full_obj, "function", obj); + + return full_obj; +} + +static char *tool_spawn_agent_execute(tool_t *self, struct json_object *args) { + (void)self; + struct json_object *persona_obj, *goal_obj; + + if (!json_object_object_get_ex(args, "persona", &persona_obj) || + !json_object_object_get_ex(args, "goal", &goal_obj)) { + return strdup("Error: Missing persona or goal"); + } + + const char *persona_str = json_object_get_string(persona_obj); + const char *goal_str = json_object_get_string(goal_obj); + + tool_registry_type_t type = TOOL_TYPE_ALL; + const char *system_prompt = NULL; + + if (strcmp(persona_str, "researcher") == 0) { + type = TOOL_TYPE_RESEARCHER; + system_prompt = "You are a specialized Research Agent. Your goal is to find, extract, and summarize information. " + "Do not attempt to write or execute code unless it's for data analysis. " + "Focus on using web search, http fetch, and reading files."; + } else if (strcmp(persona_str, "developer") == 0) { + type = TOOL_TYPE_DEVELOPER; + system_prompt = "You are a specialized Developer Agent. Your goal is to write, test, and debug code. " + "Use the terminal, file editing tools, and python execution to fulfill your task. " + "Always verify your changes by running tests or the code itself."; + } else if (strcmp(persona_str, "security") == 0) { + type = TOOL_TYPE_SECURITY; + system_prompt = "You are a specialized Security Auditor Agent. Your goal is to find vulnerabilities and perform security analysis. " + "Be pedantic and thorough. Use fuzzing, port scanning, and code analysis tools. " + "Report any findings clearly with potential impact."; + } else { + return strdup("Error: Invalid persona"); + } + + char session_id[256]; + snprintf(session_id, sizeof(session_id), "subagent-%s-%u", persona_str, (unsigned int)time(NULL)); + + messages_handle msgs = messages_create(session_id); + messages_add(msgs, "system", system_prompt); + + agent_handle agent = agent_create(goal_str, msgs); + if (!agent) { + messages_destroy(msgs); + return strdup("Error: Failed to create sub-agent"); + } + + tool_registry_t *specialized_tools = tool_registry_get_specialized(type); + agent_set_tool_registry(agent, specialized_tools); + agent_set_max_iterations(agent, 50); // Sub-agents have lower limit + + char *result = agent_run(agent, goal_str); + + agent_destroy(agent); + // specialized_tools should be destroyed? + // In tool_registry_get_specialized we create a new one. + tool_registry_destroy(specialized_tools); + + if (!result) { + return strdup("Sub-agent failed to provide a result."); + } + + return result; +} + +static void tool_spawn_agent_print_action(const char *name, struct json_object *args) { + struct json_object *persona_obj, *goal_obj; + const char *persona = "unknown"; + const char *goal = "unknown"; + + if (json_object_object_get_ex(args, "persona", &persona_obj)) persona = json_object_get_string(persona_obj); + if (json_object_object_get_ex(args, "goal", &goal_obj)) goal = json_object_get_string(goal_obj); + + printf("\033[1;34m[Agent] Spawning %s agent for: %s\033[0m\n", persona, goal); +} + +static const tool_vtable_t tool_spawn_agent_vtable = { + .get_description = tool_spawn_agent_get_description, + .execute = tool_spawn_agent_execute, + .print_action = tool_spawn_agent_print_action +}; + +tool_t *tool_spawn_agent_create(void) { + tool_agent_t *tool = calloc(1, sizeof(tool_agent_t)); + if (!tool) return NULL; + + tool->tool.vtable = &tool_spawn_agent_vtable; + tool->tool.name = "spawn_agent"; + + return &tool->tool; +} diff --git a/src/util/path.c b/src/util/path.c new file mode 100644 index 0000000..e28a057 --- /dev/null +++ b/src/util/path.c @@ -0,0 +1,153 @@ +// retoor + +#include "path.h" +#include +#include +#include +#include +#include + +char *path_expand_home(const char *path) { + if (!path) return NULL; + if (path[0] != '~') return strdup(path); + + const char *home_dir = getenv("HOME"); + if (!home_dir) home_dir = getenv("USERPROFILE"); + if (!home_dir) return strdup(path); + + size_t home_len = strlen(home_dir); + size_t path_len = strlen(path); + char *expanded = malloc(home_len + path_len); + if (!expanded) return NULL; + + strcpy(expanded, home_dir); + strcat(expanded, path + 1); + return expanded; +} + +char *path_normalize(const char *path) { + if (!path) return NULL; + + char *normalized = strdup(path); + if (!normalized) return NULL; + + size_t write_idx = 0; + for (size_t i = 0; normalized[i]; i++) { + if (normalized[i] == '/' && write_idx > 0 && normalized[write_idx - 1] == '/') { + continue; + } + normalized[write_idx++] = normalized[i]; + } + normalized[write_idx] = '\0'; + + return normalized; +} + +char *path_join(const char *base, const char *relative) { + if (!base) return relative ? strdup(relative) : NULL; + if (!relative) return strdup(base); + + size_t base_len = strlen(base); + size_t rel_len = strlen(relative); + bool needs_sep = base_len > 0 && base[base_len - 1] != '/'; + + size_t total_len = base_len + rel_len + (needs_sep ? 1 : 0); + char *joined = malloc(total_len + 1); + if (!joined) return NULL; + + strcpy(joined, base); + if (needs_sep) strcat(joined, "/"); + strcat(joined, relative); + + return joined; +} + +char *path_dirname(const char *path) { + if (!path || !*path) return strdup("."); + + char *copy = strdup(path); + if (!copy) return NULL; + + char *last_sep = strrchr(copy, '/'); + if (last_sep) { + if (last_sep == copy) { + last_sep[1] = '\0'; + } else { + *last_sep = '\0'; + } + } else { + strcpy(copy, "."); + } + + return copy; +} + +char *path_basename(const char *path) { + if (!path || !*path) return strdup("."); + + const char *last_sep = strrchr(path, '/'); + if (last_sep) { + return strdup(last_sep + 1); + } + return strdup(path); +} + +bool path_exists(const char *path) { + if (!path) return false; + struct stat st; + return stat(path, &st) == 0; +} + +bool path_is_file(const char *path) { + if (!path) return false; + struct stat st; + return stat(path, &st) == 0 && S_ISREG(st.st_mode); +} + +bool path_is_directory(const char *path) { + if (!path) return false; + struct stat st; + return stat(path, &st) == 0 && S_ISDIR(st.st_mode); +} + +bool path_ensure_parent_exists(const char *path) { + if (!path) return false; + + char *parent = path_dirname(path); + if (!parent) return false; + + bool exists = path_is_directory(parent); + if (exists) { + free(parent); + return true; + } + + char temp[4096]; + size_t len = strlen(parent); + if (len >= sizeof(temp)) { + free(parent); + return false; + } + + memcpy(temp, parent, len + 1); + if (temp[len - 1] == '/') temp[len - 1] = '\0'; + + for (char *p = temp + 1; *p; p++) { + if (*p == '/') { + *p = '\0'; + if (mkdir(temp, 0755) != 0 && errno != EEXIST) { + free(parent); + return false; + } + *p = '/'; + } + } + + if (mkdir(temp, 0755) != 0 && errno != EEXIST) { + free(parent); + return false; + } + + free(parent); + return true; +} diff --git a/src/util/path.h b/src/util/path.h new file mode 100644 index 0000000..ba30d51 --- /dev/null +++ b/src/util/path.h @@ -0,0 +1,19 @@ +// retoor + +#ifndef R_UTIL_PATH_H +#define R_UTIL_PATH_H + +#include +#include + +char *path_expand_home(const char *path); +char *path_normalize(const char *path); +char *path_join(const char *base, const char *relative); +char *path_dirname(const char *path); +char *path_basename(const char *path); +bool path_exists(const char *path); +bool path_is_file(const char *path); +bool path_is_directory(const char *path); +bool path_ensure_parent_exists(const char *path); + +#endif diff --git a/src/util/time.c b/src/util/time.c new file mode 100644 index 0000000..3c1056a --- /dev/null +++ b/src/util/time.c @@ -0,0 +1,50 @@ +// retoor + +#define _XOPEN_SOURCE +#include "time.h" +#include +#include +#include +#include + +char *time_format_iso8601(time_t t) { + struct tm *tm_info = localtime(&t); + if (!tm_info) return NULL; + + char *buf = malloc(32); + if (!buf) return NULL; + + strftime(buf, 32, "%Y-%m-%dT%H:%M:%S%z", tm_info); + return buf; +} + +char *time_format_local(time_t t, const char *format) { + struct tm *tm_info = localtime(&t); + if (!tm_info) return NULL; + + const char *fmt = format ? format : "%Y-%m-%d %H:%M:%S"; + + char *buf = malloc(64); + if (!buf) return NULL; + + strftime(buf, 64, fmt, tm_info); + return buf; +} + +time_t time_parse_iso8601(const char *str) { + if (!str) return 0; + + struct tm tm = {0}; + char *result = strptime(str, "%Y-%m-%dT%H:%M:%S", &tm); + if (!result) { + result = strptime(str, "%Y-%m-%d %H:%M:%S", &tm); + } + + if (!result) return 0; + return mktime(&tm); +} + +double time_elapsed_seconds(time_t start) { + time_t now = time(NULL); + return difftime(now, start); +} diff --git a/src/util/time.h b/src/util/time.h new file mode 100644 index 0000000..cc8599a --- /dev/null +++ b/src/util/time.h @@ -0,0 +1,14 @@ +// retoor + +#ifndef R_UTIL_TIME_H +#define R_UTIL_TIME_H + +#include +#include + +char *time_format_iso8601(time_t t); +char *time_format_local(time_t t, const char *format); +time_t time_parse_iso8601(const char *str); +double time_elapsed_seconds(time_t start); + +#endif diff --git a/tools.h b/tools.h deleted file mode 100755 index 614b110..0000000 --- a/tools.h +++ /dev/null @@ -1,1720 +0,0 @@ -// 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 "http_curl.h" -#include "r.h" -#include "utils.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "browse.h" -#include "db_utils.h" -#include "indexer.h" -#include "r.h" - -static void tool_print_action(const char *func_name, struct json_object *args) { - if (args == NULL) { - fprintf(stderr, " -> %s\n", func_name); - return; - } - - if (!strcmp(func_name, "linux_terminal_execute")) { - struct json_object *cmd; - if (json_object_object_get_ex(args, "command", &cmd)) { - fprintf(stderr, " -> Running command: %s\n", json_object_get_string(cmd)); - } - } else if (!strcmp(func_name, "linux_terminal_execute_interactive")) { - struct json_object *cmd; - if (json_object_object_get_ex(args, "command", &cmd)) { - fprintf(stderr, " -> Running interactive: %s\n", json_object_get_string(cmd)); - } - } else if (!strcmp(func_name, "read_file")) { - struct json_object *path; - if (json_object_object_get_ex(args, "path", &path)) { - fprintf(stderr, " -> Reading file: %s\n", json_object_get_string(path)); - } - } else if (!strcmp(func_name, "write_file")) { - struct json_object *path; - if (json_object_object_get_ex(args, "path", &path)) { - fprintf(stderr, " -> Writing file: %s\n", json_object_get_string(path)); - } - } else if (!strcmp(func_name, "directory_glob") || !strcmp(func_name, "directory_rglob")) { - struct json_object *path; - if (json_object_object_get_ex(args, "path", &path)) { - fprintf(stderr, " -> Listing: %s\n", json_object_get_string(path)); - } - } else if (!strcmp(func_name, "chdir")) { - struct json_object *path; - if (json_object_object_get_ex(args, "path", &path)) { - fprintf(stderr, " -> Changing directory: %s\n", json_object_get_string(path)); - } - } else if (!strcmp(func_name, "getpwd")) { - fprintf(stderr, " -> Getting current directory\n"); - } else if (!strcmp(func_name, "http_fetch")) { - struct json_object *url; - if (json_object_object_get_ex(args, "url", &url)) { - fprintf(stderr, " -> Fetching URL: %s\n", json_object_get_string(url)); - } - } else if (!strcmp(func_name, "web_search")) { - struct json_object *q; - if (json_object_object_get_ex(args, "query", &q)) { - fprintf(stderr, " -> Searching web: %s\n", json_object_get_string(q)); - } - } else if (!strcmp(func_name, "web_search_news")) { - struct json_object *q; - if (json_object_object_get_ex(args, "query", &q)) { - fprintf(stderr, " -> Searching news: %s\n", json_object_get_string(q)); - } - } else if (!strcmp(func_name, "db_get")) { - struct json_object *key; - if (json_object_object_get_ex(args, "key", &key)) { - fprintf(stderr, " -> Getting from database: %s\n", json_object_get_string(key)); - } - } else if (!strcmp(func_name, "db_set")) { - struct json_object *key; - if (json_object_object_get_ex(args, "key", &key)) { - fprintf(stderr, " -> Storing in database: %s\n", json_object_get_string(key)); - } - } else if (!strcmp(func_name, "db_query")) { - struct json_object *q; - if (json_object_object_get_ex(args, "query", &q)) { - const char *query = json_object_get_string(q); - if (strlen(query) > 60) { - fprintf(stderr, " -> Executing SQL: %.60s...\n", query); - } else { - fprintf(stderr, " -> Executing SQL: %s\n", query); - } - } - } else if (!strcmp(func_name, "mkdir")) { - struct json_object *path; - if (json_object_object_get_ex(args, "path", &path)) { - fprintf(stderr, " -> Creating directory: %s\n", json_object_get_string(path)); - } - } else if (!strcmp(func_name, "index_source_directory")) { - struct json_object *path; - if (json_object_object_get_ex(args, "path", &path)) { - fprintf(stderr, " -> Indexing directory: %s\n", json_object_get_string(path)); - } - } else if (!strcmp(func_name, "python_execute")) { - fprintf(stderr, " -> Executing Python code\n"); - } else { - fprintf(stderr, " -> %s\n", func_name); - } -} - -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 *tool_description_linux_terminal_interactive(); -struct json_object *tool_description_index_source_directory(); -struct json_object *tool_description_chdir(); -struct json_object *tool_description_getpwd(); -struct json_object *tool_description_db_set(); -struct json_object *tool_description_db_query(); -struct json_object *tool_description_db_get(); -struct json_object *tool_description_web_search_news(); -struct json_object *tool_description_web_search(); -struct json_object *tool_description_mkdir(); -struct json_object *tool_description_python_execute(); - -struct json_object *tool_description_rag_search(); -struct json_object *tool_description_rag_chunk(); -char *tool_function_rag_search(char *query, int top_k); -char *tool_function_rag_chunk(char *file_path); - -char *tool_function_python_execute(char *source_code); - -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()); - json_object_array_add(root, tool_description_linux_terminal_interactive()); - json_object_array_add(root, tool_description_index_source_directory()); - json_object_array_add(root, tool_description_chdir()); - json_object_array_add(root, tool_description_getpwd()); - json_object_array_add(root, tool_description_db_set()); - json_object_array_add(root, tool_description_db_query()); - json_object_array_add(root, tool_description_db_get()); - json_object_array_add(root, tool_description_web_search_news()); - json_object_array_add(root, tool_description_web_search()); - json_object_array_add(root, tool_description_mkdir()); - json_object_array_add(root, tool_description_python_execute()); - - return root; -} - -char *tool_function_web_search_news(char *query) { - if (query == NULL) { - return strdup("Query cannot be NULL."); - } - - char *result = web_search_news(query); - if (result == NULL) { - return strdup("Failed to fetch news."); - } - - return result; -} - -struct json_object *tool_description_web_search_news() { - 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("web_search_news")); - json_object_object_add( - function, "description", - json_object_new_string("Searches for news articles based on a query.")); - - 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 *query = json_object_new_object(); - json_object_object_add(query, "type", json_object_new_string("string")); - json_object_object_add( - query, "description", - json_object_new_string( - "The url encoded query string to search for news articles.")); - json_object_object_add(properties, "query", query); - - json_object_object_add(parameters, "properties", properties); - - struct json_object *required = json_object_new_array(); - json_object_array_add(required, json_object_new_string("query")); - 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); - - if (get_use_strict()) - json_object_object_add(function, "strict", - json_object_new_boolean(get_use_strict())); - - json_object_object_add(root, "function", function); - - return root; -} - -char *tool_function_web_search(char *query) { - if (query == NULL) { - return strdup("Query cannot be NULL."); - } - - char *result = web_search(query); - if (result == NULL) { - return strdup("Failed to search."); - } - - return result; -} - -struct json_object *tool_description_web_search() { - 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("web_search")); - json_object_object_add( - function, "description", - json_object_new_string("Searches for information based on a query using " - "search engines like google.")); - - 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 *query = json_object_new_object(); - json_object_object_add(query, "type", json_object_new_string("string")); - json_object_object_add( - query, "description", - json_object_new_string( - "The url encoded query string to search for information.")); - json_object_object_add(properties, "query", query); - - json_object_object_add(parameters, "properties", properties); - - struct json_object *required = json_object_new_array(); - json_object_array_add(required, json_object_new_string("query")); - 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); - - if (get_use_strict()) - json_object_object_add(function, "strict", - json_object_new_boolean(get_use_strict())); - - json_object_object_add(root, "function", function); - - return root; -} - -char *tool_function_db_get(char *key) { - json_object *result = db_get(key); - if (result == NULL) { - return strdup("Failed to retrieve value from the database."); - } - - char *response = strdup(json_object_to_json_string(result)); - json_object_put(result); // Free the json_object - return response; -} - -struct json_object *tool_description_db_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("db_get")); - json_object_object_add( - function, "description", - json_object_new_string( - "Retrieves a value from the database for a given key.")); - - 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 *key = json_object_new_object(); - json_object_object_add(key, "type", json_object_new_string("string")); - json_object_object_add( - key, "description", - json_object_new_string("The key to retrieve from the database.")); - json_object_object_add(properties, "key", key); - - json_object_object_add(parameters, "properties", properties); - - struct json_object *required = json_object_new_array(); - json_object_array_add(required, json_object_new_string("key")); - 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); - - if (get_use_strict()) - json_object_object_add(function, "strict", - json_object_new_boolean(get_use_strict())); - - json_object_object_add(root, "function", function); - - return root; -} - -char *tool_function_db_query(char *query) { - json_object *result = db_query(query); - if (result == NULL) { - return strdup("Failed to execute query on the database."); - } - - char *response = strdup(json_object_to_json_string(result)); - - json_object_put(result); // Free the json_object - return response; -} -struct json_object *tool_description_db_query() { - 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("db_query")); - json_object_object_add( - function, "description", - json_object_new_string( - "Executes a query on the database and returns the results.")); - - 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 *query = json_object_new_object(); - json_object_object_add(query, "type", json_object_new_string("string")); - json_object_object_add(query, "description", - json_object_new_string("The SQL query to execute.")); - json_object_object_add(properties, "query", query); - - json_object_object_add(parameters, "properties", properties); - - struct json_object *required = json_object_new_array(); - json_object_array_add(required, json_object_new_string("query")); - 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); - - if (get_use_strict()) - json_object_object_add(function, "strict", - json_object_new_boolean(get_use_strict())); - - json_object_object_add(root, "function", function); - - return root; -} - -char *tool_function_db_set(char *key, char *value) { - json_object *result = db_set(key, value); - if (result == NULL) { - return strdup("Failed to set value in the database."); - } - - const char *response = json_object_get_string(result); - json_object_put(result); // Free the json_object - return strdup(response); -} -struct json_object *tool_description_db_set() { - 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("db_set")); - json_object_object_add( - function, "description", - json_object_new_string("Sets a value in the database for a given key.")); - - 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 *key = json_object_new_object(); - json_object_object_add(key, "type", json_object_new_string("string")); - json_object_object_add( - key, "description", - json_object_new_string("The key to set in the database.")); - json_object_object_add(properties, "key", key); - - struct json_object *value = json_object_new_object(); - json_object_object_add(value, "type", json_object_new_string("string")); - json_object_object_add( - value, "description", - json_object_new_string("The value to set for the given key.")); - json_object_object_add(properties, "value", value); - - json_object_object_add(parameters, "properties", properties); - - struct json_object *required = json_object_new_array(); - json_object_array_add(required, json_object_new_string("key")); - json_object_array_add(required, json_object_new_string("value")); - 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); - - if (get_use_strict()) - json_object_object_add(function, "strict", - json_object_new_boolean(get_use_strict())); - - json_object_object_add(root, "function", function); - - return root; -} - -char *tool_function_http_get(char *url) { return curl_get(url); } - -char *tool_function_chdir(char *path) { - if (chdir(path) != 0) { - perror("chdir failed"); - return strdup("Failed to change directory!"); - } - - return strdup("Directory successfully changed."); -} - -char *tool_function_linux_terminal(char *command) { - FILE *fp; - char buffer[1024]; - size_t total_size = 0; - char *output = NULL; - - char stderr_command[1024] = {0}; - - if (!strstr(command, "2>")) { - snprintf(stderr_command, sizeof(stderr_command), "%s 2>&1", command); - command = stderr_command; - } - - fp = popen(command, "r"); - if (fp == NULL) { - perror("popen failed"); - return strdup("Popen failed!"); - } - - 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; - if (is_verbose) - fprintf(stderr, "%s", buffer); - strcpy(output + total_size, buffer); - total_size += chunk_size; - } - - pclose(fp); - return output ? output : strdup(""); -} - -char *tool_function_linux_terminal_interactive(char *command) { - int result_code = system(command); - - char *result = malloc(128); - if (result == NULL) { - return strdup("Memory allocation failed."); - } - snprintf(result, 128, "Command exited with status code %d.", result_code); - - return result; -} - -// ---- PYTHON EXECUTE TOOL ---- -char *tool_function_python_execute(char *source_code) { - char tmp_file[] = "/tmp/r_python_tool_XXXXXX.py"; - int fd = mkstemps(tmp_file, 3); // 3 for ".py" - if (fd == -1) { - return strdup("Failed to create temporary file for Python code."); - } - FILE *fp = fdopen(fd, "w"); - if (!fp) { - close(fd); - return strdup("Failed to open temporary file for writing."); - } - fwrite(source_code, 1, strlen(source_code), fp); - fclose(fp); - - char command[4096]; - snprintf(command, sizeof(command), "python3 '%s' 2>&1", tmp_file); - - FILE *proc = popen(command, "r"); - if (!proc) { - unlink(tmp_file); - return strdup("Failed to execute python3."); - } - char buffer[1024]; - size_t total = 0; - char *output = NULL; - while (fgets(buffer, sizeof(buffer), proc)) { - size_t len = strlen(buffer); - char *new_output = realloc(output, total + len + 1); - if (!new_output) { - free(output); - pclose(proc); - unlink(tmp_file); - return strdup("Memory allocation failed."); - } - output = new_output; - strcpy(output + total, buffer); - total += len; - } - if (output) - output[total] = 0; - else - output = strdup(""); - pclose(proc); - unlink(tmp_file); - return output; -} - -struct json_object *tool_description_python_execute() { - 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("python_execute")); - json_object_object_add( - function, "description", - json_object_new_string("Executes Python source code using the python3 " - "interpreter and returns stdout/stderr.")); - - 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 *source = json_object_new_object(); - json_object_object_add(source, "type", json_object_new_string("string")); - json_object_object_add( - source, "description", - json_object_new_string("Python source code to execute.")); - json_object_object_add(properties, "source", source); - - json_object_object_add(parameters, "properties", properties); - - struct json_object *required = json_object_new_array(); - json_object_array_add(required, json_object_new_string("source")); - 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(root, "function", function); - - return root; -} -// ---- END PYTHON EXECUTE TOOL ---- - -char *tool_function_getpwd() { - char *cwd = (char *)malloc(PATH_MAX); - if (cwd == NULL) { - perror("Memory allocation failed"); - return strdup("Failed to allocate memory for current working directory!"); - } - - if (getcwd(cwd, PATH_MAX) == NULL) { - free(cwd); - perror("getcwd failed"); - return strdup("Failed to get current working directory!"); - } - - return cwd; -} - -struct json_object *tool_description_getpwd() { - 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("getpwd")); - json_object_object_add( - function, "description", - json_object_new_string( - "Returns the current working directory as a string.")); - - struct json_object *parameters = json_object_new_object(); - json_object_object_add(parameters, "type", json_object_new_string("object")); - - json_object_object_add(function, "parameters", json_object_new_null()); - - json_object_object_add(root, "function", function); - - return root; -} - -struct json_object *tool_description_chdir() { - 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("chdir")); - json_object_object_add( - function, "description", - json_object_new_string( - "Changes the current working directory to the specified path. Call " - "this function when `cd` is prompted.")); - - 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 change the current working directory to.")); - 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); - - if (get_use_strict()) - json_object_object_add(function, "strict", - json_object_new_boolean(get_use_strict())); - - json_object_object_add(root, "function", function); - - return root; -} - -struct json_object *tool_description_index_source_directory() { - 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("index_source_directory")); - json_object_object_add( - function, "description", - json_object_new_string("Returns a JSON array containing every source " - "file with it's contents from given directory. " - "Execute with '.' for current directory.")); - - 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 index and retreive files from.")); - 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); - - if (get_use_strict()) - json_object_object_add(function, "strict", - json_object_new_boolean(get_use_strict())); - - json_object_object_add(root, "function", function); - - return root; -} - -struct json_object *tool_description_linux_terminal_interactive() { - 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_interactive")); - json_object_object_add( - function, "description", - json_object_new_string( - "Executes interactive terminal for user. You will not be able to " - "read the result. Do not use if you need to know output.")); - - 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( - "Executable with parameters to execute interactively.")); - json_object_object_add(properties, "command", 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("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); - - if (get_use_strict()) - json_object_object_add(function, "strict", - json_object_new_boolean(get_use_strict())); - - json_object_object_add(root, "function", function); - - return root; -} - -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); - - if (get_use_strict()) - json_object_object_add(function, "strict", - json_object_new_boolean(get_use_strict())); - - 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); - - if (get_use_strict()) - json_object_object_add(function, "strict", - json_object_new_boolean(get_use_strict())); - - json_object_object_add(root, "function", function); - - return root; -} - -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. Content should be in " - "plain format, not json encoded.")); - - 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 plain 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( - "Plain 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); - - if (get_use_strict()) - json_object_object_add(function, "strict", - json_object_new_boolean(get_use_strict())); - - json_object_object_add(root, "function", function); - - return root; -} - -char *tool_function_index_source_directory(char *path) { - char *result = index_directory(path); - if (!result) - return strdup("Failed to index directory!"); - return result; -} - -char *tool_function_mkdir(char *path); - -void ensure_parent_directory_exists(char *path_including_file_name) { - if (!path_including_file_name) - return; - - char *path = strdup(path_including_file_name); - if (path == NULL) - return; - - char *last_slash = strrchr(path, '/'); - if (last_slash != NULL) { - *last_slash = '\0'; - char *result = tool_function_mkdir(path); - if (result) - free(result); - } - free(path); -} - -char *tool_function_read_file(char *path) { - char *expanded_path = expand_home_directory(path); - if (expanded_path == NULL) { - return strdup("Failed to expand path!"); - } - FILE *fp = fopen(expanded_path, "r"); - free(expanded_path); - if (fp == NULL) { - perror("fopen failed"); - return strdup("Failed to open file for reading!"); - } - - fseek(fp, 0, SEEK_END); - long size = ftell(fp); - if (size < 0) { - fclose(fp); - return strdup("Failed to determine file size!"); - } - rewind(fp); - - char *content = (char *)malloc((size_t)size + 1); - if (content == NULL) { - fclose(fp); - return strdup("Memory allocation failed!"); - } - - size_t read_size = fread(content, 1, (size_t)size, fp); - content[read_size] = '\0'; - fclose(fp); - - return content; -} - -char *tool_function_write_file(char *path, char *content) { - db_store_file_version(path); - ensure_parent_directory_exists(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 successfully written."); -} - -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"; -} - -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) { - glob_t current_matches; - int ret = glob(pattern, GLOB_NOSORT | GLOB_TILDE, NULL, ¤t_matches); - - if (ret != 0 && ret != GLOB_NOMATCH) { - results->gl_pathc = 0; - results->gl_pathv = NULL; - results->gl_offs = 0; - return; - } - - if (results->gl_pathv == NULL) { - memset(results, 0, sizeof(glob_t)); - } - - for (size_t i = 0; i < current_matches.gl_pathc; i++) { - char *path = current_matches.gl_pathv[i]; - char *path_dup = strdup(path); - if (path_dup == NULL) { - continue; - } - - if (results->gl_pathc == 0) { - results->gl_pathc = 1; - results->gl_pathv = malloc(sizeof(char *)); - if (results->gl_pathv == NULL) { - free(path_dup); - continue; - } - results->gl_pathv[0] = path_dup; - } else { - char **new_pathv = realloc(results->gl_pathv, (results->gl_pathc + 1) * sizeof(char *)); - if (new_pathv == NULL) { - free(path_dup); - continue; - } - results->gl_pathv = new_pathv; - results->gl_pathv[results->gl_pathc] = path_dup; - results->gl_pathc++; - } - } - - DIR *dir; - struct dirent *entry; - struct stat statbuf; - - char *pattern_copy = strdup(pattern); - if (pattern_copy == NULL) { - globfree(¤t_matches); - return; - } - 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 { - dir_path = "."; - file_pattern = pattern_copy; - } - - if ((dir = opendir(dir_path)) != NULL) { - while ((entry = readdir(dir)) != NULL) { - if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) { - continue; - } - - 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); - } - - if (path_len >= PATH_MAX) { - continue; - } - - if (stat(full_path, &statbuf) == 0 && S_ISDIR(statbuf.st_mode)) { - char new_pattern[PATH_MAX]; - int new_pattern_len = - snprintf(new_pattern, PATH_MAX, "%s/%s", full_path, file_pattern); - - if (new_pattern_len >= PATH_MAX) { - continue; - } - - recursive_glob(new_pattern, results); - } - } - closedir(dir); - } - - free(pattern_copy); - globfree(¤t_matches); -} - -char *tool_function_directory_rglob(char *target_dir) { - glob_t results; - results.gl_pathc = 0; - struct stat file_stat; - char mod_time[20], create_time[20]; - - if (!strcmp(target_dir, ".")) { - target_dir[0] = '*'; - } - - recursive_glob(target_dir, &results); - - json_object *json_array = json_object_new_array(); - - for (size_t i = 0; i < results.gl_pathc; i++) { - const char *file_path = results.gl_pathv[i]; - - if (stat(file_path, &file_stat) == -1) { - perror("stat failed"); - continue; - } - - format_time(file_stat.st_mtime, mod_time, sizeof(mod_time)); - format_time(file_stat.st_ctime, create_time, sizeof(create_time)); - - 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)); - - json_object_array_add(json_array, json_entry); - } - - globfree(&results); - char *result = strdup( - json_object_to_json_string_ext(json_array, JSON_C_TO_STRING_PRETTY)); - - json_object_put(json_array); - - return result; -} - -char *tool_function_directory_glob(char *target_dir) { - glob_t results; - struct stat file_stat; - char mod_time[20], create_time[20]; - - if (!strcmp(target_dir, ".")) { - target_dir[0] = '*'; - } - - if (glob(target_dir, GLOB_TILDE, NULL, &results) != 0) { - perror("glob failed"); - return strdup(""); - } - - json_object *json_array = json_object_new_array(); - - for (size_t i = 0; i < results.gl_pathc; i++) { - const char *file_path = results.gl_pathv[i]; - - if (stat(file_path, &file_stat) == -1) { - perror("stat failed"); - continue; - } - - format_time(file_stat.st_mtime, mod_time, sizeof(mod_time)); - format_time(file_stat.st_ctime, create_time, sizeof(create_time)); - - 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)); - - json_object_array_add(json_array, json_entry); - } - - globfree(&results); - char *result = strdup( - json_object_to_json_string_ext(json_array, JSON_C_TO_STRING_PRETTY)); - - 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); - if (get_use_strict()) - json_object_object_add(function, "strict", - json_object_new_boolean(get_use_strict())); - - 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); - - if (get_use_strict()) - json_object_object_add(function, "strict", - json_object_new_boolean(get_use_strict())); - - 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); - - if (get_use_strict()) - json_object_object_add(function, "strict", - json_object_new_boolean(get_use_strict())); - - json_object_object_add(root, "function", function); - - return root; -} - -char *tool_function_mkdir(char *path) { - char temp[4096]; - char *p = NULL; - size_t len; - - len = strlen(path); - if (len >= sizeof(temp)) { - return strdup("Path too long!"); - } - memcpy(temp, path, len + 1); - - if (temp[len - 1] == '/') { - temp[len - 1] = '\0'; - } - - for (p = temp + 1; *p; p++) { - if (*p == '/') { - *p = '\0'; - if (mkdir(temp, 0777) != 0 && errno != EEXIST) { - return strdup("Failed to create directory!"); - } - *p = '/'; - } - } - if (mkdir(temp, 0777) != 0 && errno != EEXIST) { - return strdup("Failed to create directory!"); - } - return strdup("Directory successfully created."); -} - -struct json_object *tool_description_mkdir() { - 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("mkdir")); - json_object_object_add( - function, "description", - json_object_new_string( - "Creates a new directory with the specified path.")); - - 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 of the directory to create.")); - 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); - - if (get_use_strict()) - json_object_object_add(function, "strict", - json_object_new_boolean(get_use_strict())); - - 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); - 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)); - - tool_print_action(function_name, arguments); - - if (is_verbose) - fprintf(stderr, " [verbose] %s args: %s\n", - function_name, json_object_to_json_string(arguments)); - } - } - - 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 *terminal_result = tool_function_linux_terminal(command); - json_object_object_add(tool_result, "content", - json_object_new_string(terminal_result)); - free(terminal_result); - } - } - } else if (!strcmp(function_name, "linux_terminal_execute_interactive")) { - 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 *terminal_result = - tool_function_linux_terminal_interactive(command); - json_object_object_add(tool_result, "content", - json_object_new_string(terminal_result)); - free(terminal_result); - } - } - } else if (!strcmp(function_name, "chdir")) { - 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 *chdir_result = tool_function_chdir(path); - json_object_object_add(tool_result, "content", - json_object_new_string(chdir_result)); - free(chdir_result); - } - } - } else 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); - } - } - } else 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)); - free(http_result); - } - } - } else 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); - } - } - } else 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); - } - } - } else if (!strcmp(function_name, "db_query")) { - 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 *query_obj; - - if (json_object_object_get_ex(arguments, "query", &query_obj)) { - char *query = (char *)json_object_get_string(query_obj); - char *db_query_result = tool_function_db_query(query); - json_object_object_add(tool_result, "content", - json_object_new_string(db_query_result)); - ; - if (db_query_result != NULL) - free(db_query_result); - } - } - } else if (!strcmp(function_name, "db_set")) { - 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 *key_obj; - struct json_object *value_obj; - - if (json_object_object_get_ex(arguments, "key", &key_obj) && - json_object_object_get_ex(arguments, "value", &value_obj)) { - char *key = (char *)json_object_get_string(key_obj); - char *value = (char *)json_object_get_string(value_obj); - char *db_set_result = tool_function_db_set(key, value); - json_object_object_add(tool_result, "content", - json_object_new_string(db_set_result)); - free(db_set_result); - } - } - - } else if (!strcmp(function_name, "index_source_directory")) { - 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_index_source_directory(path); - json_object_object_add(tool_result, "content", - json_object_new_string(listing_result)); - free(listing_result); - } - } - } else if (!strcmp(function_name, "web_search_news")) { - 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 *query_obj; - if (json_object_object_get_ex(arguments, "query", &query_obj)) { - char *query = (char *)json_object_get_string(query_obj); - char *news_result = tool_function_web_search_news(query); - json_object_object_add(tool_result, "content", - json_object_new_string(news_result)); - free(news_result); - } - } - } else if (!strcmp(function_name, "web_search")) { - 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 *query_obj; - if (json_object_object_get_ex(arguments, "query", &query_obj)) { - char *query = (char *)json_object_get_string(query_obj); - char *news_result = tool_function_web_search(query); - json_object_object_add(tool_result, "content", - json_object_new_string(news_result)); - free(news_result); - } - } - } else if (!strcmp(function_name, "db_get")) { - 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 *key_obj; - - if (json_object_object_get_ex(arguments, "key", &key_obj)) { - char *key = (char *)json_object_get_string(key_obj); - char *db_get_result = tool_function_db_get(key); - json_object_object_add(tool_result, "content", - json_object_new_string(db_get_result)); - free(db_get_result); - } - } - } else if (!strcmp(function_name, "getpwd")) { - char *pwd_result = tool_function_getpwd(); - json_object_object_add(tool_result, "content", - json_object_new_string(pwd_result)); - free(pwd_result); - } else 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); - } - } - } else if (!strcmp(function_name, "mkdir")) { - 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 *mkdir_result = tool_function_mkdir(path); - json_object_object_add(tool_result, "content", - json_object_new_string(mkdir_result)); - free(mkdir_result); - } - } - } else if (!strcmp(function_name, "python_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 *source_obj; - if (json_object_object_get_ex(arguments, "source", &source_obj)) { - char *source = (char *)json_object_get_string(source_obj); - char *result = tool_function_python_execute(source); - json_object_object_add(tool_result, "content", - json_object_new_string(result)); - free(result); - } - } - } else { - fprintf(stderr, "Unknown function: %s\n", function_name); - json_object_object_add( - tool_result, "content", - json_object_new_string("Error: function not found.")); - } - - json_object_array_add(tools_result_messages, tool_result); - } - } - return tools_result_messages; -} - -#endif diff --git a/url.h b/url.h deleted file mode 100755 index 50dc1fb..0000000 --- a/url.h +++ /dev/null @@ -1,102 +0,0 @@ -// Written by retoor@molodetz.nl - -// This code defines a URL parser in C that extracts components such as the -// scheme, hostname, port, path, and query from a given URL. - -// Includes: -// - for standard I/O operations -// - for string manipulation functions -// - for memory allocation and management - -// MIT License -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: The above copyright -// notice and this permission notice shall be included in all copies or -// substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", -// WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED -// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE -// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR -// THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -#ifndef R_URL_H -#define R_URL_H -#include -#include -#include - -typedef struct { - char scheme[16]; - char hostname[256]; - char port[8]; - char path[512]; - char query[512]; -} url_t; - -int parse_url(const char *url, url_t *parsed_url) { - memset(parsed_url, 0, sizeof(url_t)); - - const char *scheme_end = strstr(url, "://"); - if (!scheme_end) { - return -1; - } - - strncpy(parsed_url->scheme, url, scheme_end - url); - parsed_url->scheme[scheme_end - url] = '\0'; - - const char *hostname_start = scheme_end + 3; - const char *port_start = strchr(hostname_start, ':'); - const char *path_start = strchr(hostname_start, '/'); - const char *query_start = strchr(hostname_start, '?'); - - if (port_start && (!path_start || port_start < path_start)) { - size_t hostname_length = port_start - hostname_start; - if (hostname_length >= sizeof(parsed_url->hostname)) { - fprintf(stderr, "Hostname is too long\n"); - return -1; - } - strncpy(parsed_url->hostname, hostname_start, hostname_length); - parsed_url->hostname[hostname_length] = '\0'; - - size_t port_length = query_start ? (size_t)(query_start - port_start - 1) - : strlen(port_start + 1); - if (port_length >= sizeof(parsed_url->port)) { - fprintf(stderr, "Port value is too long\n"); - return -1; - } - strncpy(parsed_url->port, port_start + 1, port_length); - parsed_url->port[port_length] = '\0'; - } else { - size_t hostname_length = - path_start ? (size_t)(path_start - hostname_start) - : (query_start ? (size_t)(query_start - hostname_start) - : strlen(hostname_start)); - if (hostname_length >= sizeof(parsed_url->hostname)) { - fprintf(stderr, "Hostname is too long\n"); - return -1; - } - strncpy(parsed_url->hostname, hostname_start, hostname_length); - parsed_url->hostname[hostname_length] = '\0'; - } - - if (path_start) { - if (query_start) { - strncpy(parsed_url->path, path_start, query_start - path_start); - parsed_url->path[query_start - path_start] = '\0'; - } else { - strcpy(parsed_url->path, path_start); - } - } - - if (query_start) { - strcpy(parsed_url->query, query_start + 1); - } - - return 0; -} -#endif \ No newline at end of file diff --git a/utils.h b/utils.h deleted file mode 100755 index d4fa2e4..0000000 --- a/utils.h +++ /dev/null @@ -1,146 +0,0 @@ -// Written by retoor@molodetz.nl - -// This header file contains utility functions for manipulating file system -// paths, focusing primarily on expanding paths starting with '~' to the home -// directory. - -// This code uses standard libraries: stdio.h, stdlib.h, and string.h, and -// conditionally includes the posix libraries pwd.h and unistd.h when expanding -// the home directory manually. - -// MIT License -// -// Permission is granted to use, copy, modify, merge, distribute, sublicense, -// and/or sell copies of the Software. The license includes conditions about -// providing a copy of the license and the limitation of liability and warranty. - -#ifndef UTILS_H -#define UTILS_H - -#include -#include -#include - -#ifdef _WIN32 -#include -#else -#include -#include -#include -#endif - -void get_current_directory() { - char buffer[PATH_MAX]; - -#ifdef _WIN32 - DWORD length = GetCurrentDirectory(PATH_MAX, buffer); - if (length > 0 && length < PATH_MAX) { - printf("Current Directory: %s\n", buffer); - } else { - printf("Error getting current directory.\n"); - } -#else - if (getcwd(buffer, sizeof(buffer)) != NULL) { - printf("Current Directory: %s\n", buffer); - } else { - perror("Error getting current directory"); - } -#endif -} - -char *expand_home_directory(const char *path) { - if (path[0] != '~') { - return strdup(path); // Return the original path if it doesn't start with ~ - } - - char *home_dir; - -#ifdef _WIN32 - home_dir = getenv("USERPROFILE"); // Get home directory on Windows -#else - struct passwd *pw = getpwuid(getuid()); - home_dir = pw->pw_dir; // Get home directory on Linux -#endif - - if (home_dir == NULL) { - return NULL; // Error getting home directory - } - - size_t home_len = strlen(home_dir); - size_t path_len = strlen(path + 1); - size_t expanded_size = home_len + path_len + 1; - char *expanded_path = (char *)malloc(expanded_size); - if (expanded_path == NULL) { - return NULL; - } - - memcpy(expanded_path, home_dir, home_len); - memcpy(expanded_path + home_len, path + 1, path_len); - expanded_path[home_len + path_len] = '\0'; - return expanded_path; -} - -unsigned long hash(const char *str) { - unsigned long hash = 5381; // Starting value - int c; - - while ((c = *str++)) { - hash = ((hash << 5) + hash) + c; // hash * 33 + c - } - - return hash; -} - -char *joinpath(const char *base_url, const char *path) { - static char result[4096]; - size_t base_len = strlen(base_url); - size_t pos = 0; - - if (base_len >= sizeof(result) - 2) { - base_len = sizeof(result) - 2; - } - memcpy(result, base_url, base_len); - pos = base_len; - - if (pos > 0 && result[pos - 1] != '/') { - result[pos++] = '/'; - } - if (path[0] == '/') { - path++; - } - - size_t path_len = strlen(path); - if (pos + path_len >= sizeof(result)) { - path_len = sizeof(result) - pos - 1; - } - memcpy(result + pos, path, path_len); - result[pos + path_len] = '\0'; - return result; -} - -char *read_file(const char *path) { - char *expanded_path = expand_home_directory(path); - if (expanded_path == NULL) { - return NULL; - } - FILE *file = fopen(expanded_path, "r"); - free(expanded_path); - if (file == NULL) { - return NULL; - } - fseek(file, 0, SEEK_END); - long size = ftell(file); - fseek(file, 0, SEEK_SET); - - char *buffer = (char *)malloc(size + 1); - size_t read = fread(buffer, 1, size, file); - if (read == 0) { - free(buffer); - return NULL; - } - - fclose(file); - buffer[read] = '\0'; - return buffer; -} -#endif