soul: parameterize CGI ID + add dharma_room_turn handler

- soul.el: SOUL_CGI_ID, SOUL_ENGRAM_PATH, SOUL_IDENTITY env vars;
  state_set("soul_snapshot_path") so callers can find it; only call
  init_soul_edges() when cgi_id == "ntn-genesis"
- chat.el: handle_dharma_room_turn — soul builds its own context from its
  own engram, assembles system prompt, calls LLM, persists episodic memory;
  also fix is_new_tool scoping bug in handle_chat_agentic (use has_tool)
- routes.el: wire dharma_room_turn event type before chat_as_soul branch
- rebuild dist/neuron: handle_dharma_room_turn now compiled in
This commit is contained in:
Will Anderson
2026-05-03 17:55:37 -05:00
parent 2000a2c8ba
commit 2665810962
58 changed files with 189 additions and 70 deletions
Vendored
+42 -7
View File
@@ -29,6 +29,7 @@ el_val_t agentic_tools_literal(void);
el_val_t dispatch_tool(el_val_t tool_name, el_val_t tool_input);
el_val_t handle_chat_agentic(el_val_t body);
el_val_t handle_chat_as_soul(el_val_t body);
el_val_t handle_dharma_room_turn(el_val_t body);
el_val_t auto_persist(el_val_t req, el_val_t resp);
el_val_t chat_default_model(void) {
@@ -240,6 +241,7 @@ el_val_t handle_chat_agentic(el_val_t body) {
map_set(h, EL_STR("anthropic-version"), EL_STR("2023-06-01"));
map_set(h, EL_STR("content-type"), EL_STR("application/json"));
el_val_t final_text = EL_STR("");
el_val_t tools_log = EL_STR("");
el_val_t iteration = 0;
el_val_t keep_going = 1;
while (keep_going && (iteration < 8)) {
@@ -273,18 +275,21 @@ el_val_t handle_chat_agentic(el_val_t body) {
el_val_t tool_result_raw = ({ el_val_t _if_result_25 = 0; if (has_tool) { _if_result_25 = (dispatch_tool(tool_name, tool_input)); } else { _if_result_25 = (EL_STR("")); } _if_result_25; });
el_val_t tool_result = ({ el_val_t _if_result_26 = 0; if ((str_len(tool_result_raw) > 6000)) { _if_result_26 = (el_str_concat(str_slice(tool_result_raw, 0, 6000), EL_STR("...[truncated]"))); } else { _if_result_26 = (tool_result_raw); } _if_result_26; });
el_val_t tool_msg = el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("{\"type\":\"tool_result\",\"tool_use_id\":\""), tool_id), EL_STR("\",\"content\":\"")), tool_result), EL_STR("\"}"));
el_val_t tool_quoted = el_str_concat(el_str_concat(EL_STR("\""), tool_name), EL_STR("\""));
tools_log = ({ el_val_t _if_result_27 = 0; if (has_tool) { _if_result_27 = (({ el_val_t _if_result_28 = 0; if (str_eq(tools_log, EL_STR(""))) { _if_result_28 = (tool_quoted); } else { _if_result_28 = (el_str_concat(el_str_concat(tools_log, EL_STR(",")), tool_quoted)); } _if_result_28; })); } else { _if_result_27 = (tools_log); } _if_result_27; });
el_val_t is_tool_turn = (str_eq(stop_reason, EL_STR("tool_use")) && has_tool);
el_val_t inner = str_slice(messages, 1, (str_len(messages) - 1));
messages = ({ el_val_t _if_result_27 = 0; if (is_tool_turn) { _if_result_27 = (el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("["), inner), EL_STR(",{\"role\":\"assistant\",\"content\":")), eff_content), EL_STR("}")), EL_STR(",{\"role\":\"user\",\"content\":[")), tool_msg), EL_STR("]}")), EL_STR("]"))); } else { _if_result_27 = (messages); } _if_result_27; });
final_text = ({ el_val_t _if_result_28 = 0; if (!is_tool_turn) { _if_result_28 = (text_out); } else { _if_result_28 = (final_text); } _if_result_28; });
keep_going = ({ el_val_t _if_result_29 = 0; if (!is_tool_turn) { _if_result_29 = (0); } else { _if_result_29 = (keep_going); } _if_result_29; });
messages = ({ el_val_t _if_result_29 = 0; if (is_tool_turn) { _if_result_29 = (el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("["), inner), EL_STR(",{\"role\":\"assistant\",\"content\":")), eff_content), EL_STR("}")), EL_STR(",{\"role\":\"user\",\"content\":[")), tool_msg), EL_STR("]}")), EL_STR("]"))); } else { _if_result_29 = (messages); } _if_result_29; });
final_text = ({ el_val_t _if_result_30 = 0; if (!is_tool_turn) { _if_result_30 = (text_out); } else { _if_result_30 = (final_text); } _if_result_30; });
keep_going = ({ el_val_t _if_result_31 = 0; if (!is_tool_turn) { _if_result_31 = (0); } else { _if_result_31 = (keep_going); } _if_result_31; });
iteration = (iteration + 1);
}
if (str_eq(final_text, EL_STR(""))) {
return EL_STR("{\"error\":\"no response\",\"reply\":\"\"}");
}
el_val_t safe_text = json_safe(final_text);
return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("{\"reply\":\""), safe_text), EL_STR("\",\"model\":\"")), model), EL_STR("\",\"agentic\":true}"));
el_val_t tools_arr = ({ el_val_t _if_result_32 = 0; if (str_eq(tools_log, EL_STR(""))) { _if_result_32 = (EL_STR("[]")); } else { _if_result_32 = (el_str_concat(el_str_concat(EL_STR("["), tools_log), EL_STR("]"))); } _if_result_32; });
return el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("{\"reply\":\""), safe_text), EL_STR("\",\"model\":\"")), model), EL_STR("\",\"agentic\":true,\"tools_used\":")), tools_arr), EL_STR("}"));
return 0;
}
@@ -299,12 +304,12 @@ el_val_t handle_chat_as_soul(el_val_t body) {
}
el_val_t message = json_get(body, EL_STR("message"));
el_val_t transcript = json_get(body, EL_STR("transcript"));
el_val_t eff_message = ({ el_val_t _if_result_30 = 0; if (str_eq(message, EL_STR(""))) { _if_result_30 = (transcript); } else { _if_result_30 = (message); } _if_result_30; });
el_val_t eff_message = ({ el_val_t _if_result_33 = 0; if (str_eq(message, EL_STR(""))) { _if_result_33 = (transcript); } else { _if_result_33 = (message); } _if_result_33; });
if (str_eq(eff_message, EL_STR(""))) {
return el_str_concat(el_str_concat(EL_STR("{\"error\":\"message or transcript is required\",\"response\":\"\",\"speaker_slug\":\""), speaker), EL_STR("\"}"));
}
el_val_t req_model = json_get(body, EL_STR("model"));
el_val_t model = ({ el_val_t _if_result_31 = 0; if (str_eq(req_model, EL_STR(""))) { _if_result_31 = (chat_default_model()); } else { _if_result_31 = (req_model); } _if_result_31; });
el_val_t model = ({ el_val_t _if_result_34 = 0; if (str_eq(req_model, EL_STR(""))) { _if_result_34 = (chat_default_model()); } else { _if_result_34 = (req_model); } _if_result_34; });
el_val_t raw_response = llm_call_system(model, system_prompt, eff_message);
el_val_t is_error = ((str_starts_with(raw_response, EL_STR("{\"error\"")) || str_starts_with(raw_response, EL_STR("{\"type\":\"error\""))) || str_contains(raw_response, EL_STR("authentication_error")));
if (is_error) {
@@ -316,10 +321,40 @@ el_val_t handle_chat_as_soul(el_val_t body) {
return 0;
}
el_val_t handle_dharma_room_turn(el_val_t body) {
el_val_t topic = json_get(body, EL_STR("topic"));
el_val_t transcript = json_get(body, EL_STR("transcript"));
el_val_t participants = json_get(body, EL_STR("participants"));
el_val_t room_id = json_get(body, EL_STR("room_id"));
el_val_t conv_id = json_get(body, EL_STR("conv_id"));
el_val_t identity = state_get(EL_STR("soul_identity"));
el_val_t cgi_id = state_get(EL_STR("soul_cgi_id"));
el_val_t model = chat_default_model();
el_val_t search_query = ({ el_val_t _if_result_35 = 0; if (str_eq(topic, EL_STR(""))) { _if_result_35 = (cgi_id); } else { _if_result_35 = (topic); } _if_result_35; });
el_val_t engram_ctx = engram_compile(search_query);
el_val_t engram_section = ({ el_val_t _if_result_36 = 0; if (str_eq(engram_ctx, EL_STR(""))) { _if_result_36 = (EL_STR("")); } else { _if_result_36 = (el_str_concat(EL_STR("\n\n[YOUR MEMORIES]\n"), engram_ctx)); } _if_result_36; });
el_val_t system_prompt = el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(el_str_concat(identity, EL_STR("\n\n[DHARMA ROOM: ")), topic), EL_STR("]")), EL_STR("\nOther participants: ")), participants), engram_section), EL_STR("\n\nRespond authentically as yourself. Be concise \xe2\x80\x94 match length to the moment. ")), EL_STR("If you want to address someone specifically, use @<their-slug>. No em dashes. No stage directions."));
el_val_t raw_response = llm_call_system(model, system_prompt, transcript);
el_val_t is_error = ((str_starts_with(raw_response, EL_STR("{\"error\"")) || str_starts_with(raw_response, EL_STR("{\"type\":\"error\""))) || str_contains(raw_response, EL_STR("authentication_error")));
if (is_error) {
return el_str_concat(el_str_concat(EL_STR("{\"error\":\"llm unavailable\",\"response\":\"\",\"cgi_id\":\""), cgi_id), EL_STR("\"}"));
}
el_val_t clean_response = clean_llm_response(raw_response);
el_val_t snap_path = state_get(EL_STR("soul_snapshot_path"));
el_val_t mem = el_str_concat(el_str_concat(el_str_concat(EL_STR("DHARMA room ["), topic), EL_STR("]. I said: ")), clean_response);
el_val_t discard_id = engram_node(mem, EL_STR("episodic"), el_from_float(0.6));
if (!str_eq(snap_path, EL_STR(""))) {
engram_save(snap_path);
}
el_val_t safe_response = json_safe(clean_response);
return el_str_concat(el_str_concat(el_str_concat(el_str_concat(EL_STR("{\"response\":\""), safe_response), EL_STR("\",\"cgi_id\":\"")), cgi_id), EL_STR("\"}"));
return 0;
}
el_val_t auto_persist(el_val_t req, el_val_t resp) {
el_val_t message = json_get(req, EL_STR("message"));
el_val_t reply = json_get(resp, EL_STR("response"));
el_val_t reply2 = ({ el_val_t _if_result_32 = 0; if (str_eq(reply, EL_STR(""))) { _if_result_32 = (json_get(resp, EL_STR("reply"))); } else { _if_result_32 = (reply); } _if_result_32; });
el_val_t reply2 = ({ el_val_t _if_result_37 = 0; if (str_eq(reply, EL_STR(""))) { _if_result_37 = (json_get(resp, EL_STR("reply"))); } else { _if_result_37 = (reply); } _if_result_37; });
if (str_eq(message, EL_STR(""))) {
return EL_STR("");
}