/** * Chat dev logger. * * A thin namespaced wrapper over `consola` that no-ops in production unless * the host app explicitly opts in via ``. The default * detection uses `isDev` from `@djangocfg/ui-core/lib/env` (NODE_ENV). * * Why a dedicated module: chat is async and event-heavy (bootstrap, transport, * SSE chunks, tool calls, regenerate, …). Inline `console.log`s rot fast and * leak into prod. A single `getChatLogger()` call gives every layer the same * namespaced sub-logger and keeps zero-cost gating in one place. * * Sub-loggers: * bootstrap — initial session bootstrap (createSession / loadHistory) * transport — outbound transport calls + responses * stream — SSE chunk / tool / message_end events * lifecycle — sendMessage, regenerate, newSession, edits * tools — tool_call_start / _delta / _end specifics * error — caught errors (always emitted as `error` level) */ import { consola, type ConsolaInstance } from 'consola'; import { isDev } from '@djangocfg/ui-core/lib'; export type ChatLogScope = | 'bootstrap' | 'transport' | 'stream' | 'lifecycle' | 'tools' | 'error'; export interface ChatLogger { bootstrap: ConsolaInstance; transport: ConsolaInstance; stream: ConsolaInstance; lifecycle: ConsolaInstance; tools: ConsolaInstance; error: ConsolaInstance; /** True when this logger is actually emitting (host opted in or NODE_ENV=development). */ enabled: boolean; } const SCOPES: ChatLogScope[] = [ 'bootstrap', 'transport', 'stream', 'lifecycle', 'tools', 'error', ]; /** Module-level cache so all hooks/components share the same logger instance per `enabled` mode. */ const cache = new Map(); function buildLogger(enabled: boolean): ChatLogger { const root = consola.withTag('chat'); const subs = Object.fromEntries( SCOPES.map((scope) => [scope, root.withTag(scope)]), ) as Record; if (!enabled) { // Silence everything except `error` — surfaced errors should never go // missing even if the host didn't opt in to debug logs. for (const scope of SCOPES) { if (scope === 'error') continue; subs[scope].level = -999; } } return { ...subs, enabled }; } /** * Get the chat logger. * @param debug Explicit override from the host. `undefined` falls back to `isDev`. */ export function getChatLogger(debug?: boolean): ChatLogger { const enabled = debug ?? isDev; let logger = cache.get(enabled); if (!logger) { logger = buildLogger(enabled); cache.set(enabled, logger); } return logger; }