v0.0.48 - Remove duplicated tools context, drop legacy variable resolver paths, and update template docs for tool-driven assembly
This commit is contained in:
@@ -53,11 +53,11 @@ Skills support context modes (`inject`, `full`, `override`) and per-skill LLM fa
|
||||
|
||||
Didactyl will support local inference, which is very privacy preserving. Remote inference does however have it's advantages, and in those cases Didactyl supports using Bitcoin Lightning and eCash inference providers.
|
||||
|
||||
## Current Status — v0.0.47
|
||||
## Current Status — v0.0.48
|
||||
|
||||
**Active build — this project is barely working. Experiment at your own risk.**
|
||||
|
||||
> Last release update: v0.0.47 — Implement Phase 1 tool-driven soul template context assembly and context_* tools
|
||||
> Last release update: v0.0.48 — Remove duplicated tools context, drop legacy variable resolver paths, and update template docs for tool-driven assembly
|
||||
|
||||
- Connects to configured relays with auto-reconnect and relay state transition logging
|
||||
- Publishes configured startup events per relay as each relay becomes connected
|
||||
@@ -314,9 +314,9 @@ Didactyl builds tier-aware context:
|
||||
|
||||
- **ADMIN** request context — assembled from the soul's `---template---` section (if present), otherwise hardcoded order:
|
||||
1. Soul personality (everything above `---template---` in kind `31120`)
|
||||
2. Named template sections in order — e.g. `admin_profile`, `admin_notes`, `admin_relays`, `adopted_skills`, `dm_history`, `message`, `triggering_event`, `tools`
|
||||
3. Each section resolves `{{variable}}` placeholders from live data at call time
|
||||
4. Provider-specific content overrides per section (e.g. XML tags for Anthropic)
|
||||
2. Named template sections in order using `tool:` directives (for example `context_admin_profile`, `context_admin_notes`, `context_tasks`, `context_user_message`, `dm_history`)
|
||||
3. Each section executes its configured context tool, optionally extracting `result_field` (default: `content`)
|
||||
4. Provider-specific content overrides per section remain supported for literal `content:` sections
|
||||
5. Section names are used in `context.log` headers and `/api/context/parts` response
|
||||
- **WoT** request context: Soul + WoT chat-only instruction + current user message (no tools)
|
||||
- **STRANGER**: no LLM call when configured to reply statically
|
||||
|
||||
+21
-36
@@ -3,55 +3,40 @@
|
||||
```yaml
|
||||
- section: admin_identity
|
||||
role: system
|
||||
content: |
|
||||
## Administrator Identity (source: config.admin.pubkey)
|
||||
|
||||
This is your administrator! Admin pubkey (hex): {{admin_pubkey}}
|
||||
tool: context_admin_identity
|
||||
skip_if_empty: true
|
||||
|
||||
- section: admin_profile
|
||||
role: system
|
||||
content: |
|
||||
## Administrator Kind 0 Profile (source: nostr kind 0)
|
||||
tool: context_admin_profile
|
||||
skip_if_empty: true
|
||||
|
||||
Administrator kind 0 profile content (JSON): {{admin_kind0_json}}
|
||||
provider:
|
||||
anthropic: |
|
||||
<admin_kind0_profile source="nostr_kind_0">
|
||||
{{admin_kind0_json}}
|
||||
</admin_kind0_profile>
|
||||
|
||||
- section: admin_relay_list
|
||||
- section: admin_contacts
|
||||
role: system
|
||||
content: |
|
||||
## Administrator Relay List (source: nostr kind 10002)
|
||||
tool: context_admin_contacts
|
||||
skip_if_empty: true
|
||||
|
||||
Administrator kind 10002 relay-list content (JSON): {{admin_kind10002_json}}
|
||||
|
||||
- section: startup_events
|
||||
- section: admin_relays
|
||||
role: system
|
||||
content: |
|
||||
## Startup Events Memory (source: config.startup_events)
|
||||
tool: context_admin_relays
|
||||
skip_if_empty: true
|
||||
|
||||
Startup events memory (kinds/content/tags): {{startup_events_json}}
|
||||
|
||||
- section: adopted_skills
|
||||
- section: admin_notes
|
||||
role: system
|
||||
content: |
|
||||
{{adopted_skills_content}}
|
||||
tool: context_admin_notes
|
||||
skip_if_empty: true
|
||||
|
||||
- section: agent_tasks
|
||||
- section: tasks
|
||||
role: system
|
||||
content: |
|
||||
{{tasks_content}}
|
||||
tool: context_tasks
|
||||
skip_if_empty: true
|
||||
|
||||
- section: conversation
|
||||
role: user
|
||||
tool: context_user_message
|
||||
skip_if_empty: true
|
||||
|
||||
- section: dm_history
|
||||
role: expand
|
||||
limit: 12
|
||||
|
||||
- section: admin_notes
|
||||
role: system
|
||||
content: |
|
||||
## Administrator Recent Notes (source: nostr kind 1)
|
||||
|
||||
{{admin_notes_content}}
|
||||
```
|
||||
|
||||
+1
-2
@@ -121,7 +121,7 @@ The skill replaces the soul's system prompt but keeps the standard context assem
|
||||
|
||||
### Template Variables
|
||||
|
||||
A skill template can reference these context parts:
|
||||
Skill templates may still use placeholders (for example `{{message}}` or `{{triggering_event}}`) for deterministic triggered-skill interpolation, but the soul runtime context is now assembled primarily through template `tool:` directives rather than a separate `{{tools}}`/variable-resolver layer.
|
||||
|
||||
| Variable | Source |
|
||||
|----------|--------|
|
||||
@@ -133,7 +133,6 @@ A skill template can reference these context parts:
|
||||
| `{{dm_history}}` | Recent DM conversation |
|
||||
| `{{message}}` | Current user message |
|
||||
| `{{triggering_event}}` | For triggered skills, the event JSON |
|
||||
| `{{tools}}` | Available tool schemas |
|
||||
|
||||
---
|
||||
|
||||
|
||||
+11
-16
@@ -106,22 +106,16 @@ Simple YAML-like format parsed in C. Each section has:
|
||||
|---|---|---|
|
||||
| `section` | yes | Section name for logging and API identification |
|
||||
| `role` | yes | Chat message role: `system`, `user`, `assistant`, or `expand` |
|
||||
| `content` | no | Content template with `{{variable}}` placeholders |
|
||||
| `limit` | no | Integer limit for variable-length sections like DM history or notes |
|
||||
| `tool` | recommended | Tool name executed by template builder (for example `context_admin_profile`) |
|
||||
| `args` | no | JSON args string passed to the tool (default `{}`) |
|
||||
| `result_field` | no | Field extracted from tool JSON result (default `content`) |
|
||||
| `content` | optional fallback | Literal content when no `tool` is specified |
|
||||
| `limit` | no | Integer limit for variable-length sections like DM history |
|
||||
| `provider` | no | Provider-specific override — see below |
|
||||
|
||||
### Variable Placeholders
|
||||
### Tool-Driven Context Resolution
|
||||
|
||||
| Variable | Source | Description |
|
||||
|---|---|---|
|
||||
| `{{admin_pubkey}}` | `config.admin.pubkey` | Admin hex pubkey |
|
||||
| `{{admin_kind0_json}}` | `nostr_handler_get_admin_kind0_context()` | Admin profile JSON |
|
||||
| `{{admin_kind10002_json}}` | `nostr_handler_get_admin_kind10002_context()` | Admin relay list JSON |
|
||||
| `{{startup_events_json}}` | Serialized startup events | Startup events array |
|
||||
| `{{adopted_skills_content}}` | Adoption list cache | Formatted skill instructions |
|
||||
| `{{admin_notes_content}}` | `nostr_handler_get_admin_kind1_notes_context()` | Recent kind 1 notes |
|
||||
| `{{agent_pubkey}}` | `config.keys.public_key_hex` | Agent own pubkey |
|
||||
| `{{agent_npub}}` | Derived from pubkey | Agent npub |
|
||||
Context sections should use `tool:` directives as the single runtime data path, which removes the legacy per-variable resolver duplication.
|
||||
|
||||
### Special Section Types
|
||||
|
||||
@@ -165,10 +159,11 @@ flowchart TD
|
||||
DM[Incoming DM] --> BUILD[Build context from template]
|
||||
BUILD --> EMIT_SOUL[Emit personality as first system message]
|
||||
EMIT_SOUL --> FOREACH[For each template section]
|
||||
FOREACH --> RESOLVE[Resolve {{variables}} from live data]
|
||||
RESOLVE --> CHECK_PROVIDER{Provider override?}
|
||||
FOREACH --> CALL_TOOL[Execute section tool or use literal content]
|
||||
CALL_TOOL --> PICK_FIELD[Extract result_field or content]
|
||||
PICK_FIELD --> CHECK_PROVIDER{Provider override?}
|
||||
CHECK_PROVIDER -->|Yes| USE_OVERRIDE[Use provider-specific content]
|
||||
CHECK_PROVIDER -->|No| USE_DEFAULT[Use default content]
|
||||
CHECK_PROVIDER -->|No| USE_DEFAULT[Use tool/literal output]
|
||||
USE_OVERRIDE --> EMIT[Emit as chat message]
|
||||
USE_DEFAULT --> EMIT
|
||||
EMIT --> FOREACH
|
||||
|
||||
-285
@@ -62,11 +62,6 @@ static int g_adopted_skills_count = 0;
|
||||
static time_t g_adopted_skills_last_refresh_at = 0;
|
||||
static pthread_mutex_t g_adopted_skills_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
typedef struct {
|
||||
didactyl_sender_tier_t sender_tier;
|
||||
const char* current_user_message;
|
||||
} agent_template_resolver_ctx_t;
|
||||
|
||||
static uint64_t fnv1a64(const char* s) {
|
||||
uint64_t h = 1469598103934665603ULL;
|
||||
if (!s) return h;
|
||||
@@ -1986,280 +1981,6 @@ static cJSON* build_recent_admin_dm_history_messages(const char* current_message
|
||||
return messages;
|
||||
}
|
||||
|
||||
static char* build_startup_events_json_string(void) {
|
||||
if (!g_cfg || g_cfg->startup_event_count <= 0 || !g_cfg->startup_events) {
|
||||
return strdup("[]");
|
||||
}
|
||||
|
||||
cJSON* arr = cJSON_CreateArray();
|
||||
if (!arr) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (int i = 0; i < g_cfg->startup_event_count; i++) {
|
||||
startup_event_t* se = &g_cfg->startup_events[i];
|
||||
if (!se || se->kind == 31120) {
|
||||
continue;
|
||||
}
|
||||
|
||||
cJSON* item = cJSON_CreateObject();
|
||||
if (!item) {
|
||||
cJSON_Delete(arr);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
cJSON_AddNumberToObject(item, "kind", se->kind);
|
||||
cJSON_AddStringToObject(item, "content", se->content ? se->content : "");
|
||||
|
||||
cJSON* tags = NULL;
|
||||
if (se->tags_json) {
|
||||
tags = cJSON_Parse(se->tags_json);
|
||||
}
|
||||
if (!tags || !cJSON_IsArray(tags)) {
|
||||
cJSON_Delete(tags);
|
||||
tags = cJSON_CreateArray();
|
||||
}
|
||||
cJSON_AddItemToObject(item, "tags", tags);
|
||||
cJSON_AddItemToArray(arr, item);
|
||||
}
|
||||
|
||||
char* out = cJSON_PrintUnformatted(arr);
|
||||
cJSON_Delete(arr);
|
||||
return out;
|
||||
}
|
||||
|
||||
static char* build_adopted_skills_payload_string(void) {
|
||||
cJSON* tmp = cJSON_CreateArray();
|
||||
if (!tmp) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (append_adopted_skills_context(tmp) != 0) {
|
||||
cJSON_Delete(tmp);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
cJSON* first = cJSON_GetArrayItem(tmp, 0);
|
||||
cJSON* content = first ? cJSON_GetObjectItemCaseSensitive(first, "content") : NULL;
|
||||
char* out = (content && cJSON_IsString(content) && content->valuestring)
|
||||
? strdup(content->valuestring)
|
||||
: strdup("");
|
||||
cJSON_Delete(tmp);
|
||||
return out;
|
||||
}
|
||||
|
||||
static char* build_tool_schemas_json_string(void) {
|
||||
char* schema_json = tools_build_openai_schema_json(&g_tools_ctx);
|
||||
return schema_json ? schema_json : strdup("[]");
|
||||
}
|
||||
|
||||
static char* build_tasks_content_string(void) {
|
||||
const char* fallback =
|
||||
"### Current Tasks\n\n"
|
||||
"Your active task list - short-term working memory for tracking plan steps.\n\n"
|
||||
"- [ ] No active tasks yet.\n";
|
||||
|
||||
if (!g_cfg) {
|
||||
return strdup(fallback);
|
||||
}
|
||||
|
||||
const char* cwd = g_cfg->tools.shell.working_directory[0] != '\0'
|
||||
? g_cfg->tools.shell.working_directory
|
||||
: ".";
|
||||
|
||||
char tasks_path[1024];
|
||||
int pn = 0;
|
||||
if (strcmp(cwd, ".") == 0) {
|
||||
pn = snprintf(tasks_path, sizeof(tasks_path), "tasks.json");
|
||||
} else {
|
||||
pn = snprintf(tasks_path, sizeof(tasks_path), "%s/%s", cwd, "tasks.json");
|
||||
}
|
||||
if (pn <= 0 || (size_t)pn >= sizeof(tasks_path)) {
|
||||
return strdup(fallback);
|
||||
}
|
||||
|
||||
FILE* fp = fopen(tasks_path, "rb");
|
||||
if (!fp) {
|
||||
return strdup(fallback);
|
||||
}
|
||||
|
||||
if (fseek(fp, 0, SEEK_END) != 0) {
|
||||
fclose(fp);
|
||||
return strdup(fallback);
|
||||
}
|
||||
|
||||
long len = ftell(fp);
|
||||
if (len < 0) {
|
||||
fclose(fp);
|
||||
return strdup(fallback);
|
||||
}
|
||||
|
||||
if (fseek(fp, 0, SEEK_SET) != 0) {
|
||||
fclose(fp);
|
||||
return strdup(fallback);
|
||||
}
|
||||
|
||||
char* raw = (char*)malloc((size_t)len + 1U);
|
||||
if (!raw) {
|
||||
fclose(fp);
|
||||
return strdup(fallback);
|
||||
}
|
||||
|
||||
size_t n = fread(raw, 1, (size_t)len, fp);
|
||||
fclose(fp);
|
||||
if (n != (size_t)len) {
|
||||
free(raw);
|
||||
return strdup(fallback);
|
||||
}
|
||||
raw[len] = '\0';
|
||||
|
||||
cJSON* root = cJSON_Parse(raw);
|
||||
free(raw);
|
||||
if (!root || !cJSON_IsObject(root)) {
|
||||
cJSON_Delete(root);
|
||||
return strdup(fallback);
|
||||
}
|
||||
|
||||
cJSON* tasks = cJSON_GetObjectItemCaseSensitive(root, "tasks");
|
||||
if (!tasks || !cJSON_IsArray(tasks)) {
|
||||
cJSON_Delete(root);
|
||||
return strdup(fallback);
|
||||
}
|
||||
|
||||
size_t cap = 1024U;
|
||||
size_t used = 0U;
|
||||
char* out = (char*)malloc(cap);
|
||||
if (!out) {
|
||||
cJSON_Delete(root);
|
||||
return strdup(fallback);
|
||||
}
|
||||
out[0] = '\0';
|
||||
|
||||
if (appendf(&out,
|
||||
&cap,
|
||||
&used,
|
||||
"### Current Tasks\n\n"
|
||||
"Your active task list - short-term working memory for tracking plan steps.\n\n") != 0) {
|
||||
free(out);
|
||||
cJSON_Delete(root);
|
||||
return strdup(fallback);
|
||||
}
|
||||
|
||||
int written = 0;
|
||||
int tcount = cJSON_GetArraySize(tasks);
|
||||
for (int i = 0; i < tcount; i++) {
|
||||
cJSON* task = cJSON_GetArrayItem(tasks, i);
|
||||
if (!task || !cJSON_IsObject(task)) continue;
|
||||
|
||||
cJSON* id = cJSON_GetObjectItemCaseSensitive(task, "id");
|
||||
cJSON* text = cJSON_GetObjectItemCaseSensitive(task, "text");
|
||||
cJSON* status = cJSON_GetObjectItemCaseSensitive(task, "status");
|
||||
|
||||
if (!text || !cJSON_IsString(text) || !text->valuestring || text->valuestring[0] == '\0') {
|
||||
continue;
|
||||
}
|
||||
|
||||
const char* status_s = (status && cJSON_IsString(status) && status->valuestring)
|
||||
? status->valuestring
|
||||
: "pending";
|
||||
const char* mark = " ";
|
||||
if (strcmp(status_s, "done") == 0) {
|
||||
mark = "x";
|
||||
} else if (strcmp(status_s, "active") == 0) {
|
||||
mark = "-";
|
||||
}
|
||||
|
||||
int id_value = (id && cJSON_IsNumber(id)) ? (int)id->valuedouble : (i + 1);
|
||||
if (appendf(&out, &cap, &used, "- [%s] %d. %s\n", mark, id_value, text->valuestring) != 0) {
|
||||
free(out);
|
||||
cJSON_Delete(root);
|
||||
return strdup(fallback);
|
||||
}
|
||||
written++;
|
||||
}
|
||||
|
||||
cJSON_Delete(root);
|
||||
|
||||
if (written <= 0) {
|
||||
free(out);
|
||||
return strdup(fallback);
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
static char* safe_strdup(const char* s) {
|
||||
return strdup(s ? s : "");
|
||||
}
|
||||
|
||||
static char* agent_template_resolve_var(const char* var_name, void* user_data) {
|
||||
agent_template_resolver_ctx_t* ctx = (agent_template_resolver_ctx_t*)user_data;
|
||||
if (!var_name || !g_cfg) {
|
||||
return strdup("");
|
||||
}
|
||||
|
||||
if (strcmp(var_name, "admin_pubkey") == 0) {
|
||||
return safe_strdup(g_cfg->admin.pubkey);
|
||||
}
|
||||
if (strcmp(var_name, "agent_pubkey") == 0) {
|
||||
return safe_strdup(g_cfg->keys.public_key_hex);
|
||||
}
|
||||
if (strcmp(var_name, "admin_kind0_json") == 0) {
|
||||
char* v = nostr_handler_get_admin_kind0_context();
|
||||
return v ? v : strdup("");
|
||||
}
|
||||
if (strcmp(var_name, "admin_kind3_json") == 0) {
|
||||
char* v = nostr_handler_get_admin_kind3_context();
|
||||
return v ? v : strdup("[]");
|
||||
}
|
||||
if (strcmp(var_name, "admin_kind10002_json") == 0) {
|
||||
char* v = nostr_handler_get_admin_kind10002_context();
|
||||
return v ? v : strdup("");
|
||||
}
|
||||
if (strcmp(var_name, "admin_profile_plain") == 0) {
|
||||
char* v = build_admin_profile_plain_text();
|
||||
return v ? v : strdup("");
|
||||
}
|
||||
if (strcmp(var_name, "admin_relay_list_plain") == 0) {
|
||||
char* v = build_admin_relay_list_plain_text();
|
||||
return v ? v : strdup("");
|
||||
}
|
||||
if (strcmp(var_name, "sender_verification") == 0) {
|
||||
didactyl_sender_tier_t tier = ctx ? ctx->sender_tier : DIDACTYL_SENDER_ADMIN;
|
||||
char* v = build_sender_verification_text(tier);
|
||||
return v ? v : strdup("");
|
||||
}
|
||||
if (strcmp(var_name, "startup_events_json") == 0) {
|
||||
char* v = build_startup_events_json_string();
|
||||
return v ? v : strdup("[]");
|
||||
}
|
||||
if (strcmp(var_name, "adopted_skills_content") == 0) {
|
||||
char* v = build_adopted_skills_payload_string();
|
||||
return v ? v : strdup("");
|
||||
}
|
||||
if (strcmp(var_name, "tool_schemas_json") == 0) {
|
||||
char* v = build_tool_schemas_json_string();
|
||||
return v ? v : strdup("[]");
|
||||
}
|
||||
if (strcmp(var_name, "tasks_content") == 0 || strcmp(var_name, "tasks_json") == 0) {
|
||||
char* v = build_tasks_content_string();
|
||||
return v ? v : strdup("");
|
||||
}
|
||||
if (strcmp(var_name, "admin_notes_content") == 0 || strcmp(var_name, "admin_kind1_json") == 0) {
|
||||
char* v = build_admin_recent_posts_text();
|
||||
return v ? v : strdup("");
|
||||
}
|
||||
if (strcmp(var_name, "conversation_json") == 0) {
|
||||
return safe_strdup(ctx ? ctx->current_user_message : "");
|
||||
}
|
||||
if (strcmp(var_name, "admin_recent_posts") == 0) {
|
||||
char* v = build_admin_recent_posts_text();
|
||||
return v ? v : strdup("");
|
||||
}
|
||||
|
||||
return strdup("");
|
||||
}
|
||||
|
||||
int agent_init(didactyl_config_t* config, const char* system_context) {
|
||||
if (!config || !system_context) {
|
||||
return -1;
|
||||
@@ -2385,10 +2106,6 @@ int agent_build_admin_messages_json(const char* current_user_message,
|
||||
dm_history = cJSON_CreateArray();
|
||||
}
|
||||
|
||||
agent_template_resolver_ctx_t resolver_ctx;
|
||||
resolver_ctx.sender_tier = sender_tier;
|
||||
resolver_ctx.current_user_message = current_user_message;
|
||||
|
||||
llm_config_t cfg;
|
||||
memset(&cfg, 0, sizeof(cfg));
|
||||
const char* provider_name = NULL;
|
||||
@@ -2401,8 +2118,6 @@ int agent_build_admin_messages_json(const char* current_user_message,
|
||||
|
||||
messages = prompt_template_build_messages(&g_prompt_template,
|
||||
provider_name,
|
||||
agent_template_resolve_var,
|
||||
&resolver_ctx,
|
||||
&g_tools_ctx,
|
||||
dm_history,
|
||||
AGENT_HISTORY_TURNS,
|
||||
|
||||
+2
-2
@@ -12,8 +12,8 @@
|
||||
// Using DIDACTYL_ prefix to avoid conflicts with nostr_core_lib VERSION macros
|
||||
#define DIDACTYL_VERSION_MAJOR 0
|
||||
#define DIDACTYL_VERSION_MINOR 0
|
||||
#define DIDACTYL_VERSION_PATCH 47
|
||||
#define DIDACTYL_VERSION "v0.0.47"
|
||||
#define DIDACTYL_VERSION_PATCH 48
|
||||
#define DIDACTYL_VERSION "v0.0.48"
|
||||
|
||||
// Agent metadata
|
||||
#define DIDACTYL_NAME "Didactyl"
|
||||
|
||||
+1
-78
@@ -97,81 +97,6 @@ static int append_text(char** buf, size_t* cap, size_t* used, const char* s) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static char* resolve_placeholders(const char* tpl,
|
||||
prompt_var_resolver_fn resolver_fn,
|
||||
void* resolver_user_data) {
|
||||
if (!tpl) return strdup("");
|
||||
|
||||
size_t cap = strlen(tpl) + 64U;
|
||||
char* out = (char*)malloc(cap ? cap : 128U);
|
||||
if (!out) return NULL;
|
||||
out[0] = '\0';
|
||||
size_t used = 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* literal = dup_range(p, (size_t)(open - p));
|
||||
if (!literal) {
|
||||
free(out);
|
||||
return NULL;
|
||||
}
|
||||
int rc = append_text(&out, &cap, &used, literal);
|
||||
free(literal);
|
||||
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* name = dup_range(open + 2, (size_t)(close - (open + 2)));
|
||||
if (!name) {
|
||||
free(out);
|
||||
return NULL;
|
||||
}
|
||||
char* name_trim = ltrim_inplace(name);
|
||||
rtrim_inplace(name_trim);
|
||||
|
||||
char* val = resolver_fn ? resolver_fn(name_trim, resolver_user_data) : NULL;
|
||||
if (!val) {
|
||||
val = strdup("");
|
||||
}
|
||||
|
||||
int rc = 0;
|
||||
if (val) {
|
||||
rc = append_text(&out, &cap, &used, val);
|
||||
}
|
||||
free(val);
|
||||
free(name);
|
||||
if (rc != 0) {
|
||||
free(out);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
p = close + 2;
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
static void init_section_defaults(prompt_template_section_t* sec) {
|
||||
if (!sec) return;
|
||||
memset(sec->name, 0, sizeof(sec->name));
|
||||
@@ -515,8 +440,6 @@ static char* extract_tool_result_content(const prompt_template_section_t* sec, c
|
||||
|
||||
cJSON* prompt_template_build_messages(const prompt_template_t* tmpl,
|
||||
const char* provider_name,
|
||||
prompt_var_resolver_fn resolver_fn,
|
||||
void* resolver_user_data,
|
||||
tools_context_t* tools_ctx,
|
||||
cJSON* dm_history_messages,
|
||||
int dm_history_default_limit,
|
||||
@@ -577,7 +500,7 @@ cJSON* prompt_template_build_messages(const prompt_template_t* tmpl,
|
||||
strcmp(provider_name, sec->provider_name) == 0) {
|
||||
tpl = sec->provider_content_template;
|
||||
}
|
||||
resolved = resolve_placeholders(tpl ? tpl : "", resolver_fn, resolver_user_data);
|
||||
resolved = strdup(tpl ? tpl : "");
|
||||
}
|
||||
if (!resolved) {
|
||||
cJSON_Delete(out);
|
||||
|
||||
@@ -28,7 +28,6 @@ typedef struct {
|
||||
int section_count;
|
||||
} prompt_template_t;
|
||||
|
||||
typedef char* (*prompt_var_resolver_fn)(const char* var_name, void* user_data);
|
||||
typedef void (*prompt_template_emit_hook_fn)(const char* section_name,
|
||||
int message_index,
|
||||
void* user_data);
|
||||
@@ -37,8 +36,6 @@ int prompt_template_parse(const char* soul_content, prompt_template_t* out_templ
|
||||
|
||||
cJSON* prompt_template_build_messages(const prompt_template_t* tmpl,
|
||||
const char* provider_name,
|
||||
prompt_var_resolver_fn resolver_fn,
|
||||
void* resolver_user_data,
|
||||
tools_context_t* tools_ctx,
|
||||
cJSON* dm_history_messages,
|
||||
int dm_history_default_limit,
|
||||
|
||||
Reference in New Issue
Block a user