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
|
A simple IRC server implemented in Python as a package named `henry`.
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
- Basic IRC commands: NICK, USER, JOIN, PRIVMSG, QUIT
|
||||||
|
- Supports multiple clients and channels
|
||||||
|
|
||||||
- **Flexible AI Integration**: Support for multiple AI models including:
|
## Installation
|
||||||
- OpenAI GPT-3.5-turbo (default, if you did not set API key)
|
|
||||||
- Ollama
|
|
||||||
- Anthropic Claude
|
|
||||||
- Grok
|
|
||||||
|
|
||||||
- **Customizable Behavior**: Configure tool behavior through `~/.rcontext.txt`
|
Clone the repository or copy the `henry` package directory.
|
||||||
- **Agent Support**: Intelligent context-aware assistance
|
|
||||||
- **Markdown Output**: Clean, readable documentation generation
|
|
||||||
|
|
||||||
## Prerequisites
|
## Usage
|
||||||
|
|
||||||
- Linux operating system
|
Run the IRC server using the CLI entry point:
|
||||||
- Configured AI model access
|
|
||||||
|
|
||||||
## 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
|
```bash
|
||||||
export R_MODEL="qwen2.5:3b"
|
python -m henry.cli
|
||||||
export R_BASE_URL="https://ollama.molodetz.nl"
|
|
||||||
./r
|
|
||||||
```
|
```
|
||||||
|
|
||||||
#### 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
|
```bash
|
||||||
export R_MODEL="claude-3-5-haiku-20241022"
|
# Using irssi client
|
||||||
export R_BASE_URL="https://api.anthropic.com"
|
irssi -c 127.0.0.1 -p 6667
|
||||||
export R_KEY="sk-ant-[your-key]"
|
|
||||||
./r
|
|
||||||
```
|
```
|
||||||
|
|
||||||
#### OpenAI Configuration
|
## License
|
||||||
```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.
|
|
||||||
|
|
||||||
|
|
||||||
|
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) {
|
char *chat_json(const char *role, const char *message) {
|
||||||
chat_free();
|
chat_free();
|
||||||
json_object *root_object = json_object_new_object();
|
json_object *root_object = json_object_new_object();
|
||||||
|
if (root_object == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
json_object_object_add(root_object, "model",
|
json_object_object_add(root_object, "model",
|
||||||
json_object_new_string(get_prompt_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, "messages", json_object_get(message_list()));
|
||||||
// json_object_object_add(root_object, "max_tokens",
|
|
||||||
// json_object_new_int(prompt_max_tokens));
|
|
||||||
json_object_object_add(root_object, "temperature",
|
json_object_object_add(root_object, "temperature",
|
||||||
json_object_new_double(PROMPT_TEMPERATURE));
|
json_object_new_double(PROMPT_TEMPERATURE));
|
||||||
|
|
||||||
|
_prompt = root_object;
|
||||||
return (char *)json_object_to_json_string_ext(root_object,
|
return (char *)json_object_to_json_string_ext(root_object,
|
||||||
JSON_C_TO_STRING_PRETTY);
|
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 *db;
|
||||||
sqlite3_stmt *stmt;
|
sqlite3_stmt *stmt;
|
||||||
json_object *result = json_object_new_object();
|
json_object *result = json_object_new_object();
|
||||||
const char *value = NULL;
|
if (result == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
int rc = sqlite3_open(db_file_expanded(), &db);
|
int rc = sqlite3_open(db_file_expanded(), &db);
|
||||||
if (rc != SQLITE_OK) {
|
if (rc != SQLITE_OK) {
|
||||||
|
json_object_put(result);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *sql = "SELECT value FROM kv_store WHERE key = ?";
|
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);
|
sqlite3_bind_text(stmt, 1, key, -1, SQLITE_STATIC);
|
||||||
|
|
||||||
if (sqlite3_step(stmt) == SQLITE_ROW) {
|
if (sqlite3_step(stmt) == SQLITE_ROW) {
|
||||||
value = (const char *)sqlite3_column_text(stmt, 0);
|
const char *value = (const char *)sqlite3_column_text(stmt, 0);
|
||||||
}
|
if (value) {
|
||||||
|
json_object_object_add(result, "value", json_object_new_string(value));
|
||||||
if (value) {
|
} else {
|
||||||
json_object_object_add(result, "value", json_object_new_string(value));
|
json_object_object_add(result, "error",
|
||||||
|
json_object_new_string("Key not found"));
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
json_object_object_add(result, "error",
|
json_object_object_add(result, "error",
|
||||||
json_object_new_string("Key not found"));
|
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();
|
json_object *result = json_object_new_array();
|
||||||
|
if (result == NULL) {
|
||||||
int rc = sqlite3_open(db_file_expanded(), &db);
|
|
||||||
if (rc != SQLITE_OK) {
|
|
||||||
return 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) {
|
while (sqlite3_step(stmt) == SQLITE_ROW) {
|
||||||
json_object *row = json_object_new_object();
|
json_object *row = json_object_new_object();
|
||||||
for (int i = 0; i < sqlite3_column_count(stmt); i++) {
|
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) {
|
void db_store_file_version(const char *path) {
|
||||||
char *expanded = expand_home_directory(path);
|
char *expanded = expand_home_directory(path);
|
||||||
|
if (expanded == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
char *content = read_file(expanded);
|
char *content = read_file(expanded);
|
||||||
if (!content) {
|
if (!content) {
|
||||||
|
free(expanded);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
fprintf(stderr, "Creating backup:: %s\n", expanded);
|
fprintf(stderr, "Creating backup:: %s\n", expanded);
|
||||||
char *formatted = sqlite3_mprintf(
|
char *formatted = sqlite3_mprintf(
|
||||||
"INSERT INTO file_version_history (path, content) VALUES (%Q, %Q)",
|
"INSERT INTO file_version_history (path, content) VALUES (%Q, %Q)",
|
||||||
expanded, content);
|
expanded, content);
|
||||||
db_execute(formatted);
|
if (formatted) {
|
||||||
sqlite3_free(formatted);
|
db_execute(formatted);
|
||||||
|
sqlite3_free(formatted);
|
||||||
|
}
|
||||||
free(content);
|
free(content);
|
||||||
|
free(expanded);
|
||||||
}
|
}
|
||||||
char *db_get_schema() {
|
char *db_get_schema() {
|
||||||
json_object *tables =
|
json_object *tables =
|
||||||
db_query("SELECT * FROM sqlite_master WHERE type='table'");
|
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);
|
json_object_put(tables);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -29,6 +29,7 @@
|
|||||||
|
|
||||||
#include "auth.h"
|
#include "auth.h"
|
||||||
#include <curl/curl.h>
|
#include <curl/curl.h>
|
||||||
|
#include <stdint.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
@ -42,6 +43,12 @@ static size_t WriteCallback(void *contents, size_t size, size_t nmemb,
|
|||||||
void *userp) {
|
void *userp) {
|
||||||
size_t total_size = size * nmemb;
|
size_t total_size = size * nmemb;
|
||||||
struct ResponseBuffer *response = (struct ResponseBuffer *)userp;
|
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);
|
char *ptr = realloc(response->data, response->size + total_size + 1);
|
||||||
if (ptr == NULL) {
|
if (ptr == NULL) {
|
||||||
fprintf(stderr, "Failed to allocate memory for response\n");
|
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;
|
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;
|
struct stat file_stat;
|
||||||
if (stat(path, &file_stat) == 0) {
|
if (stat(path, &file_stat) == 0) {
|
||||||
FileInfo info;
|
FileInfo info;
|
||||||
@ -75,7 +78,9 @@ static void get_file_info(const char *path) {
|
|||||||
info.type[sizeof(info.type) - 1] = '\0';
|
info.type[sizeof(info.type) - 1] = '\0';
|
||||||
info.size_bytes = file_stat.st_size;
|
info.size_bytes = file_stat.st_size;
|
||||||
file_list[file_count++] = info;
|
file_list[file_count++] = info;
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
char *index_directory(const char *dir_path) {
|
char *index_directory(const char *dir_path) {
|
||||||
@ -87,6 +92,10 @@ char *index_directory(const char *dir_path) {
|
|||||||
|
|
||||||
struct dirent *entry;
|
struct dirent *entry;
|
||||||
json_object *jarray = json_object_new_array();
|
json_object *jarray = json_object_new_array();
|
||||||
|
if (jarray == NULL) {
|
||||||
|
closedir(dir);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
while ((entry = readdir(dir)) != NULL) {
|
while ((entry = readdir(dir)) != NULL) {
|
||||||
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
|
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);
|
free(subdir_json);
|
||||||
}
|
}
|
||||||
} else if (is_valid_extension(entry->d_name)) {
|
} else if (is_valid_extension(entry->d_name)) {
|
||||||
get_file_info(full_path);
|
if (get_file_info(full_path) == 0 && file_count > 0) {
|
||||||
json_object *jfile = json_object_new_object();
|
json_object *jfile = json_object_new_object();
|
||||||
json_object_object_add(
|
if (jfile == NULL) {
|
||||||
jfile, "file_name",
|
continue;
|
||||||
json_object_new_string(file_list[file_count - 1].name));
|
}
|
||||||
json_object_object_add(
|
json_object_object_add(
|
||||||
jfile, "modification_date",
|
jfile, "file_name",
|
||||||
json_object_new_string(file_list[file_count - 1].modification_date));
|
json_object_new_string(file_list[file_count - 1].name));
|
||||||
json_object_object_add(
|
json_object_object_add(
|
||||||
jfile, "creation_date",
|
jfile, "modification_date",
|
||||||
json_object_new_string(file_list[file_count - 1].creation_date));
|
json_object_new_string(file_list[file_count - 1].modification_date));
|
||||||
json_object_object_add(
|
json_object_object_add(
|
||||||
jfile, "type",
|
jfile, "creation_date",
|
||||||
json_object_new_string(file_list[file_count - 1].type));
|
json_object_new_string(file_list[file_count - 1].creation_date));
|
||||||
json_object_object_add(
|
json_object_object_add(
|
||||||
jfile, "size_bytes",
|
jfile, "type",
|
||||||
json_object_new_int64(file_list[file_count - 1].size_bytes));
|
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);
|
closedir(dir);
|
||||||
|
|||||||
31
line.h
31
line.h
@ -25,10 +25,14 @@ bool line_initialized = false;
|
|||||||
|
|
||||||
char *get_history_file() {
|
char *get_history_file() {
|
||||||
static char result[4096];
|
static char result[4096];
|
||||||
result[0] = 0;
|
result[0] = '\0';
|
||||||
|
|
||||||
char *expanded = expand_home_directory(HISTORY_FILE);
|
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);
|
free(expanded);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -56,21 +60,32 @@ char *line_command_generator(const char *text, int state) {
|
|||||||
|
|
||||||
char *line_file_generator(const char *text, int state) {
|
char *line_file_generator(const char *text, int state) {
|
||||||
static int list_index;
|
static int list_index;
|
||||||
glob_t glob_result;
|
static glob_t glob_result;
|
||||||
|
static int glob_valid = 0;
|
||||||
char pattern[1024];
|
char pattern[1024];
|
||||||
|
|
||||||
if (!state) {
|
if (!state) {
|
||||||
|
if (glob_valid) {
|
||||||
|
globfree(&glob_result);
|
||||||
|
glob_valid = 0;
|
||||||
|
}
|
||||||
list_index = 0;
|
list_index = 0;
|
||||||
snprintf(pattern, sizeof(pattern), "%s*",
|
snprintf(pattern, sizeof(pattern), "%s*", text);
|
||||||
text); // Create a pattern for glob
|
if (glob(pattern, GLOB_NOSORT, NULL, &glob_result) == 0) {
|
||||||
glob(pattern, GLOB_NOSORT, NULL, &glob_result);
|
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++]);
|
return strdup(glob_result.gl_pathv[list_index++]);
|
||||||
}
|
}
|
||||||
|
|
||||||
globfree(&glob_result);
|
if (glob_valid) {
|
||||||
|
globfree(&glob_result);
|
||||||
|
glob_valid = 0;
|
||||||
|
}
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
43
main.c
43
main.c
@ -1,3 +1,4 @@
|
|||||||
|
#include "agent.h"
|
||||||
#include "db_utils.h"
|
#include "db_utils.h"
|
||||||
#include "line.h"
|
#include "line.h"
|
||||||
#include "markdown.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[]) {
|
static bool try_prompt(int argc, char *argv[]) {
|
||||||
char *prompt = get_prompt_from_args(argc, argv);
|
char *prompt = get_prompt_from_args(argc, argv);
|
||||||
if (prompt) {
|
if (prompt) {
|
||||||
char *response = openai_chat("user", prompt);
|
char *response = agent_chat(prompt);
|
||||||
if (!response) {
|
if (!response) {
|
||||||
printf("Could not get response from server\n");
|
printf("Could not get response from server\n");
|
||||||
free(prompt);
|
free(prompt);
|
||||||
@ -211,7 +212,7 @@ static void repl(void) {
|
|||||||
exit(0);
|
exit(0);
|
||||||
|
|
||||||
while (line && *line != '\n') {
|
while (line && *line != '\n') {
|
||||||
char *response = openai_chat("user", line);
|
char *response = agent_chat(line);
|
||||||
if (response) {
|
if (response) {
|
||||||
render(response);
|
render(response);
|
||||||
printf("\n");
|
printf("\n");
|
||||||
@ -222,7 +223,8 @@ static void repl(void) {
|
|||||||
}
|
}
|
||||||
free(response);
|
free(response);
|
||||||
} else {
|
} else {
|
||||||
exit(0);
|
fprintf(stderr, "Agent returned no response\n");
|
||||||
|
line = NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -233,28 +235,49 @@ static void init(void) {
|
|||||||
line_init();
|
line_init();
|
||||||
auth_init();
|
auth_init();
|
||||||
db_initialize();
|
db_initialize();
|
||||||
|
|
||||||
char *schema = db_get_schema();
|
char *schema = db_get_schema();
|
||||||
char payload[1024 * 1024] = {0};
|
char payload[1024 * 1024] = {0};
|
||||||
|
|
||||||
snprintf(
|
snprintf(
|
||||||
payload, sizeof(payload),
|
payload, sizeof(payload),
|
||||||
"# LOCAL DATABASE"
|
"# AUTONOMOUS AGENT INSTRUCTIONS\n"
|
||||||
"Your have a local database that you can mutate using the query tool and "
|
"You are an autonomous AI agent. You operate in a loop: reason about the task, "
|
||||||
"the get and set tool."
|
"select and execute tools when needed, observe results, and continue until the goal is achieved.\n\n"
|
||||||
"If you set a value using the tool, make sure that the key is stemmed "
|
"## Reasoning Pattern (ReAct)\n"
|
||||||
"and lowercased to prevent double entries."
|
"For complex tasks, think step-by-step:\n"
|
||||||
"Dialect is sqlite. This is the schema in json format: %s. ",
|
"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);
|
schema);
|
||||||
|
|
||||||
free(schema);
|
free(schema);
|
||||||
fprintf(stderr, "Loading... 📨");
|
fprintf(stderr, "Loading...");
|
||||||
openai_system(payload);
|
openai_system(payload);
|
||||||
|
|
||||||
char *env_system_message = get_env_system_message();
|
char *env_system_message = get_env_system_message();
|
||||||
if (env_system_message && *env_system_message) {
|
if (env_system_message && *env_system_message) {
|
||||||
openai_system(env_system_message);
|
openai_system(env_system_message);
|
||||||
free(env_system_message);
|
free(env_system_message);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!openai_include(".rcontext.txt")) {
|
if (!openai_include(".rcontext.txt")) {
|
||||||
openai_include("~/.rcontext.txt");
|
openai_include("~/.rcontext.txt");
|
||||||
}
|
}
|
||||||
|
|
||||||
fprintf(stderr, "\r \r");
|
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) {
|
char *tool_result) {
|
||||||
struct json_object *messages = message_list();
|
struct json_object *messages = message_list();
|
||||||
struct json_object *message = json_object_new_object();
|
struct json_object *message = json_object_new_object();
|
||||||
|
if (message == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
json_object_object_add(message, "tool_call_id",
|
json_object_object_add(message, "tool_call_id",
|
||||||
json_object_new_string(tool_call_id));
|
json_object_new_string(tool_call_id));
|
||||||
|
|
||||||
if (strlen(tool_result) > 104000) {
|
size_t result_len = strlen(tool_result);
|
||||||
tool_result[104000] = '\0';
|
if (result_len > 104000) {
|
||||||
|
result_len = 104000;
|
||||||
}
|
}
|
||||||
|
|
||||||
json_object_object_add(message, "tool_result",
|
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);
|
json_object_array_add(messages, message);
|
||||||
return 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 *message_add(const char *role, const char *content) {
|
||||||
struct json_object *messages = message_list();
|
struct json_object *messages = message_list();
|
||||||
struct json_object *message = json_object_new_object();
|
struct json_object *message = json_object_new_object();
|
||||||
|
if (message == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
json_object_object_add(message, "role", json_object_new_string(role));
|
json_object_object_add(message, "role", json_object_new_string(role));
|
||||||
|
|
||||||
if (content) {
|
if (content) {
|
||||||
char *formatted_content = strdup(content);
|
size_t content_len = strlen(content);
|
||||||
if (strlen(formatted_content) > 1048570) {
|
if (content_len > 1048570) {
|
||||||
formatted_content[1048570] = '\0';
|
content_len = 1048570;
|
||||||
}
|
}
|
||||||
json_object_object_add(message, "content",
|
json_object_object_add(message, "content",
|
||||||
json_object_new_string(formatted_content));
|
json_object_new_string_len(content, (int)content_len));
|
||||||
free(formatted_content);
|
|
||||||
}
|
}
|
||||||
if (!strcmp(role, "user")) {
|
if (!strcmp(role, "user")) {
|
||||||
json_object_object_add(message, "tools", tools_descriptions());
|
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) &&
|
if (json_object_object_get_ex(parsed_json, "error", &error_object) &&
|
||||||
message_array) {
|
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: ");
|
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");
|
fprintf(stderr, "\n");
|
||||||
free(all_messages);
|
|
||||||
|
|
||||||
fprintf(stderr, "%s\n", json_object_to_json_string(parsed_json));
|
fprintf(stderr, "%s\n", json_object_to_json_string(parsed_json));
|
||||||
|
|
||||||
json_object_put(parsed_json);
|
json_object_put(parsed_json);
|
||||||
messages_remove_last();
|
messages_remove_last();
|
||||||
|
|
||||||
messages_remove_last();
|
messages_remove_last();
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|||||||
2
r.h
2
r.h
@ -4,7 +4,7 @@
|
|||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
bool is_verbose = true;
|
bool is_verbose = false;
|
||||||
|
|
||||||
char *models_api_url = "https://api.openai.com/v1/models";
|
char *models_api_url = "https://api.openai.com/v1/models";
|
||||||
char *completions_api_url = "https://api.openai.com/v1/chat/completions";
|
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
|
## Code Structure
|
||||||
- **Chat Prompt Management**: Facilitates creating JSON-based chat prompts using an AI model configuration.
|
- The application has a simple structure with a single source file.
|
||||||
- **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.
|
|
||||||
|
|
||||||
### Highlights
|
## Testing
|
||||||
- Modular functions ensure clean separation of concerns and improve code readability.
|
- Basic test setup to verify that `main()` returns 0.
|
||||||
- Utilizes OpenSSL for secure HTTP requests and JSON-C for efficient JSON handling.
|
- The test code attempts to call `main()` directly, leading to a multiple definition error.
|
||||||
- The inclusion of various open-source alternatives like Rasa and Botpress are suggested for expanding functionalities.
|
|
||||||
|
|
||||||
### Licensing
|
## Issues Identified
|
||||||
All code is licensed under the MIT License, granting permission for free use, distribution, and modification while disclaiming warranties.
|
- 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
|
## Recommendations
|
||||||
- Enhanced error handling for JSON operations and API interactions.
|
- Refactor the application code to isolate logic from `main()` to allow easier testing.
|
||||||
- Optimal memory management practices, including safe allocation and deallocation of resources.
|
- Expand the code to include more functionalities and corresponding tests.
|
||||||
- The use of modern and secure methods for sensitive information management (e.g., API keys).
|
- Review and improve build process and structure for maintainability.
|
||||||
|
|
||||||
### Open Source Alternatives
|
The review will be updated after fixing these issues.
|
||||||
- **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.
|
|
||||||
155
tools.h
155
tools.h
@ -33,6 +33,96 @@
|
|||||||
#include "indexer.h"
|
#include "indexer.h"
|
||||||
#include "r.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_http_get();
|
||||||
struct json_object *tool_description_linux_terminal();
|
struct json_object *tool_description_linux_terminal();
|
||||||
struct json_object *tool_description_directory_glob();
|
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) {
|
char *tool_function_linux_terminal_interactive(char *command) {
|
||||||
int result_code = system(command);
|
int result_code = system(command);
|
||||||
|
|
||||||
char *result = malloc(100);
|
char *result = malloc(128);
|
||||||
result[0] = 0;
|
if (result == NULL) {
|
||||||
sprintf(result, "Command exited with status code %d.", result_code);
|
return strdup("Memory allocation failed.");
|
||||||
|
}
|
||||||
|
snprintf(result, 128, "Command exited with status code %d.", result_code);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -836,17 +928,24 @@ void ensure_parent_directory_exists(char *path_including_file_name) {
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
char *path = strdup(path_including_file_name);
|
char *path = strdup(path_including_file_name);
|
||||||
|
if (path == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
char *last_slash = strrchr(path, '/');
|
char *last_slash = strrchr(path, '/');
|
||||||
if (last_slash != NULL) {
|
if (last_slash != NULL) {
|
||||||
*last_slash = '\0';
|
*last_slash = '\0';
|
||||||
free(tool_function_mkdir(path));
|
char *result = tool_function_mkdir(path);
|
||||||
|
if (result)
|
||||||
|
free(result);
|
||||||
}
|
}
|
||||||
free(path);
|
free(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
char *tool_function_read_file(char *path) {
|
char *tool_function_read_file(char *path) {
|
||||||
|
|
||||||
char *expanded_path = expand_home_directory(path);
|
char *expanded_path = expand_home_directory(path);
|
||||||
|
if (expanded_path == NULL) {
|
||||||
|
return strdup("Failed to expand path!");
|
||||||
|
}
|
||||||
FILE *fp = fopen(expanded_path, "r");
|
FILE *fp = fopen(expanded_path, "r");
|
||||||
free(expanded_path);
|
free(expanded_path);
|
||||||
if (fp == NULL) {
|
if (fp == NULL) {
|
||||||
@ -856,16 +955,19 @@ char *tool_function_read_file(char *path) {
|
|||||||
|
|
||||||
fseek(fp, 0, SEEK_END);
|
fseek(fp, 0, SEEK_END);
|
||||||
long size = ftell(fp);
|
long size = ftell(fp);
|
||||||
|
if (size < 0) {
|
||||||
|
fclose(fp);
|
||||||
|
return strdup("Failed to determine file size!");
|
||||||
|
}
|
||||||
rewind(fp);
|
rewind(fp);
|
||||||
|
|
||||||
char *content = (char *)malloc(size + 1);
|
char *content = (char *)malloc((size_t)size + 1);
|
||||||
if (content == NULL) {
|
if (content == NULL) {
|
||||||
fclose(fp);
|
fclose(fp);
|
||||||
return strdup("Memory allocation failed!");
|
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';
|
content[read_size] = '\0';
|
||||||
fclose(fp);
|
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++) {
|
for (size_t i = 0; i < current_matches.gl_pathc; i++) {
|
||||||
char *path = current_matches.gl_pathv[i];
|
char *path = current_matches.gl_pathv[i];
|
||||||
|
char *path_dup = strdup(path);
|
||||||
|
if (path_dup == NULL) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (results->gl_pathc == 0) {
|
if (results->gl_pathc == 0) {
|
||||||
results->gl_pathc = 1;
|
results->gl_pathc = 1;
|
||||||
results->gl_pathv = malloc(sizeof(char *));
|
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 {
|
} 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_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;
|
struct stat statbuf;
|
||||||
|
|
||||||
char *pattern_copy = strdup(pattern);
|
char *pattern_copy = strdup(pattern);
|
||||||
|
if (pattern_copy == NULL) {
|
||||||
|
globfree(¤t_matches);
|
||||||
|
return;
|
||||||
|
}
|
||||||
char *last_slash = strrchr(pattern_copy, '/');
|
char *last_slash = strrchr(pattern_copy, '/');
|
||||||
char *dir_path;
|
char *dir_path;
|
||||||
char *file_pattern;
|
char *file_pattern;
|
||||||
@ -1214,12 +1332,15 @@ struct json_object *tool_description_linux_terminal() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
char *tool_function_mkdir(char *path) {
|
char *tool_function_mkdir(char *path) {
|
||||||
char temp[2048];
|
char temp[4096];
|
||||||
char *p = NULL;
|
char *p = NULL;
|
||||||
size_t len;
|
size_t len;
|
||||||
|
|
||||||
snprintf(temp, sizeof(temp), "%s", path);
|
len = strlen(path);
|
||||||
len = strlen(temp);
|
if (len >= sizeof(temp)) {
|
||||||
|
return strdup("Path too long!");
|
||||||
|
}
|
||||||
|
memcpy(temp, path, len + 1);
|
||||||
|
|
||||||
if (temp[len - 1] == '/') {
|
if (temp[len - 1] == '/') {
|
||||||
temp[len - 1] = '\0';
|
temp[len - 1] = '\0';
|
||||||
@ -1314,8 +1435,10 @@ struct json_object *tools_execute(struct json_object *tools_array) {
|
|||||||
struct json_object *arguments =
|
struct json_object *arguments =
|
||||||
json_tokener_parse(json_object_get_string(arguments_obj));
|
json_tokener_parse(json_object_get_string(arguments_obj));
|
||||||
|
|
||||||
|
tool_print_action(function_name, arguments);
|
||||||
|
|
||||||
if (is_verbose)
|
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));
|
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
|
return NULL; // Error getting home directory
|
||||||
}
|
}
|
||||||
|
|
||||||
// Allocate memory for the expanded path
|
size_t home_len = strlen(home_dir);
|
||||||
size_t expanded_size = strlen(home_dir) + strlen(path);
|
size_t path_len = strlen(path + 1);
|
||||||
|
size_t expanded_size = home_len + path_len + 1;
|
||||||
char *expanded_path = (char *)malloc(expanded_size);
|
char *expanded_path = (char *)malloc(expanded_size);
|
||||||
if (expanded_path == NULL) {
|
if (expanded_path == NULL) {
|
||||||
return NULL; // Memory allocation error
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Construct the expanded path
|
memcpy(expanded_path, home_dir, home_len);
|
||||||
snprintf(expanded_path, expanded_size, "%s%s", home_dir, path + 1);
|
memcpy(expanded_path + home_len, path + 1, path_len);
|
||||||
|
expanded_path[home_len + path_len] = '\0';
|
||||||
return expanded_path;
|
return expanded_path;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -90,21 +92,37 @@ unsigned long hash(const char *str) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
char *joinpath(const char *base_url, const char *path) {
|
char *joinpath(const char *base_url, const char *path) {
|
||||||
static char result[1024];
|
static char result[4096];
|
||||||
result[0] = '\0';
|
size_t base_len = strlen(base_url);
|
||||||
strcat(result, base_url);
|
size_t pos = 0;
|
||||||
if (base_url[strlen(base_url) - 1] != '/') {
|
|
||||||
strcat(result, "/");
|
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] == '/') {
|
if (path[0] == '/') {
|
||||||
path++;
|
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;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
char *read_file(const char *path) {
|
char *read_file(const char *path) {
|
||||||
char *expanded_path = expand_home_directory(path);
|
char *expanded_path = expand_home_directory(path);
|
||||||
|
if (expanded_path == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
FILE *file = fopen(expanded_path, "r");
|
FILE *file = fopen(expanded_path, "r");
|
||||||
free(expanded_path);
|
free(expanded_path);
|
||||||
if (file == NULL) {
|
if (file == NULL) {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user