221 lines
7.2 KiB
C
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;
|
|
}
|