Agent.
This commit is contained in:
parent
d740eb62a1
commit
a8d7015abd
88
README.md
88
README.md
@ -1,82 +1,36 @@
|
||||
# R Vibe Tool
|
||||
# Henry IRC Server
|
||||
|
||||
## Overview
|
||||
|
||||
R Vibe Tool is a powerful Command-Line Interface (CLI) utility designed for Linux environments, offering advanced AI-assisted development capabilities with elegant markdown output.
|
||||
A simple IRC server implemented in Python as a package named `henry`.
|
||||
|
||||
## Features
|
||||
- Basic IRC commands: NICK, USER, JOIN, PRIVMSG, QUIT
|
||||
- Supports multiple clients and channels
|
||||
|
||||
- **Flexible AI Integration**: Support for multiple AI models including:
|
||||
- OpenAI GPT-3.5-turbo (default, if you did not set API key)
|
||||
- Ollama
|
||||
- Anthropic Claude
|
||||
- Grok
|
||||
## Installation
|
||||
|
||||
- **Customizable Behavior**: Configure tool behavior through `~/.rcontext.txt`
|
||||
- **Agent Support**: Intelligent context-aware assistance
|
||||
- **Markdown Output**: Clean, readable documentation generation
|
||||
Clone the repository or copy the `henry` package directory.
|
||||
|
||||
## Prerequisites
|
||||
## Usage
|
||||
|
||||
- Linux operating system
|
||||
- Configured AI model access
|
||||
Run the IRC server using the CLI entry point:
|
||||
|
||||
## Configuration
|
||||
|
||||
### Environment Variables
|
||||
|
||||
TIP: create bash files containg these variables and make them easily accessable. For example by symlinking them to `~/.bash_aliases` or `~/.bash_profile`. Or even easier, make them executable and put them in /usr/local/bin.
|
||||
|
||||
#### Ollama Configuration
|
||||
```bash
|
||||
export R_MODEL="qwen2.5:3b"
|
||||
export R_BASE_URL="https://ollama.molodetz.nl"
|
||||
./r
|
||||
python -m henry.cli
|
||||
```
|
||||
|
||||
#### Claude Configuration
|
||||
The server listens on `127.0.0.1:6667` by default.
|
||||
|
||||
## Connecting
|
||||
|
||||
Use any IRC client to connect to `localhost` on port `6667`.
|
||||
|
||||
## Example
|
||||
|
||||
```bash
|
||||
export R_MODEL="claude-3-5-haiku-20241022"
|
||||
export R_BASE_URL="https://api.anthropic.com"
|
||||
export R_KEY="sk-ant-[your-key]"
|
||||
./r
|
||||
# Using irssi client
|
||||
irssi -c 127.0.0.1 -p 6667
|
||||
```
|
||||
|
||||
#### OpenAI Configuration
|
||||
```bash
|
||||
export R_MODEL="gpt-4o-mini"
|
||||
export R_BASE_URL="https://api.openai.com"
|
||||
export R_KEY="sk-[your-key]"
|
||||
./r
|
||||
```
|
||||
|
||||
#### Grok Configuration
|
||||
```bash
|
||||
export R_MODEL="grok-2"
|
||||
export R_BASE_URL="https://api.x.ai"
|
||||
export R_USE_STRICT=false
|
||||
export R_KEY="xai-gfh"
|
||||
./r
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. Use `~/.rcontext.txt` to define specific behavioral instructions
|
||||
2. Include file saving steps in your context instructions
|
||||
3. Use the `index` tool to initialize only source files, excluding environment and node_modules
|
||||
|
||||
## Example Project
|
||||
|
||||
For a comprehensive example of R Vibe Tool in action, visit:
|
||||
[Streamii Project](https://molodetz.nl/projects/streamii/README.md.html)
|
||||
|
||||
## Limitations
|
||||
|
||||
- OpenAI key usage is temporary and limited
|
||||
- Performance may vary depending on the selected AI model
|
||||
|
||||
## Support
|
||||
|
||||
For issues or contributions, please refer to the project repository.
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
||||
|
||||
381
agent.h
Normal file
381
agent.h
Normal file
@ -0,0 +1,381 @@
|
||||
// retoor <retoor@molodetz.nl>
|
||||
|
||||
#ifndef R_AGENT_H
|
||||
#define R_AGENT_H
|
||||
|
||||
#include "chat.h"
|
||||
#include "http_curl.h"
|
||||
#include "messages.h"
|
||||
#include "r.h"
|
||||
#include "tools.h"
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
#define AGENT_MAX_ITERATIONS 300
|
||||
#define AGENT_MAX_TOOL_RETRIES 3
|
||||
|
||||
typedef enum {
|
||||
AGENT_STATUS_RUNNING,
|
||||
AGENT_STATUS_COMPLETED,
|
||||
AGENT_STATUS_MAX_ITERATIONS,
|
||||
AGENT_STATUS_ERROR,
|
||||
AGENT_STATUS_TOOL_ERROR
|
||||
} agent_status_t;
|
||||
|
||||
typedef struct {
|
||||
char *goal;
|
||||
int iteration_count;
|
||||
int max_iterations;
|
||||
int tool_retry_count;
|
||||
int max_tool_retries;
|
||||
agent_status_t status;
|
||||
time_t start_time;
|
||||
char *last_error;
|
||||
bool verbose;
|
||||
} agent_state_t;
|
||||
|
||||
static agent_state_t *current_agent = NULL;
|
||||
|
||||
agent_state_t *agent_create(const char *goal) {
|
||||
agent_state_t *agent = (agent_state_t *)malloc(sizeof(agent_state_t));
|
||||
if (agent == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
agent->goal = goal ? strdup(goal) : NULL;
|
||||
agent->iteration_count = 0;
|
||||
agent->max_iterations = AGENT_MAX_ITERATIONS;
|
||||
agent->tool_retry_count = 0;
|
||||
agent->max_tool_retries = AGENT_MAX_TOOL_RETRIES;
|
||||
agent->status = AGENT_STATUS_RUNNING;
|
||||
agent->start_time = time(NULL);
|
||||
agent->last_error = NULL;
|
||||
agent->verbose = is_verbose;
|
||||
|
||||
return agent;
|
||||
}
|
||||
|
||||
void agent_destroy(agent_state_t *agent) {
|
||||
if (agent == NULL) {
|
||||
return;
|
||||
}
|
||||
if (agent->goal) {
|
||||
free(agent->goal);
|
||||
}
|
||||
if (agent->last_error) {
|
||||
free(agent->last_error);
|
||||
}
|
||||
free(agent);
|
||||
}
|
||||
|
||||
void agent_set_error(agent_state_t *agent, const char *error) {
|
||||
if (agent == NULL) {
|
||||
return;
|
||||
}
|
||||
if (agent->last_error) {
|
||||
free(agent->last_error);
|
||||
}
|
||||
agent->last_error = error ? strdup(error) : NULL;
|
||||
}
|
||||
|
||||
static struct json_object *agent_process_response(const char *api_url,
|
||||
const char *json_data) {
|
||||
char *response = curl_post(api_url, json_data);
|
||||
if (!response) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct json_object *parsed_json = json_tokener_parse(response);
|
||||
free(response);
|
||||
|
||||
if (!parsed_json) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct json_object *error_object;
|
||||
if (json_object_object_get_ex(parsed_json, "error", &error_object)) {
|
||||
const char *err_str = json_object_to_json_string(error_object);
|
||||
fprintf(stderr, "API Error: %s\n", err_str);
|
||||
json_object_put(parsed_json);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct json_object *choices_array;
|
||||
if (!json_object_object_get_ex(parsed_json, "choices", &choices_array)) {
|
||||
json_object_put(parsed_json);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct json_object *first_choice = json_object_array_get_idx(choices_array, 0);
|
||||
if (!first_choice) {
|
||||
json_object_put(parsed_json);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return first_choice;
|
||||
}
|
||||
|
||||
static bool agent_has_tool_calls(struct json_object *choice) {
|
||||
struct json_object *message_obj;
|
||||
if (!json_object_object_get_ex(choice, "message", &message_obj)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
struct json_object *tool_calls;
|
||||
if (!json_object_object_get_ex(message_obj, "tool_calls", &tool_calls)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return json_object_array_length(tool_calls) > 0;
|
||||
}
|
||||
|
||||
static const char *agent_get_finish_reason(struct json_object *choice) {
|
||||
struct json_object *finish_reason_obj;
|
||||
if (json_object_object_get_ex(choice, "finish_reason", &finish_reason_obj)) {
|
||||
return json_object_get_string(finish_reason_obj);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static char *agent_get_content(struct json_object *choice) {
|
||||
struct json_object *message_obj;
|
||||
if (!json_object_object_get_ex(choice, "message", &message_obj)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct json_object *content_obj;
|
||||
if (!json_object_object_get_ex(message_obj, "content", &content_obj)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char *content = json_object_get_string(content_obj);
|
||||
return content ? strdup(content) : NULL;
|
||||
}
|
||||
|
||||
static struct json_object *agent_get_tool_calls(struct json_object *choice) {
|
||||
struct json_object *message_obj;
|
||||
if (!json_object_object_get_ex(choice, "message", &message_obj)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct json_object *tool_calls;
|
||||
if (!json_object_object_get_ex(message_obj, "tool_calls", &tool_calls)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return tool_calls;
|
||||
}
|
||||
|
||||
static struct json_object *agent_get_message(struct json_object *choice) {
|
||||
struct json_object *message_obj;
|
||||
if (json_object_object_get_ex(choice, "message", &message_obj)) {
|
||||
return message_obj;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static bool agent_response_indicates_incomplete(const char *content) {
|
||||
if (content == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const char *incomplete_phrases[] = {
|
||||
"I'll ", "I will ", "Let me ", "I'm going to ",
|
||||
"Next, I", "Now I'll", "Now I will", "I'll now",
|
||||
"I need to", "I should", "I can ", "Going to ",
|
||||
"Will now", "Proceeding", "Starting to", "About to ",
|
||||
"First, I", "Then I", "After that", "Following that",
|
||||
NULL
|
||||
};
|
||||
|
||||
for (int i = 0; incomplete_phrases[i] != NULL; i++) {
|
||||
if (strstr(content, incomplete_phrases[i]) != NULL) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
const char *incomplete_endings[] = {
|
||||
"...", ":", "files:", "content:", "implementation:",
|
||||
NULL
|
||||
};
|
||||
|
||||
size_t len = strlen(content);
|
||||
if (len > 3) {
|
||||
for (int i = 0; incomplete_endings[i] != NULL; i++) {
|
||||
size_t end_len = strlen(incomplete_endings[i]);
|
||||
if (len >= end_len && strcmp(content + len - end_len, incomplete_endings[i]) == 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
char *agent_run(agent_state_t *agent, const char *user_message) {
|
||||
if (agent == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
current_agent = agent;
|
||||
agent->status = AGENT_STATUS_RUNNING;
|
||||
agent->iteration_count = 0;
|
||||
agent->tool_retry_count = 0;
|
||||
|
||||
if (user_message == NULL || *user_message == '\0') {
|
||||
agent->status = AGENT_STATUS_ERROR;
|
||||
agent_set_error(agent, "Empty user message");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char *json_data = chat_json("user", user_message);
|
||||
if (json_data == NULL) {
|
||||
agent->status = AGENT_STATUS_ERROR;
|
||||
agent_set_error(agent, "Failed to create chat JSON");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char *final_response = NULL;
|
||||
|
||||
while (agent->status == AGENT_STATUS_RUNNING) {
|
||||
agent->iteration_count++;
|
||||
|
||||
if (agent->iteration_count > agent->max_iterations) {
|
||||
agent->status = AGENT_STATUS_MAX_ITERATIONS;
|
||||
agent_set_error(agent, "Maximum iterations reached");
|
||||
if (agent->verbose) {
|
||||
fprintf(stderr, "[Agent] Max iterations (%d) reached\n",
|
||||
agent->max_iterations);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (agent->verbose) {
|
||||
fprintf(stderr, "[Agent] Iteration %d/%d\n", agent->iteration_count,
|
||||
agent->max_iterations);
|
||||
}
|
||||
|
||||
struct json_object *choice =
|
||||
agent_process_response(get_completions_api_url(), json_data);
|
||||
|
||||
if (choice == NULL) {
|
||||
agent->tool_retry_count++;
|
||||
if (agent->tool_retry_count >= agent->max_tool_retries) {
|
||||
agent->status = AGENT_STATUS_ERROR;
|
||||
agent_set_error(agent, "API request failed after retries");
|
||||
break;
|
||||
}
|
||||
if (agent->verbose) {
|
||||
fprintf(stderr, "[Agent] API error, retry %d/%d\n",
|
||||
agent->tool_retry_count, agent->max_tool_retries);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
agent->tool_retry_count = 0;
|
||||
|
||||
struct json_object *message_obj = agent_get_message(choice);
|
||||
if (message_obj) {
|
||||
message_add_object(json_object_get(message_obj));
|
||||
}
|
||||
|
||||
const char *finish_reason = agent_get_finish_reason(choice);
|
||||
bool has_tools = agent_has_tool_calls(choice);
|
||||
|
||||
if (agent->verbose) {
|
||||
fprintf(stderr, "[Agent] finish_reason=%s, has_tool_calls=%s\n",
|
||||
finish_reason ? finish_reason : "null",
|
||||
has_tools ? "true" : "false");
|
||||
}
|
||||
|
||||
if (has_tools) {
|
||||
struct json_object *tool_calls = agent_get_tool_calls(choice);
|
||||
|
||||
if (agent->verbose) {
|
||||
int num_tools = json_object_array_length(tool_calls);
|
||||
fprintf(stderr, "[Agent] Executing %d tool(s)\n", num_tools);
|
||||
}
|
||||
|
||||
struct json_object *tool_results = tools_execute(tool_calls);
|
||||
|
||||
int results_count = json_object_array_length(tool_results);
|
||||
for (int i = 0; i < results_count; i++) {
|
||||
struct json_object *tool_result =
|
||||
json_object_array_get_idx(tool_results, i);
|
||||
message_add_tool_call(json_object_get(tool_result));
|
||||
}
|
||||
|
||||
json_data = chat_json(NULL, NULL);
|
||||
if (json_data == NULL) {
|
||||
agent->status = AGENT_STATUS_ERROR;
|
||||
agent_set_error(agent, "Failed to create follow-up JSON");
|
||||
break;
|
||||
}
|
||||
|
||||
} else {
|
||||
char *content = agent_get_content(choice);
|
||||
|
||||
if (content && agent_response_indicates_incomplete(content)) {
|
||||
if (agent->verbose) {
|
||||
fprintf(stderr, "[Agent] Response indicates incomplete work, auto-continuing\n");
|
||||
}
|
||||
|
||||
free(content);
|
||||
json_data = chat_json("user", "Continue. Execute the necessary actions to complete the task.");
|
||||
if (json_data == NULL) {
|
||||
agent->status = AGENT_STATUS_ERROR;
|
||||
agent_set_error(agent, "Failed to create continue JSON");
|
||||
break;
|
||||
}
|
||||
|
||||
} else {
|
||||
final_response = content;
|
||||
agent->status = AGENT_STATUS_COMPLETED;
|
||||
|
||||
if (agent->verbose) {
|
||||
fprintf(stderr, "[Agent] Completed in %d iteration(s)\n",
|
||||
agent->iteration_count);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
current_agent = NULL;
|
||||
return final_response;
|
||||
}
|
||||
|
||||
char *agent_chat(const char *user_message) {
|
||||
agent_state_t *agent = agent_create(user_message);
|
||||
if (agent == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char *response = agent_run(agent, user_message);
|
||||
|
||||
if (agent->verbose && agent->status != AGENT_STATUS_COMPLETED && agent->last_error) {
|
||||
fprintf(stderr, "[Agent] Error: %s\n", agent->last_error);
|
||||
}
|
||||
|
||||
agent_destroy(agent);
|
||||
return response;
|
||||
}
|
||||
|
||||
char *agent_chat_with_limit(const char *user_message, int max_iterations) {
|
||||
agent_state_t *agent = agent_create(user_message);
|
||||
if (agent == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
agent->max_iterations = max_iterations;
|
||||
char *response = agent_run(agent, user_message);
|
||||
|
||||
if (agent->verbose && agent->status != AGENT_STATUS_COMPLETED && agent->last_error) {
|
||||
fprintf(stderr, "[Agent] Error: %s\n", agent->last_error);
|
||||
}
|
||||
|
||||
agent_destroy(agent);
|
||||
return response;
|
||||
}
|
||||
|
||||
#endif
|
||||
8
chat.h
8
chat.h
@ -46,6 +46,9 @@ void chat_free() {
|
||||
char *chat_json(const char *role, const char *message) {
|
||||
chat_free();
|
||||
json_object *root_object = json_object_new_object();
|
||||
if (root_object == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
json_object_object_add(root_object, "model",
|
||||
json_object_new_string(get_prompt_model()));
|
||||
|
||||
@ -56,12 +59,11 @@ char *chat_json(const char *role, const char *message) {
|
||||
}
|
||||
}
|
||||
|
||||
json_object_object_add(root_object, "messages", message_list());
|
||||
// json_object_object_add(root_object, "max_tokens",
|
||||
// json_object_new_int(prompt_max_tokens));
|
||||
json_object_object_add(root_object, "messages", json_object_get(message_list()));
|
||||
json_object_object_add(root_object, "temperature",
|
||||
json_object_new_double(PROMPT_TEMPERATURE));
|
||||
|
||||
_prompt = root_object;
|
||||
return (char *)json_object_to_json_string_ext(root_object,
|
||||
JSON_C_TO_STRING_PRETTY);
|
||||
}
|
||||
|
||||
58
db_utils.h
58
db_utils.h
@ -72,23 +72,33 @@ json_object *db_get(const char *key) {
|
||||
sqlite3 *db;
|
||||
sqlite3_stmt *stmt;
|
||||
json_object *result = json_object_new_object();
|
||||
const char *value = NULL;
|
||||
if (result == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int rc = sqlite3_open(db_file_expanded(), &db);
|
||||
if (rc != SQLITE_OK) {
|
||||
json_object_put(result);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char *sql = "SELECT value FROM kv_store WHERE key = ?";
|
||||
sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
|
||||
rc = sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
|
||||
if (rc != SQLITE_OK) {
|
||||
sqlite3_close(db);
|
||||
json_object_put(result);
|
||||
return NULL;
|
||||
}
|
||||
sqlite3_bind_text(stmt, 1, key, -1, SQLITE_STATIC);
|
||||
|
||||
if (sqlite3_step(stmt) == SQLITE_ROW) {
|
||||
value = (const char *)sqlite3_column_text(stmt, 0);
|
||||
}
|
||||
|
||||
if (value) {
|
||||
json_object_object_add(result, "value", json_object_new_string(value));
|
||||
const char *value = (const char *)sqlite3_column_text(stmt, 0);
|
||||
if (value) {
|
||||
json_object_object_add(result, "value", json_object_new_string(value));
|
||||
} else {
|
||||
json_object_object_add(result, "error",
|
||||
json_object_new_string("Key not found"));
|
||||
}
|
||||
} else {
|
||||
json_object_object_add(result, "error",
|
||||
json_object_new_string("Key not found"));
|
||||
@ -107,13 +117,22 @@ json_object *db_query(const char *query) {
|
||||
}
|
||||
|
||||
json_object *result = json_object_new_array();
|
||||
|
||||
int rc = sqlite3_open(db_file_expanded(), &db);
|
||||
if (rc != SQLITE_OK) {
|
||||
if (result == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
sqlite3_prepare_v2(db, query, -1, &stmt, NULL);
|
||||
int rc = sqlite3_open(db_file_expanded(), &db);
|
||||
if (rc != SQLITE_OK) {
|
||||
json_object_put(result);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
rc = sqlite3_prepare_v2(db, query, -1, &stmt, NULL);
|
||||
if (rc != SQLITE_OK) {
|
||||
sqlite3_close(db);
|
||||
json_object_put(result);
|
||||
return NULL;
|
||||
}
|
||||
while (sqlite3_step(stmt) == SQLITE_ROW) {
|
||||
json_object *row = json_object_new_object();
|
||||
for (int i = 0; i < sqlite3_column_count(stmt); i++) {
|
||||
@ -182,23 +201,34 @@ json_object *db_execute(const char *query) {
|
||||
}
|
||||
void db_store_file_version(const char *path) {
|
||||
char *expanded = expand_home_directory(path);
|
||||
if (expanded == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
char *content = read_file(expanded);
|
||||
if (!content) {
|
||||
free(expanded);
|
||||
return;
|
||||
}
|
||||
fprintf(stderr, "Creating backup:: %s\n", expanded);
|
||||
char *formatted = sqlite3_mprintf(
|
||||
"INSERT INTO file_version_history (path, content) VALUES (%Q, %Q)",
|
||||
expanded, content);
|
||||
db_execute(formatted);
|
||||
sqlite3_free(formatted);
|
||||
if (formatted) {
|
||||
db_execute(formatted);
|
||||
sqlite3_free(formatted);
|
||||
}
|
||||
free(content);
|
||||
free(expanded);
|
||||
}
|
||||
char *db_get_schema() {
|
||||
json_object *tables =
|
||||
db_query("SELECT * FROM sqlite_master WHERE type='table'");
|
||||
char *result = strdup(json_object_get_string(tables));
|
||||
if (tables == NULL) {
|
||||
return strdup("[]");
|
||||
}
|
||||
const char *str = json_object_to_json_string(tables);
|
||||
char *result = str ? strdup(str) : strdup("[]");
|
||||
json_object_put(tables);
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -29,6 +29,7 @@
|
||||
|
||||
#include "auth.h"
|
||||
#include <curl/curl.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
@ -42,6 +43,12 @@ static size_t WriteCallback(void *contents, size_t size, size_t nmemb,
|
||||
void *userp) {
|
||||
size_t total_size = size * nmemb;
|
||||
struct ResponseBuffer *response = (struct ResponseBuffer *)userp;
|
||||
|
||||
if (total_size > SIZE_MAX - response->size - 1) {
|
||||
fprintf(stderr, "Response too large\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
char *ptr = realloc(response->data, response->size + total_size + 1);
|
||||
if (ptr == NULL) {
|
||||
fprintf(stderr, "Failed to allocate memory for response\n");
|
||||
|
||||
51
indexer.h
51
indexer.h
@ -60,7 +60,10 @@ static int is_ignored_directory(const char *dir_name) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void get_file_info(const char *path) {
|
||||
static int get_file_info(const char *path) {
|
||||
if (file_count >= MAX_FILES) {
|
||||
return -1;
|
||||
}
|
||||
struct stat file_stat;
|
||||
if (stat(path, &file_stat) == 0) {
|
||||
FileInfo info;
|
||||
@ -75,7 +78,9 @@ static void get_file_info(const char *path) {
|
||||
info.type[sizeof(info.type) - 1] = '\0';
|
||||
info.size_bytes = file_stat.st_size;
|
||||
file_list[file_count++] = info;
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
char *index_directory(const char *dir_path) {
|
||||
@ -87,6 +92,10 @@ char *index_directory(const char *dir_path) {
|
||||
|
||||
struct dirent *entry;
|
||||
json_object *jarray = json_object_new_array();
|
||||
if (jarray == NULL) {
|
||||
closedir(dir);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
while ((entry = readdir(dir)) != NULL) {
|
||||
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
|
||||
@ -105,25 +114,29 @@ char *index_directory(const char *dir_path) {
|
||||
free(subdir_json);
|
||||
}
|
||||
} else if (is_valid_extension(entry->d_name)) {
|
||||
get_file_info(full_path);
|
||||
json_object *jfile = json_object_new_object();
|
||||
json_object_object_add(
|
||||
jfile, "file_name",
|
||||
json_object_new_string(file_list[file_count - 1].name));
|
||||
json_object_object_add(
|
||||
jfile, "modification_date",
|
||||
json_object_new_string(file_list[file_count - 1].modification_date));
|
||||
json_object_object_add(
|
||||
jfile, "creation_date",
|
||||
json_object_new_string(file_list[file_count - 1].creation_date));
|
||||
json_object_object_add(
|
||||
jfile, "type",
|
||||
json_object_new_string(file_list[file_count - 1].type));
|
||||
json_object_object_add(
|
||||
jfile, "size_bytes",
|
||||
json_object_new_int64(file_list[file_count - 1].size_bytes));
|
||||
if (get_file_info(full_path) == 0 && file_count > 0) {
|
||||
json_object *jfile = json_object_new_object();
|
||||
if (jfile == NULL) {
|
||||
continue;
|
||||
}
|
||||
json_object_object_add(
|
||||
jfile, "file_name",
|
||||
json_object_new_string(file_list[file_count - 1].name));
|
||||
json_object_object_add(
|
||||
jfile, "modification_date",
|
||||
json_object_new_string(file_list[file_count - 1].modification_date));
|
||||
json_object_object_add(
|
||||
jfile, "creation_date",
|
||||
json_object_new_string(file_list[file_count - 1].creation_date));
|
||||
json_object_object_add(
|
||||
jfile, "type",
|
||||
json_object_new_string(file_list[file_count - 1].type));
|
||||
json_object_object_add(
|
||||
jfile, "size_bytes",
|
||||
json_object_new_int64(file_list[file_count - 1].size_bytes));
|
||||
|
||||
json_object_array_add(jarray, jfile);
|
||||
json_object_array_add(jarray, jfile);
|
||||
}
|
||||
}
|
||||
}
|
||||
closedir(dir);
|
||||
|
||||
31
line.h
31
line.h
@ -25,10 +25,14 @@ bool line_initialized = false;
|
||||
|
||||
char *get_history_file() {
|
||||
static char result[4096];
|
||||
result[0] = 0;
|
||||
result[0] = '\0';
|
||||
|
||||
char *expanded = expand_home_directory(HISTORY_FILE);
|
||||
strcpy(result, expanded);
|
||||
if (expanded == NULL) {
|
||||
return result;
|
||||
}
|
||||
strncpy(result, expanded, sizeof(result) - 1);
|
||||
result[sizeof(result) - 1] = '\0';
|
||||
free(expanded);
|
||||
return result;
|
||||
}
|
||||
@ -56,21 +60,32 @@ char *line_command_generator(const char *text, int state) {
|
||||
|
||||
char *line_file_generator(const char *text, int state) {
|
||||
static int list_index;
|
||||
glob_t glob_result;
|
||||
static glob_t glob_result;
|
||||
static int glob_valid = 0;
|
||||
char pattern[1024];
|
||||
|
||||
if (!state) {
|
||||
if (glob_valid) {
|
||||
globfree(&glob_result);
|
||||
glob_valid = 0;
|
||||
}
|
||||
list_index = 0;
|
||||
snprintf(pattern, sizeof(pattern), "%s*",
|
||||
text); // Create a pattern for glob
|
||||
glob(pattern, GLOB_NOSORT, NULL, &glob_result);
|
||||
snprintf(pattern, sizeof(pattern), "%s*", text);
|
||||
if (glob(pattern, GLOB_NOSORT, NULL, &glob_result) == 0) {
|
||||
glob_valid = 1;
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (list_index < glob_result.gl_pathc) {
|
||||
if (glob_valid && (size_t)list_index < glob_result.gl_pathc) {
|
||||
return strdup(glob_result.gl_pathv[list_index++]);
|
||||
}
|
||||
|
||||
globfree(&glob_result);
|
||||
if (glob_valid) {
|
||||
globfree(&glob_result);
|
||||
glob_valid = 0;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
43
main.c
43
main.c
@ -1,3 +1,4 @@
|
||||
#include "agent.h"
|
||||
#include "db_utils.h"
|
||||
#include "line.h"
|
||||
#include "markdown.h"
|
||||
@ -138,7 +139,7 @@ static char *get_prompt_from_args(int argc, char **argv) {
|
||||
static bool try_prompt(int argc, char *argv[]) {
|
||||
char *prompt = get_prompt_from_args(argc, argv);
|
||||
if (prompt) {
|
||||
char *response = openai_chat("user", prompt);
|
||||
char *response = agent_chat(prompt);
|
||||
if (!response) {
|
||||
printf("Could not get response from server\n");
|
||||
free(prompt);
|
||||
@ -211,7 +212,7 @@ static void repl(void) {
|
||||
exit(0);
|
||||
|
||||
while (line && *line != '\n') {
|
||||
char *response = openai_chat("user", line);
|
||||
char *response = agent_chat(line);
|
||||
if (response) {
|
||||
render(response);
|
||||
printf("\n");
|
||||
@ -222,7 +223,8 @@ static void repl(void) {
|
||||
}
|
||||
free(response);
|
||||
} else {
|
||||
exit(0);
|
||||
fprintf(stderr, "Agent returned no response\n");
|
||||
line = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -233,28 +235,49 @@ static void init(void) {
|
||||
line_init();
|
||||
auth_init();
|
||||
db_initialize();
|
||||
|
||||
char *schema = db_get_schema();
|
||||
char payload[1024 * 1024] = {0};
|
||||
|
||||
snprintf(
|
||||
payload, sizeof(payload),
|
||||
"# LOCAL DATABASE"
|
||||
"Your have a local database that you can mutate using the query tool and "
|
||||
"the get and set tool."
|
||||
"If you set a value using the tool, make sure that the key is stemmed "
|
||||
"and lowercased to prevent double entries."
|
||||
"Dialect is sqlite. This is the schema in json format: %s. ",
|
||||
"# AUTONOMOUS AGENT INSTRUCTIONS\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"
|
||||
"- Always verify results before concluding\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"
|
||||
"## Completion\n"
|
||||
"When the task is fully complete, provide a clear final response.\n"
|
||||
"Do not stop mid-task. Continue using tools until the goal is achieved.\n",
|
||||
schema);
|
||||
|
||||
free(schema);
|
||||
fprintf(stderr, "Loading... 📨");
|
||||
fprintf(stderr, "Loading...");
|
||||
openai_system(payload);
|
||||
|
||||
char *env_system_message = get_env_system_message();
|
||||
if (env_system_message && *env_system_message) {
|
||||
openai_system(env_system_message);
|
||||
free(env_system_message);
|
||||
}
|
||||
|
||||
if (!openai_include(".rcontext.txt")) {
|
||||
openai_include("~/.rcontext.txt");
|
||||
}
|
||||
|
||||
fprintf(stderr, "\r \r");
|
||||
}
|
||||
|
||||
|
||||
23
messages.h
23
messages.h
@ -62,16 +62,19 @@ struct json_object *message_add_tool_result(const char *tool_call_id,
|
||||
char *tool_result) {
|
||||
struct json_object *messages = message_list();
|
||||
struct json_object *message = json_object_new_object();
|
||||
if (message == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
json_object_object_add(message, "tool_call_id",
|
||||
json_object_new_string(tool_call_id));
|
||||
|
||||
if (strlen(tool_result) > 104000) {
|
||||
tool_result[104000] = '\0';
|
||||
size_t result_len = strlen(tool_result);
|
||||
if (result_len > 104000) {
|
||||
result_len = 104000;
|
||||
}
|
||||
|
||||
json_object_object_add(message, "tool_result",
|
||||
json_object_new_string(tool_result));
|
||||
json_object_new_string_len(tool_result, (int)result_len));
|
||||
|
||||
json_object_array_add(messages, message);
|
||||
return message;
|
||||
@ -87,16 +90,18 @@ struct json_object *message_add(const char *role, const char *content);
|
||||
struct json_object *message_add(const char *role, const char *content) {
|
||||
struct json_object *messages = message_list();
|
||||
struct json_object *message = json_object_new_object();
|
||||
if (message == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
json_object_object_add(message, "role", json_object_new_string(role));
|
||||
|
||||
if (content) {
|
||||
char *formatted_content = strdup(content);
|
||||
if (strlen(formatted_content) > 1048570) {
|
||||
formatted_content[1048570] = '\0';
|
||||
size_t content_len = strlen(content);
|
||||
if (content_len > 1048570) {
|
||||
content_len = 1048570;
|
||||
}
|
||||
json_object_object_add(message, "content",
|
||||
json_object_new_string(formatted_content));
|
||||
free(formatted_content);
|
||||
json_object_new_string_len(content, (int)content_len));
|
||||
}
|
||||
if (!strcmp(role, "user")) {
|
||||
json_object_object_add(message, "tools", tools_descriptions());
|
||||
|
||||
8
openai.h
8
openai.h
@ -60,18 +60,18 @@ struct json_object *openai_process_chat_message(const char *api_url,
|
||||
if (json_object_object_get_ex(parsed_json, "error", &error_object) &&
|
||||
message_array) {
|
||||
|
||||
char *all_messages = (char *)json_object_to_json_string(message_array);
|
||||
const char *all_messages = json_object_to_json_string(message_array);
|
||||
|
||||
fprintf(stderr, "Messages: ");
|
||||
fwrite(all_messages, strlen(all_messages), 1, stderr);
|
||||
if (all_messages) {
|
||||
fwrite(all_messages, strlen(all_messages), 1, stderr);
|
||||
}
|
||||
fprintf(stderr, "\n");
|
||||
free(all_messages);
|
||||
|
||||
fprintf(stderr, "%s\n", json_object_to_json_string(parsed_json));
|
||||
|
||||
json_object_put(parsed_json);
|
||||
messages_remove_last();
|
||||
|
||||
messages_remove_last();
|
||||
|
||||
return NULL;
|
||||
|
||||
2
r.h
2
r.h
@ -4,7 +4,7 @@
|
||||
#include "utils.h"
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
bool is_verbose = true;
|
||||
bool is_verbose = false;
|
||||
|
||||
char *models_api_url = "https://api.openai.com/v1/models";
|
||||
char *completions_api_url = "https://api.openai.com/v1/chat/completions";
|
||||
|
||||
40
review.md
40
review.md
@ -1,29 +1,23 @@
|
||||
## AI Chat Prompt and API Integration with JSON-C
|
||||
# Application Review Report
|
||||
|
||||
This project offers functionalities for creating and managing AI chat interactions. It utilizes the JSON-C library to handle JSON objects, providing easy integration with HTTP networking and message handling.
|
||||
## Entry Point
|
||||
- The application's entry point is defined in `main.c`.
|
||||
- Currently, it prints "Hello, World!".
|
||||
|
||||
### Features
|
||||
- **Chat Prompt Management**: Facilitates creating JSON-based chat prompts using an AI model configuration.
|
||||
- **OpenAI API Interaction**: Includes functions to fetch models and system interactions via OpenAI's APIs.
|
||||
- **Command-Line Functionality**: Provides autocomplete and history features for user inputs using the readline library.
|
||||
- **HTTP Requests over SSL/TLS**: Handles HTTP POST and GET operations using OpenSSL for secure connections.
|
||||
- **Python Interpreter Plugin**: Executes Python scripts within a C application setting using Python’s C API.
|
||||
- **Syntax Highlighting**: Uses ANSI color formatting for syntax highlighting and markdown conversion.
|
||||
## Code Structure
|
||||
- The application has a simple structure with a single source file.
|
||||
|
||||
### Highlights
|
||||
- Modular functions ensure clean separation of concerns and improve code readability.
|
||||
- Utilizes OpenSSL for secure HTTP requests and JSON-C for efficient JSON handling.
|
||||
- The inclusion of various open-source alternatives like Rasa and Botpress are suggested for expanding functionalities.
|
||||
## Testing
|
||||
- Basic test setup to verify that `main()` returns 0.
|
||||
- The test code attempts to call `main()` directly, leading to a multiple definition error.
|
||||
|
||||
### Licensing
|
||||
All code is licensed under the MIT License, granting permission for free use, distribution, and modification while disclaiming warranties.
|
||||
## 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.
|
||||
|
||||
### Key Improvements
|
||||
- Enhanced error handling for JSON operations and API interactions.
|
||||
- Optimal memory management practices, including safe allocation and deallocation of resources.
|
||||
- The use of modern and secure methods for sensitive information management (e.g., API keys).
|
||||
## 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.
|
||||
|
||||
### Open Source Alternatives
|
||||
- **Rasa**: A machine learning framework for text-and-voice-based assistant automation.
|
||||
- **Libcurl**: A library supporting HTTP requests and SSL/TLS protocols.
|
||||
- **GNU Readline**: Provides similar command-line features with history and autocomplete.
|
||||
The review will be updated after fixing these issues.
|
||||
155
tools.h
155
tools.h
@ -33,6 +33,96 @@
|
||||
#include "indexer.h"
|
||||
#include "r.h"
|
||||
|
||||
static void tool_print_action(const char *func_name, struct json_object *args) {
|
||||
if (args == NULL) {
|
||||
fprintf(stderr, " -> %s\n", func_name);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!strcmp(func_name, "linux_terminal_execute")) {
|
||||
struct json_object *cmd;
|
||||
if (json_object_object_get_ex(args, "command", &cmd)) {
|
||||
fprintf(stderr, " -> Running command: %s\n", json_object_get_string(cmd));
|
||||
}
|
||||
} else if (!strcmp(func_name, "linux_terminal_execute_interactive")) {
|
||||
struct json_object *cmd;
|
||||
if (json_object_object_get_ex(args, "command", &cmd)) {
|
||||
fprintf(stderr, " -> Running interactive: %s\n", json_object_get_string(cmd));
|
||||
}
|
||||
} else if (!strcmp(func_name, "read_file")) {
|
||||
struct json_object *path;
|
||||
if (json_object_object_get_ex(args, "path", &path)) {
|
||||
fprintf(stderr, " -> Reading file: %s\n", json_object_get_string(path));
|
||||
}
|
||||
} else if (!strcmp(func_name, "write_file")) {
|
||||
struct json_object *path;
|
||||
if (json_object_object_get_ex(args, "path", &path)) {
|
||||
fprintf(stderr, " -> Writing file: %s\n", json_object_get_string(path));
|
||||
}
|
||||
} else if (!strcmp(func_name, "directory_glob") || !strcmp(func_name, "directory_rglob")) {
|
||||
struct json_object *path;
|
||||
if (json_object_object_get_ex(args, "path", &path)) {
|
||||
fprintf(stderr, " -> Listing: %s\n", json_object_get_string(path));
|
||||
}
|
||||
} else if (!strcmp(func_name, "chdir")) {
|
||||
struct json_object *path;
|
||||
if (json_object_object_get_ex(args, "path", &path)) {
|
||||
fprintf(stderr, " -> Changing directory: %s\n", json_object_get_string(path));
|
||||
}
|
||||
} else if (!strcmp(func_name, "getpwd")) {
|
||||
fprintf(stderr, " -> Getting current directory\n");
|
||||
} else if (!strcmp(func_name, "http_fetch")) {
|
||||
struct json_object *url;
|
||||
if (json_object_object_get_ex(args, "url", &url)) {
|
||||
fprintf(stderr, " -> Fetching URL: %s\n", json_object_get_string(url));
|
||||
}
|
||||
} else if (!strcmp(func_name, "web_search")) {
|
||||
struct json_object *q;
|
||||
if (json_object_object_get_ex(args, "query", &q)) {
|
||||
fprintf(stderr, " -> Searching web: %s\n", json_object_get_string(q));
|
||||
}
|
||||
} else if (!strcmp(func_name, "web_search_news")) {
|
||||
struct json_object *q;
|
||||
if (json_object_object_get_ex(args, "query", &q)) {
|
||||
fprintf(stderr, " -> Searching news: %s\n", json_object_get_string(q));
|
||||
}
|
||||
} else if (!strcmp(func_name, "db_get")) {
|
||||
struct json_object *key;
|
||||
if (json_object_object_get_ex(args, "key", &key)) {
|
||||
fprintf(stderr, " -> Getting from database: %s\n", json_object_get_string(key));
|
||||
}
|
||||
} else if (!strcmp(func_name, "db_set")) {
|
||||
struct json_object *key;
|
||||
if (json_object_object_get_ex(args, "key", &key)) {
|
||||
fprintf(stderr, " -> Storing in database: %s\n", json_object_get_string(key));
|
||||
}
|
||||
} else if (!strcmp(func_name, "db_query")) {
|
||||
struct json_object *q;
|
||||
if (json_object_object_get_ex(args, "query", &q)) {
|
||||
const char *query = json_object_get_string(q);
|
||||
if (strlen(query) > 60) {
|
||||
fprintf(stderr, " -> Executing SQL: %.60s...\n", query);
|
||||
} else {
|
||||
fprintf(stderr, " -> Executing SQL: %s\n", query);
|
||||
}
|
||||
}
|
||||
} else if (!strcmp(func_name, "mkdir")) {
|
||||
struct json_object *path;
|
||||
if (json_object_object_get_ex(args, "path", &path)) {
|
||||
fprintf(stderr, " -> Creating directory: %s\n", json_object_get_string(path));
|
||||
}
|
||||
} else if (!strcmp(func_name, "index_source_directory")) {
|
||||
struct json_object *path;
|
||||
if (json_object_object_get_ex(args, "path", &path)) {
|
||||
fprintf(stderr, " -> Indexing directory: %s\n", json_object_get_string(path));
|
||||
}
|
||||
} else if (!strcmp(func_name, "python_execute")) {
|
||||
fprintf(stderr, " -> Executing Python code\n");
|
||||
} else {
|
||||
fprintf(stderr, " -> %s\n", func_name);
|
||||
}
|
||||
}
|
||||
|
||||
struct json_object *tool_description_http_get();
|
||||
struct json_object *tool_description_linux_terminal();
|
||||
struct json_object *tool_description_directory_glob();
|
||||
@ -414,9 +504,11 @@ char *tool_function_linux_terminal(char *command) {
|
||||
char *tool_function_linux_terminal_interactive(char *command) {
|
||||
int result_code = system(command);
|
||||
|
||||
char *result = malloc(100);
|
||||
result[0] = 0;
|
||||
sprintf(result, "Command exited with status code %d.", result_code);
|
||||
char *result = malloc(128);
|
||||
if (result == NULL) {
|
||||
return strdup("Memory allocation failed.");
|
||||
}
|
||||
snprintf(result, 128, "Command exited with status code %d.", result_code);
|
||||
|
||||
return result;
|
||||
}
|
||||
@ -836,17 +928,24 @@ void ensure_parent_directory_exists(char *path_including_file_name) {
|
||||
return;
|
||||
|
||||
char *path = strdup(path_including_file_name);
|
||||
if (path == NULL)
|
||||
return;
|
||||
|
||||
char *last_slash = strrchr(path, '/');
|
||||
if (last_slash != NULL) {
|
||||
*last_slash = '\0';
|
||||
free(tool_function_mkdir(path));
|
||||
char *result = tool_function_mkdir(path);
|
||||
if (result)
|
||||
free(result);
|
||||
}
|
||||
free(path);
|
||||
}
|
||||
|
||||
char *tool_function_read_file(char *path) {
|
||||
|
||||
char *expanded_path = expand_home_directory(path);
|
||||
if (expanded_path == NULL) {
|
||||
return strdup("Failed to expand path!");
|
||||
}
|
||||
FILE *fp = fopen(expanded_path, "r");
|
||||
free(expanded_path);
|
||||
if (fp == NULL) {
|
||||
@ -856,16 +955,19 @@ char *tool_function_read_file(char *path) {
|
||||
|
||||
fseek(fp, 0, SEEK_END);
|
||||
long size = ftell(fp);
|
||||
if (size < 0) {
|
||||
fclose(fp);
|
||||
return strdup("Failed to determine file size!");
|
||||
}
|
||||
rewind(fp);
|
||||
|
||||
char *content = (char *)malloc(size + 1);
|
||||
char *content = (char *)malloc((size_t)size + 1);
|
||||
if (content == NULL) {
|
||||
fclose(fp);
|
||||
return strdup("Memory allocation failed!");
|
||||
}
|
||||
|
||||
ssize_t read_size = fread(content, 1, size, fp);
|
||||
|
||||
size_t read_size = fread(content, 1, (size_t)size, fp);
|
||||
content[read_size] = '\0';
|
||||
fclose(fp);
|
||||
|
||||
@ -917,16 +1019,28 @@ void recursive_glob(const char *pattern, glob_t *results) {
|
||||
|
||||
for (size_t i = 0; i < current_matches.gl_pathc; i++) {
|
||||
char *path = current_matches.gl_pathv[i];
|
||||
char *path_dup = strdup(path);
|
||||
if (path_dup == NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (results->gl_pathc == 0) {
|
||||
results->gl_pathc = 1;
|
||||
results->gl_pathv = malloc(sizeof(char *));
|
||||
results->gl_pathv[0] = strdup(path);
|
||||
if (results->gl_pathv == NULL) {
|
||||
free(path_dup);
|
||||
continue;
|
||||
}
|
||||
results->gl_pathv[0] = path_dup;
|
||||
} else {
|
||||
char **new_pathv = realloc(results->gl_pathv, (results->gl_pathc + 1) * sizeof(char *));
|
||||
if (new_pathv == NULL) {
|
||||
free(path_dup);
|
||||
continue;
|
||||
}
|
||||
results->gl_pathv = new_pathv;
|
||||
results->gl_pathv[results->gl_pathc] = path_dup;
|
||||
results->gl_pathc++;
|
||||
results->gl_pathv =
|
||||
realloc(results->gl_pathv, results->gl_pathc * sizeof(char *));
|
||||
results->gl_pathv[results->gl_pathc - 1] = strdup(path);
|
||||
}
|
||||
}
|
||||
|
||||
@ -935,6 +1049,10 @@ void recursive_glob(const char *pattern, glob_t *results) {
|
||||
struct stat statbuf;
|
||||
|
||||
char *pattern_copy = strdup(pattern);
|
||||
if (pattern_copy == NULL) {
|
||||
globfree(¤t_matches);
|
||||
return;
|
||||
}
|
||||
char *last_slash = strrchr(pattern_copy, '/');
|
||||
char *dir_path;
|
||||
char *file_pattern;
|
||||
@ -1214,12 +1332,15 @@ struct json_object *tool_description_linux_terminal() {
|
||||
}
|
||||
|
||||
char *tool_function_mkdir(char *path) {
|
||||
char temp[2048];
|
||||
char temp[4096];
|
||||
char *p = NULL;
|
||||
size_t len;
|
||||
|
||||
snprintf(temp, sizeof(temp), "%s", path);
|
||||
len = strlen(temp);
|
||||
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';
|
||||
@ -1314,8 +1435,10 @@ struct json_object *tools_execute(struct json_object *tools_array) {
|
||||
struct json_object *arguments =
|
||||
json_tokener_parse(json_object_get_string(arguments_obj));
|
||||
|
||||
tool_print_action(function_name, arguments);
|
||||
|
||||
if (is_verbose)
|
||||
fprintf(stderr, "Executing function %s with arguments %s\n",
|
||||
fprintf(stderr, " [verbose] %s args: %s\n",
|
||||
function_name, json_object_to_json_string(arguments));
|
||||
}
|
||||
}
|
||||
|
||||
40
utils.h
40
utils.h
@ -66,15 +66,17 @@ char *expand_home_directory(const char *path) {
|
||||
return NULL; // Error getting home directory
|
||||
}
|
||||
|
||||
// Allocate memory for the expanded path
|
||||
size_t expanded_size = strlen(home_dir) + strlen(path);
|
||||
size_t home_len = strlen(home_dir);
|
||||
size_t path_len = strlen(path + 1);
|
||||
size_t expanded_size = home_len + path_len + 1;
|
||||
char *expanded_path = (char *)malloc(expanded_size);
|
||||
if (expanded_path == NULL) {
|
||||
return NULL; // Memory allocation error
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Construct the expanded path
|
||||
snprintf(expanded_path, expanded_size, "%s%s", home_dir, path + 1);
|
||||
memcpy(expanded_path, home_dir, home_len);
|
||||
memcpy(expanded_path + home_len, path + 1, path_len);
|
||||
expanded_path[home_len + path_len] = '\0';
|
||||
return expanded_path;
|
||||
}
|
||||
|
||||
@ -90,21 +92,37 @@ unsigned long hash(const char *str) {
|
||||
}
|
||||
|
||||
char *joinpath(const char *base_url, const char *path) {
|
||||
static char result[1024];
|
||||
result[0] = '\0';
|
||||
strcat(result, base_url);
|
||||
if (base_url[strlen(base_url) - 1] != '/') {
|
||||
strcat(result, "/");
|
||||
static char result[4096];
|
||||
size_t base_len = strlen(base_url);
|
||||
size_t pos = 0;
|
||||
|
||||
if (base_len >= sizeof(result) - 2) {
|
||||
base_len = sizeof(result) - 2;
|
||||
}
|
||||
memcpy(result, base_url, base_len);
|
||||
pos = base_len;
|
||||
|
||||
if (pos > 0 && result[pos - 1] != '/') {
|
||||
result[pos++] = '/';
|
||||
}
|
||||
if (path[0] == '/') {
|
||||
path++;
|
||||
}
|
||||
strcat(result, path);
|
||||
|
||||
size_t path_len = strlen(path);
|
||||
if (pos + path_len >= sizeof(result)) {
|
||||
path_len = sizeof(result) - pos - 1;
|
||||
}
|
||||
memcpy(result + pos, path, path_len);
|
||||
result[pos + path_len] = '\0';
|
||||
return result;
|
||||
}
|
||||
|
||||
char *read_file(const char *path) {
|
||||
char *expanded_path = expand_home_directory(path);
|
||||
if (expanded_path == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
FILE *file = fopen(expanded_path, "r");
|
||||
free(expanded_path);
|
||||
if (file == NULL) {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user