src / utils / logger.ts
import type { GeneratorController } from "@lmstudio/sdk";
/* -------------------------------------------------------------------------- */
/* SHARTI Logger */
/* */
/* Design principles: */
/* • Single decision point: visibility determined by numeric thresholds. */
/* • Extensible: adding a new level = 1 constant + 1 table row. */
/* • Channels declaratively define their console target and chat policy. */
/* -------------------------------------------------------------------------- */
/* ── Severity levels (higher = more verbose) ─────────────────────────────── */
const MINIMAL = 0;
const NORMAL = 1;
const DEBUG = 2;
// Future: VERBOSE = 3, TRACE = 4, …
export type LogLevel = "minimal" | "normal" | "debug";
const LOG_LEVEL_MAP: Record<LogLevel, number> = {
minimal: MINIMAL,
normal: NORMAL,
debug: DEBUG,
};
/* ── Channel definitions ─────────────────────────────────────────────────── */
interface ChannelDef {
/** Minimum enabled level for this channel to fire (inclusive). */
threshold: number;
/** Target console writer. */
write: (msg: string) => void;
/** Emoji prefix for user chat. Null = no automatic chat delivery. */
chatEmoji: string | null;
}
const CH: Record<"debug" | "info" | "notice" | "warn" | "error", ChannelDef> = {
debug: { threshold: DEBUG, write: (m) => console.debug(m), chatEmoji: null },
info: { threshold: NORMAL, write: (m) => console.info(m), chatEmoji: null },
notice: { threshold: MINIMAL, write: (m) => console.info(m), chatEmoji: null },
warn: { threshold: MINIMAL, write: (m) => console.warn(m), chatEmoji: "⚠️" },
error: { threshold: MINIMAL, write: (m) => console.error(m), chatEmoji: "❌" },
};
/* ── Public API ──────────────────────────────────────────────────────────── */
export interface ShartiLoggerOptions {
logLevel: LogLevel;
}
export class ShartiLogger {
private readonly level: number;
private readonly ctl: GeneratorController;
constructor(ctl: GeneratorController, options: ShartiLoggerOptions) {
this.ctl = ctl;
this.level = LOG_LEVEL_MAP[options.logLevel] ?? NORMAL;
}
/* ── Internal dispatch ─────────────────────────────────────────────────── */
private emit(key: keyof typeof CH, message: string): void {
const ch = CH[key];
if (ch.threshold > this.level) return;
ch.write(message);
if (ch.chatEmoji !== null) {
this.notifyUser(`${ch.chatEmoji} ${message}`);
}
}
/* ── Console channels ──────────────────────────────────────────────────── */
/** Full diagnostic detail. Active at level ≥ DEBUG. */
debug(message: string): void {
this.emit("debug", message);
}
/** Operational info (request overview, per-request stats). Active at level ≥ NORMAL. */
info(message: string): void {
this.emit("info", message);
}
/** Always-on console note (turn summary, important milestones). Active at all levels. */
notice(message: string): void {
this.emit("notice", message);
}
/** Warning: console + user chat. Active at all levels. */
warn(message: string): void {
this.emit("warn", message);
}
/** Error: console + user chat. Active at all levels. */
error(message: string): void {
this.emit("error", message);
}
/* ── User chat channel ─────────────────────────────────────────────────── */
/** Direct message to the user in the chat dialog. Bypasses all level checks. */
notifyUser(message: string): void {
this.ctl.fragmentGenerated(message);
}
}
src / utils / logger.ts
import type { GeneratorController } from "@lmstudio/sdk";
/* -------------------------------------------------------------------------- */
/* SHARTI Logger */
/* */
/* Design principles: */
/* • Single decision point: visibility determined by numeric thresholds. */
/* • Extensible: adding a new level = 1 constant + 1 table row. */
/* • Channels declaratively define their console target and chat policy. */
/* -------------------------------------------------------------------------- */
/* ── Severity levels (higher = more verbose) ─────────────────────────────── */
const MINIMAL = 0;
const NORMAL = 1;
const DEBUG = 2;
// Future: VERBOSE = 3, TRACE = 4, …
export type LogLevel = "minimal" | "normal" | "debug";
const LOG_LEVEL_MAP: Record<LogLevel, number> = {
minimal: MINIMAL,
normal: NORMAL,
debug: DEBUG,
};
/* ── Channel definitions ─────────────────────────────────────────────────── */
interface ChannelDef {
/** Minimum enabled level for this channel to fire (inclusive). */
threshold: number;
/** Target console writer. */
write: (msg: string) => void;
/** Emoji prefix for user chat. Null = no automatic chat delivery. */
chatEmoji: string | null;
}
const CH: Record<"debug" | "info" | "notice" | "warn" | "error", ChannelDef> = {
debug: { threshold: DEBUG, write: (m) => console.debug(m), chatEmoji: null },
info: { threshold: NORMAL, write: (m) => console.info(m), chatEmoji: null },
notice: { threshold: MINIMAL, write: (m) => console.info(m), chatEmoji: null },
warn: { threshold: MINIMAL, write: (m) => console.warn(m), chatEmoji: "⚠️" },
error: { threshold: MINIMAL, write: (m) => console.error(m), chatEmoji: "❌" },
};
/* ── Public API ──────────────────────────────────────────────────────────── */
export interface ShartiLoggerOptions {
logLevel: LogLevel;
}
export class ShartiLogger {
private readonly level: number;
private readonly ctl: GeneratorController;
constructor(ctl: GeneratorController, options: ShartiLoggerOptions) {
this.ctl = ctl;
this.level = LOG_LEVEL_MAP[options.logLevel] ?? NORMAL;
}
/* ── Internal dispatch ─────────────────────────────────────────────────── */
private emit(key: keyof typeof CH, message: string): void {
const ch = CH[key];
if (ch.threshold > this.level) return;
ch.write(message);
if (ch.chatEmoji !== null) {
this.notifyUser(`${ch.chatEmoji} ${message}`);
}
}
/* ── Console channels ──────────────────────────────────────────────────── */
/** Full diagnostic detail. Active at level ≥ DEBUG. */
debug(message: string): void {
this.emit("debug", message);
}
/** Operational info (request overview, per-request stats). Active at level ≥ NORMAL. */
info(message: string): void {
this.emit("info", message);
}
/** Always-on console note (turn summary, important milestones). Active at all levels. */
notice(message: string): void {
this.emit("notice", message);
}
/** Warning: console + user chat. Active at all levels. */
warn(message: string): void {
this.emit("warn", message);
}
/** Error: console + user chat. Active at all levels. */
error(message: string): void {
this.emit("error", message);
}
/* ── User chat channel ─────────────────────────────────────────────────── */
/** Direct message to the user in the chat dialog. Bypasses all level checks. */
notifyUser(message: string): void {
this.ctl.fragmentGenerated(message);
}
}