ux improved

This commit is contained in:
Lucas Tettamanti
2026-01-17 04:13:35 -03:00
parent 98e3d78e3d
commit 63b9ecef61
35 changed files with 4266 additions and 75 deletions

View File

@@ -65,7 +65,7 @@ export async function touchConversationState({ tenant_id, wa_chat_id }) {
on conflict (tenant_id, wa_chat_id)
do update set
updated_at = now()
returning tenant_id, wa_chat_id, state, last_intent, context, updated_at
returning tenant_id, wa_chat_id, state, last_intent, last_order_id, context, state_updated_at, updated_at, created_at
`;
const { rows } = await pool.query(q, [tenant_id, wa_chat_id]);
return rows[0] || null;
@@ -272,10 +272,16 @@ export async function getRunById({ tenant_id, run_id }) {
export async function getRecentMessagesForLLM({
tenant_id,
wa_chat_id,
limit = 20,
maxCharsPerMessage = 800,
}) {
const lim = Math.max(1, Math.min(50, parseInt(limit, 10) || 20));
const limRaw = parseInt(process.env.LIMIT_CONVERSATIONS || "", 10);
const maxCharsRaw = parseInt(process.env.MAX_CHARS_PER_MESSAGE || "", 10);
if (!Number.isFinite(limRaw) || limRaw <= 0) {
throw new Error("LIMIT_CONVERSATIONS env is required and must be a positive integer");
}
if (!Number.isFinite(maxCharsRaw) || maxCharsRaw <= 0) {
throw new Error("MAX_CHARS_PER_MESSAGE env is required and must be a positive integer");
}
const lim = Math.max(1, Math.min(50, limRaw));
const q = `
select direction, ts, text
from wa_messages
@@ -290,7 +296,7 @@ export async function getRecentMessagesForLLM({
return rows.reverse().map((r) => ({
role: r.direction === "in" ? "user" : "assistant",
content: String(r.text).trim().slice(0, maxCharsPerMessage),
content: String(r.text).trim().slice(0, maxCharsRaw),
}));
}
@@ -557,6 +563,28 @@ export async function searchProductAliases({ tenant_id, q = "", limit = 20 }) {
}));
}
export async function getRecoRules({ tenant_id }) {
const sql = `
select id, tenant_id, rule_key, trigger, queries, boosts, ask_slots, active, priority, created_at, updated_at
from product_reco_rules
where tenant_id=$1 and active=true
order by priority asc, id asc
`;
const { rows } = await pool.query(sql, [tenant_id]);
return rows;
}
export async function getRecoRuleByKey({ tenant_id, rule_key }) {
const sql = `
select id, tenant_id, rule_key, trigger, queries, boosts, ask_slots, active, priority, created_at, updated_at
from product_reco_rules
where tenant_id=$1 and rule_key=$2
limit 1
`;
const { rows } = await pool.query(sql, [tenant_id, rule_key]);
return rows[0] || null;
}
export async function getProductEmbedding({ tenant_id, content_hash }) {
const sql = `
select tenant_id, content_hash, content_text, embedding, model, updated_at

View File

@@ -1,6 +1,5 @@
import crypto from "crypto";
import {
getConversationState,
insertMessage,
insertRun,
touchConversationState,
@@ -124,17 +123,56 @@ export async function processMessage({
meta = null,
}) {
const { started_at, mark, msBetween } = makePerf();
// #region agent log
fetch("http://127.0.0.1:7242/ingest/86c7b1cd-c414-4eae-852c-08e57e562b3b", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
sessionId: "debug-session",
runId: "pre-fix",
hypothesisId: "H2",
location: "pipeline.js:128",
message: "processMessage_enter",
data: {
tenantId: tenantId || null,
provider,
chat_id: chat_id || null,
text_len: String(text || "").length,
},
timestamp: Date.now(),
}),
}).catch(() => {});
// #endregion
await touchConversationState({ tenant_id: tenantId, wa_chat_id: chat_id });
const prev = await touchConversationState({ tenant_id: tenantId, wa_chat_id: chat_id });
mark("start");
const stageDebug = dbg.perf;
const prev = await getConversationState(tenantId, chat_id);
mark("after_getConversationState");
mark("after_touchConversationState");
const isStale =
prev?.state_updated_at &&
Date.now() - new Date(prev.state_updated_at).getTime() > 24 * 60 * 60 * 1000;
const prev_state = isStale ? "IDLE" : prev?.state || "IDLE";
// #region agent log
fetch("http://127.0.0.1:7242/ingest/86c7b1cd-c414-4eae-852c-08e57e562b3b", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
sessionId: "debug-session",
runId: "pre-fix",
hypothesisId: "H3",
location: "pipeline.js:150",
message: "conversation_state_loaded",
data: {
prev_state,
isStale: Boolean(isStale),
state_updated_at: prev?.state_updated_at || null,
has_context: Boolean(prev?.context && typeof prev?.context === "object"),
},
timestamp: Date.now(),
}),
}).catch(() => {});
// #endregion
let externalCustomerId = await getExternalCustomerIdByChat({
tenant_id: tenantId,
wa_chat_id: chat_id,
@@ -158,7 +196,6 @@ export async function processMessage({
const history = await getRecentMessagesForLLM({
tenant_id: tenantId,
wa_chat_id: chat_id,
limit: 20,
});
const conversation_history = collapseAssistantMessages(history);
mark("after_getRecentMessagesForLLM_for_plan");
@@ -185,6 +222,26 @@ export async function processMessage({
llmMeta = { kind: "nlu_v3", audit: decision.audit || null };
tools = [];
mark("after_turn_v3");
// #region agent log
fetch("http://127.0.0.1:7242/ingest/86c7b1cd-c414-4eae-852c-08e57e562b3b", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
sessionId: "debug-session",
runId: "pre-fix",
hypothesisId: "H4",
location: "pipeline.js:198",
message: "turn_v3_result",
data: {
intent: plan?.intent || null,
next_state: plan?.next_state || null,
missing_fields: Array.isArray(plan?.missing_fields) ? plan.missing_fields.length : null,
actions_count: Array.isArray(decision?.actions) ? decision.actions.length : null,
},
timestamp: Date.now(),
}),
}).catch(() => {});
// #endregion
const runStatus = llmMeta?.error ? "warn" : "ok";
const isSimulated = provider === "sim" || meta?.source === "sim";
@@ -397,8 +454,8 @@ export async function processMessage({
run_id,
end_to_end_ms,
ms: {
db_state_ms: msBetween("start", "after_getConversationState"),
db_identity_ms: msBetween("after_getConversationState", "after_getExternalCustomerIdByChat"),
db_state_ms: msBetween("start", "after_touchConversationState"),
db_identity_ms: msBetween("after_touchConversationState", "after_getExternalCustomerIdByChat"),
insert_in_ms: msBetween("after_getExternalCustomerIdByChat", "after_insertMessage_in"),
history_for_plan_ms: msBetween("after_insertMessage_in", "after_getRecentMessagesForLLM_for_plan"),
insert_run_ms: msBetween("before_insertRun", "after_insertRun"),