// retoor <retoor@molodetz.nl>
#include "r_config.h"
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct r_config_t {
char *api_url;
char *models_url;
char *model;
char *api_key;
char *db_path;
char *session_id;
char *system_message;
double temperature;
int max_tokens;
bool use_tools;
bool use_strict;
bool verbose;
};
static struct r_config_t *instance = NULL;
static char *strdup_safe(const char *s) {
return s ? strdup(s) : NULL;
}
static bool resolve_env_bool(const char *env_name, bool default_val) {
const char *val = getenv(env_name);
if (!val) return default_val;
if (!strcmp(val, "true") || !strcmp(val, "1")) return true;
if (!strcmp(val, "false") || !strcmp(val, "0")) return false;
return default_val;
}
static const char *resolve_api_key(void) {
const char * key = getenv("R_KEY");
if (key && *key) return key;
key = getenv("OPENROUTER_API_KEY");
if (key && *key) return key;
key = getenv("OPENAI_API_KEY");
if (key && *key) return key;
return "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";
}
static bool is_valid_session_id(const char *session_id) {
if (!session_id || !*session_id) return false;
if (strlen(session_id) > 255) return false;
for (const char *p = session_id; *p; p++) {
if (!isalnum((unsigned char)*p) && *p != '-' && *p != '_' && *p != '.') {
return false;
}
}
return true;
}
r_config_handle r_config_get_instance(void) {
if (instance) return instance;
instance = calloc(1, sizeof(struct r_config_t));
if (!instance) return NULL;
const char *base_url = getenv("R_BASE_URL");
if (base_url && *base_url) {
size_t len = strlen(base_url);
instance->api_url = malloc(len + 32);
instance->models_url = malloc(len + 32);
if (instance->api_url && instance->models_url) {
snprintf(instance->api_url, len + 32, "%s/v1/chat/completions", base_url);
snprintf(instance->models_url, len + 32, "%s/v1/models", base_url);
}
} else {
instance->api_url = strdup("https://api.openai.com/v1/chat/completions");
instance->models_url = strdup("https://api.openai.com/v1/models");
}
const char *model = getenv("R_MODEL");
instance->model = strdup(model && *model ? model : "gpt-4o-mini");
instance->api_key = strdup(resolve_api_key());
instance->db_path = strdup("~/.r.db");
instance->temperature = 0.1;
const char *max_tokens_env = getenv("R_MAX_TOKENS");
instance->max_tokens = max_tokens_env ? atoi(max_tokens_env) : 4096;
instance->use_tools = resolve_env_bool("R_USE_TOOLS", true);
instance->use_strict = resolve_env_bool("R_USE_STRICT", true);
instance->verbose = false;
const char *session = getenv("R_SESSION");
if (session && is_valid_session_id(session)) {
instance->session_id = strdup(session);
} else {
instance->session_id = NULL;
}
instance->system_message = strdup_safe(getenv("R_SYSTEM_MESSAGE"));
return instance;
}
void r_config_destroy(void) {
if (!instance) return;
free(instance->api_url);
free(instance->models_url);
free(instance->model);
free(instance->api_key);
free(instance->db_path);
free(instance->session_id);
free(instance->system_message);
free(instance);
instance = NULL;
}
const char *r_config_get_api_url(r_config_handle cfg) {
return cfg ? cfg->api_url : NULL;
}
const char *r_config_get_models_url(r_config_handle cfg) {
return cfg ? cfg->models_url : NULL;
}
const char *r_config_get_model(r_config_handle cfg) {
return cfg ? cfg->model : NULL;
}
void r_config_set_model(r_config_handle cfg, const char *model) {
if (!cfg || !model) return;
free(cfg->model);
cfg->model = strdup(model);
}
const char *r_config_get_api_key(r_config_handle cfg) {
return cfg ? cfg->api_key : NULL;
}
const char *r_config_get_db_path(r_config_handle cfg) {
return cfg ? cfg->db_path : NULL;
}
bool r_config_use_tools(r_config_handle cfg) {
return cfg ? cfg->use_tools : true;
}
bool r_config_use_strict(r_config_handle cfg) {
return cfg ? cfg->use_strict : true;
}
bool r_config_is_verbose(r_config_handle cfg) {
return cfg ? cfg->verbose : false;
}
void r_config_set_verbose(r_config_handle cfg, bool verbose) {
if (cfg) cfg->verbose = verbose;
}
double r_config_get_temperature(r_config_handle cfg) {
return cfg ? cfg->temperature : 0.1;
}
int r_config_get_max_tokens(r_config_handle cfg) {
return cfg ? cfg->max_tokens : 4096;
}
const char *r_config_get_session_id(r_config_handle cfg) {
return cfg ? cfg->session_id : NULL;
}
bool r_config_set_session_id(r_config_handle cfg, const char *session_id) {
if (!cfg || !is_valid_session_id(session_id)) return false;
free(cfg->session_id);
cfg->session_id = strdup(session_id);
return cfg->session_id != NULL;
}
const char *r_config_get_system_message(r_config_handle cfg) {
return cfg ? cfg->system_message : NULL;
}