mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-04 06:12:10 +03:00
fix(memory): harden extractLastUserText + add missing configureCta i18n key
Third code-review pass on plan 21 found two follow-up issues from the
previous round.
1. extractLastUserText accepted Responses API items with role===undefined
regardless of their type. function_call_output / tool_call_output /
reasoning items would slip through and be treated as user query input,
leaking the tool's reply or the model's chain of thought into the
memory retrieval query.
Fix: when role is undefined, skip items whose type is in a denylist of
non-user item types (function_call, function_call_output, tool_call,
tool_call_output, reasoning, computer_call, computer_call_output,
web_search_call, file_search_call). Also reject non-text content
parts inside multi-modal arrays (image_url, tool_use, ...) so that
image-only or tool-only user messages do not produce a query made of
irrelevant fragments.
2. MemoryEngineStatus introduced t("engine.configureCta") in round 2 but
the key was never added to en.json / pt-BR.json — even with the new
EN fallback merger, the CTA would render the literal key path.
Added "Configure" / "Configurar" to both locales.
Verified: typecheck:core clean; vitest UI 46/46; cli-memory-commands,
memory-settings, and mcp-memory-tools-strategy isolated sanity all
green; grep audit of memory.* i18n keys used by the UI confirms zero
missing keys in en.json.
This commit is contained in:
@@ -2230,18 +2230,32 @@ export async function handleChatCore({
|
||||
// tier-2) never fire from the chat hot path — they only fire in the
|
||||
// Playground (retrievePreview, which gets `query` as a positional arg).
|
||||
const lastUserQuery = ((): string => {
|
||||
// Responses API item types that are NOT user input — never accept
|
||||
// their text as the retrieval query (e.g. function_call_output is the
|
||||
// tool's reply, reasoning is the model's chain of thought).
|
||||
const NON_USER_TYPES = new Set([
|
||||
"function_call",
|
||||
"function_call_output",
|
||||
"tool_call",
|
||||
"tool_call_output",
|
||||
"reasoning",
|
||||
"computer_call",
|
||||
"computer_call_output",
|
||||
"web_search_call",
|
||||
"file_search_call",
|
||||
]);
|
||||
|
||||
function pickFrom(arr: unknown[]): string {
|
||||
for (let i = arr.length - 1; i >= 0; i--) {
|
||||
const item = arr[i] as Record<string, unknown> | undefined;
|
||||
if (!item) continue;
|
||||
// Chat API: role==="user"; Responses API: role on input items, or
|
||||
// type==="input_text"; some clients omit role entirely on the
|
||||
// first input item — accept those too as a last resort.
|
||||
if (
|
||||
item.role !== undefined &&
|
||||
item.role !== "user"
|
||||
) {
|
||||
continue;
|
||||
// Chat API: only role==="user" items. Responses API items often
|
||||
// have type instead of role — skip non-user types like
|
||||
// function_call_output so the tool's reply doesn't leak into the
|
||||
// memory query.
|
||||
if (item.role !== undefined && item.role !== "user") continue;
|
||||
if (item.role === undefined && typeof item.type === "string") {
|
||||
if (NON_USER_TYPES.has(item.type)) continue;
|
||||
}
|
||||
const content = item.content ?? item.text;
|
||||
if (typeof content === "string" && content.trim().length > 0) {
|
||||
@@ -2254,6 +2268,16 @@ export async function handleChatCore({
|
||||
parts.push(p);
|
||||
} else if (p && typeof p === "object") {
|
||||
const pp = p as Record<string, unknown>;
|
||||
// Skip non-text content parts (image_url, tool_use, etc.)
|
||||
const ptype = typeof pp.type === "string" ? pp.type : "";
|
||||
if (
|
||||
ptype &&
|
||||
ptype !== "text" &&
|
||||
ptype !== "input_text" &&
|
||||
ptype !== "output_text"
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
const t = pp.text ?? pp.input_text;
|
||||
if (typeof t === "string") parts.push(t);
|
||||
}
|
||||
|
||||
@@ -3044,7 +3044,8 @@
|
||||
"qdrantDisabled": "Disabled",
|
||||
"qdrantOk": "Healthy ({latencyMs}ms)",
|
||||
"qdrantError": "Connection error",
|
||||
"needsReindex": "{count} memory(ies) need reindexing"
|
||||
"needsReindex": "{count} memory(ies) need reindexing",
|
||||
"configureCta": "Configure"
|
||||
},
|
||||
"episodic": "Episodic",
|
||||
"export": "Export",
|
||||
|
||||
@@ -3041,7 +3041,8 @@
|
||||
"qdrantDisabled": "Desabilitado",
|
||||
"qdrantOk": "Saudável ({latencyMs}ms)",
|
||||
"qdrantError": "Erro de conexão",
|
||||
"needsReindex": "{count} memória(s) precisam de reindexação"
|
||||
"needsReindex": "{count} memória(s) precisam de reindexação",
|
||||
"configureCta": "Configurar"
|
||||
},
|
||||
"episodic": "Episódica",
|
||||
"export": "Exportar",
|
||||
|
||||
Reference in New Issue
Block a user