Update session.

This commit is contained in:
retoor 2025-12-26 07:24:13 +01:00
parent a8d7015abd
commit 5cf55f423c
6 changed files with 296 additions and 221 deletions

194
browse.h
View File

@ -1,201 +1,23 @@
#include "http_curl.h"
#include <curl/curl.h>
#include <json-c/json.h>
#include <json-c/json_util.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char *url_encode(char *s) { return curl_easy_escape(NULL, s, 0); }
char *web_search_news(char *q) {
const int MAX_RETRIES = 3;
for (int retry = 0; retry < MAX_RETRIES; retry++) {
char *news = malloc(4096);
if (!news)
return NULL;
news[0] = 0;
char *q_encoded = url_encode(q);
snprintf(
news, 4096,
"https://search.molodetz.nl/"
"search?q=%s&format=json&categories=news&preferences=eJx1WMuy4zYO_"
"Zp4o4ormaRqahZepZL5gexVFAlLiEhCzYdt3a8PoIdFWt2Ldl8fkCCIxwForRL0FBDirQc"
"PQdmLVb7PqoebyokulrSycPP2Il81uclCgtvlrh6oybcBItkHhNsFHW9qp0Cv-"
"faXshEuDtJA5vb_P_--RHWHCCro4fbLJQ3g4BZRdF1YQbYptqzLw7NNqtt2G8JDOyn-"
"eqXQX9ZtbUyz3UzU4BOEVlnsveO_t_3KPJTXYNrt3BX9liHMLfo2YWIFK4j-"
"jh4Ta9WBrN3QdZ8YplcvzazKgk63v0OGy0BphDneDNwV3-CSg23vFJxKCX1_"
"mwKkNF8MRtVZtgJ8j57d_L9e9W0bSaOyjQOD6qf__"
"MG366CJ7OCxbRdHxhptHmiA2nb571OmskGWuRxRi8hOfFJj0edXMyk9ijpWm0TmvYqN3As"
"f0LZ3tOtJ09g4DIFCibGfG_"
"6UU0K52CuOvFGbSo8vis0TR9yADi3Kv8LaDlOX9QhpX5IM9v2hcvHDVWt9NVBs0xN78Q4B"
"OI7bTg5QjAyyCzUKztgMU7M6rXCegJxQDMknAwbgi9Pk7SYDMamEvIA4IQMjEOiJ5vhu2G"
"T519NZfSFbzS3MLmS1BQf-BCWHHGet3nZq4uX8KdY4-"
"gcnccyx6tdXYcLdBBJzdydyDps0qOS4usplAThR6J6eKkBjMHAGSy6v_"
"rwH9CMqXW6Y58IJPeDXwPVQIFw5qtvjQQY6CP32lWXAgSS3fycyAZQptxP1fNXJqlmyKx4"
"XKCWOHgilU7muIEjNCO2UkRhUF5R8bEcOnPEQVsevALzeycNK4lWKZfmOznSHYej6HArF6"
"FVxPnr-EynH72N7VnHEAlPJXOj5B-NAxzFcHEGFuRG-"
"jVhc31IXE1zDbjfTp9KT8nudTbtT_"
"azUsY3MHKGsGprAb4ccpwrICoFqKMBEhQkrW2Dc65TLb2XBcwEcoirHp9xxDT-2_"
"QGMwXSiu0ApMVETx5SgSA0mS51jvE4zN4793loZM0tKubw5drEMfx7IwglfbZKsbOTjjTr"
"lE-omat6lQskeicaZEnGMRvH17ook9-TlKr_vlqp0EcaLE-"
"glTQ91JhvwxyouYoDx7L4Nr3y35PyEltKxP-Ru7sHtOTEBhJS7MtyLU3n1yJufT-"
"gK0az0fD46ZMcdqVhmuRdEDn2JPemFI3mOZRNnT34WTtmN4nwbQ237Cp3OWuETQ8ZHL5lX"
"LHwwlS0da7vonJ2zc1U1R2VEykHDkN9pOvbXnva8u4CvWy4PMC_"
"lTRAm3aYcYVzJ3bVJFs1LBU6TsgeHFz7K-Ko4llo6VqyVmw4GEGUbbRfLeCT4_"
"H721oJWbl2Qk_"
"u6qSuVSSHzkBRL1tcD3Efi8eXtQW0pmzsnPygsd3NHfRrJnRLk7lmt4eEG4QORtjyXGPsf"
"uczYhWWjNH3DI5LMV7gQd7GehKmbIe_"
"NRBSymdvgVawUnxqVKpce3bREefaCubIU0uyICdEXCXy3qMeS63k5ljTeCz3tbYKb2mHj2"
"qTKA7a2dYrmhlfx3LBTRDf8O_Q0bOcth49stoplzwDn5mYnwZWKaq5dV-"
"QI4Ucy7gDpRzLRzK44i79-freYty1bY6M7b_Nc4LH0hkghBeWj5SZc-o9D0z-"
"q0DrFI6ch_wOz3-JBxYF5-jsrzN49HL6WxD9C6-hrqArAPZkP6-"
"N5JIJ9npDqUjw1Sf5FSBXZ7zLuepxRasvwWh4TD2CJZ7p9sJuM8PexaOKXhHS7TYrymoFY"
"1tAkhFJky_L9ulzouNeEMt10qojIRMxNkUvCyAi0J_-7c5d3Ptr5npuFbLldY7hfo1_z_"
"HhCHEbmzomVxcXmaafVb08mhlLnAtS1sUKnMlrhU8UExZNX0zF5xXKqD9gPqTGqIoX1Zm6"
"OWGde5G6rdl8czYkyc3qdNPxU0CMxyd4tPfdWEcfcZZ_"
"y3pUyz345vhMnChNO8igujySLhufvsFbV96aNY3H2kYfhOJTPGjVQdbcFqB05U_6YEd7I-"
"_Gj0DItSsIWyx7oqkfmE7v5k4Y7ojF-"
"gtXxAnzLVLt6mZaWtn1C9xmqgsWyaoCVpdLkllGv1vCg-cPLggp1cTleTwlVCk9pVQo_"
"3tWlaInbEb4n2XtQjl_eQ92jUKcv8pVtjkcpxw-w5k2GphT_-ttv_30dmiN8eeWq_"
"cvkWCJePSTpypyoG_OTgvE4FgMcn62reuRul-QBKYo-yPSprB34CP_"
"hQ8c9vTolpXDFYvbldvCorr4A54Cs8HnKWeDP9lnpS7_vPyIcP7RMNvPYF2-"
"k2vUnpGfgsl86aUzsy7XeKLQ8IunxPSzu28QzPJW08nNO4EA91uFJWZ05VhSWsuQ3Dbc4F"
"qVtHrb3Fv2d5BSp11XZhZ8WzP-3fwFOiVFV",
q_encoded);
free(q_encoded);
char *ret = curl_get(news);
free(news);
if (!ret)
continue;
json_object *json_ret = json_tokener_parse(ret);
if (!json_ret) {
free(ret);
continue;
}
json_object *json_results = json_object_object_get(json_ret, "results");
json_object *json_result = json_object_array_get_idx(json_results, 0);
if (json_result) {
json_object_put(json_ret);
return ret;
}
json_object_put(json_ret);
free(ret);
}
return NULL;
}
char *web_search(char *q) {
const int MAX_RETRIES = 3;
for (int retry = 0; retry < MAX_RETRIES; retry++) {
char *news = malloc(4096);
if (!news)
return NULL;
news[0] = 0;
char url[4096];
char *q_encoded = url_encode(q);
snprintf(
news, 4096,
"https://search.molodetz.nl/"
"search?q=%s&format=json&preferences=eJx1WMuy4zYO_"
"Zp4o4ormaRqahZepZL5gexVFAlLiEhCzYdt3a8PoIdFWt2Ldl8fkCCIxwForRL0FBDirQc"
"PQdmLVb7PqoebyokulrSycPP2Il81uclCgtvlrh6oybcBItkHhNsFHW9qp0Cv-"
"faXshEuDtJA5vb_P_--RHWHCCro4fbLJQ3g4BZRdF1YQbYptqzLw7NNqtt2G8JDOyn-"
"eqXQX9ZtbUyz3UzU4BOEVlnsveO_t_3KPJTXYNrt3BX9liHMLfo2YWIFK4j-"
"jh4Ta9WBrN3QdZ8YplcvzazKgk63v0OGy0BphDneDNwV3-CSg23vFJxKCX1_"
"mwKkNF8MRtVZtgJ8j57d_L9e9W0bSaOyjQOD6qf__"
"MG366CJ7OCxbRdHxhptHmiA2nb571OmskGWuRxRi8hOfFJj0edXMyk9ijpWm0TmvYqN3As"
"f0LZ3tOtJ09g4DIFCibGfG_"
"6UU0K52CuOvFGbSo8vis0TR9yADi3Kv8LaDlOX9QhpX5IM9v2hcvHDVWt9NVBs0xN78Q4B"
"OI7bTg5QjAyyCzUKztgMU7M6rXCegJxQDMknAwbgi9Pk7SYDMamEvIA4IQMjEOiJ5vhu2G"
"T519NZfSFbzS3MLmS1BQf-BCWHHGet3nZq4uX8KdY4-"
"gcnccyx6tdXYcLdBBJzdydyDps0qOS4usplAThR6J6eKkBjMHAGSy6v_"
"rwH9CMqXW6Y58IJPeDXwPVQIFw5qtvjQQY6CP32lWXAgSS3fycyAZQptxP1fNXJqlmyKx4"
"XKCWOHgilU7muIEjNCO2UkRhUF5R8bEcOnPEQVsevALzeycNK4lWKZfmOznSHYej6HArF6"
"FVxPnr-EynH72N7VnHEAlPJXOj5B-NAxzFcHEGFuRG-"
"jVhc31IXE1zDbjfTp9KT8nudTbtT_"
"azUsY3MHKGsGprAb4ccpwrICoFqKMBEhQkrW2Dc65TLb2XBcwEcoirHp9xxDT-2_"
"QGMwXSiu0ApMVETx5SgSA0mS51jvE4zN4793loZM0tKubw5drEMfx7IwglfbZKsbOTjjTr"
"lE-omat6lQskeicaZEnGMRvH17ook9-TlKr_vlqp0EcaLE-"
"glTQ91JhvwxyouYoDx7L4Nr3y35PyEltKxP-Ru7sHtOTEBhJS7MtyLU3n1yJufT-"
"gK0az0fD46ZMcdqVhmuRdEDn2JPemFI3mOZRNnT34WTtmN4nwbQ237Cp3OWuETQ8ZHL5lX"
"LHwwlS0da7vonJ2zc1U1R2VEykHDkN9pOvbXnva8u4CvWy4PMC_"
"lTRAm3aYcYVzJ3bVJFs1LBU6TsgeHFz7K-Ko4llo6VqyVmw4GEGUbbRfLeCT4_"
"H721oJWbl2Qk_"
"u6qSuVSSHzkBRL1tcD3Efi8eXtQW0pmzsnPygsd3NHfRrJnRLk7lmt4eEG4QORtjyXGPsf"
"uczYhWWjNH3DI5LMV7gQd7GehKmbIe_"
"NRBSymdvgVawUnxqVKpce3bREefaCubIU0uyICdEXCXy3qMeS63k5ljTeCz3tbYKb2mHj2"
"qTKA7a2dYrmhlfx3LBTRDf8O_Q0bOcth49stoplzwDn5mYnwZWKaq5dV-"
"QI4Ucy7gDpRzLRzK44i79-freYty1bY6M7b_Nc4LH0hkghBeWj5SZc-o9D0z-"
"q0DrFI6ch_wOz3-JBxYF5-jsrzN49HL6WxD9C6-hrqArAPZkP6-"
"N5JIJ9npDqUjw1Sf5FSBXZ7zLuepxRasvwWh4TD2CJZ7p9sJuM8PexaOKXhHS7TYrymoFY"
"1tAkhFJky_L9ulzouNeEMt10qojIRMxNkUvCyAi0J_-7c5d3Ptr5npuFbLldY7hfo1_z_"
"HhCHEbmzomVxcXmaafVb08mhlLnAtS1sUKnMlrhU8UExZNX0zF5xXKqD9gPqTGqIoX1Zm6"
"OWGde5G6rdl8czYkyc3qdNPxU0CMxyd4tPfdWEcfcZZ_"
"y3pUyz345vhMnChNO8igujySLhufvsFbV96aNY3H2kYfhOJTPGjVQdbcFqB05U_6YEd7I-"
"_Gj0DItSsIWyx7oqkfmE7v5k4Y7ojF-"
"gtXxAnzLVLt6mZaWtn1C9xmqgsWyaoCVpdLkllGv1vCg-cPLggp1cTleTwlVCk9pVQo_"
"3tWlaInbEb4n2XtQjl_eQ92jUKcv8pVtjkcpxw-w5k2GphT_-ttv_30dmiN8eeWq_"
"cvkWCJePSTpypyoG_OTgvE4FgMcn62reuRul-QBKYo-yPSprB34CP_"
"hQ8c9vTolpXDFYvbldvCorr4A54Cs8HnKWeDP9lnpS7_vPyIcP7RMNvPYF2-"
"k2vUnpGfgsl86aUzsy7XeKLQ8IunxPSzu28QzPJW08nNO4EA91uFJWZ05VhSWsuQ3Dbc4F"
"qVtHrb3Fv2d5BSp11XZhZ8WzP-3fwFOiVFV",
q_encoded);
if (!q_encoded) {
return NULL;
}
snprintf(url, sizeof(url), "https://static.molodetz.nl/search.cgi?query=%s", q_encoded);
free(q_encoded);
char *ret = curl_get(news);
free(news);
if (!ret)
continue;
json_object *json_ret = json_tokener_parse(ret);
if (!json_ret) {
free(ret);
continue;
}
json_object *json_results = json_object_object_get(json_ret, "results");
json_object *json_result = json_object_array_get_idx(json_results, 0);
if (json_result) {
json_object_put(json_ret);
char *ret = curl_get(url);
return ret;
}
json_object_put(json_ret);
free(ret);
}
return NULL;
}
char *web_search_engine(char *q) {
const int MAX_RETRIES = 3;
for (int retry = 0; retry < MAX_RETRIES; retry++) {
char *searx = malloc(4096);
if (!searx)
return NULL;
searx[0] = 0;
snprintf(searx, 4096, "https://searx.molodetz.nl/search?q=%s&format=json",
q);
char *ret = curl_get(searx);
free(searx);
if (!ret)
continue;
json_object *json_ret = json_tokener_parse(ret);
if (!json_ret) {
free(ret);
continue;
}
json_object *json_results = json_object_object_get(json_ret, "results");
json_object *json_result = json_object_array_get_idx(json_results, 0);
if (json_result) {
json_object_put(json_ret);
return ret;
}
json_object_put(json_ret);
free(ret);
}
return NULL;
char *web_search_news(char *q) {
return web_search(q);
}

View File

@ -33,6 +33,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
struct ResponseBuffer {
char *data;
@ -61,18 +62,36 @@ static size_t WriteCallback(void *contents, size_t size, size_t nmemb,
return total_size;
}
#define HTTP_MAX_RETRIES 3
#define HTTP_RETRY_DELAY_MS 2000
char *curl_post(const char *url, const char *data) {
CURL *curl;
CURLcode res;
struct ResponseBuffer response = {malloc(1), 0};
struct ResponseBuffer response = {NULL, 0};
int retry_count = 0;
if (!response.data)
while (retry_count < HTTP_MAX_RETRIES) {
if (response.data) {
free(response.data);
}
response.data = malloc(1);
response.size = 0;
if (!response.data) {
return NULL;
}
response.data[0] = '\0';
curl = curl_easy_init();
if (curl) {
if (!curl) {
free(response.data);
return NULL;
}
struct curl_slist *headers = NULL;
curl_easy_setopt(curl, CURLOPT_URL, url);
curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 10L);
curl_easy_setopt(curl, CURLOPT_TIMEOUT, 300L);
headers = curl_slist_append(headers, "Content-Type: application/json");
char bearer_header[1337];
snprintf(bearer_header, sizeof(bearer_header), "Authorization: Bearer %s",
@ -82,16 +101,26 @@ char *curl_post(const char *url, const char *data) {
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&response);
res = curl_easy_perform(curl);
if (res != CURLE_OK) {
fprintf(stderr, "Url: %s\n", data);
fprintf(stderr, "Data: %s\n", data);
fprintf(stderr, "An error occurred: %s\n", curl_easy_strerror(res));
}
curl_slist_free_all(headers);
curl_easy_cleanup(curl);
if (res == CURLE_OK) {
return response.data;
}
retry_count++;
fprintf(stderr, "Network error: %s (attempt %d/%d)\n",
curl_easy_strerror(res), retry_count, HTTP_MAX_RETRIES);
if (retry_count < HTTP_MAX_RETRIES) {
fprintf(stderr, "Retrying in %d seconds...\n", HTTP_RETRY_DELAY_MS / 1000);
usleep(HTTP_RETRY_DELAY_MS * 1000);
}
}
fprintf(stderr, "Failed after %d attempts.\n", HTTP_MAX_RETRIES);
free(response.data);
return NULL;
}
@ -99,15 +128,30 @@ char *curl_post(const char *url, const char *data) {
char *curl_get(const char *url) {
CURL *curl;
CURLcode res;
struct ResponseBuffer response = {malloc(1), 0};
struct ResponseBuffer response = {NULL, 0};
int retry_count = 0;
if (!response.data)
while (retry_count < HTTP_MAX_RETRIES) {
if (response.data) {
free(response.data);
}
response.data = malloc(1);
response.size = 0;
if (!response.data) {
return NULL;
}
response.data[0] = '\0';
curl = curl_easy_init();
if (curl) {
if (!curl) {
free(response.data);
return NULL;
}
struct curl_slist *headers = NULL;
curl_easy_setopt(curl, CURLOPT_URL, url);
curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 10L);
curl_easy_setopt(curl, CURLOPT_TIMEOUT, 60L);
headers = curl_slist_append(headers, "Content-Type: application/json");
char bearer_header[1337];
snprintf(bearer_header, sizeof(bearer_header), "Authorization: Bearer %s",
@ -116,17 +160,28 @@ char *curl_get(const char *url) {
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&response);
res = curl_easy_perform(curl);
if (res != CURLE_OK) {
fprintf(stderr, "An error occurred: %s\n", curl_easy_strerror(res));
}
curl_slist_free_all(headers);
curl_easy_cleanup(curl);
} else {
if (res == CURLE_OK) {
return response.data;
}
retry_count++;
fprintf(stderr, "Network error: %s (attempt %d/%d)\n",
curl_easy_strerror(res), retry_count, HTTP_MAX_RETRIES);
if (retry_count < HTTP_MAX_RETRIES) {
fprintf(stderr, "Retrying in %d seconds...\n", HTTP_RETRY_DELAY_MS / 1000);
usleep(HTTP_RETRY_DELAY_MS * 1000);
}
}
fprintf(stderr, "Failed after %d attempts.\n", HTTP_MAX_RETRIES);
free(response.data);
return NULL;
}
return response.data;
}
#endif

73
main.c
View File

@ -114,6 +114,11 @@ static char *get_prompt_from_args(int argc, char **argv) {
} else if (strcmp(argv[i], "--nh") == 0) {
SYNTAX_HIGHLIGHT_ENABLED = false;
fprintf(stderr, "Syntax highlighting disabled.\n");
} else if (strncmp(argv[i], "--session=", 10) == 0) {
continue;
} else if (strcmp(argv[i], "-s") == 0 || strcmp(argv[i], "--session") == 0) {
i++;
continue;
} else {
strcat(system, argv[i]);
strcat(system, (i < argc - 1) ? " " : ".");
@ -184,6 +189,17 @@ static void repl(void) {
printf("%s\n", message_json());
continue;
}
if (!strncmp(line, "!clear", 6)) {
messages_remove();
fprintf(stderr, "Session cleared.\n");
continue;
}
if (!strncmp(line, "!session", 8)) {
init_session_id();
printf("Session: %s\n", get_session_id());
printf("DB Key: %s\n", get_session_db_key());
continue;
}
if (!strncmp(line, "!verbose", 8)) {
is_verbose = !is_verbose;
fprintf(stderr, "%s\n",
@ -239,9 +255,15 @@ static void init(void) {
char *schema = db_get_schema();
char payload[1024 * 1024] = {0};
time_t now = time(NULL);
struct tm *tm_info = localtime(&now);
char datetime[64];
strftime(datetime, sizeof(datetime), "%Y-%m-%d %H:%M:%S %Z", tm_info);
snprintf(
payload, sizeof(payload),
"# AUTONOMOUS AGENT INSTRUCTIONS\n"
"Current date/time: %s\n\n"
"You are an autonomous AI agent. You operate in a loop: reason about the task, "
"select and execute tools when needed, observe results, and continue until the goal is achieved.\n\n"
"## Reasoning Pattern (ReAct)\n"
@ -253,16 +275,24 @@ static void init(void) {
"## 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"
"- You can call multiple tools in sequence to accomplish complex tasks\n\n"
"## CRITICAL OUTPUT RULES\n"
"- You MUST include the actual content/data from tool results in your response\n"
"- When you search the web, QUOTE the relevant information found\n"
"- When you run a command, SHOW the output\n"
"- NEVER say 'I found information' without showing what you found\n"
"- NEVER say 'task complete' or 'report provided' - SHOW THE ACTUAL DATA\n"
"- The user cannot see tool results - only YOUR response. Include everything relevant.\n\n"
"## Local Database\n"
"You have a local SQLite database accessible via db_query, db_get, and db_set tools.\n"
"Use stemmed, lowercase keys to prevent duplicates.\n"
"Schema: %s\n\n"
"## 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);
"## Response Format\n"
"Your response IS the only thing the user sees. Tool outputs are hidden from them.\n"
"You MUST copy/paste relevant data from tool results into your response.\n"
"Bad: 'I searched and found information about X.'\n"
"Good: 'Here is what I found: [actual content from search results]'\n",
datetime, schema);
free(schema);
fprintf(stderr, "Loading...");
@ -297,15 +327,46 @@ static void handle_sigint(int sig) {
}
}
static void parse_session_arg(int argc, char *argv[]) {
for (int i = 1; i < argc; i++) {
if (strncmp(argv[i], "--session=", 10) == 0) {
const char *name = argv[i] + 10;
if (!set_session_id(name)) {
fprintf(stderr, "Error: Invalid session name '%s'\n", name);
exit(1);
}
return;
}
if ((strcmp(argv[i], "-s") == 0 || strcmp(argv[i], "--session") == 0) && i + 1 < argc) {
const char *name = argv[++i];
if (!set_session_id(name)) {
fprintf(stderr, "Error: Invalid session name '%s'\n", name);
exit(1);
}
return;
}
}
char *env_session = get_env_session();
if (env_session) {
if (!set_session_id(env_session)) {
fprintf(stderr, "Warning: Invalid R_SESSION '%s', using default\n", env_session);
}
}
}
int main(int argc, char *argv[]) {
signal(SIGINT, handle_sigint);
parse_session_arg(argc, argv);
init();
char *env_string = get_env_string();
if (env_string && *env_string) {
openai_system(env_string);
free(env_string);
}
messages_load_conversation();
if (try_prompt(argc, argv))
return 0;

View File

@ -28,8 +28,129 @@
#include "db_utils.h"
#include "json-c/json.h"
#include "tools.h"
#include "utils.h"
#include <string.h>
#include <unistd.h>
struct json_object *message_array = NULL;
static char session_id[256] = {0};
static char override_session_id[256] = {0};
struct json_object *message_list();
bool set_session_id(const char *name) {
if (name == NULL || *name == '\0') {
return false;
}
size_t len = strlen(name);
if (len > 200) {
return false;
}
for (const char *p = name; *p; p++) {
if (*p == '/' || *p == '\\') {
return false;
}
}
strncpy(override_session_id, name, sizeof(override_session_id) - 1);
override_session_id[sizeof(override_session_id) - 1] = '\0';
return true;
}
const char *get_session_id() {
if (session_id[0] == '\0') {
if (override_session_id[0] != '\0') {
strncpy(session_id, override_session_id, sizeof(session_id) - 1);
session_id[sizeof(session_id) - 1] = '\0';
}
}
return session_id;
}
void init_session_id() {
if (session_id[0] != '\0') {
return;
}
if (override_session_id[0] != '\0') {
strncpy(session_id, override_session_id, sizeof(session_id) - 1);
session_id[sizeof(session_id) - 1] = '\0';
return;
}
char *tty = ttyname(STDIN_FILENO);
if (tty) {
unsigned long h = 5381;
for (char *p = tty; *p; p++) {
h = ((h << 5) + h) + (unsigned char)*p;
}
snprintf(session_id, sizeof(session_id), "%lu", h);
} else {
snprintf(session_id, sizeof(session_id), "%d", getppid());
}
}
char *get_session_db_key() {
static char key[512];
init_session_id();
snprintf(key, sizeof(key), "session:%s", session_id);
return key;
}
bool messages_save() {
char *key = get_session_db_key();
const char *json_str = json_object_to_json_string_ext(message_list(), JSON_C_TO_STRING_PLAIN);
if (!json_str) {
return false;
}
json_object *result = db_set(key, json_str);
if (result) {
json_object_put(result);
return true;
}
return false;
}
bool messages_load_conversation() {
char *key = get_session_db_key();
json_object *result = db_get(key);
if (!result) {
return false;
}
json_object *value_obj;
if (!json_object_object_get_ex(result, "value", &value_obj)) {
json_object_put(result);
return false;
}
const char *json_str = json_object_get_string(value_obj);
if (!json_str) {
json_object_put(result);
return false;
}
struct json_object *loaded = json_tokener_parse(json_str);
json_object_put(result);
if (!loaded || !json_object_is_type(loaded, json_type_array)) {
if (loaded) {
json_object_put(loaded);
}
return false;
}
int len = json_object_array_length(loaded);
int added = 0;
for (int i = 0; i < len; i++) {
struct json_object *msg = json_object_array_get_idx(loaded, i);
struct json_object *role_obj;
if (json_object_object_get_ex(msg, "role", &role_obj)) {
const char *role = json_object_get_string(role_obj);
if (role && strcmp(role, "system") != 0) {
json_object_array_add(message_list(), json_object_get(msg));
added++;
}
} else {
json_object_array_add(message_list(), json_object_get(msg));
added++;
}
}
json_object_put(loaded);
return added > 0;
}
struct json_object *message_list() {
if (!message_array) {
@ -42,19 +163,25 @@ bool messages_remove_last() {
int size = json_object_array_length(messages);
if (size) {
json_object_array_del_idx(messages, size - 1, 1);
messages_save();
return true;
}
return false;
}
void messages_remove() {
while (messages_remove_last())
continue;
if (message_array) {
json_object_put(message_array);
message_array = NULL;
}
message_list();
messages_save();
}
struct json_object *message_add_tool_call(struct json_object *message) {
struct json_object *messages = message_list();
json_object_array_add(messages, message);
messages_save();
return message;
}
@ -77,12 +204,14 @@ struct json_object *message_add_tool_result(const char *tool_call_id,
json_object_new_string_len(tool_result, (int)result_len));
json_object_array_add(messages, message);
messages_save();
return message;
}
void message_add_object(json_object *message) {
struct json_object *messages = message_list();
json_object_array_add(messages, message);
messages_save();
}
struct json_object *message_add(const char *role, const char *content);
@ -110,6 +239,9 @@ struct json_object *message_add(const char *role, const char *content) {
}
json_object_array_add(messages, message);
if (strcmp(role, "system") != 0) {
messages_save();
}
return message;
}

5
r.h
View File

@ -58,6 +58,11 @@ char *get_env_system_message() {
return NULL;
}
char *get_env_session() {
char *session = getenv("R_SESSION");
return (session && *session) ? session : NULL;
}
bool get_use_strict() {
if (getenv("R_USE_STRICT") != NULL) {
const char *value = getenv("R_USE_STRICT");

View File

@ -232,9 +232,9 @@ char *tool_function_web_search(char *query) {
return strdup("Query cannot be NULL.");
}
char *result = web_search_news(query);
char *result = web_search(query);
if (result == NULL) {
return strdup("Failed to fetch news.");
return strdup("Failed to search.");
}
return result;