/**
* 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;
}