diff --git a/chat.el b/chat.el index da5bf05..4b31510 100644 --- a/chat.el +++ b/chat.el @@ -21,8 +21,28 @@ fn engram_compile(intent: String) -> String { let act_part: String = if act_ok { activate_json } else { "" } let srch_part: String = if srch_ok { search_json } else { "" } - let sep: String = if !str_eq(act_part, "") && !str_eq(srch_part, "") { "\n" } else { "" } - let ctx: String = act_part + sep + srch_part + + // Fallback: when vector search returns nothing (no embeddings), fetch pinned + // high-salience nodes by their known IDs. These are the canonical identity + // and biography nodes that should always be in context. + // engram_get_node_json(id) returns a single node as JSON or "" if missing. + let scan_part: String = if !act_ok && !srch_ok { + let family_node: String = engram_get_node_json("knw-35940684-abc4-42f0-b942-818f66b1f69a") + let origin_node: String = engram_get_node_json("knw-729fc901-8335-44c4-9f3a-b150b4aa0915") + let fam_ok: Bool = !str_eq(family_node, "") && !str_eq(family_node, "null") + let orig_ok: Bool = !str_eq(origin_node, "") && !str_eq(origin_node, "null") + let fam_str: String = if fam_ok { family_node } else { "" } + let orig_str: String = if orig_ok { origin_node } else { "" } + let sep: String = if fam_ok && orig_ok { "\n" } else { "" } + let combined: String = fam_str + sep + orig_str + if str_eq(combined, "") { "" } else { combined } + } else { + "" + } + + let sep1: String = if !str_eq(act_part, "") && !str_eq(srch_part, "") { "\n" } else { "" } + let sep2: String = if (!str_eq(act_part, "") || !str_eq(srch_part, "")) && !str_eq(scan_part, "") { "\n" } else { "" } + let ctx: String = act_part + sep1 + srch_part + sep2 + scan_part if str_eq(ctx, "") { return "" } @@ -80,6 +100,21 @@ fn hist_trim(hist: String) -> String { return hist } +// clean_llm_response — strips GPT-2 BPE byte-to-unicode artifacts that vLLM +// emits when the tokenizer hasn't decoded back to raw bytes. +// +// Ġ (U+0120) = leading space on a BPE token → plain space +// Ċ (U+010A) = newline byte encoded as BPE token → \n +// ĉ (U+0109) = tab byte → tab (rare) +// +// Applied to every LLM response before it reaches callers. +fn clean_llm_response(s: String) -> String { + let s1: String = str_replace(s, "Ġ", " ") + let s2: String = str_replace(s1, "Ċ", "\n") + let s3: String = str_replace(s2, "ĉ", "\t") + return s3 +} + fn handle_chat(body: String) -> String { let message: String = json_get(body, "message") if str_eq(message, "") { @@ -109,7 +144,8 @@ fn handle_chat(body: String) -> String { return "{\"error\":\"llm unavailable\",\"response\":\"\"}" } - let safe_response: String = json_safe(raw_response) + let clean_response: String = clean_llm_response(raw_response) + let safe_response: String = json_safe(clean_response) let updated_hist: String = hist_append(stored_hist, "user", message) let updated_hist2: String = hist_append(updated_hist, "assistant", raw_response) @@ -246,7 +282,8 @@ fn handle_chat_as_soul(body: String) -> String { return "{\"error\":\"llm unavailable\",\"response\":\"\",\"speaker_slug\":\"" + speaker + "\",\"model\":\"" + model + "\"}" } - let safe_response: String = json_safe(raw_response) + let clean_response: String = clean_llm_response(raw_response) + let safe_response: String = json_safe(clean_response) return "{\"response\":\"" + safe_response + "\",\"model\":\"" + model + "\",\"speaker_slug\":\"" + speaker + "\"}" } diff --git a/chat.elh b/chat.elh index 74c15fc..cf0dd77 100644 --- a/chat.elh +++ b/chat.elh @@ -5,6 +5,7 @@ extern fn json_safe(s: String) -> String extern fn build_system_prompt(ctx: String) -> String extern fn hist_append(hist: String, role: String, content: String) -> String extern fn hist_trim(hist: String) -> String +extern fn clean_llm_response(s: String) -> String extern fn handle_chat(body: String) -> String extern fn handle_see(body: String) -> String extern fn studio_tools_json() -> String diff --git a/dist/neuron b/dist/neuron index 92276b1..489ddb1 100755 Binary files a/dist/neuron and b/dist/neuron differ diff --git a/dist/soul.c b/dist/soul.c index 79df5ca..475786c 100644 --- a/dist/soul.c +++ b/dist/soul.c @@ -35,6 +35,7 @@ el_val_t json_safe(el_val_t s); el_val_t build_system_prompt(el_val_t ctx); el_val_t hist_append(el_val_t hist, el_val_t role, el_val_t content); el_val_t hist_trim(el_val_t hist); +el_val_t clean_llm_response(el_val_t s); el_val_t handle_chat(el_val_t body); el_val_t handle_see(el_val_t body); el_val_t studio_tools_json(void); @@ -52,6 +53,10 @@ el_val_t handle_dharma(el_val_t path, el_val_t method, el_val_t body); el_val_t handle_tool(el_val_t path, el_val_t method, el_val_t body); el_val_t handle_nlg(el_val_t path, el_val_t method, el_val_t body); el_val_t render_studio(void); +el_val_t elp_extract_topic(el_val_t msg); +el_val_t elp_detect_predicate(el_val_t msg); +el_val_t elp_parse(el_val_t msg); +el_val_t handle_elp_chat(el_val_t body); el_val_t strip_query(el_val_t path); el_val_t err_404(el_val_t path); el_val_t err_405(el_val_t method, el_val_t path); diff --git a/routes.el b/routes.el index 869262e..a2dfd36 100644 --- a/routes.el +++ b/routes.el @@ -2,6 +2,7 @@ import "memory.el" import "awareness.el" import "chat.el" import "studio.el" +import "elp-input.el" fn strip_query(path: String) -> String { let q: Int = str_index_of(path, "?") @@ -246,6 +247,9 @@ fn handle_request(method: String, path: String, body: String) -> String { if str_eq(clean, "/synthesize") { return route_synthesize(body) } + if str_eq(clean, "/api/elp/chat") { + return handle_elp_chat(body) + } if str_eq(clean, "/api/chat") { let agentic_flag: Bool = json_get_bool(body, "agentic") let reply: String = if agentic_flag { diff --git a/soul.el b/soul.el index ab88c52..59daac1 100644 --- a/soul.el +++ b/soul.el @@ -3,6 +3,7 @@ import "memory.el" import "awareness.el" import "chat.el" import "studio.el" +import "elp-input.el" import "routes.el" cgi "neuron-soul" {