feat: add log-level system to suppress noisy fallback debug output

Replace console.debug/log/warn calls in model router, retry, and daemon
startup with a structured logger that respects a configurable log_level.
Default level is 'info', suppressing verbose fallback debug messages in
the TUI while keeping them available via config when needed.

- Add src/logger.ts with debug/info/warn/error/silent levels
- Wire log_level into config schema (default: 'info')
- Initialize log level in both daemon and TUI startup paths
- Convert all console.debug in router.ts and retry.ts to logger.debug
- Convert console.log/warn in daemon/models.ts to logger.info/warn
This commit is contained in:
William Valentin
2026-02-09 21:23:07 -08:00
parent 94946eb7a8
commit 35f4cab0dc
8 changed files with 79 additions and 16 deletions
+3 -1
View File
@@ -1,3 +1,5 @@
import { logger } from '../logger.js';
export interface RetryConfig {
/** Maximum number of retry attempts (default: 3). Does not count the initial attempt. */
maxRetries: number;
@@ -60,7 +62,7 @@ export async function withRetry<T>(
const delay = Math.min(baseDelay, config.maxDelayMs);
const jitter = delay * (0.5 + Math.random() * 0.5); // 50-100% of delay
console.debug(
logger.debug(
`[retry] ${label ?? 'operation'} attempt ${attempt + 1}/${config.maxRetries} failed: ${lastError.message}. Retrying in ${Math.round(jitter)}ms...`,
);
+11 -10
View File
@@ -1,6 +1,7 @@
import type { ChatRequest, ChatResponse, ChatStreamEvent, ModelClient } from './types.js';
import { withRetry } from './retry.js';
import type { RetryConfig } from './retry.js';
import { logger } from '../logger.js';
export type ModelTier = 'fast' | 'default' | 'complex' | 'local';
@@ -76,7 +77,7 @@ export class ModelRouter implements ModelClient {
return await primaryClient.chat(request);
} catch (error) {
errors.push(error instanceof Error ? error : new Error(String(error)));
console.debug(`Primary model failed: ${errors[0].message}`);
logger.debug(`Primary model failed: ${errors[0].message}`);
}
// Try tier-specific fallbacks first
@@ -84,12 +85,12 @@ export class ModelRouter implements ModelClient {
for (let i = 0; i < tierFallbackList.length; i++) {
try {
const reason = `Primary model failed (${errors[0].message}), using tier fallback #${i + 1}`;
console.debug(reason);
logger.debug(reason);
const response = await tierFallbackList[i].chat(request);
return { ...response, fallback: true, fallbackReason: reason };
} catch (error) {
errors.push(error instanceof Error ? error : new Error(String(error)));
console.debug(`Tier fallback #${i + 1} failed: ${errors[errors.length - 1].message}`);
logger.debug(`Tier fallback #${i + 1} failed: ${errors[errors.length - 1].message}`);
}
}
@@ -98,12 +99,12 @@ export class ModelRouter implements ModelClient {
const fallbackClient = this.fallbackChain[i];
try {
const reason = `Primary model failed (${errors[0].message}), using global fallback #${i + 1}`;
console.debug(reason);
logger.debug(reason);
const response = await fallbackClient.chat(request);
return { ...response, fallback: true, fallbackReason: reason };
} catch (error) {
errors.push(error instanceof Error ? error : new Error(String(error)));
console.debug(`Global fallback #${i + 1} failed: ${errors[errors.length - 1].message}`);
logger.debug(`Global fallback #${i + 1} failed: ${errors[errors.length - 1].message}`);
}
}
@@ -121,7 +122,7 @@ export class ModelRouter implements ModelClient {
if (event.type === 'error') {
hasError = true;
primaryError = event.error?.message ?? 'Unknown error';
console.debug(`Primary stream failed: ${primaryError}`);
logger.debug(`Primary stream failed: ${primaryError}`);
break;
}
yield event;
@@ -139,14 +140,14 @@ export class ModelRouter implements ModelClient {
if (!fallbackClient.chatStream) continue;
const reason = `Primary model failed (${primaryError}), using tier fallback #${i + 1}`;
console.debug(reason);
logger.debug(reason);
yield { type: 'fallback_warning', fallbackReason: reason };
let hasError = false;
for await (const event of fallbackClient.chatStream(request)) {
if (event.type === 'error') {
hasError = true;
console.debug(`Tier fallback stream #${i + 1} failed: ${event.error?.message}`);
logger.debug(`Tier fallback stream #${i + 1} failed: ${event.error?.message}`);
break;
}
yield event;
@@ -161,14 +162,14 @@ export class ModelRouter implements ModelClient {
if (!fallbackClient.chatStream) continue;
const reason = `Primary model failed (${primaryError}), using global fallback #${i + 1}`;
console.debug(reason);
logger.debug(reason);
yield { type: 'fallback_warning', fallbackReason: reason };
let hasError = false;
for await (const event of fallbackClient.chatStream(request)) {
if (event.type === 'error') {
hasError = true;
console.debug(`Global fallback stream #${i + 1} failed: ${event.error?.message}`);
logger.debug(`Global fallback stream #${i + 1} failed: ${event.error?.message}`);
break;
}
yield event;