Files
didactyl/src/prompt_template.c
T

221 lines
7.2 KiB
C

#define _POSIX_C_SOURCE 200809L
#include "prompt_template.h"
#include "cjson/cJSON.h"
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
static char* dup_range(const char* s, size_t n) {
char* out = (char*)malloc(n + 1U);
if (!out) return NULL;
if (n > 0) memcpy(out, s, n);
out[n] = '\0';
return out;
}
static char* ltrim_inplace(char* s) {
if (!s) return s;
while (*s && isspace((unsigned char)*s)) s++;
return s;
}
static void rtrim_inplace(char* s) {
if (!s) return;
size_t n = strlen(s);
while (n > 0 && isspace((unsigned char)s[n - 1])) {
s[n - 1] = '\0';
n--;
}
}
static int append_text(char** buf, size_t* cap, size_t* used, const char* s) {
if (!buf || !cap || !used || !s) return -1;
size_t n = strlen(s);
if (*used + n + 1U > *cap) {
size_t next = *cap;
while (*used + n + 1U > next) {
next = (next == 0U) ? 256U : (next * 2U);
}
char* grown = (char*)realloc(*buf, next);
if (!grown) return -1;
*buf = grown;
*cap = next;
}
memcpy(*buf + *used, s, n);
*used += n;
(*buf)[*used] = '\0';
return 0;
}
static const char* map_variable_tool_name(const char* var_name) {
if (!var_name || var_name[0] == '\0') return NULL;
if (strcmp(var_name, "admin_profile") == 0) return "nostr_admin_profile";
if (strcmp(var_name, "admin_notes") == 0) return "nostr_admin_notes";
if (strcmp(var_name, "admin_relays") == 0) return "nostr_admin_relays";
if (strcmp(var_name, "adopted_skills") == 0) return "adopted_skills";
if (strcmp(var_name, "triggering_event") == 0) return "trigger_event";
return var_name;
}
char* prompt_template_resolve_inline_variables(const char* tpl, tools_context_t* tools_ctx) {
if (!tpl) return strdup("");
size_t cap = strlen(tpl) + 64U;
size_t used = 0U;
char* out = (char*)malloc(cap);
if (!out) return NULL;
out[0] = '\0';
const char* p = tpl;
while (*p) {
const char* open = strstr(p, "{{");
if (!open) {
if (append_text(&out, &cap, &used, p) != 0) {
free(out);
return NULL;
}
break;
}
if (open > p) {
char* prefix = dup_range(p, (size_t)(open - p));
if (!prefix) {
free(out);
return NULL;
}
int rc = append_text(&out, &cap, &used, prefix);
free(prefix);
if (rc != 0) {
free(out);
return NULL;
}
}
const char* close = strstr(open + 2, "}}");
if (!close) {
if (append_text(&out, &cap, &used, open) != 0) {
free(out);
return NULL;
}
break;
}
char* var = dup_range(open + 2, (size_t)(close - (open + 2)));
if (!var) {
free(out);
return NULL;
}
char* var_trim = ltrim_inplace(var);
rtrim_inplace(var_trim);
const char* replacement = "";
char* replacement_owned = NULL;
char* call_name_owned = NULL;
char* call_args_owned = NULL;
const char* lookup_name = var_trim;
const char* call_args_json = "{}";
size_t var_len = strlen(var_trim);
if (var_len > 0 && var_trim[var_len - 1] == ')') {
char* open_paren = strchr(var_trim, '(');
if (open_paren && open_paren < (var_trim + var_len - 1)) {
call_name_owned = dup_range(var_trim, (size_t)(open_paren - var_trim));
if (!call_name_owned) {
free(var);
free(out);
return NULL;
}
char* call_name_trim = ltrim_inplace(call_name_owned);
rtrim_inplace(call_name_trim);
char* raw_args = dup_range(open_paren + 1,
(size_t)((var_trim + var_len - 1) - (open_paren + 1)));
if (!raw_args) {
free(call_name_owned);
free(var);
free(out);
return NULL;
}
char* raw_args_trim = ltrim_inplace(raw_args);
rtrim_inplace(raw_args_trim);
if (call_name_trim[0] != '\0') {
lookup_name = call_name_trim;
if (raw_args_trim[0] != '\0') {
cJSON* parsed_args = cJSON_Parse(raw_args_trim);
if (parsed_args) {
call_args_owned = strdup(raw_args_trim);
cJSON_Delete(parsed_args);
}
}
}
free(raw_args);
}
}
if (call_args_owned) {
call_args_json = call_args_owned;
}
if (strcmp(lookup_name, "message") == 0) {
replacement = (tools_ctx && tools_ctx->template_current_user_message)
? tools_ctx->template_current_user_message
: "";
} else if (tools_ctx) {
const char* tool_name = map_variable_tool_name(lookup_name);
if (tool_name && tool_name[0] != '\0') {
char* tool_result = tools_execute(tools_ctx, tool_name, call_args_json);
if (tool_result) {
cJSON* root = cJSON_Parse(tool_result);
if (root && cJSON_IsObject(root)) {
cJSON* success = cJSON_GetObjectItemCaseSensitive(root, "success");
if (!success || !cJSON_IsBool(success) || cJSON_IsTrue(success)) {
cJSON* content = cJSON_GetObjectItemCaseSensitive(root, "content");
if (content && cJSON_IsString(content) && content->valuestring) {
replacement_owned = strdup(content->valuestring);
} else if (content) {
replacement_owned = cJSON_PrintUnformatted(content);
}
}
}
cJSON_Delete(root);
free(tool_result);
}
}
if (!replacement_owned && tools_ctx->template_skill_lookup && lookup_name[0] != '\0') {
const char* skill_content = tools_ctx->template_skill_lookup(
tools_ctx->template_skill_lookup_user_data,
lookup_name);
if (skill_content && skill_content[0] != '\0') {
replacement_owned = strdup(skill_content);
}
}
replacement = replacement_owned ? replacement_owned : "";
}
if (append_text(&out, &cap, &used, replacement) != 0) {
free(call_args_owned);
free(call_name_owned);
free(replacement_owned);
free(var);
free(out);
return NULL;
}
free(call_args_owned);
free(call_name_owned);
free(replacement_owned);
free(var);
p = close + 2;
}
return out;
}