feat(openclaw): configurable logging with structured output (#739)
* feat(openclaw): configurable logging with structured output Replace raw console.log/warn/error spam with a structured logger. New plugin settings: logLevel, logSummaryIntervalMs, logCompact. Bank mission log demoted to verbose-only. Retain/recall batched into periodic summaries. Each recall now shows memory count injected. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * use api.logger for framework-consistent output, show autoRecall/autoRetain on init Route all log output through OpenClaw's api.logger instead of raw console calls. Matches mem0 plugin style. Startup now shows mode + feature flags. Dropped logCompact setting (framework handles formatting). Added subtle slate-blue color to hindsight prefix for visual differentiation. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * add bank name to init and summary logs, fix singular/plural consistency Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * rename log levels to standard: off, error, warning, info, debug Per review feedback — use standard level names instead of custom ones. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: billy <billy@oclaw.local> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Co-authored-by: Nicolò Boschi <boschi1997@gmail.com>
This commit is contained in:
parent
f8285b7b90
commit
d441ab814d
5 changed files with 228 additions and 28 deletions
|
|
@ -202,9 +202,20 @@
|
|||
},
|
||||
"debug": {
|
||||
"type": "boolean",
|
||||
"description": "Enable debug logging for Hindsight plugin operations.",
|
||||
"description": "Enable debug logging for Hindsight plugin operations. Equivalent to logLevel: 'debug'.",
|
||||
"default": false
|
||||
}
|
||||
},
|
||||
"logLevel": {
|
||||
"type": "string",
|
||||
"description": "Console log verbosity. 'off' = no output, 'error' = errors only, 'warning' = errors + warnings, 'info' = key events + periodic summaries, 'debug' = all details.",
|
||||
"enum": ["off", "error", "warning", "info", "debug"],
|
||||
"default": "info"
|
||||
},
|
||||
"logSummaryIntervalMs": {
|
||||
"type": "number",
|
||||
"description": "Interval in ms to batch retain/recall log summaries. 0 = log every event individually. Default: 300000 (5 min).",
|
||||
"default": 300000
|
||||
},
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
|
|
@ -331,6 +342,13 @@
|
|||
},
|
||||
"debug": {
|
||||
"label": "Debug"
|
||||
}
|
||||
},
|
||||
"logLevel": {
|
||||
"label": "Log Level"
|
||||
},
|
||||
"logSummaryIntervalMs": {
|
||||
"label": "Log Summary Interval (ms)",
|
||||
"placeholder": "300000"
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import type {
|
|||
RecallRequest,
|
||||
RecallResponse,
|
||||
} from './types.js';
|
||||
import * as log from './logger.js';
|
||||
|
||||
const execFileAsync = promisify(execFile);
|
||||
|
||||
|
|
@ -106,9 +107,9 @@ export class HindsightClient {
|
|||
const body = await res.text().catch(() => '');
|
||||
throw new Error(`HTTP ${res.status}: ${body}`);
|
||||
}
|
||||
console.log(`[Hindsight] Bank mission set via HTTP`);
|
||||
log.verbose('bank mission set via HTTP');
|
||||
} catch (error) {
|
||||
console.warn(`[Hindsight] Could not set bank mission (bank may not exist yet): ${error}`);
|
||||
log.warn(`could not set bank mission (bank may not exist yet): ${error}`);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -117,10 +118,10 @@ export class HindsightClient {
|
|||
const args = [...baseArgs, '--profile', 'openclaw', 'bank', 'mission', this.bankId, sanitize(mission)];
|
||||
try {
|
||||
const { stdout } = await execFileAsync(cmd, args, { maxBuffer: MAX_BUFFER });
|
||||
console.log(`[Hindsight] Bank mission set: ${stdout.trim()}`);
|
||||
log.verbose(`bank mission set: ${stdout.trim()}`);
|
||||
} catch (error) {
|
||||
// Don't fail if mission set fails - bank might not exist yet, will be created on first retain
|
||||
console.warn(`[Hindsight] Could not set bank mission (bank may not exist yet): ${error}`);
|
||||
log.warn(`could not set bank mission (bank may not exist yet): ${error}`);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -157,7 +158,7 @@ export class HindsightClient {
|
|||
}
|
||||
|
||||
const data = await res.json();
|
||||
console.log(`[Hindsight] Retained via HTTP (async): ${JSON.stringify(data).substring(0, 200)}`);
|
||||
log.verbose(`retained via HTTP (async): ${JSON.stringify(data).substring(0, 200)}`);
|
||||
|
||||
return {
|
||||
message: 'Memory queued for background processing',
|
||||
|
|
@ -183,7 +184,7 @@ export class HindsightClient {
|
|||
const args = [...baseArgs, '--profile', 'openclaw', 'memory', 'retain-files', this.bankId, tempFile, '--async'];
|
||||
|
||||
const { stdout } = await execFileAsync(cmd, args, { maxBuffer: MAX_BUFFER });
|
||||
console.log(`[Hindsight] Retained (async): ${stdout.trim()}`);
|
||||
log.verbose(`retained (async): ${stdout.trim()}`);
|
||||
|
||||
return {
|
||||
message: 'Memory queued for background processing',
|
||||
|
|
@ -211,7 +212,7 @@ export class HindsightClient {
|
|||
// Defense-in-depth: truncate query to stay under API's 500-token limit
|
||||
const MAX_QUERY_CHARS = 800;
|
||||
const query = request.query.length > MAX_QUERY_CHARS
|
||||
? (console.warn(`[Hindsight] Truncating recall query from ${request.query.length} to ${MAX_QUERY_CHARS} chars`),
|
||||
? (log.warn(`truncating recall query from ${request.query.length} to ${MAX_QUERY_CHARS} chars`),
|
||||
request.query.substring(0, MAX_QUERY_CHARS))
|
||||
: request.query;
|
||||
const body: Record<string, unknown> = {
|
||||
|
|
|
|||
|
|
@ -4,11 +4,13 @@ import { HindsightClient, type HindsightClientOptions } from './client.js';
|
|||
import { createHash } from 'crypto';
|
||||
import { dirname } from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
import * as log from './logger.js';
|
||||
import { configureLogger, setApiLogger, stopLogger } from './logger.js';
|
||||
|
||||
// Debug logging: silent by default, enable with debug: true in plugin config
|
||||
// Debug logging: silent by default, enable with debug: true or logLevel: 'debug'
|
||||
let debugEnabled = false;
|
||||
const debug = (...args: unknown[]) => {
|
||||
if (debugEnabled) console.log(...args);
|
||||
if (debugEnabled) log.verbose(args.map(a => typeof a === 'string' ? a.replace(/^\[Hindsight\]\s*/, '') : String(a)).join(' '));
|
||||
};
|
||||
|
||||
// Module-level state
|
||||
|
|
@ -113,7 +115,7 @@ async function lazyReinit(): Promise<void> {
|
|||
initPromise = Promise.resolve();
|
||||
debug('[Hindsight] ✓ Lazy re-initialization succeeded');
|
||||
} catch (error) {
|
||||
console.warn(`[Hindsight] Lazy re-initialization failed (will retry in ${REINIT_COOLDOWN_MS / 1000}s):`, error instanceof Error ? error.message : error);
|
||||
log.warn(`lazy re-init failed (retry in ${REINIT_COOLDOWN_MS / 1000}s): ${error instanceof Error ? error.message : error}`);
|
||||
} finally {
|
||||
isReinitInProgress = false;
|
||||
}
|
||||
|
|
@ -175,7 +177,7 @@ if (typeof global !== 'undefined') {
|
|||
debug(`[Hindsight] Set mission for new bank: ${bankId}`);
|
||||
} catch (error) {
|
||||
// Log but don't fail - bank mission is not critical
|
||||
console.warn(`[Hindsight] Could not set bank mission for ${bankId}: ${error}`);
|
||||
log.warn(`could not set bank mission for ${bankId}: ${error}`);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -453,7 +455,7 @@ export function deriveBankId(ctx: PluginHookAgentContext | undefined, pluginConf
|
|||
const validFields = new Set(['agent', 'channel', 'user', 'provider']);
|
||||
for (const f of fields) {
|
||||
if (!validFields.has(f)) {
|
||||
console.warn(`[Hindsight] Unknown dynamicBankGranularity field "${f}" — will resolve to "unknown" in bank ID. Valid fields: agent, channel, user, provider`);
|
||||
log.warn(`unknown dynamicBankGranularity field "${f}" — will resolve to "unknown". Valid: agent, channel, user, provider`);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -738,7 +740,15 @@ export default function (api: MoltbotPluginAPI) {
|
|||
|
||||
// Get plugin config first (needed for LLM detection and debug flag)
|
||||
const pluginConfig = getPluginConfig(api);
|
||||
debugEnabled = pluginConfig.debug ?? false;
|
||||
// If logLevel is 'debug', also enable legacy debug flag
|
||||
debugEnabled = pluginConfig.debug ?? (pluginConfig.logLevel === 'debug');
|
||||
|
||||
// Configure structured logger — route through OpenClaw's api.logger for consistent formatting
|
||||
if (api.logger) setApiLogger(api.logger);
|
||||
configureLogger({
|
||||
logLevel: pluginConfig.logLevel ?? (pluginConfig.debug ? 'debug' : 'info'),
|
||||
logSummaryIntervalMs: pluginConfig.logSummaryIntervalMs,
|
||||
});
|
||||
|
||||
// Store config globally for bank ID derivation in hooks
|
||||
currentPluginConfig = pluginConfig;
|
||||
|
|
@ -817,6 +827,12 @@ export default function (api: MoltbotPluginAPI) {
|
|||
await client.setBankMission(pluginConfig.bankMission);
|
||||
}
|
||||
|
||||
if (!isInitialized) {
|
||||
const mode = 'external API';
|
||||
const autoRecall = pluginConfig.autoRecall !== false;
|
||||
const autoRetain = pluginConfig.autoRetain !== false;
|
||||
log.info(`initialized (mode: ${mode}, bank: ${defaultBankId}, autoRecall: ${autoRecall}, autoRetain: ${autoRetain})`);
|
||||
}
|
||||
isInitialized = true;
|
||||
debug('[Hindsight] ✓ Ready (external API mode)');
|
||||
} else {
|
||||
|
|
@ -856,11 +872,17 @@ export default function (api: MoltbotPluginAPI) {
|
|||
await client.setBankMission(pluginConfig.bankMission);
|
||||
}
|
||||
|
||||
if (!isInitialized) {
|
||||
const mode = 'local daemon';
|
||||
const autoRecall = pluginConfig.autoRecall !== false;
|
||||
const autoRetain = pluginConfig.autoRetain !== false;
|
||||
log.info(`initialized (mode: ${mode}, bank: ${defaultBankId}, autoRecall: ${autoRecall}, autoRetain: ${autoRetain})`);
|
||||
}
|
||||
isInitialized = true;
|
||||
debug('[Hindsight] ✓ Ready');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[Hindsight] Initialization error:', error);
|
||||
log.error('initialization error', error);
|
||||
throw error;
|
||||
}
|
||||
})();
|
||||
|
|
@ -880,7 +902,7 @@ export default function (api: MoltbotPluginAPI) {
|
|||
try {
|
||||
await initPromise;
|
||||
} catch (error) {
|
||||
console.error('[Hindsight] Initial initialization failed:', error);
|
||||
log.error('initial initialization failed', error);
|
||||
// Continue to health check below
|
||||
}
|
||||
}
|
||||
|
|
@ -894,7 +916,7 @@ export default function (api: MoltbotPluginAPI) {
|
|||
debug('[Hindsight] External API is healthy');
|
||||
return;
|
||||
} catch (error) {
|
||||
console.error('[Hindsight] External API health check failed:', error);
|
||||
log.error('external API health check failed', error);
|
||||
// Reset state for reinitialization attempt
|
||||
client = null;
|
||||
clientOptions = null;
|
||||
|
|
@ -1003,9 +1025,10 @@ export default function (api: MoltbotPluginAPI) {
|
|||
banksWithMissionSet.clear();
|
||||
isInitialized = false;
|
||||
|
||||
stopLogger();
|
||||
debug('[Hindsight] Service stopped');
|
||||
} catch (error) {
|
||||
console.error('[Hindsight] Service stop error:', error);
|
||||
log.error('service stop error', error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
|
@ -1143,6 +1166,8 @@ ${memoriesFormatted}
|
|||
</hindsight_memories>`;
|
||||
|
||||
debug(`[Hindsight] Auto-recall: Injecting ${results.length} memories from bank ${bankId}`);
|
||||
log.info(`injecting ${results.length} memories into context (bank: ${bankId})`);
|
||||
log.trackRecall(bankId, results.length);
|
||||
|
||||
// Inject recalled memories. Position is configurable to preserve prompt caching
|
||||
// when agents have large static system prompts.
|
||||
|
|
@ -1158,11 +1183,11 @@ ${memoriesFormatted}
|
|||
}
|
||||
} catch (error) {
|
||||
if (error instanceof DOMException && error.name === 'TimeoutError') {
|
||||
console.warn(`[Hindsight] Auto-recall timed out after ${pluginConfig.recallTimeoutMs ?? DEFAULT_RECALL_TIMEOUT_MS}ms, skipping memory injection`);
|
||||
log.warn(`[Hindsight] Auto-recall timed out after ${pluginConfig.recallTimeoutMs ?? DEFAULT_RECALL_TIMEOUT_MS}ms, skipping memory injection`);
|
||||
} else if (error instanceof Error && error.name === 'AbortError') {
|
||||
console.warn(`[Hindsight] Auto-recall aborted after ${pluginConfig.recallTimeoutMs ?? DEFAULT_RECALL_TIMEOUT_MS}ms, skipping memory injection`);
|
||||
log.warn(`[Hindsight] Auto-recall aborted after ${pluginConfig.recallTimeoutMs ?? DEFAULT_RECALL_TIMEOUT_MS}ms, skipping memory injection`);
|
||||
} else {
|
||||
console.error('[Hindsight] Auto-recall error:', error);
|
||||
log.error('auto-recall error', error);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
|
@ -1250,7 +1275,7 @@ ${memoriesFormatted}
|
|||
// Wait for client to be ready
|
||||
const clientGlobal = (global as any).__hindsightClient;
|
||||
if (!clientGlobal) {
|
||||
console.warn('[Hindsight] Client global not found, skipping retain');
|
||||
log.warn('client global not found, skipping retain');
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -1259,7 +1284,7 @@ ${memoriesFormatted}
|
|||
// Get client configured for this context's bank (async to handle mission setup)
|
||||
const client = await clientGlobal.getClientForContext(effectiveCtxForRetain);
|
||||
if (!client) {
|
||||
console.warn('[Hindsight] Client not initialized, skipping retain');
|
||||
log.warn('client not initialized, skipping retain');
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -1282,16 +1307,17 @@ ${memoriesFormatted}
|
|||
},
|
||||
});
|
||||
|
||||
log.trackRetain(bankId, messageCount);
|
||||
debug(`[Hindsight] Retained ${messageCount} messages to bank ${bankId} for session ${documentId}`);
|
||||
} catch (error) {
|
||||
console.error('[Hindsight] Error retaining messages:', error);
|
||||
log.error('error retaining messages', error);
|
||||
}
|
||||
});
|
||||
debug('[Hindsight] Hooks registered');
|
||||
} catch (error) {
|
||||
console.error('[Hindsight] Plugin loading error:', error);
|
||||
log.error('plugin loading error', error);
|
||||
if (error instanceof Error) {
|
||||
console.error('[Hindsight] Error stack:', error.stack);
|
||||
log.error('error stack', error.stack);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
|
|
|
|||
147
hindsight-integrations/openclaw/src/logger.ts
Normal file
147
hindsight-integrations/openclaw/src/logger.ts
Normal file
|
|
@ -0,0 +1,147 @@
|
|||
/**
|
||||
* Hindsight OpenClaw plugin logger.
|
||||
*
|
||||
* Routes output through OpenClaw's api.logger for consistent formatting
|
||||
* with other plugins (same colors/timestamps as mem0, etc.).
|
||||
*
|
||||
* Features:
|
||||
* - Configurable log level: 'off' | 'error' | 'warning' | 'info' | 'debug'
|
||||
* - Batched retain/recall summaries instead of per-event spam
|
||||
*/
|
||||
|
||||
export type LogLevel = 'off' | 'error' | 'warning' | 'info' | 'debug';
|
||||
|
||||
export interface LoggerConfig {
|
||||
/** Minimum severity to print. Default: 'info' */
|
||||
logLevel?: LogLevel;
|
||||
/** Interval in ms to print batched retain/recall summaries. 0 = print every event. Default: 300000 (5 min) */
|
||||
logSummaryIntervalMs?: number;
|
||||
}
|
||||
|
||||
// Muted blue (38;5;103 = slate/dusty blue from 256-color palette)
|
||||
const PREFIX = '\x1b[38;5;103mhindsight:\x1b[0m';
|
||||
|
||||
const LEVEL_RANK: Record<LogLevel, number> = {
|
||||
off: 0,
|
||||
error: 1,
|
||||
warning: 2,
|
||||
info: 3,
|
||||
debug: 4,
|
||||
};
|
||||
|
||||
// Output backend — set via setApiLogger, falls back to console
|
||||
let apiLogger: { info(msg: string): void; warn(msg: string): void; error(msg: string): void } = {
|
||||
info: (msg) => console.log(msg),
|
||||
warn: (msg) => console.warn(msg),
|
||||
error: (msg) => console.error(msg),
|
||||
};
|
||||
|
||||
// Batched summary state
|
||||
let retainCount = 0;
|
||||
let retainMsgTotal = 0;
|
||||
let recallCount = 0;
|
||||
let recallMemoriesCount = 0;
|
||||
const banksSeen = new Set<string>();
|
||||
let lastSummaryTime = Date.now();
|
||||
let summaryTimer: ReturnType<typeof setInterval> | null = null;
|
||||
|
||||
let currentLevel: LogLevel = 'info';
|
||||
let currentSummaryIntervalMs = 300_000; // 5 min
|
||||
|
||||
/** Bind to OpenClaw's api.logger for consistent output formatting */
|
||||
export function setApiLogger(logger: { info(msg: string): void; warn(msg: string): void; error(msg: string): void }): void {
|
||||
apiLogger = logger;
|
||||
}
|
||||
|
||||
export function configureLogger(cfg: LoggerConfig): void {
|
||||
currentLevel = cfg.logLevel ?? 'info';
|
||||
currentSummaryIntervalMs = cfg.logSummaryIntervalMs ?? 300_000;
|
||||
|
||||
// Restart summary timer
|
||||
if (summaryTimer) {
|
||||
clearInterval(summaryTimer);
|
||||
summaryTimer = null;
|
||||
}
|
||||
if (currentSummaryIntervalMs > 0 && LEVEL_RANK[currentLevel] >= LEVEL_RANK['info']) {
|
||||
summaryTimer = setInterval(flushSummary, currentSummaryIntervalMs);
|
||||
summaryTimer.unref?.(); // don't keep process alive
|
||||
}
|
||||
}
|
||||
|
||||
function allowed(level: LogLevel): boolean {
|
||||
return LEVEL_RANK[currentLevel] >= LEVEL_RANK[level];
|
||||
}
|
||||
|
||||
/** Info-level log (requires 'info' or higher) */
|
||||
export function info(msg: string): void {
|
||||
if (!allowed('info')) return;
|
||||
apiLogger.info(`${PREFIX} ${msg}`);
|
||||
}
|
||||
|
||||
/** Debug log (requires 'debug') */
|
||||
export function verbose(msg: string): void {
|
||||
if (!allowed('debug')) return;
|
||||
apiLogger.info(`${PREFIX} ${msg}`);
|
||||
}
|
||||
|
||||
/** Warning (requires 'warning' or higher) */
|
||||
export function warn(msg: string): void {
|
||||
if (!allowed('warning')) return;
|
||||
apiLogger.warn(`${PREFIX} ${msg}`);
|
||||
}
|
||||
|
||||
/** Error (requires 'error' or higher) */
|
||||
export function error(msg: string, err?: unknown): void {
|
||||
if (!allowed('error')) return;
|
||||
const detail = err instanceof Error ? err.message : (err ? String(err) : '');
|
||||
apiLogger.error(`${PREFIX} ${detail ? `${msg}: ${detail}` : msg}`);
|
||||
}
|
||||
|
||||
/** Track a retain event for batched summary */
|
||||
export function trackRetain(bankId: string, messageCount: number): void {
|
||||
retainCount++;
|
||||
retainMsgTotal += messageCount;
|
||||
banksSeen.add(bankId);
|
||||
if (currentSummaryIntervalMs === 0 && allowed('info')) {
|
||||
apiLogger.info(`${PREFIX} auto-retained ${messageCount} messages (bank: ${bankId})`);
|
||||
}
|
||||
}
|
||||
|
||||
/** Track a recall event for batched summary */
|
||||
export function trackRecall(bankId: string, memoriesFound: number): void {
|
||||
recallCount++;
|
||||
recallMemoriesCount += memoriesFound;
|
||||
banksSeen.add(bankId);
|
||||
// per-event logging is handled by info() call at the injection site
|
||||
}
|
||||
|
||||
/** Flush the batched summary to console */
|
||||
export function flushSummary(): void {
|
||||
if (!allowed('info')) return;
|
||||
if (retainCount === 0 && recallCount === 0) return;
|
||||
|
||||
const elapsed = Math.round((Date.now() - lastSummaryTime) / 1000);
|
||||
const parts: string[] = [];
|
||||
if (recallCount > 0) parts.push(`${recallCount} recalls (${recallMemoriesCount} memories injected)`);
|
||||
if (retainCount > 0) parts.push(`${retainCount} retains (${retainMsgTotal} messages captured)`);
|
||||
const bankList = [...banksSeen];
|
||||
const bankLabel = bankList.length === 1 ? 'bank' : 'banks';
|
||||
const banks = bankList.length > 0 ? ` (${bankLabel}: ${bankList.join(', ')})` : '';
|
||||
apiLogger.info(`${PREFIX} ${parts.join(', ')} in ${elapsed}s${banks}`);
|
||||
|
||||
retainCount = 0;
|
||||
retainMsgTotal = 0;
|
||||
recallCount = 0;
|
||||
recallMemoriesCount = 0;
|
||||
banksSeen.clear();
|
||||
lastSummaryTime = Date.now();
|
||||
}
|
||||
|
||||
/** Cleanup (call on plugin stop) */
|
||||
export function stopLogger(): void {
|
||||
flushSummary();
|
||||
if (summaryTimer) {
|
||||
clearInterval(summaryTimer);
|
||||
summaryTimer = null;
|
||||
}
|
||||
}
|
||||
|
|
@ -11,6 +11,12 @@ export interface MoltbotPluginAPI {
|
|||
registerService(config: ServiceConfig): void;
|
||||
// OpenClaw hook handler signature: (event, ctx?) where ctx contains channel/sender info
|
||||
on(event: string, handler: (event: any, ctx?: any) => void | Promise<void | PluginPromptHookResult>): void;
|
||||
// OpenClaw framework logger — handles coloring/formatting consistently across plugins
|
||||
logger: {
|
||||
info(msg: string): void;
|
||||
warn(msg: string): void;
|
||||
error(msg: string): void;
|
||||
};
|
||||
// Add more as needed
|
||||
}
|
||||
|
||||
|
|
@ -75,6 +81,8 @@ export interface PluginConfig {
|
|||
recallPromptPreamble?: string; // Prompt preamble placed above recalled memories. Default: built-in guidance text.
|
||||
recallInjectionPosition?: 'prepend' | 'append' | 'user'; // Where to inject recalled memories. 'prepend' = start of system prompt (default), 'append' = end of system prompt (preserves prompt cache), 'user' = before user message.
|
||||
debug?: boolean; // Enable debug logging (default: false)
|
||||
logLevel?: 'off' | 'error' | 'warning' | 'info' | 'debug'; // Console log verbosity (default: 'info').
|
||||
logSummaryIntervalMs?: number; // Batch retain/recall log summaries over this interval in ms. 0 = log every event. Default: 300000 (5 min).
|
||||
}
|
||||
|
||||
export interface ServiceConfig {
|
||||
|
|
|
|||
Loading…
Reference in a new issue