This commit is contained in:
2026-01-28 19:34:39 +01:00
parent 930e0889fd
commit 3f8e1ec53a
43 changed files with 4086 additions and 132 deletions
-4
View File
@@ -1,4 +0,0 @@
#!/bin/sh
HERE="$(dirname "$(readlink -f "$0")")"
export LD_LIBRARY_PATH="$HERE/usr/lib:$LD_LIBRARY_PATH"
exec "$HERE/usr/bin/r" "$@"
+71 -52
View File
@@ -1,75 +1,94 @@
all: build build_rpylib run build_mingw
# retoor <retoor@molodetz.nl>
# Variables for compiler and flags
CC = gcc
CFLAGS = -Ofast -Werror -Wall -lreadline -lncurses -lcurl -ljson-c -lsqlite3 -lm -lpthread $(pkg-config --cflags --libs gnutls gmp) -lssl -lcrypto
CFLAGS = -Ofast -Werror -Wall -I./include
LDFLAGS = -lreadline -lncurses -lcurl -ljson-c -lsqlite3 -lm -lpthread -lssl -lcrypto
# MinGW Variables
MINGW_CC = x86_64-w64-mingw32-gcc # Change to x86_64-w64-mingw32-gcc for 64-bit
MINGW_CFLAGS = -Ofast -Werror -Wall -lreadline -lcurl -lssl -lcrypto -ljson-c -lm -lglob
SRCDIR = src
TOOLSDIR = src/tools
BUILDDIR = build
BINDIR = bin
# Targets
build: publish
mkdir -p bin
$(CC) main.c $(CFLAGS) -o bin/r
cp bin/r r
publish r
SRC_CORE = $(SRCDIR)/r_error.c \
$(SRCDIR)/r_config.c \
$(SRCDIR)/tool_registry.c \
$(SRCDIR)/db.c \
$(SRCDIR)/http_client.c \
$(SRCDIR)/messages.c \
$(SRCDIR)/agent.c \
$(SRCDIR)/bash_executor.c \
$(SRCDIR)/main.c
appimage:
SRC_TOOLS = $(TOOLSDIR)/tools_init.c \
$(TOOLSDIR)/tool_terminal.c \
$(TOOLSDIR)/tool_file.c \
$(TOOLSDIR)/tool_db.c \
$(TOOLSDIR)/tool_http.c \
$(TOOLSDIR)/tool_python.c \
$(TOOLSDIR)/tool_indexer.c
SRC = $(SRC_CORE) $(SRC_TOOLS)
OBJ_CORE = $(patsubst $(SRCDIR)/%.c,$(BUILDDIR)/%.o,$(SRC_CORE))
OBJ_TOOLS = $(patsubst $(TOOLSDIR)/%.c,$(BUILDDIR)/tools/%.o,$(SRC_TOOLS))
OBJ = $(OBJ_CORE) $(OBJ_TOOLS)
.PHONY: all clean build build_legacy run
all: build
build: $(BINDIR)/r
cp $(BINDIR)/r r
$(BINDIR)/r: $(OBJ)
@mkdir -p $(BINDIR)
$(CC) $(OBJ) $(LDFLAGS) -o $@
$(BUILDDIR)/%.o: $(SRCDIR)/%.c
@mkdir -p $(dir $@)
$(CC) $(CFLAGS) -c $< -o $@
$(BUILDDIR)/tools/%.o: $(TOOLSDIR)/%.c
@mkdir -p $(dir $@)
$(CC) $(CFLAGS) -c $< -o $@
build_legacy:
@mkdir -p $(BINDIR)
$(CC) main.c $(CFLAGS) $(LDFLAGS) -o $(BINDIR)/r_legacy
cp $(BINDIR)/r_legacy r
build_rpylib:
$(CC) -shared -o rpylib.so -fPIC rpylib.c -lpython3.12 `python3-config --includes` -I/usr/include/CL -ljson-c -lcurl -lsqlite3
run: build
./r --verbose
install:
cp ./r /usr/local/bin/r
clean:
rm -rf $(BUILDDIR) $(BINDIR)
rm -f r
appimage: build
-@rm -rf AppImage
mkdir -p AppImage
mkdir -p AppImage/usr
mkdir -p AppImage/usr/bin
mkdir -p AppImage/lib
cp AppRun AppImage/AppRun
cp r.desktop AppImage/r.desktop
cp r.png AppImage/r.png
cp bin/r AppImage/usr/bin/r
cp $(BINDIR)/r AppImage/usr/bin/r
./collect_so_files.sh
#./prepare_app_image AppImage/usr/bin/r AppImage
appimagetool-x86_64.AppImage AppImage
mv r-x86_64.AppImage r
publish:
curl -OJ https://retoor.molodetz.nl/api/packages/retoor/generic/publish/1.0.0/publish
chmod +x publish
./publish r
build_rpylib:
$(CC) -shared -o rpylib.so -fPIC rpylib.c -lpython3.12 `python3-config --includes` -I/usr/include/CL -ljson-c -lcurl -lsqlite3
publish rpylib.so
# New MinGW build target
build_mingw:
$(MINGW_CC) main.c $(MINGW_CFLAGS) -o r.exe
publish r.exe
run:
./r --verbose
run_free:
./rf --verbose
run_rd:
./rd --verbose
run_mingw:
./r.exe --verbose
docker: docker_make docker_run
docker_make:
docker build -t r .
docker_run:
docker run -v .:/app --rm -it r
build_deb:
dpkg-deb --build r_package
# --- RAG Side Project ---
rag_test: rag_test.c rag.c db_utils.c rag.h db_utils.h
$(CC) -o rag_test rag_test.c rag.c db_utils.c -lsqlite3 -ljson-c
run_rag_test: rag_test
./rag_test
Regular → Executable
View File
+10
View File
@@ -30,6 +30,11 @@ void auth_init() {
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;
@@ -49,6 +54,11 @@ const char *resolve_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;
Regular → Executable
View File
-27
View File
@@ -1,27 +0,0 @@
#!/bin/bash
# Script: collect_so_files.sh
BINARY="AppImage/usr/bin/r"
LIB_DIR="AppImage/usr/lib"
mkdir -p "$LIB_DIR"
# Function to copy a library and its dependencies
copy_with_deps() {
local lib="$1"
if [ -f "$lib" ] && [ ! -f "$LIB_DIR/$(basename "$lib")" ]; then
cp "$lib" "$LIB_DIR/"
echo "Copied: $lib"
# Recursively check dependencies of this library
ldd "$lib" | grep -o '/[^ ]\+' | while read -r dep; do
if [ -f "$dep" ]; then
copy_with_deps "$dep"
fi
done
fi
}
# Start with the binarys dependencies
ldd "$BINARY" | grep -o '/[^ ]\+' | while read -r lib; do
copy_with_deps "$lib"
done
-10
View File
@@ -1,10 +0,0 @@
services:
shell:
build: .
command: sh
tty: true
stdin_open: true
working_dir: /home
volumes:
- ./:/home
BIN
View File
Binary file not shown.
Regular → Executable
View File
+38
View File
@@ -0,0 +1,38 @@
// retoor <retoor@molodetz.nl>
#ifndef R_AGENT_H
#define R_AGENT_H
#include "messages.h"
#include "r_error.h"
#include <stdbool.h>
#define AGENT_MAX_ITERATIONS 300
#define AGENT_MAX_TOOL_RETRIES 3
typedef enum {
AGENT_STATE_IDLE,
AGENT_STATE_RUNNING,
AGENT_STATE_EXECUTING_TOOLS,
AGENT_STATE_COMPLETED,
AGENT_STATE_MAX_ITERATIONS,
AGENT_STATE_ERROR
} agent_state_t;
typedef struct agent_t *agent_handle;
agent_handle agent_create(const char *goal, messages_handle messages);
void agent_destroy(agent_handle agent);
void agent_set_max_iterations(agent_handle agent, int max);
void agent_set_verbose(agent_handle agent, bool verbose);
agent_state_t agent_get_state(agent_handle agent);
const char *agent_get_error(agent_handle agent);
int agent_get_iteration_count(agent_handle agent);
char *agent_run(agent_handle agent, const char *user_message);
char *agent_chat(const char *user_message, messages_handle messages);
char *agent_chat_with_limit(const char *user_message, int max_iterations, messages_handle messages);
#endif
+9
View File
@@ -0,0 +1,9 @@
// retoor <retoor@molodetz.nl>
#ifndef R_BASH_EXECUTOR_H
#define R_BASH_EXECUTOR_H
#include <stdbool.h>
char *r_bash_execute(const char *command, bool interactive);
#endif
Executable
+24
View File
@@ -0,0 +1,24 @@
// retoor <retoor@molodetz.nl>
#ifndef R_DB_H
#define R_DB_H
#include "r_error.h"
#include <json-c/json.h>
typedef struct db_t *db_handle;
db_handle db_open(const char *path);
void db_close(db_handle db);
r_status_t db_init(db_handle db);
r_status_t db_kv_set(db_handle db, const char *key, const char *value);
r_status_t db_kv_get(db_handle db, const char *key, char **value);
r_status_t db_execute(db_handle db, const char *sql, struct json_object **result);
char *db_get_schema(db_handle db);
r_status_t db_store_file_version(db_handle db, const char *path);
r_status_t db_save_conversation(db_handle db, const char *session_key, const char *data);
r_status_t db_load_conversation(db_handle db, const char *session_key, char **data);
#endif
+26
View File
@@ -0,0 +1,26 @@
// retoor <retoor@molodetz.nl>
#ifndef R_HTTP_CLIENT_H
#define R_HTTP_CLIENT_H
#include "r_error.h"
#include <stdbool.h>
typedef struct http_client_t *http_client_handle;
http_client_handle http_client_create(const char *bearer_token);
void http_client_destroy(http_client_handle client);
void http_client_set_show_spinner(http_client_handle client, bool show);
void http_client_set_timeout(http_client_handle client, long timeout_seconds);
void http_client_set_connect_timeout(http_client_handle client, long timeout_seconds);
r_status_t http_post(http_client_handle client, const char *url,
const char *data, char **response);
r_status_t http_get(http_client_handle client, const char *url, char **response);
r_status_t http_post_simple(const char *url, const char *bearer_token,
const char *data, char **response);
r_status_t http_get_simple(const char *url, const char *bearer_token, char **response);
#endif
+33
View File
@@ -0,0 +1,33 @@
// retoor <retoor@molodetz.nl>
#ifndef R_MESSAGES_H
#define R_MESSAGES_H
#include "r_error.h"
#include <json-c/json.h>
#include <stdbool.h>
typedef struct messages_t *messages_handle;
messages_handle messages_create(const char *session_id);
void messages_destroy(messages_handle msgs);
r_status_t messages_set_session_id(messages_handle msgs, const char *session_id);
const char *messages_get_session_id(messages_handle msgs);
r_status_t messages_add(messages_handle msgs, const char *role, const char *content);
r_status_t messages_add_object(messages_handle msgs, struct json_object *message);
r_status_t messages_add_tool_call(messages_handle msgs, struct json_object *message);
r_status_t messages_add_tool_result(messages_handle msgs, const char *tool_call_id, const char *result);
r_status_t messages_remove_last(messages_handle msgs);
r_status_t messages_clear(messages_handle msgs);
r_status_t messages_save(messages_handle msgs);
r_status_t messages_load(messages_handle msgs);
struct json_object *messages_to_json(messages_handle msgs);
char *messages_to_string(messages_handle msgs);
int messages_count(messages_handle msgs);
#endif
+33
View File
@@ -0,0 +1,33 @@
// retoor <retoor@molodetz.nl>
#ifndef R_CONFIG_H
#define R_CONFIG_H
#include <stdbool.h>
typedef struct r_config_t *r_config_handle;
r_config_handle r_config_get_instance(void);
void r_config_destroy(void);
const char *r_config_get_api_url(r_config_handle cfg);
const char *r_config_get_models_url(r_config_handle cfg);
const char *r_config_get_model(r_config_handle cfg);
void r_config_set_model(r_config_handle cfg, const char *model);
const char *r_config_get_api_key(r_config_handle cfg);
const char *r_config_get_db_path(r_config_handle cfg);
bool r_config_use_tools(r_config_handle cfg);
bool r_config_use_strict(r_config_handle cfg);
bool r_config_is_verbose(r_config_handle cfg);
void r_config_set_verbose(r_config_handle cfg, bool verbose);
double r_config_get_temperature(r_config_handle cfg);
const char *r_config_get_session_id(r_config_handle cfg);
bool r_config_set_session_id(r_config_handle cfg, const char *session_id);
const char *r_config_get_system_message(r_config_handle cfg);
#endif
+39
View File
@@ -0,0 +1,39 @@
// retoor <retoor@molodetz.nl>
#ifndef R_ERROR_H
#define R_ERROR_H
typedef enum {
R_SUCCESS = 0,
R_ERROR_INVALID_ARG,
R_ERROR_OUT_OF_MEMORY,
R_ERROR_NOT_FOUND,
R_ERROR_PARSE,
R_ERROR_DB_CONNECTION,
R_ERROR_DB_QUERY,
R_ERROR_DB_NOT_FOUND,
R_ERROR_HTTP_CONNECTION,
R_ERROR_HTTP_TIMEOUT,
R_ERROR_HTTP_RESPONSE,
R_ERROR_JSON_PARSE,
R_ERROR_FILE_NOT_FOUND,
R_ERROR_FILE_READ,
R_ERROR_FILE_WRITE,
R_ERROR_TOOL_NOT_FOUND,
R_ERROR_TOOL_EXECUTION,
R_ERROR_API_KEY_MISSING,
R_ERROR_API_ERROR,
R_ERROR_MAX_ITERATIONS,
R_ERROR_SESSION_INVALID,
R_ERROR_UNKNOWN
} r_status_t;
const char *r_status_string(r_status_t status);
#define R_CHECK(expr) do { r_status_t _s = (expr); if (_s != R_SUCCESS) return _s; } while(0)
#define R_CHECK_NULL(ptr) do { if (!(ptr)) return R_ERROR_INVALID_ARG; } while(0)
#define R_CHECK_ALLOC(ptr) do { if (!(ptr)) return R_ERROR_OUT_OF_MEMORY; } while(0)
#endif
Executable
+42
View File
@@ -0,0 +1,42 @@
// retoor <retoor@molodetz.nl>
#ifndef R_TOOL_H
#define R_TOOL_H
#include "r_error.h"
#include <json-c/json.h>
#include <stdbool.h>
#include <stddef.h>
typedef struct tool_t tool_t;
typedef struct {
struct json_object *(*get_description)(void);
char *(*execute)(tool_t *self, struct json_object *args);
void (*print_action)(const char *name, struct json_object *args);
} tool_vtable_t;
struct tool_t {
const tool_vtable_t *vtable;
const char *name;
};
typedef struct {
tool_t **tools;
size_t count;
size_t capacity;
} tool_registry_t;
tool_registry_t *tool_registry_create(void);
void tool_registry_destroy(tool_registry_t *registry);
r_status_t tool_registry_register(tool_registry_t *registry, tool_t *tool);
tool_t *tool_registry_find(tool_registry_t *registry, const char *name);
struct json_object *tool_registry_get_descriptions(tool_registry_t *registry);
struct json_object *tool_registry_execute(tool_registry_t *registry,
struct json_object *tool_calls,
bool verbose);
tool_registry_t *tools_get_registry(void);
void tools_registry_shutdown(void);
#endif
Regular → Executable
+6
View File
@@ -189,6 +189,11 @@ static void repl(void) {
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");
@@ -213,6 +218,7 @@ static void repl(void) {
json_object_to_json_string(tools_descriptions()));
continue;
}
if (!strncmp(line, "!models", 7)) {
printf("Current model: %s\n", openai_fetch_models());
continue;
Regular → Executable
View File
-6
View File
@@ -1,6 +0,0 @@
[Desktop Entry]
Name=r
Exec=usr/bin/r
Type=Application
Icon=r
Categories=Utility;
Regular → Executable
+5 -1
View File
@@ -84,8 +84,12 @@ bool get_use_strict() {
char *get_completions_api_url() {
if (getenv("R_BASE_URL") != NULL) {
return joinpath(getenv("R_BASE_URL"), "v1/chat/completions");
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() {
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

-23
View File
@@ -1,23 +0,0 @@
# Application Review Report
## Entry Point
- The application's entry point is defined in `main.c`.
- Currently, it prints "Hello, World!".
## Code Structure
- The application has a simple structure with a single source file.
## Testing
- Basic test setup to verify that `main()` returns 0.
- The test code attempts to call `main()` directly, leading to a multiple definition error.
## Issues Identified
- The test setup is incorrect because calling `main()` directly causes a multiple definition error.
- Proper testing should involve refactoring the code to separate logic from the entry point.
## Recommendations
- Refactor the application code to isolate logic from `main()` to allow easier testing.
- Expand the code to include more functionalities and corresponding tests.
- Review and improve build process and structure for maintainability.
The review will be updated after fixing these issues.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

-10
View File
@@ -1,10 +0,0 @@
from setuptools import setup, Extension
module = Extension("rpylib", sources=["rpylib.c"])
setup(
name="rpylib",
version="1.0",
description="AI Module",
ext_modules=[module],
)
Executable
+415
View File
@@ -0,0 +1,415 @@
// retoor <retoor@molodetz.nl>
#include "agent.h"
#include "http_client.h"
#include "r_config.h"
#include "tool.h"
#include <json-c/json.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
struct agent_t {
char *goal;
int iteration_count;
int max_iterations;
int tool_retry_count;
int max_tool_retries;
agent_state_t state;
time_t start_time;
char *last_error;
bool verbose;
messages_handle messages;
bool owns_messages;
http_client_handle http;
tool_registry_t *tools;
};
static 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
};
static const char *incomplete_endings[] = {
"...", ":", "files:", "content:", "implementation:",
NULL
};
extern tool_registry_t *tools_get_registry(void);
agent_handle agent_create(const char *goal, messages_handle messages) {
struct agent_t *agent = calloc(1, sizeof(struct agent_t));
if (!agent) return NULL;
if (goal) {
agent->goal = strdup(goal);
if (!agent->goal) {
free(agent);
return NULL;
}
}
r_config_handle cfg = r_config_get_instance();
agent->iteration_count = 0;
agent->max_iterations = AGENT_MAX_ITERATIONS;
agent->tool_retry_count = 0;
agent->max_tool_retries = AGENT_MAX_TOOL_RETRIES;
agent->state = AGENT_STATE_IDLE;
agent->start_time = time(NULL);
agent->verbose = r_config_is_verbose(cfg);
if (messages) {
agent->messages = messages;
agent->owns_messages = false;
} else {
agent->messages = messages_create(r_config_get_session_id(cfg));
agent->owns_messages = true;
}
if (!agent->messages) {
free(agent->goal);
free(agent);
return NULL;
}
agent->http = http_client_create(r_config_get_api_key(cfg));
if (!agent->http) {
if (agent->owns_messages) {
messages_destroy(agent->messages);
}
free(agent->goal);
free(agent);
return NULL;
}
agent->tools = tools_get_registry();
return agent;
}
void agent_destroy(agent_handle agent) {
if (!agent) return;
if (agent->http) http_client_destroy(agent->http);
if (agent->messages && agent->owns_messages) messages_destroy(agent->messages);
free(agent->goal);
free(agent->last_error);
free(agent);
}
void agent_set_max_iterations(agent_handle agent, int max) {
if (agent) agent->max_iterations = max;
}
void agent_set_verbose(agent_handle agent, bool verbose) {
if (agent) agent->verbose = verbose;
}
agent_state_t agent_get_state(agent_handle agent) {
return agent ? agent->state : AGENT_STATE_ERROR;
}
const char *agent_get_error(agent_handle agent) {
return agent ? agent->last_error : NULL;
}
int agent_get_iteration_count(agent_handle agent) {
return agent ? agent->iteration_count : 0;
}
static void agent_set_error(agent_handle agent, const char *error) {
if (!agent) return;
free(agent->last_error);
agent->last_error = error ? strdup(error) : NULL;
}
static char *agent_build_request(agent_handle agent, const char *role, const char *message) {
r_config_handle cfg = r_config_get_instance();
struct json_object *root = json_object_new_object();
if (!root) return NULL;
json_object_object_add(root, "model",
json_object_new_string(r_config_get_model(cfg)));
if (role && message) {
messages_add(agent->messages, role, message);
if (r_config_use_tools(cfg) && agent->tools) {
json_object_object_add(root, "tools",
tool_registry_get_descriptions(agent->tools));
}
}
json_object_object_add(root, "messages",
json_object_get(messages_to_json(agent->messages)));
json_object_object_add(root, "temperature",
json_object_new_double(r_config_get_temperature(cfg)));
char *result = strdup(json_object_to_json_string_ext(root, JSON_C_TO_STRING_PRETTY));
json_object_put(root);
return result;
}
static struct json_object *agent_process_response(agent_handle agent, const char *json_data) {
r_config_handle cfg = r_config_get_instance();
char *response = NULL;
r_status_t status = http_post(agent->http, r_config_get_api_url(cfg), json_data, &response);
if (status != R_SUCCESS || !response) {
return NULL;
}
struct json_object *parsed = json_tokener_parse(response);
free(response);
if (!parsed) return NULL;
struct json_object *error_obj;
if (json_object_object_get_ex(parsed, "error", &error_obj)) {
const char *err_str = json_object_to_json_string(error_obj);
fprintf(stderr, "API Error: %s\n", err_str);
json_object_put(parsed);
return NULL;
}
struct json_object *choices;
if (!json_object_object_get_ex(parsed, "choices", &choices)) {
json_object_put(parsed);
return NULL;
}
struct json_object *first_choice = json_object_array_get_idx(choices, 0);
if (!first_choice) {
json_object_put(parsed);
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 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 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 bool agent_response_indicates_incomplete(const char *content) {
if (!content) return false;
for (int i = 0; incomplete_phrases[i]; i++) {
if (strstr(content, incomplete_phrases[i])) return true;
}
size_t len = strlen(content);
if (len > 3) {
for (int i = 0; incomplete_endings[i]; 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_handle agent, const char *user_message) {
if (!agent) return NULL;
agent->state = AGENT_STATE_RUNNING;
agent->iteration_count = 0;
agent->tool_retry_count = 0;
if (!user_message || !*user_message) {
agent->state = AGENT_STATE_ERROR;
agent_set_error(agent, "Empty user message");
return NULL;
}
messages_load(agent->messages);
char *json_data = agent_build_request(agent, "user", user_message);
if (!json_data) {
agent->state = AGENT_STATE_ERROR;
agent_set_error(agent, "Failed to create chat JSON");
return NULL;
}
char *final_response = NULL;
while (agent->state == AGENT_STATE_RUNNING) {
agent->iteration_count++;
if (agent->iteration_count > agent->max_iterations) {
agent->state = AGENT_STATE_MAX_ITERATIONS;
agent_set_error(agent, "Maximum iterations reached");
if (agent->verbose) {
fprintf(stderr, "[Agent] Max iterations (%d) reached\n", agent->max_iterations);
}
free(json_data);
break;
}
if (agent->verbose) {
fprintf(stderr, "[Agent] Iteration %d/%d\n",
agent->iteration_count, agent->max_iterations);
}
struct json_object *choice = agent_process_response(agent, json_data);
free(json_data);
json_data = NULL;
if (!choice) {
agent->tool_retry_count++;
if (agent->tool_retry_count >= agent->max_tool_retries) {
agent->state = AGENT_STATE_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);
}
json_data = agent_build_request(agent, NULL, NULL);
continue;
}
agent->tool_retry_count = 0;
struct json_object *message_obj = agent_get_message(choice);
if (message_obj) {
messages_add_object(agent->messages, json_object_get(message_obj));
}
bool has_tools = agent_has_tool_calls(choice);
if (agent->verbose) {
fprintf(stderr, "[Agent] has_tool_calls=%s\n", has_tools ? "true" : "false");
}
if (has_tools) {
agent->state = AGENT_STATE_EXECUTING_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 *results = tool_registry_execute(agent->tools, tool_calls, agent->verbose);
int count = json_object_array_length(results);
for (int i = 0; i < count; i++) {
struct json_object *result = json_object_array_get_idx(results, i);
messages_add_tool_call(agent->messages, json_object_get(result));
}
agent->state = AGENT_STATE_RUNNING;
json_data = agent_build_request(agent, NULL, NULL);
if (!json_data) {
agent->state = AGENT_STATE_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 = agent_build_request(agent, "user",
"Continue. Execute the necessary actions to complete the task.");
if (!json_data) {
agent->state = AGENT_STATE_ERROR;
agent_set_error(agent, "Failed to create continue JSON");
break;
}
} else {
final_response = content;
agent->state = AGENT_STATE_COMPLETED;
if (agent->verbose) {
fprintf(stderr, "[Agent] Completed in %d iteration(s)\n",
agent->iteration_count);
}
}
}
}
free(json_data);
return final_response;
}
char *agent_chat(const char *user_message, messages_handle messages) {
agent_handle agent = agent_create(user_message, messages);
if (!agent) return NULL;
char *response = agent_run(agent, user_message);
if (agent->verbose && agent->state != AGENT_STATE_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, messages_handle messages) {
agent_handle agent = agent_create(user_message, messages);
if (!agent) return NULL;
agent_set_max_iterations(agent, max_iterations);
char *response = agent_run(agent, user_message);
if (agent->verbose && agent->state != AGENT_STATE_COMPLETED && agent->last_error) {
fprintf(stderr, "[Agent] Error: %s\n", agent->last_error);
}
agent_destroy(agent);
return response;
}
+106
View File
@@ -0,0 +1,106 @@
// retoor <retoor@molodetz.nl>
#define _GNU_SOURCE
#include "bash_executor.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
char *r_bash_execute(const char *command, bool interactive) {
if (!command) {
return strdup("Error: null command");
}
size_t len = strlen(command);
char *cmd_with_nl = malloc(len + 2);
if (!cmd_with_nl) {
return strdup("Error: memory allocation failed");
}
strcpy(cmd_with_nl, command);
if (len > 0 && cmd_with_nl[len - 1] != '\n') {
cmd_with_nl[len] = '\n';
cmd_with_nl[len + 1] = '\0';
} else if (len == 0) {
cmd_with_nl[0] = '\n';
cmd_with_nl[1] = '\0';
}
char tmp_script[] = "/tmp/r_bash_XXXXXX.sh";
int fd = mkstemps(tmp_script, 3);
if (fd == -1) {
free(cmd_with_nl);
perror("mkstemps");
return strdup("Error: failed to create temp script");
}
if (write(fd, cmd_with_nl, strlen(cmd_with_nl)) == -1) {
close(fd);
free(cmd_with_nl);
unlink(tmp_script);
return strdup("Error: failed to write to temp script");
}
close(fd);
free(cmd_with_nl);
char *output = NULL;
if (interactive) {
char *run_cmd = NULL;
if (asprintf(&run_cmd, "bash %s", tmp_script) == -1) {
unlink(tmp_script);
return strdup("Error: asprintf failed");
}
int status = system(run_cmd);
free(run_cmd);
if (asprintf(&output, "Command exited with status %d", status) == -1) {
output = strdup("Command completed (asprintf failed for status).");
}
} else {
char *run_cmd = NULL;
if (asprintf(&run_cmd, "bash %s 2>&1", tmp_script) == -1) {
unlink(tmp_script);
return strdup("Error: asprintf failed");
}
FILE *fp = popen(run_cmd, "r");
free(run_cmd);
if (!fp) {
unlink(tmp_script);
return strdup("Error: popen failed");
}
char buffer[1024];
size_t total_size = 0;
while (fgets(buffer, sizeof(buffer), fp)) {
fprintf(stderr, "%s", buffer);
size_t chunk_len = strlen(buffer);
char *new_output = realloc(output, total_size + chunk_len + 1);
if (!new_output) {
free(output);
pclose(fp);
unlink(tmp_script);
return strdup("Error: memory allocation failed");
}
output = new_output;
strcpy(output + total_size, buffer);
total_size += chunk_len;
}
pclose(fp);
if (!output) {
output = strdup("");
}
}
unlink(tmp_script);
return output;
}
Executable
+308
View File
@@ -0,0 +1,308 @@
// retoor <retoor@molodetz.nl>
#include "db.h"
#include "r_config.h"
#include <sqlite3.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
struct db_t {
sqlite3 *conn;
char *path;
};
static char *expand_home_directory(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;
}
db_handle db_open(const char *path) {
struct db_t *db = calloc(1, sizeof(struct db_t));
if (!db) return NULL;
if (!path) {
r_config_handle cfg = r_config_get_instance();
path = r_config_get_db_path(cfg);
}
db->path = expand_home_directory(path);
if (!db->path) {
free(db);
return NULL;
}
if (sqlite3_open(db->path, &db->conn) != SQLITE_OK) {
free(db->path);
free(db);
return NULL;
}
db_init(db);
return db;
}
void db_close(db_handle db) {
if (!db) return;
if (db->conn) sqlite3_close(db->conn);
free(db->path);
free(db);
}
r_status_t db_init(db_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"
");";
char *err_msg = NULL;
if (sqlite3_exec(db->conn, sql, NULL, NULL, &err_msg) != SQLITE_OK) {
sqlite3_free(err_msg);
return R_ERROR_DB_QUERY;
}
return R_SUCCESS;
}
r_status_t db_kv_set(db_handle db, const char *key, const char *value) {
if (!db || !db->conn || !key || !value) return R_ERROR_INVALID_ARG;
char *sql = sqlite3_mprintf(
"INSERT OR REPLACE INTO kv (key, value, updated_at) VALUES (%Q, %Q, CURRENT_TIMESTAMP)",
key, value);
if (!sql) return R_ERROR_OUT_OF_MEMORY;
char *err_msg = NULL;
int rc = sqlite3_exec(db->conn, sql, NULL, NULL, &err_msg);
sqlite3_free(sql);
if (rc != SQLITE_OK) {
sqlite3_free(err_msg);
return R_ERROR_DB_QUERY;
}
return R_SUCCESS;
}
r_status_t db_kv_get(db_handle db, const char *key, char **value) {
if (!db || !db->conn || !key || !value) return R_ERROR_INVALID_ARG;
*value = NULL;
const char *sql = "SELECT value FROM kv WHERE key = ?";
sqlite3_stmt *stmt = NULL;
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 rc = sqlite3_step(stmt);
if (rc == SQLITE_ROW) {
const char *val = (const char *)sqlite3_column_text(stmt, 0);
*value = val ? strdup(val) : NULL;
}
sqlite3_finalize(stmt);
return (rc == SQLITE_ROW) ? R_SUCCESS : R_ERROR_DB_NOT_FOUND;
}
r_status_t db_execute(db_handle db, const char *sql, struct json_object **result) {
if (!db || !db->conn || !sql || !result) return R_ERROR_INVALID_ARG;
*result = NULL;
const char *select_check = sql;
while (*select_check == ' ') select_check++;
if (strncasecmp(select_check, "SELECT", 6) != 0) {
char *err_msg = NULL;
int rc = sqlite3_exec(db->conn, sql, NULL, NULL, &err_msg);
if (rc != SQLITE_OK) {
struct json_object *error_obj = json_object_new_object();
json_object_object_add(error_obj, "error",
json_object_new_string(err_msg ? err_msg : "Query failed"));
sqlite3_free(err_msg);
*result = error_obj;
return R_ERROR_DB_QUERY;
}
struct json_object *success_obj = json_object_new_object();
json_object_object_add(success_obj, "success", json_object_new_boolean(1));
*result = success_obj;
return R_SUCCESS;
}
sqlite3_stmt *stmt = NULL;
if (sqlite3_prepare_v2(db->conn, sql, -1, &stmt, NULL) != SQLITE_OK) {
struct json_object *error_obj = json_object_new_object();
json_object_object_add(error_obj, "error",
json_object_new_string(sqlite3_errmsg(db->conn)));
*result = error_obj;
return R_ERROR_DB_QUERY;
}
struct json_object *array = json_object_new_array();
int col_count = sqlite3_column_count(stmt);
while (sqlite3_step(stmt) == SQLITE_ROW) {
struct json_object *row = json_object_new_object();
for (int i = 0; i < col_count; i++) {
const char *col_name = sqlite3_column_name(stmt, i);
int col_type = sqlite3_column_type(stmt, i);
switch (col_type) {
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_NULL:
json_object_object_add(row, col_name, NULL);
break;
default:
json_object_object_add(row, col_name,
json_object_new_string((const char *)sqlite3_column_text(stmt, i)));
break;
}
}
json_object_array_add(array, row);
}
sqlite3_finalize(stmt);
*result = array;
return R_SUCCESS;
}
char *db_get_schema(db_handle db) {
if (!db || !db->conn) return strdup("Database not available");
struct json_object *result = NULL;
r_status_t status = db_execute(db,
"SELECT name, sql FROM sqlite_master WHERE type='table' ORDER BY name",
&result);
if (status != R_SUCCESS || !result) {
return strdup("Failed to get schema");
}
char *schema = strdup(json_object_to_json_string_ext(result, JSON_C_TO_STRING_PRETTY));
json_object_put(result);
return schema;
}
r_status_t db_store_file_version(db_handle db, const char *path) {
if (!db || !db->conn || !path) return R_ERROR_INVALID_ARG;
FILE *fp = fopen(path, "r");
if (!fp) return R_ERROR_FILE_NOT_FOUND;
fseek(fp, 0, SEEK_END);
long size = ftell(fp);
rewind(fp);
if (size <= 0 || size > 1000000) {
fclose(fp);
return R_SUCCESS;
}
char *content = malloc(size + 1);
if (!content) {
fclose(fp);
return R_ERROR_OUT_OF_MEMORY;
}
size_t read_size = fread(content, 1, size, fp);
content[read_size] = '\0';
fclose(fp);
char *sql = sqlite3_mprintf(
"INSERT INTO file_versions (path, content) VALUES (%Q, %Q)",
path, content);
free(content);
if (!sql) return R_ERROR_OUT_OF_MEMORY;
char *err_msg = NULL;
int rc = sqlite3_exec(db->conn, sql, NULL, NULL, &err_msg);
sqlite3_free(sql);
sqlite3_free(err_msg);
return (rc == SQLITE_OK) ? R_SUCCESS : R_ERROR_DB_QUERY;
}
r_status_t db_save_conversation(db_handle db, const char *session_key, const char *data) {
if (!db || !db->conn || !session_key || !data) return R_ERROR_INVALID_ARG;
char *sql = sqlite3_mprintf(
"INSERT OR REPLACE INTO conversations (session_key, data, updated_at) "
"VALUES (%Q, %Q, CURRENT_TIMESTAMP)",
session_key, data);
if (!sql) return R_ERROR_OUT_OF_MEMORY;
char *err_msg = NULL;
int rc = sqlite3_exec(db->conn, sql, NULL, NULL, &err_msg);
sqlite3_free(sql);
sqlite3_free(err_msg);
return (rc == SQLITE_OK) ? R_SUCCESS : R_ERROR_DB_QUERY;
}
r_status_t db_load_conversation(db_handle db, const char *session_key, char **data) {
if (!db || !db->conn || !session_key || !data) return R_ERROR_INVALID_ARG;
*data = NULL;
const char *sql = "SELECT data FROM conversations WHERE session_key = ?";
sqlite3_stmt *stmt = NULL;
if (sqlite3_prepare_v2(db->conn, sql, -1, &stmt, NULL) != SQLITE_OK) {
return R_ERROR_DB_QUERY;
}
sqlite3_bind_text(stmt, 1, session_key, -1, SQLITE_STATIC);
int rc = sqlite3_step(stmt);
if (rc == SQLITE_ROW) {
const char *val = (const char *)sqlite3_column_text(stmt, 0);
*data = val ? strdup(val) : NULL;
}
sqlite3_finalize(stmt);
return (rc == SQLITE_ROW) ? R_SUCCESS : R_ERROR_DB_NOT_FOUND;
}
+313
View File
@@ -0,0 +1,313 @@
// retoor <retoor@molodetz.nl>
#include "http_client.h"
#include <curl/curl.h>
#include <pthread.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#define HTTP_MAX_RETRIES 3
#define HTTP_RETRY_DELAY_MS 2000
struct http_client_t {
char *bearer_token;
long timeout_seconds;
long connect_timeout_seconds;
bool show_spinner;
};
struct response_buffer_t {
char *data;
size_t size;
};
static struct timespec spinner_start_time = {0, 0};
static volatile int spinner_running = 0;
static double get_elapsed_seconds(void) {
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;
}
static size_t write_callback(void *contents, size_t size, size_t nmemb, void *userp) {
size_t total_size = size * nmemb;
struct response_buffer_t *response = (struct response_buffer_t *)userp;
if (total_size > SIZE_MAX - response->size - 1) {
return 0;
}
char *ptr = realloc(response->data, response->size + total_size + 1);
if (!ptr) {
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;
}
http_client_handle http_client_create(const char *bearer_token) {
struct http_client_t *client = calloc(1, sizeof(struct http_client_t));
if (!client) return NULL;
if (bearer_token) {
client->bearer_token = strdup(bearer_token);
if (!client->bearer_token) {
free(client);
return NULL;
}
}
client->timeout_seconds = 300;
client->connect_timeout_seconds = 10;
client->show_spinner = true;
return client;
}
void http_client_destroy(http_client_handle client) {
if (!client) return;
free(client->bearer_token);
free(client);
}
void http_client_set_show_spinner(http_client_handle client, bool show) {
if (client) client->show_spinner = show;
}
void http_client_set_timeout(http_client_handle client, long timeout_seconds) {
if (client) client->timeout_seconds = timeout_seconds;
}
void http_client_set_connect_timeout(http_client_handle client, long timeout_seconds) {
if (client) client->connect_timeout_seconds = timeout_seconds;
}
r_status_t http_post(http_client_handle client, const char *url,
const char *data, char **response) {
if (!client || !url || !response) return R_ERROR_INVALID_ARG;
CURL *curl = NULL;
struct curl_slist *headers = NULL;
struct response_buffer_t resp = {NULL, 0};
int retry_count = 0;
pthread_t spinner_tid = 0;
r_status_t status = R_SUCCESS;
*response = NULL;
if (client->show_spinner) {
clock_gettime(CLOCK_MONOTONIC, &spinner_start_time);
spinner_running = 1;
pthread_create(&spinner_tid, NULL, spinner_thread, NULL);
}
while (retry_count < HTTP_MAX_RETRIES) {
free(resp.data);
resp.data = malloc(1);
resp.size = 0;
if (!resp.data) {
status = R_ERROR_OUT_OF_MEMORY;
goto cleanup;
}
resp.data[0] = '\0';
curl = curl_easy_init();
if (!curl) {
status = R_ERROR_HTTP_CONNECTION;
goto cleanup;
}
curl_easy_setopt(curl, CURLOPT_URL, url);
curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, client->connect_timeout_seconds);
curl_easy_setopt(curl, CURLOPT_TIMEOUT, client->timeout_seconds);
headers = curl_slist_append(headers, "Content-Type: application/json");
if (client->bearer_token) {
char bearer_header[2048];
snprintf(bearer_header, sizeof(bearer_header), "Authorization: Bearer %s",
client->bearer_token);
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, write_callback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&resp);
CURLcode res = curl_easy_perform(curl);
curl_slist_free_all(headers);
headers = NULL;
curl_easy_cleanup(curl);
curl = NULL;
if (res == CURLE_OK) {
*response = resp.data;
resp.data = NULL;
status = R_SUCCESS;
goto cleanup;
}
retry_count++;
if (client->show_spinner) {
spinner_running = 0;
pthread_join(spinner_tid, NULL);
spinner_tid = 0;
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);
if (client->show_spinner) {
clock_gettime(CLOCK_MONOTONIC, &spinner_start_time);
spinner_running = 1;
pthread_create(&spinner_tid, NULL, spinner_thread, NULL);
}
}
}
status = R_ERROR_HTTP_TIMEOUT;
cleanup:
if (client->show_spinner && spinner_tid) {
spinner_running = 0;
pthread_join(spinner_tid, NULL);
fprintf(stderr, "\r \r");
fflush(stderr);
}
if (headers) curl_slist_free_all(headers);
if (curl) curl_easy_cleanup(curl);
free(resp.data);
return status;
}
r_status_t http_get(http_client_handle client, const char *url, char **response) {
if (!client || !url || !response) return R_ERROR_INVALID_ARG;
CURL *curl = NULL;
struct curl_slist *headers = NULL;
struct response_buffer_t resp = {NULL, 0};
int retry_count = 0;
r_status_t status = R_SUCCESS;
*response = NULL;
while (retry_count < HTTP_MAX_RETRIES) {
free(resp.data);
resp.data = malloc(1);
resp.size = 0;
if (!resp.data) {
status = R_ERROR_OUT_OF_MEMORY;
goto cleanup;
}
resp.data[0] = '\0';
curl = curl_easy_init();
if (!curl) {
status = R_ERROR_HTTP_CONNECTION;
goto cleanup;
}
curl_easy_setopt(curl, CURLOPT_URL, url);
curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, client->connect_timeout_seconds);
curl_easy_setopt(curl, CURLOPT_TIMEOUT, 60L);
headers = curl_slist_append(headers, "Content-Type: application/json");
if (client->bearer_token) {
char bearer_header[2048];
snprintf(bearer_header, sizeof(bearer_header), "Authorization: Bearer %s",
client->bearer_token);
headers = curl_slist_append(headers, bearer_header);
}
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&resp);
CURLcode res = curl_easy_perform(curl);
curl_slist_free_all(headers);
headers = NULL;
curl_easy_cleanup(curl);
curl = NULL;
if (res == CURLE_OK) {
*response = resp.data;
resp.data = NULL;
status = R_SUCCESS;
goto cleanup;
}
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);
}
}
status = R_ERROR_HTTP_TIMEOUT;
cleanup:
if (headers) curl_slist_free_all(headers);
if (curl) curl_easy_cleanup(curl);
free(resp.data);
return status;
}
r_status_t http_post_simple(const char *url, const char *bearer_token,
const char *data, char **response) {
http_client_handle client = http_client_create(bearer_token);
if (!client) return R_ERROR_OUT_OF_MEMORY;
r_status_t status = http_post(client, url, data, response);
http_client_destroy(client);
return status;
}
r_status_t http_get_simple(const char *url, const char *bearer_token, char **response) {
http_client_handle client = http_client_create(bearer_token);
if (!client) return R_ERROR_OUT_OF_MEMORY;
http_client_set_show_spinner(client, false);
r_status_t status = http_get(client, url, response);
http_client_destroy(client);
return status;
}
Executable
+423
View File
@@ -0,0 +1,423 @@
// retoor <retoor@molodetz.nl>
#include "agent.h"
#include "db.h"
#include "http_client.h"
#include "r_config.h"
#include "r_error.h"
#include "tool.h"
#include "../line.h"
#include "../markdown.h"
#include "../utils.h"
#include <json-c/json.h>
#include <locale.h>
#include <signal.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
static volatile sig_atomic_t sigint_count = 0;
static time_t first_sigint_time = 0;
static bool syntax_highlight_enabled = true;
static bool api_mode = false;
static db_handle global_db = NULL;
static messages_handle global_messages = NULL;
extern tool_registry_t *tools_get_registry(void);
extern void tools_registry_shutdown(void);
static void render(const char *content);
static bool include_file(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 cleanup(void);
static void handle_sigint(int sig);
static char *get_env_string(void) {
FILE *fp = popen("env", "r");
if (!fp) return NULL;
size_t buffer_size = 1024;
size_t total_size = 0;
char *output = malloc(buffer_size);
if (!output) {
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) {
free(output);
pclose(fp);
return NULL;
}
output = temp;
}
}
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) {
r_config_handle cfg = r_config_get_instance();
char *prompt = malloc(10 * 1024 * 1024 + 1);
char *system_msg = malloc(1024 * 1024);
if (!prompt || !system_msg) {
free(prompt);
free(system_msg);
return NULL;
}
system_msg[0] = '\0';
bool get_from_stdin = false;
for (int i = 1; i < argc; i++) {
if (strcmp(argv[i], "--stdin") == 0) {
fprintf(stderr, "Reading from stdin.\n");
get_from_stdin = true;
} else if (strcmp(argv[i], "--verbose") == 0) {
r_config_set_verbose(cfg, 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);
include_file(py_file_path);
free(py_file_path);
} else if (strcmp(argv[i], "--context") == 0 && i + 1 < argc) {
char *context_file_path = argv[++i];
fprintf(stderr, "Including \"%s\".\n", context_file_path);
include_file(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_msg, argv[i]);
strcat(system_msg, (i < argc - 1) ? " " : ".");
}
}
if (get_from_stdin) {
if (*system_msg && global_messages) {
messages_add(global_messages, "system", system_msg);
}
prompt = get_prompt_from_stdin(prompt);
free(system_msg);
} else {
free(prompt);
prompt = system_msg;
}
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, global_messages);
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 include_file(const char *path) {
char *file_content = read_file(path);
if (!file_content) return false;
if (global_messages) {
messages_add(global_messages, "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) {
r_config_handle cfg = r_config_get_instance();
tool_registry_t *tools = tools_get_registry();
line_init();
char *line = NULL;
while (true) {
line = line_read("> ");
if (!line || !*line) continue;
if (!strncmp(line, "!dump", 5)) {
char *json = messages_to_string(global_messages);
if (json) {
printf("%s\n", json);
free(json);
}
continue;
}
if (!strncmp(line, "!clear", 6)) {
messages_clear(global_messages);
fprintf(stderr, "Session cleared.\n");
continue;
}
if (!strncmp(line, "!session", 8)) {
printf("Session: %s\n", messages_get_session_id(global_messages));
continue;
}
if (!strncmp(line, "!verbose", 8)) {
bool verbose = !r_config_is_verbose(cfg);
r_config_set_verbose(cfg, verbose);
fprintf(stderr, "%s\n", verbose ? "Verbose mode enabled" : "Verbose mode disabled");
continue;
}
if (line && *line != '\n') {
line_add_history(line);
}
if (!strncmp(line, "!tools", 6)) {
struct json_object *descs = tool_registry_get_descriptions(tools);
printf("Available tools: %s\n", json_object_to_json_string(descs));
continue;
}
if (!strncmp(line, "!models", 7)) {
http_client_handle http = http_client_create(r_config_get_api_key(cfg));
if (http) {
http_client_set_show_spinner(http, false);
char *response = NULL;
if (http_get(http, r_config_get_models_url(cfg), &response) == R_SUCCESS && response) {
printf("Models: %s\n", response);
free(response);
}
http_client_destroy(http);
}
continue;
}
if (!strncmp(line, "!model", 6)) {
if (line[6] == ' ') {
r_config_set_model(cfg, line + 7);
}
printf("Current model: %s\n", r_config_get_model(cfg));
continue;
}
if (!strncmp(line, "exit", 4)) {
exit(0);
}
while (line && *line != '\n') {
char *response = agent_chat(line, global_messages);
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();
r_config_handle cfg = r_config_get_instance();
global_db = db_open(NULL);
global_messages = messages_create(r_config_get_session_id(cfg));
char *schema = db_get_schema(global_db);
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 ? schema : "{}");
free(schema);
fprintf(stderr, "Loading...");
if (global_messages) {
messages_add(global_messages, "system", payload);
}
const char *env_system_msg = r_config_get_system_message(cfg);
if (env_system_msg && *env_system_msg && global_messages) {
messages_add(global_messages, "system", env_system_msg);
}
if (!include_file(".rcontext.txt")) {
include_file("~/.rcontext.txt");
}
fprintf(stderr, "\r \r");
}
static void cleanup(void) {
if (global_messages) {
messages_destroy(global_messages);
global_messages = NULL;
}
if (global_db) {
db_close(global_db);
global_db = NULL;
}
tools_registry_shutdown();
r_config_destroy();
}
static void handle_sigint(int sig) {
(void)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) {
cleanup();
exit(0);
} else {
sigint_count = 1;
first_sigint_time = current_time;
}
}
}
static void parse_session_arg(int argc, char *argv[]) {
r_config_handle cfg = r_config_get_instance();
for (int i = 1; i < argc; i++) {
if (strncmp(argv[i], "--session=", 10) == 0) {
const char *name = argv[i] + 10;
if (!r_config_set_session_id(cfg, 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 (!r_config_set_session_id(cfg, name)) {
fprintf(stderr, "Error: Invalid session name '%s'\n", name);
exit(1);
}
return;
}
}
}
int main(int argc, char *argv[]) {
signal(SIGINT, handle_sigint);
atexit(cleanup);
parse_session_arg(argc, argv);
init();
char *env_string = get_env_string();
if (env_string && *env_string && global_messages) {
messages_add(global_messages, "system", env_string);
free(env_string);
}
messages_load(global_messages);
if (try_prompt(argc, argv)) {
return 0;
}
repl();
return 0;
}
Executable
+235
View File
@@ -0,0 +1,235 @@
// retoor <retoor@molodetz.nl>
#include "messages.h"
#include "db.h"
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define MAX_CONTENT_LENGTH 1048570
#define MAX_TOOL_RESULT_LENGTH 104000
struct messages_t {
struct json_object *array;
char *session_id;
db_handle db;
bool loaded;
};
static bool is_valid_session_id(const char *session_id) {
if (!session_id || !*session_id) return false;
if (strlen(session_id) > 200) return false;
for (const char *p = session_id; *p; p++) {
if (!isalnum((unsigned char)*p) && *p != '-' && *p != '_' && *p != '.') {
return false;
}
}
return true;
}
static char *generate_default_session_id(void) {
char *session_id = malloc(32);
if (!session_id) return NULL;
snprintf(session_id, 32, "session-%d", getppid());
return session_id;
}
messages_handle messages_create(const char *session_id) {
struct messages_t *msgs = calloc(1, sizeof(struct messages_t));
if (!msgs) return NULL;
msgs->array = json_object_new_array();
if (!msgs->array) {
free(msgs);
return NULL;
}
if (session_id && is_valid_session_id(session_id)) {
msgs->session_id = strdup(session_id);
} else {
msgs->session_id = generate_default_session_id();
}
if (!msgs->session_id) {
json_object_put(msgs->array);
free(msgs);
return NULL;
}
msgs->db = db_open(NULL);
return msgs;
}
void messages_destroy(messages_handle msgs) {
if (!msgs) return;
if (msgs->array) json_object_put(msgs->array);
if (msgs->db) db_close(msgs->db);
free(msgs->session_id);
free(msgs);
}
r_status_t messages_set_session_id(messages_handle msgs, const char *session_id) {
if (!msgs || !is_valid_session_id(session_id)) return R_ERROR_INVALID_ARG;
free(msgs->session_id);
msgs->session_id = strdup(session_id);
msgs->loaded = false;
return msgs->session_id ? R_SUCCESS : R_ERROR_OUT_OF_MEMORY;
}
const char *messages_get_session_id(messages_handle msgs) {
return msgs ? msgs->session_id : NULL;
}
r_status_t messages_add(messages_handle msgs, const char *role, const char *content) {
if (!msgs || !msgs->array || !role) return R_ERROR_INVALID_ARG;
struct json_object *message = json_object_new_object();
if (!message) return R_ERROR_OUT_OF_MEMORY;
json_object_object_add(message, "role", json_object_new_string(role));
if (content) {
size_t len = strlen(content);
if (len > MAX_CONTENT_LENGTH) len = MAX_CONTENT_LENGTH;
json_object_object_add(message, "content",
json_object_new_string_len(content, (int)len));
}
json_object_array_add(msgs->array, message);
if (strcmp(role, "system") != 0) {
messages_save(msgs);
}
return R_SUCCESS;
}
r_status_t messages_add_object(messages_handle msgs, struct json_object *message) {
if (!msgs || !msgs->array || !message) return R_ERROR_INVALID_ARG;
json_object_array_add(msgs->array, message);
messages_save(msgs);
return R_SUCCESS;
}
r_status_t messages_add_tool_call(messages_handle msgs, struct json_object *message) {
if (!msgs || !msgs->array || !message) return R_ERROR_INVALID_ARG;
json_object_array_add(msgs->array, message);
messages_save(msgs);
return R_SUCCESS;
}
r_status_t messages_add_tool_result(messages_handle msgs, const char *tool_call_id, const char *result) {
if (!msgs || !msgs->array || !tool_call_id || !result) return R_ERROR_INVALID_ARG;
struct json_object *message = json_object_new_object();
if (!message) return R_ERROR_OUT_OF_MEMORY;
json_object_object_add(message, "tool_call_id", json_object_new_string(tool_call_id));
size_t len = strlen(result);
if (len > MAX_TOOL_RESULT_LENGTH) len = MAX_TOOL_RESULT_LENGTH;
json_object_object_add(message, "tool_result",
json_object_new_string_len(result, (int)len));
json_object_array_add(msgs->array, message);
messages_save(msgs);
return R_SUCCESS;
}
r_status_t messages_remove_last(messages_handle msgs) {
if (!msgs || !msgs->array) return R_ERROR_INVALID_ARG;
int size = json_object_array_length(msgs->array);
if (size == 0) return R_ERROR_NOT_FOUND;
json_object_array_del_idx(msgs->array, size - 1, 1);
messages_save(msgs);
return R_SUCCESS;
}
r_status_t messages_clear(messages_handle msgs) {
if (!msgs) return R_ERROR_INVALID_ARG;
if (msgs->array) json_object_put(msgs->array);
msgs->array = json_object_new_array();
if (!msgs->array) return R_ERROR_OUT_OF_MEMORY;
messages_save(msgs);
return R_SUCCESS;
}
r_status_t messages_save(messages_handle msgs) {
if (!msgs || !msgs->array || !msgs->db) return R_ERROR_INVALID_ARG;
char key[512];
snprintf(key, sizeof(key), "session:%s", msgs->session_id);
const char *json_str = json_object_to_json_string_ext(msgs->array, JSON_C_TO_STRING_PLAIN);
if (!json_str) return R_ERROR_OUT_OF_MEMORY;
return db_save_conversation(msgs->db, key, json_str);
}
r_status_t messages_load(messages_handle msgs) {
if (!msgs || !msgs->db) return R_ERROR_INVALID_ARG;
if (msgs->loaded) return R_SUCCESS;
char key[512];
snprintf(key, sizeof(key), "session:%s", msgs->session_id);
char *data = NULL;
r_status_t status = db_load_conversation(msgs->db, key, &data);
if (status != R_SUCCESS || !data) {
if (status == R_SUCCESS) msgs->loaded = true;
return status == R_SUCCESS ? R_ERROR_NOT_FOUND : status;
}
struct json_object *loaded = json_tokener_parse(data);
free(data);
if (!loaded || !json_object_is_type(loaded, json_type_array)) {
if (loaded) json_object_put(loaded);
return R_ERROR_PARSE;
}
int len = json_object_array_length(loaded);
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(msgs->array, json_object_get(msg));
}
} else {
json_object_array_add(msgs->array, json_object_get(msg));
}
}
json_object_put(loaded);
msgs->loaded = true;
return R_SUCCESS;
}
struct json_object *messages_to_json(messages_handle msgs) {
return msgs ? msgs->array : NULL;
}
char *messages_to_string(messages_handle msgs) {
if (!msgs || !msgs->array) return NULL;
return strdup(json_object_to_json_string_ext(msgs->array, JSON_C_TO_STRING_PRETTY));
}
int messages_count(messages_handle msgs) {
if (!msgs || !msgs->array) return 0;
return json_object_array_length(msgs->array);
}
Executable
+181
View File
@@ -0,0 +1,181 @@
// retoor <retoor@molodetz.nl>
#include "r_config.h"
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct r_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 r_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;
}
r_config_handle r_config_get_instance(void) {
if (instance) return instance;
instance = calloc(1, sizeof(struct r_config_t));
if (!instance) return NULL;
const char *base_url = getenv("R_BASE_URL");
if (base_url && *base_url) {
size_t len = strlen(base_url);
instance->api_url = malloc(len + 32);
instance->models_url = malloc(len + 32);
if (instance->api_url && instance->models_url) {
snprintf(instance->api_url, len + 32, "%s/v1/chat/completions", base_url);
snprintf(instance->models_url, len + 32, "%s/v1/models", base_url);
}
} else {
instance->api_url = strdup("https://api.openai.com/v1/chat/completions");
instance->models_url = strdup("https://api.openai.com/v1/models");
}
const char *model = getenv("R_MODEL");
instance->model = strdup(model && *model ? model : "gpt-4o-mini");
instance->api_key = strdup(resolve_api_key());
instance->db_path = strdup("~/.r.db");
instance->temperature = 0.1;
instance->use_tools = resolve_env_bool("R_USE_TOOLS", true);
instance->use_strict = resolve_env_bool("R_USE_STRICT", true);
instance->verbose = false;
const char *session = getenv("R_SESSION");
if (session && is_valid_session_id(session)) {
instance->session_id = strdup(session);
} else {
instance->session_id = NULL;
}
instance->system_message = strdup_safe(getenv("R_SYSTEM_MESSAGE"));
return instance;
}
void r_config_destroy(void) {
if (!instance) return;
free(instance->api_url);
free(instance->models_url);
free(instance->model);
free(instance->api_key);
free(instance->db_path);
free(instance->session_id);
free(instance->system_message);
free(instance);
instance = NULL;
}
const char *r_config_get_api_url(r_config_handle cfg) {
return cfg ? cfg->api_url : NULL;
}
const char *r_config_get_models_url(r_config_handle cfg) {
return cfg ? cfg->models_url : NULL;
}
const char *r_config_get_model(r_config_handle cfg) {
return cfg ? cfg->model : NULL;
}
void r_config_set_model(r_config_handle cfg, const char *model) {
if (!cfg || !model) return;
free(cfg->model);
cfg->model = strdup(model);
}
const char *r_config_get_api_key(r_config_handle cfg) {
return cfg ? cfg->api_key : NULL;
}
const char *r_config_get_db_path(r_config_handle cfg) {
return cfg ? cfg->db_path : NULL;
}
bool r_config_use_tools(r_config_handle cfg) {
return cfg ? cfg->use_tools : true;
}
bool r_config_use_strict(r_config_handle cfg) {
return cfg ? cfg->use_strict : true;
}
bool r_config_is_verbose(r_config_handle cfg) {
return cfg ? cfg->verbose : false;
}
void r_config_set_verbose(r_config_handle cfg, bool verbose) {
if (cfg) cfg->verbose = verbose;
}
double r_config_get_temperature(r_config_handle cfg) {
return cfg ? cfg->temperature : 0.1;
}
const char *r_config_get_session_id(r_config_handle cfg) {
return cfg ? cfg->session_id : NULL;
}
bool r_config_set_session_id(r_config_handle cfg, const char *session_id) {
if (!cfg || !is_valid_session_id(session_id)) return false;
free(cfg->session_id);
cfg->session_id = strdup(session_id);
return cfg->session_id != NULL;
}
const char *r_config_get_system_message(r_config_handle cfg) {
return cfg ? cfg->system_message : NULL;
}
Executable
+35
View File
@@ -0,0 +1,35 @@
// retoor <retoor@molodetz.nl>
#include "r_error.h"
static const char *error_messages[] = {
[R_SUCCESS] = "Success",
[R_ERROR_INVALID_ARG] = "Invalid argument",
[R_ERROR_OUT_OF_MEMORY] = "Out of memory",
[R_ERROR_NOT_FOUND] = "Not found",
[R_ERROR_PARSE] = "Parse error",
[R_ERROR_DB_CONNECTION] = "Database connection failed",
[R_ERROR_DB_QUERY] = "Database query failed",
[R_ERROR_DB_NOT_FOUND] = "Database key not found",
[R_ERROR_HTTP_CONNECTION] = "HTTP connection failed",
[R_ERROR_HTTP_TIMEOUT] = "HTTP request timed out",
[R_ERROR_HTTP_RESPONSE] = "HTTP response error",
[R_ERROR_JSON_PARSE] = "JSON parse error",
[R_ERROR_FILE_NOT_FOUND] = "File not found",
[R_ERROR_FILE_READ] = "File read error",
[R_ERROR_FILE_WRITE] = "File write error",
[R_ERROR_TOOL_NOT_FOUND] = "Tool not found",
[R_ERROR_TOOL_EXECUTION] = "Tool execution failed",
[R_ERROR_API_KEY_MISSING] = "API key missing",
[R_ERROR_API_ERROR] = "API error",
[R_ERROR_MAX_ITERATIONS] = "Maximum iterations reached",
[R_ERROR_SESSION_INVALID] = "Invalid session name",
[R_ERROR_UNKNOWN] = "Unknown error"
};
const char *r_status_string(r_status_t status) {
if (status < 0 || status > R_ERROR_UNKNOWN) {
return error_messages[R_ERROR_UNKNOWN];
}
return error_messages[status];
}
+157
View File
@@ -0,0 +1,157 @@
// retoor <retoor@molodetz.nl>
#include "tool.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
tool_registry_t *tool_registry_create(void) {
tool_registry_t *registry = calloc(1, sizeof(tool_registry_t));
if (!registry) return NULL;
registry->capacity = 32;
registry->tools = calloc(registry->capacity, sizeof(tool_t *));
if (!registry->tools) {
free(registry);
return NULL;
}
return registry;
}
void tool_registry_destroy(tool_registry_t *registry) {
if (!registry) return;
free(registry->tools);
free(registry);
}
r_status_t tool_registry_register(tool_registry_t *registry, tool_t *tool) {
if (!registry || !tool) return R_ERROR_INVALID_ARG;
if (registry->count >= registry->capacity) {
size_t new_capacity = registry->capacity * 2;
tool_t **new_tools = realloc(registry->tools, new_capacity * sizeof(tool_t *));
if (!new_tools) return R_ERROR_OUT_OF_MEMORY;
registry->tools = new_tools;
registry->capacity = new_capacity;
}
registry->tools[registry->count++] = tool;
return R_SUCCESS;
}
tool_t *tool_registry_find(tool_registry_t *registry, const char *name) {
if (!registry || !name) return NULL;
for (size_t i = 0; i < registry->count; i++) {
if (strcmp(registry->tools[i]->name, name) == 0) {
return registry->tools[i];
}
}
return NULL;
}
struct json_object *tool_registry_get_descriptions(tool_registry_t *registry) {
if (!registry) return NULL;
struct json_object *array = json_object_new_array();
if (!array) return NULL;
for (size_t i = 0; i < registry->count; i++) {
tool_t *tool = registry->tools[i];
if (tool->vtable->get_description) {
struct json_object *desc = tool->vtable->get_description();
if (desc) {
json_object_array_add(array, desc);
}
}
}
return array;
}
struct json_object *tool_registry_execute(tool_registry_t *registry,
struct json_object *tool_calls,
bool verbose) {
if (!registry || !tool_calls) return NULL;
struct json_object *results = json_object_new_array();
if (!results) return NULL;
int len = json_object_array_length(tool_calls);
for (int i = 0; i < len; i++) {
struct json_object *call = json_object_array_get_idx(tool_calls, i);
struct json_object *result = json_object_new_object();
struct json_object *id_obj = json_object_object_get(call, "id");
if (id_obj) {
json_object_object_add(result, "tool_call_id",
json_object_new_string(json_object_get_string(id_obj)));
}
json_object_object_add(result, "role", json_object_new_string("tool"));
struct json_object *type_obj;
if (json_object_object_get_ex(call, "type", &type_obj)) {
if (strcmp(json_object_get_string(type_obj), "function") != 0) {
json_object_put(result);
continue;
}
}
struct json_object *function_obj;
if (!json_object_object_get_ex(call, "function", &function_obj)) {
json_object_object_add(result, "content",
json_object_new_string("Error: missing function object"));
json_object_array_add(results, result);
continue;
}
struct json_object *name_obj;
if (!json_object_object_get_ex(function_obj, "name", &name_obj)) {
json_object_object_add(result, "content",
json_object_new_string("Error: missing function name"));
json_object_array_add(results, result);
continue;
}
const char *name = json_object_get_string(name_obj);
tool_t *tool = tool_registry_find(registry, name);
if (!tool) {
fprintf(stderr, "Unknown function: %s\n", name);
json_object_object_add(result, "content",
json_object_new_string("Error: function not found."));
json_object_array_add(results, result);
continue;
}
struct json_object *args = NULL;
struct json_object *args_obj;
if (json_object_object_get_ex(function_obj, "arguments", &args_obj)) {
args = json_tokener_parse(json_object_get_string(args_obj));
}
if (tool->vtable->print_action) {
tool->vtable->print_action(tool->name, args);
}
if (verbose && args) {
fprintf(stderr, " [verbose] %s args: %s\n",
tool->name, json_object_to_json_string(args));
}
char *output = NULL;
if (tool->vtable->execute) {
output = tool->vtable->execute(tool, args);
}
json_object_object_add(result, "content",
json_object_new_string(output ? output : ""));
free(output);
if (args) json_object_put(args);
json_object_array_add(results, result);
}
return results;
}
+270
View File
@@ -0,0 +1,270 @@
// retoor <retoor@molodetz.nl>
#include "tool.h"
#include "db.h"
#include "r_config.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static struct json_object *db_get_get_description(void);
static char *db_get_execute(tool_t *self, struct json_object *args);
static void db_get_print_action(const char *name, struct json_object *args);
static struct json_object *db_set_get_description(void);
static char *db_set_execute(tool_t *self, struct json_object *args);
static void db_set_print_action(const char *name, struct json_object *args);
static struct json_object *db_query_get_description(void);
static char *db_query_execute(tool_t *self, struct json_object *args);
static void db_query_print_action(const char *name, struct json_object *args);
static const tool_vtable_t db_get_vtable = {
.get_description = db_get_get_description,
.execute = db_get_execute,
.print_action = db_get_print_action
};
static const tool_vtable_t db_set_vtable = {
.get_description = db_set_get_description,
.execute = db_set_execute,
.print_action = db_set_print_action
};
static const tool_vtable_t db_query_vtable = {
.get_description = db_query_get_description,
.execute = db_query_execute,
.print_action = db_query_print_action
};
static tool_t db_get_tool = { .vtable = &db_get_vtable, .name = "db_get" };
static tool_t db_set_tool = { .vtable = &db_set_vtable, .name = "db_set" };
static tool_t db_query_tool = { .vtable = &db_query_vtable, .name = "db_query" };
tool_t *tool_db_get_create(void) { return &db_get_tool; }
tool_t *tool_db_set_create(void) { return &db_set_tool; }
tool_t *tool_db_query_create(void) { return &db_query_tool; }
static void db_get_print_action(const char *name, struct json_object *args) {
(void)name;
if (!args) return;
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));
}
}
static char *db_get_execute(tool_t *self, struct json_object *args) {
(void)self;
struct json_object *key_obj;
if (!json_object_object_get_ex(args, "key", &key_obj)) {
return strdup("Error: missing 'key' argument");
}
db_handle db = db_open(NULL);
if (!db) return strdup("Failed to open database.");
char *value = NULL;
r_status_t status = db_kv_get(db, json_object_get_string(key_obj), &value);
db_close(db);
if (status != R_SUCCESS || !value) {
return strdup("Failed to retrieve value from the database.");
}
struct json_object *result = json_object_new_object();
json_object_object_add(result, "value", json_object_new_string(value));
char *response = strdup(json_object_to_json_string(result));
json_object_put(result);
free(value);
return response;
}
static struct json_object *db_get_get_description(void) {
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);
r_config_handle cfg = r_config_get_instance();
if (r_config_use_strict(cfg)) {
json_object_object_add(function, "strict", json_object_new_boolean(1));
}
json_object_object_add(root, "function", function);
return root;
}
static void db_set_print_action(const char *name, struct json_object *args) {
(void)name;
if (!args) return;
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));
}
}
static char *db_set_execute(tool_t *self, struct json_object *args) {
(void)self;
struct json_object *key_obj, *value_obj;
if (!json_object_object_get_ex(args, "key", &key_obj)) {
return strdup("Error: missing 'key' argument");
}
if (!json_object_object_get_ex(args, "value", &value_obj)) {
return strdup("Error: missing 'value' argument");
}
db_handle db = db_open(NULL);
if (!db) return strdup("Failed to open database.");
r_status_t status = db_kv_set(db, json_object_get_string(key_obj),
json_object_get_string(value_obj));
db_close(db);
if (status != R_SUCCESS) {
return strdup("Failed to set value in the database.");
}
return strdup("Value successfully stored.");
}
static struct json_object *db_set_get_description(void) {
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);
r_config_handle cfg = r_config_get_instance();
if (r_config_use_strict(cfg)) {
json_object_object_add(function, "strict", json_object_new_boolean(1));
}
json_object_object_add(root, "function", function);
return root;
}
static void db_query_print_action(const char *name, struct json_object *args) {
(void)name;
if (!args) return;
struct json_object *query;
if (json_object_object_get_ex(args, "query", &query)) {
const char *q = json_object_get_string(query);
if (strlen(q) > 60) {
fprintf(stderr, " -> Executing SQL: %.60s...\n", q);
} else {
fprintf(stderr, " -> Executing SQL: %s\n", q);
}
}
}
static char *db_query_execute(tool_t *self, struct json_object *args) {
(void)self;
struct json_object *query_obj;
if (!json_object_object_get_ex(args, "query", &query_obj)) {
return strdup("Error: missing 'query' argument");
}
db_handle db = db_open(NULL);
if (!db) return strdup("Failed to open database.");
struct json_object *result = NULL;
r_status_t status = db_execute(db, json_object_get_string(query_obj), &result);
db_close(db);
if (status != R_SUCCESS) {
return strdup("Failed to execute query on the database.");
}
char *response = strdup(json_object_to_json_string(result));
json_object_put(result);
return response;
}
static struct json_object *db_query_get_description(void) {
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);
r_config_handle cfg = r_config_get_instance();
if (r_config_use_strict(cfg)) {
json_object_object_add(function, "strict", json_object_new_boolean(1));
}
json_object_object_add(root, "function", function);
return root;
}
+563
View File
@@ -0,0 +1,563 @@
// retoor <retoor@molodetz.nl>
#include "tool.h"
#include "r_config.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
#include <dirent.h>
#include <glob.h>
#include <errno.h>
#include <time.h>
#include <limits.h>
static char *expand_home_directory(const char *path);
static const char *get_file_type(struct stat *st);
static void format_time(time_t raw_time, char *buffer, size_t size);
static struct json_object *read_file_get_description(void);
static char *read_file_execute(tool_t *self, struct json_object *args);
static void read_file_print_action(const char *name, struct json_object *args);
static struct json_object *write_file_get_description(void);
static char *write_file_execute(tool_t *self, struct json_object *args);
static void write_file_print_action(const char *name, struct json_object *args);
static struct json_object *directory_glob_get_description(void);
static char *directory_glob_execute(tool_t *self, struct json_object *args);
static void directory_glob_print_action(const char *name, struct json_object *args);
static struct json_object *mkdir_get_description(void);
static char *mkdir_execute(tool_t *self, struct json_object *args);
static void mkdir_print_action(const char *name, struct json_object *args);
static struct json_object *chdir_get_description(void);
static char *chdir_execute(tool_t *self, struct json_object *args);
static void chdir_print_action(const char *name, struct json_object *args);
static struct json_object *getpwd_get_description(void);
static char *getpwd_execute(tool_t *self, struct json_object *args);
static void getpwd_print_action(const char *name, struct json_object *args);
static const tool_vtable_t read_file_vtable = {
.get_description = read_file_get_description,
.execute = read_file_execute,
.print_action = read_file_print_action
};
static const tool_vtable_t write_file_vtable = {
.get_description = write_file_get_description,
.execute = write_file_execute,
.print_action = write_file_print_action
};
static const tool_vtable_t directory_glob_vtable = {
.get_description = directory_glob_get_description,
.execute = directory_glob_execute,
.print_action = directory_glob_print_action
};
static const tool_vtable_t mkdir_vtable = {
.get_description = mkdir_get_description,
.execute = mkdir_execute,
.print_action = mkdir_print_action
};
static const tool_vtable_t chdir_vtable = {
.get_description = chdir_get_description,
.execute = chdir_execute,
.print_action = chdir_print_action
};
static const tool_vtable_t getpwd_vtable = {
.get_description = getpwd_get_description,
.execute = getpwd_execute,
.print_action = getpwd_print_action
};
static tool_t read_file_tool = { .vtable = &read_file_vtable, .name = "read_file" };
static tool_t write_file_tool = { .vtable = &write_file_vtable, .name = "write_file" };
static tool_t directory_glob_tool = { .vtable = &directory_glob_vtable, .name = "directory_glob" };
static tool_t mkdir_tool = { .vtable = &mkdir_vtable, .name = "mkdir" };
static tool_t chdir_tool = { .vtable = &chdir_vtable, .name = "chdir" };
static tool_t getpwd_tool = { .vtable = &getpwd_vtable, .name = "getpwd" };
tool_t *tool_read_file_create(void) { return &read_file_tool; }
tool_t *tool_write_file_create(void) { return &write_file_tool; }
tool_t *tool_directory_glob_create(void) { return &directory_glob_tool; }
tool_t *tool_mkdir_create(void) { return &mkdir_tool; }
tool_t *tool_chdir_create(void) { return &chdir_tool; }
tool_t *tool_getpwd_create(void) { return &getpwd_tool; }
static char *expand_home_directory(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;
}
static 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";
}
static 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);
}
static char *make_dir_recursive(const char *path) {
char temp[4096];
char *p = NULL;
size_t 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.");
}
static void ensure_parent_directory_exists(const char *path_including_file_name) {
if (!path_including_file_name) return;
char *path = strdup(path_including_file_name);
if (!path) return;
char *last_slash = strrchr(path, '/');
if (last_slash) {
*last_slash = '\0';
char *result = make_dir_recursive(path);
free(result);
}
free(path);
}
static void read_file_print_action(const char *name, struct json_object *args) {
(void)name;
if (!args) return;
struct json_object *path;
if (json_object_object_get_ex(args, "path", &path)) {
fprintf(stderr, " -> Reading file: %s\n", json_object_get_string(path));
}
}
static char *read_file_execute(tool_t *self, struct json_object *args) {
(void)self;
struct json_object *path_obj;
if (!json_object_object_get_ex(args, "path", &path_obj)) {
return strdup("Error: missing 'path' argument");
}
char *expanded_path = expand_home_directory(json_object_get_string(path_obj));
if (!expanded_path) return strdup("Failed to expand path!");
FILE *fp = fopen(expanded_path, "r");
free(expanded_path);
if (!fp) 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 = malloc((size_t)size + 1);
if (!content) {
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;
}
static struct json_object *read_file_get_description(void) {
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);
r_config_handle cfg = r_config_get_instance();
if (r_config_use_strict(cfg)) {
json_object_object_add(function, "strict", json_object_new_boolean(1));
}
json_object_object_add(root, "function", function);
return root;
}
static void write_file_print_action(const char *name, struct json_object *args) {
(void)name;
if (!args) return;
struct json_object *path;
if (json_object_object_get_ex(args, "path", &path)) {
fprintf(stderr, " -> Writing file: %s\n", json_object_get_string(path));
}
}
static char *write_file_execute(tool_t *self, struct json_object *args) {
(void)self;
struct json_object *path_obj, *content_obj;
if (!json_object_object_get_ex(args, "path", &path_obj)) {
return strdup("Error: missing 'path' argument");
}
if (!json_object_object_get_ex(args, "content", &content_obj)) {
return strdup("Error: missing 'content' argument");
}
const char *path = json_object_get_string(path_obj);
const char *content = json_object_get_string(content_obj);
ensure_parent_directory_exists(path);
FILE *fp = fopen(path, "w+");
if (!fp) return strdup("Failed to open file for writing!");
fwrite(content, 1, strlen(content), fp);
fclose(fp);
return strdup("File successfully written.");
}
static struct json_object *write_file_get_description(void) {
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);
r_config_handle cfg = r_config_get_instance();
if (r_config_use_strict(cfg)) {
json_object_object_add(function, "strict", json_object_new_boolean(1));
}
json_object_object_add(root, "function", function);
return root;
}
static void directory_glob_print_action(const char *name, struct json_object *args) {
(void)name;
if (!args) return;
struct json_object *path;
if (json_object_object_get_ex(args, "path", &path)) {
fprintf(stderr, " -> Listing: %s\n", json_object_get_string(path));
}
}
static char *directory_glob_execute(tool_t *self, struct json_object *args) {
(void)self;
struct json_object *path_obj;
if (!json_object_object_get_ex(args, "path", &path_obj)) {
return strdup("Error: missing 'path' argument");
}
char target_dir[PATH_MAX];
strncpy(target_dir, json_object_get_string(path_obj), PATH_MAX - 1);
target_dir[PATH_MAX - 1] = '\0';
if (strcmp(target_dir, ".") == 0) {
target_dir[0] = '*';
target_dir[1] = '\0';
}
glob_t results;
struct stat file_stat;
char mod_time[20], create_time[20];
if (glob(target_dir, GLOB_TILDE, NULL, &results) != 0) {
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) 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;
}
static struct json_object *directory_glob_get_description(void) {
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);
r_config_handle cfg = r_config_get_instance();
if (r_config_use_strict(cfg)) {
json_object_object_add(function, "strict", json_object_new_boolean(1));
}
json_object_object_add(root, "function", function);
return root;
}
static void mkdir_print_action(const char *name, struct json_object *args) {
(void)name;
if (!args) return;
struct json_object *path;
if (json_object_object_get_ex(args, "path", &path)) {
fprintf(stderr, " -> Creating directory: %s\n", json_object_get_string(path));
}
}
static char *mkdir_execute(tool_t *self, struct json_object *args) {
(void)self;
struct json_object *path_obj;
if (!json_object_object_get_ex(args, "path", &path_obj)) {
return strdup("Error: missing 'path' argument");
}
return make_dir_recursive(json_object_get_string(path_obj));
}
static struct json_object *mkdir_get_description(void) {
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);
r_config_handle cfg = r_config_get_instance();
if (r_config_use_strict(cfg)) {
json_object_object_add(function, "strict", json_object_new_boolean(1));
}
json_object_object_add(root, "function", function);
return root;
}
static void chdir_print_action(const char *name, struct json_object *args) {
(void)name;
if (!args) return;
struct json_object *path;
if (json_object_object_get_ex(args, "path", &path)) {
fprintf(stderr, " -> Changing directory: %s\n", json_object_get_string(path));
}
}
static char *chdir_execute(tool_t *self, struct json_object *args) {
(void)self;
struct json_object *path_obj;
if (!json_object_object_get_ex(args, "path", &path_obj)) {
return strdup("Error: missing 'path' argument");
}
if (chdir(json_object_get_string(path_obj)) != 0) {
return strdup("Failed to change directory!");
}
return strdup("Directory successfully changed.");
}
static struct json_object *chdir_get_description(void) {
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);
r_config_handle cfg = r_config_get_instance();
if (r_config_use_strict(cfg)) {
json_object_object_add(function, "strict", json_object_new_boolean(1));
}
json_object_object_add(root, "function", function);
return root;
}
static void getpwd_print_action(const char *name, struct json_object *args) {
(void)name;
(void)args;
fprintf(stderr, " -> Getting current directory\n");
}
static char *getpwd_execute(tool_t *self, struct json_object *args) {
(void)self;
(void)args;
char *cwd = malloc(PATH_MAX);
if (!cwd) return strdup("Failed to allocate memory for current working directory!");
if (!getcwd(cwd, PATH_MAX)) {
free(cwd);
return strdup("Failed to get current working directory!");
}
return cwd;
}
static struct json_object *getpwd_get_description(void) {
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."));
json_object_object_add(function, "parameters", json_object_new_null());
json_object_object_add(root, "function", function);
return root;
}
+236
View File
@@ -0,0 +1,236 @@
// retoor <retoor@molodetz.nl>
#include "tool.h"
#include "http_client.h"
#include "r_config.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <curl/curl.h>
static struct json_object *http_fetch_get_description(void);
static char *http_fetch_execute(tool_t *self, struct json_object *args);
static void http_fetch_print_action(const char *name, struct json_object *args);
static struct json_object *web_search_get_description(void);
static char *web_search_execute(tool_t *self, struct json_object *args);
static void web_search_print_action(const char *name, struct json_object *args);
static struct json_object *web_search_news_get_description(void);
static char *web_search_news_execute(tool_t *self, struct json_object *args);
static const tool_vtable_t http_fetch_vtable = {
.get_description = http_fetch_get_description,
.execute = http_fetch_execute,
.print_action = http_fetch_print_action
};
static const tool_vtable_t web_search_vtable = {
.get_description = web_search_get_description,
.execute = web_search_execute,
.print_action = web_search_print_action
};
static const tool_vtable_t web_search_news_vtable = {
.get_description = web_search_news_get_description,
.execute = web_search_news_execute,
.print_action = web_search_print_action
};
static tool_t http_fetch_tool = { .vtable = &http_fetch_vtable, .name = "http_fetch" };
static tool_t web_search_tool = { .vtable = &web_search_vtable, .name = "web_search" };
static tool_t web_search_news_tool = { .vtable = &web_search_news_vtable, .name = "web_search_news" };
tool_t *tool_http_fetch_create(void) { return &http_fetch_tool; }
tool_t *tool_web_search_create(void) { return &web_search_tool; }
tool_t *tool_web_search_news_create(void) { return &web_search_news_tool; }
static void http_fetch_print_action(const char *name, struct json_object *args) {
(void)name;
if (!args) return;
struct json_object *url;
if (json_object_object_get_ex(args, "url", &url)) {
fprintf(stderr, " -> Fetching URL: %s\n", json_object_get_string(url));
}
}
static char *http_fetch_execute(tool_t *self, struct json_object *args) {
(void)self;
struct json_object *url_obj;
if (!json_object_object_get_ex(args, "url", &url_obj)) {
return strdup("Error: missing 'url' argument");
}
http_client_handle client = http_client_create(NULL);
if (!client) return strdup("Failed to create HTTP client.");
char *response = NULL;
r_status_t status = http_get(client, json_object_get_string(url_obj), &response);
http_client_destroy(client);
if (status != R_SUCCESS || !response) {
return strdup("Failed to fetch URL.");
}
return response;
}
static struct json_object *http_fetch_get_description(void) {
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);
r_config_handle cfg = r_config_get_instance();
if (r_config_use_strict(cfg)) {
json_object_object_add(function, "strict", json_object_new_boolean(1));
}
json_object_object_add(root, "function", function);
return root;
}
static void web_search_print_action(const char *name, struct json_object *args) {
(void)name;
if (!args) return;
struct json_object *query;
if (json_object_object_get_ex(args, "query", &query)) {
fprintf(stderr, " -> Searching web: %s\n", json_object_get_string(query));
}
}
static char *do_web_search(const char *query) {
if (!query) return strdup("Query cannot be NULL.");
char *q_encoded = curl_easy_escape(NULL, query, 0);
if (!q_encoded) return strdup("Failed to encode query.");
char url[4096];
snprintf(url, sizeof(url), "https://static.molodetz.nl/search.cgi?query=%s", q_encoded);
curl_free(q_encoded);
http_client_handle client = http_client_create(NULL);
if (!client) return strdup("Failed to create HTTP client.");
char *response = NULL;
r_status_t status = http_get(client, url, &response);
http_client_destroy(client);
if (status != R_SUCCESS || !response) {
return strdup("Failed to search.");
}
return response;
}
static char *web_search_execute(tool_t *self, struct json_object *args) {
(void)self;
struct json_object *query_obj;
if (!json_object_object_get_ex(args, "query", &query_obj)) {
return strdup("Error: missing 'query' argument");
}
return do_web_search(json_object_get_string(query_obj));
}
static struct json_object *web_search_get_description(void) {
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);
r_config_handle cfg = r_config_get_instance();
if (r_config_use_strict(cfg)) {
json_object_object_add(function, "strict", json_object_new_boolean(1));
}
json_object_object_add(root, "function", function);
return root;
}
static char *web_search_news_execute(tool_t *self, struct json_object *args) {
(void)self;
struct json_object *query_obj;
if (!json_object_object_get_ex(args, "query", &query_obj)) {
return strdup("Error: missing 'query' argument");
}
return do_web_search(json_object_get_string(query_obj));
}
static struct json_object *web_search_news_get_description(void) {
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);
r_config_handle cfg = r_config_get_instance();
if (r_config_use_strict(cfg)) {
json_object_object_add(function, "strict", json_object_new_boolean(1));
}
json_object_object_add(root, "function", function);
return root;
}
+190
View File
@@ -0,0 +1,190 @@
// retoor <retoor@molodetz.nl>
#include "tool.h"
#include "r_config.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dirent.h>
#include <sys/stat.h>
#include <limits.h>
static struct json_object *index_source_directory_get_description(void);
static char *index_source_directory_execute(tool_t *self, struct json_object *args);
static void index_source_directory_print_action(const char *name, struct json_object *args);
static const tool_vtable_t index_source_directory_vtable = {
.get_description = index_source_directory_get_description,
.execute = index_source_directory_execute,
.print_action = index_source_directory_print_action
};
static tool_t index_source_directory_tool = {
.vtable = &index_source_directory_vtable,
.name = "index_source_directory"
};
tool_t *tool_index_source_directory_create(void) {
return &index_source_directory_tool;
}
static void index_source_directory_print_action(const char *name, struct json_object *args) {
(void)name;
if (!args) return;
struct json_object *path;
if (json_object_object_get_ex(args, "path", &path)) {
fprintf(stderr, " -> Indexing directory: %s\n", json_object_get_string(path));
}
}
static int is_source_file(const char *filename) {
const char *extensions[] = {
".c", ".h", ".cpp", ".hpp", ".cc", ".cxx",
".py", ".js", ".ts", ".java", ".go", ".rs",
".rb", ".php", ".swift", ".kt", ".scala",
".sh", ".bash", ".zsh", ".fish",
".json", ".xml", ".yaml", ".yml", ".toml",
".md", ".txt", ".rst",
".html", ".css", ".scss", ".sass",
".sql", ".graphql",
".lua", ".pl", ".pm",
".r", ".R", ".jl",
".vim", ".el", ".clj", ".cljs",
".ex", ".exs", ".erl", ".hrl",
".hs", ".lhs", ".ml", ".mli",
".f", ".f90", ".f95",
".asm", ".s",
".makefile", ".mk", ".cmake",
NULL
};
const char *ext = strrchr(filename, '.');
if (!ext) {
if (strcmp(filename, "Makefile") == 0 ||
strcmp(filename, "makefile") == 0 ||
strcmp(filename, "Dockerfile") == 0 ||
strcmp(filename, "Jenkinsfile") == 0 ||
strcmp(filename, "Vagrantfile") == 0 ||
strcmp(filename, "Gemfile") == 0 ||
strcmp(filename, "Rakefile") == 0) {
return 1;
}
return 0;
}
for (int i = 0; extensions[i]; i++) {
if (strcasecmp(ext, extensions[i]) == 0) return 1;
}
return 0;
}
static void index_directory_recursive(const char *path, struct json_object *result_array) {
DIR *dir = opendir(path);
if (!dir) return;
struct dirent *entry;
while ((entry = readdir(dir))) {
if (entry->d_name[0] == '.') continue;
if (strcmp(entry->d_name, "node_modules") == 0) continue;
if (strcmp(entry->d_name, "__pycache__") == 0) continue;
if (strcmp(entry->d_name, ".git") == 0) continue;
if (strcmp(entry->d_name, "build") == 0) continue;
if (strcmp(entry->d_name, "dist") == 0) continue;
if (strcmp(entry->d_name, "vendor") == 0) continue;
char full_path[PATH_MAX];
snprintf(full_path, sizeof(full_path), "%s/%s", path, entry->d_name);
struct stat st;
if (stat(full_path, &st) != 0) continue;
if (S_ISDIR(st.st_mode)) {
index_directory_recursive(full_path, result_array);
} else if (S_ISREG(st.st_mode) && is_source_file(entry->d_name)) {
FILE *fp = fopen(full_path, "r");
if (!fp) continue;
fseek(fp, 0, SEEK_END);
long size = ftell(fp);
rewind(fp);
if (size > 100000) {
fclose(fp);
continue;
}
char *content = malloc(size + 1);
if (!content) {
fclose(fp);
continue;
}
size_t read_size = fread(content, 1, size, fp);
content[read_size] = '\0';
fclose(fp);
struct json_object *file_obj = json_object_new_object();
json_object_object_add(file_obj, "path", json_object_new_string(full_path));
json_object_object_add(file_obj, "content", json_object_new_string(content));
json_object_array_add(result_array, file_obj);
free(content);
}
}
closedir(dir);
}
static char *index_source_directory_execute(tool_t *self, struct json_object *args) {
(void)self;
struct json_object *path_obj;
if (!json_object_object_get_ex(args, "path", &path_obj)) {
return strdup("Error: missing 'path' argument");
}
const char *path = json_object_get_string(path_obj);
struct json_object *result_array = json_object_new_array();
index_directory_recursive(path, result_array);
char *result = strdup(json_object_to_json_string_ext(result_array, JSON_C_TO_STRING_PRETTY));
json_object_put(result_array);
return result;
}
static struct json_object *index_source_directory_get_description(void) {
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);
r_config_handle cfg = r_config_get_instance();
if (r_config_use_strict(cfg)) {
json_object_object_add(function, "strict", json_object_new_boolean(1));
}
json_object_object_add(root, "function", function);
return root;
}
+103
View File
@@ -0,0 +1,103 @@
// retoor <retoor@molodetz.nl>
#include "tool.h"
#include "r_config.h"
#include "bash_executor.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
static struct json_object *python_execute_get_description(void);
static char *python_execute_execute(tool_t *self, struct json_object *args);
static void python_execute_print_action(const char *name, struct json_object *args);
static const tool_vtable_t python_execute_vtable = {
.get_description = python_execute_get_description,
.execute = python_execute_execute,
.print_action = python_execute_print_action
};
static tool_t python_execute_tool = {
.vtable = &python_execute_vtable,
.name = "python_execute"
};
tool_t *tool_python_execute_create(void) {
return &python_execute_tool;
}
static void python_execute_print_action(const char *name, struct json_object *args) {
(void)name;
(void)args;
fprintf(stderr, " -> Executing Python code\n");
}
static char *python_execute_execute(tool_t *self, struct json_object *args) {
(void)self;
struct json_object *source_obj;
if (!json_object_object_get_ex(args, "source", &source_obj)) {
return strdup("Error: missing 'source' argument");
}
const char *source_code = json_object_get_string(source_obj);
char *output = NULL;
int fd = -1;
char tmp_file[] = "/tmp/r_python_tool_XXXXXX.py";
fd = mkstemps(tmp_file, 3);
if (fd == -1) {
return strdup("Failed to create temporary file for Python code.");
}
FILE *fp = fdopen(fd, "w");
if (!fp) {
close(fd);
unlink(tmp_file);
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'", tmp_file);
output = r_bash_execute(command, false);
unlink(tmp_file);
return output;
}
static struct json_object *python_execute_get_description(void) {
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;
}
+160
View File
@@ -0,0 +1,160 @@
// retoor <retoor@molodetz.nl>
#include "tool.h"
#include "r_config.h"
#include "bash_executor.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static struct json_object *terminal_get_description(void);
static char *terminal_execute(tool_t *self, struct json_object *args);
static void terminal_print_action(const char *name, struct json_object *args);
static struct json_object *terminal_interactive_get_description(void);
static char *terminal_interactive_execute(tool_t *self, struct json_object *args);
static const tool_vtable_t terminal_vtable = {
.get_description = terminal_get_description,
.execute = terminal_execute,
.print_action = terminal_print_action
};
static const tool_vtable_t terminal_interactive_vtable = {
.get_description = terminal_interactive_get_description,
.execute = terminal_interactive_execute,
.print_action = terminal_print_action
};
static tool_t terminal_tool = {
.vtable = &terminal_vtable,
.name = "linux_terminal_execute"
};
static tool_t terminal_interactive_tool = {
.vtable = &terminal_interactive_vtable,
.name = "linux_terminal_execute_interactive"
};
tool_t *tool_terminal_create(void) {
return &terminal_tool;
}
tool_t *tool_terminal_interactive_create(void) {
return &terminal_interactive_tool;
}
static void terminal_print_action(const char *name, struct json_object *args) {
if (!args) {
fprintf(stderr, " -> %s\n", name);
return;
}
struct json_object *cmd;
if (json_object_object_get_ex(args, "command", &cmd)) {
if (strcmp(name, "linux_terminal_execute_interactive") == 0) {
fprintf(stderr, " -> Running interactive: %s\n", json_object_get_string(cmd));
} else {
fprintf(stderr, " -> Running command: %s\n", json_object_get_string(cmd));
}
}
}
static char *terminal_execute(tool_t *self, struct json_object *args) {
(void)self;
struct json_object *cmd_obj;
if (!json_object_object_get_ex(args, "command", &cmd_obj)) {
return strdup("Error: missing 'command' argument");
}
const char *command = json_object_get_string(cmd_obj);
return r_bash_execute(command, false);
}
static char *terminal_interactive_execute(tool_t *self, struct json_object *args) {
(void)self;
struct json_object *cmd_obj;
if (!json_object_object_get_ex(args, "command", &cmd_obj)) {
return strdup("Error: missing 'command' argument");
}
const char *command = json_object_get_string(cmd_obj);
return r_bash_execute(command, true);
}
static struct json_object *terminal_get_description(void) {
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 *cmd = json_object_new_object();
json_object_object_add(cmd, "type", json_object_new_string("string"));
json_object_object_add(cmd, "description",
json_object_new_string("Bash command to execute."));
json_object_object_add(properties, "command", cmd);
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);
r_config_handle cfg = r_config_get_instance();
if (r_config_use_strict(cfg)) {
json_object_object_add(function, "strict", json_object_new_boolean(1));
}
json_object_object_add(root, "function", function);
return root;
}
static struct json_object *terminal_interactive_get_description(void) {
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 *cmd = json_object_new_object();
json_object_object_add(cmd, "type", json_object_new_string("string"));
json_object_object_add(cmd, "description",
json_object_new_string("Executable with parameters to execute interactively."));
json_object_object_add(properties, "command", cmd);
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);
r_config_handle cfg = r_config_get_instance();
if (r_config_use_strict(cfg)) {
json_object_object_add(function, "strict", json_object_new_boolean(1));
}
json_object_object_add(root, "function", function);
return root;
}
+56
View File
@@ -0,0 +1,56 @@
// retoor <retoor@molodetz.nl>
#include "tool.h"
#include <stdlib.h>
extern tool_t *tool_terminal_create(void);
extern tool_t *tool_terminal_interactive_create(void);
extern tool_t *tool_read_file_create(void);
extern tool_t *tool_write_file_create(void);
extern tool_t *tool_directory_glob_create(void);
extern tool_t *tool_mkdir_create(void);
extern tool_t *tool_chdir_create(void);
extern tool_t *tool_getpwd_create(void);
extern tool_t *tool_http_fetch_create(void);
extern tool_t *tool_web_search_create(void);
extern tool_t *tool_web_search_news_create(void);
extern tool_t *tool_db_get_create(void);
extern tool_t *tool_db_set_create(void);
extern tool_t *tool_db_query_create(void);
extern tool_t *tool_python_execute_create(void);
extern tool_t *tool_index_source_directory_create(void);
static tool_registry_t *global_registry = NULL;
tool_registry_t *tools_get_registry(void) {
if (global_registry) return global_registry;
global_registry = tool_registry_create();
if (!global_registry) return NULL;
tool_registry_register(global_registry, tool_terminal_create());
tool_registry_register(global_registry, tool_terminal_interactive_create());
tool_registry_register(global_registry, tool_read_file_create());
tool_registry_register(global_registry, tool_write_file_create());
tool_registry_register(global_registry, tool_directory_glob_create());
tool_registry_register(global_registry, tool_mkdir_create());
tool_registry_register(global_registry, tool_chdir_create());
tool_registry_register(global_registry, tool_getpwd_create());
tool_registry_register(global_registry, tool_http_fetch_create());
tool_registry_register(global_registry, tool_web_search_create());
tool_registry_register(global_registry, tool_web_search_news_create());
tool_registry_register(global_registry, tool_db_get_create());
tool_registry_register(global_registry, tool_db_set_create());
tool_registry_register(global_registry, tool_db_query_create());
tool_registry_register(global_registry, tool_python_execute_create());
tool_registry_register(global_registry, tool_index_source_directory_create());
return global_registry;
}
void tools_registry_shutdown(void) {
if (global_registry) {
tool_registry_destroy(global_registry);
global_registry = NULL;
}
}
Regular → Executable
View File