mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-15 06:52:04 +00:00
The narrative read route narrowed every non-English locale to German, so a French, Spanish, Italian or Polish account read the German narrative row and warmed it from the German prompt body. Carry the resolved locale through instead: the row is keyed (user, period, locale) across the full UI locale union, and the four locales now compose the English instruction body with their language named and the locale's own reply-language directive appended last. The German and English prompts are unchanged apart from the version string. The nightly warm keeps the user's stored locale for the same reason — it has to write the key the read route reads — and the coach and dashboard narrative recalls read that same key. Coach memory stays de/en: it is model-facing context, not user-facing prose, so the four locales compose and key English rather than German. NARRATIVE_PROMPT_VERSION 1.13.0 -> 1.14.0.
65 lines
2.3 KiB
TypeScript
65 lines
2.3 KiB
TypeScript
/**
|
|
* v1.11.1 — generator-free contract for the Coach long-term-memory refresh
|
|
* queue. The chat route enqueues a single-conversation refresh here without
|
|
* importing the concrete generators (which would pull the provider chain into
|
|
* the route bundle). The worker handler (`runCoachMemoryRefresh`) calls the
|
|
* summary + fact generators; it re-uses the queue name from here so there is
|
|
* one source of truth.
|
|
*
|
|
* One combined queue does BOTH conversation-summary refresh and durable-fact
|
|
* extraction in a single worker job: when a long conversation crosses the
|
|
* history-window cap, both are usually due together, so one job + one
|
|
* singleton window avoids a second queue and a redundant wake-up.
|
|
*
|
|
* Mirrors `period-narrative-shared.ts`: queue name + payload type + enqueue
|
|
* helper here, the concrete dispatch in the worker.
|
|
*/
|
|
import { getGlobalBoss } from "@/lib/jobs/boss-instance";
|
|
import { annotate } from "@/lib/logging/context";
|
|
|
|
export const COACH_MEMORY_REFRESH_QUEUE = "coach-memory-refresh";
|
|
|
|
export interface CoachMemoryRefreshPayload {
|
|
conversationId: string;
|
|
userId: string;
|
|
/** Locale to compose the summary / facts prose in; defaults to "en". */
|
|
locale?: "de" | "en";
|
|
}
|
|
|
|
/**
|
|
* Fire-and-forget enqueue from the chat turn once a conversation has grown past
|
|
* the history-window cap. A `singletonKey` per conversation collapses repeated
|
|
* turns within a short window into one queued job. No-ops cleanly when the
|
|
* global boss is unavailable (a web process without an embedded worker) — the
|
|
* memory simply stays as-is until the next eligible turn.
|
|
*/
|
|
export async function enqueueCoachMemoryRefresh(payload: {
|
|
conversationId: string;
|
|
userId: string;
|
|
locale: "de" | "en";
|
|
}): Promise<void> {
|
|
const boss = getGlobalBoss();
|
|
if (!boss) return;
|
|
try {
|
|
await boss.send(
|
|
COACH_MEMORY_REFRESH_QUEUE,
|
|
{
|
|
conversationId: payload.conversationId,
|
|
userId: payload.userId,
|
|
locale: payload.locale,
|
|
} satisfies CoachMemoryRefreshPayload,
|
|
{
|
|
singletonKey: `refresh:${payload.conversationId}`,
|
|
singletonSeconds: 120,
|
|
},
|
|
);
|
|
annotate({
|
|
action: { name: "coach.memory.refresh.enqueued" },
|
|
meta: { locale: payload.locale },
|
|
});
|
|
} catch {
|
|
// Best-effort — a failure just leaves the Coach memory unchanged until the
|
|
// next eligible turn re-enqueues.
|
|
}
|
|
}
|