feat: wire LogService into LLM layer for error/warning logging

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
root
2026-03-10 02:22:20 +00:00
parent 191770acbe
commit ff53096d94
4 changed files with 16 additions and 5 deletions
+2 -2
View File
@@ -65,7 +65,7 @@ export class GameLoop {
this.runtimeConstants = createRuntimeConstants();
this.world = new World();
this.map = new GameMap();
this.llmService = createLlmService();
this.llmService = createLlmService(this.logService);
this.narrationService = createNarrationService(this.llmService);
this.eventMemoryService = createEventMemoryService();
this.thoughtSystem = createThoughtSystem(this.llmService, this.narrationService, this.eventMemoryService);
@@ -228,7 +228,7 @@ export class GameLoop {
}
generateNpcBackstory(entityId: number): void {
generateBackstoryAndDesires(this.world, entityId, this.llmService, this.tick);
generateBackstoryAndDesires(this.world, entityId, this.llmService, this.tick, this.logService);
}
private spawnInitialNPCs(count: number): void {
+3
View File
@@ -4,6 +4,7 @@ import type { LlmService } from './llmService.js';
import type { ItemRegistry } from '../industry/itemRegistry.js';
import type { RecipeRegistry } from '../industry/recipeRegistry.js';
import type { StructureData } from '../systems/buildingSystem.js';
import type { LogService } from '../services/logService.js';
const STAT_KEYS: (keyof Stats)[] = [
'strength', 'dexterity', 'constitution', 'intelligence', 'perception',
@@ -87,6 +88,7 @@ export async function generateBackstoryAndDesires(
entityId: number,
llmService: LlmService,
tick: number,
logService?: LogService,
): Promise<void> {
const name = world.getComponent<string>(entityId, 'name');
const stats = world.getComponent<Stats>(entityId, 'stats');
@@ -109,6 +111,7 @@ export async function generateBackstoryAndDesires(
parsed = JSON.parse(result);
} catch {
// JSON parse failed — use raw string as backstory fallback
logService?.log('warning', 'LLM', `JSON parse failed for backstory (entity ${entityId}), using raw string`);
world.addComponent<string>(entityId, 'backstory', result);
return;
}
+7 -2
View File
@@ -5,6 +5,7 @@ import { createUsageCounters } from './usageCounters.js';
import { renderTemplate } from './promptTemplate.js';
import { templates } from './templates.js';
import { createTokenTracker, type TokenTracker } from './tokenTracker.js';
import type { LogService } from '../services/logService.js';
export interface LlmService {
generate(templateName: string, variables: Record<string, string>): Promise<string | null>;
@@ -17,7 +18,7 @@ export interface LlmService {
tokenUsage(): TokenTracker;
}
export function createLlmService(): LlmService {
export function createLlmService(logService?: LogService): LlmService {
const config = getLlmConfig();
if (!config.enabled || !config.model) {
@@ -33,7 +34,7 @@ export function createLlmService(): LlmService {
};
}
const client = createOpenRouterClient(config);
const client = createOpenRouterClient(config, logService);
const queue = createGenerationQueue(client, {
requestsPerMinute: config.requestsPerMinute,
});
@@ -56,6 +57,7 @@ export function createLlmService(): LlmService {
currentModel = config.fallbackModel;
console.log(`[LLM] Switched to fallback model: ${currentModel}`);
logService?.log('warning', 'LLM', `Switched to fallback model: ${currentModel}`);
const resetTime = resetAt ?? getNextMidnightUTC();
const delay = Math.max(0, resetTime - Date.now());
@@ -65,6 +67,7 @@ export function createLlmService(): LlmService {
currentModel = config.model;
switchBackTimer = null;
console.log(`[LLM] Switched back to primary model: ${currentModel}`);
logService?.log('info', 'LLM', `Switched back to primary model: ${currentModel}`);
}, delay);
}
@@ -82,6 +85,7 @@ export function createLlmService(): LlmService {
const result = await queue.enqueue({ ...rendered, model: currentModel });
if (isRateLimited(result)) {
logService?.log('warning', 'LLM', 'Rate limited, switching to fallback');
switchToFallback(result.resetAt);
return null;
}
@@ -95,6 +99,7 @@ export function createLlmService(): LlmService {
const totalRequests = Object.values(counters.getStats()).reduce((sum, s) => sum + s.total, 0);
if (totalRequests % 100 === 0) {
console.log(`[LLM] ${counters.getSummary()}`);
logService?.log('info', 'LLM', counters.getSummary());
}
return result.content;
}
+4 -1
View File
@@ -1,5 +1,6 @@
import type { LlmConfig } from '../config/llmConfig.js';
import type { RenderedPrompt } from './promptTemplate.js';
import type { LogService } from '../services/logService.js';
const OPENROUTER_URL = 'https://openrouter.ai/api/v1/chat/completions';
@@ -38,7 +39,7 @@ export interface OpenRouterClient {
complete(request: CompletionRequest): Promise<CompletionResult>;
}
export function createOpenRouterClient(config: LlmConfig): OpenRouterClient {
export function createOpenRouterClient(config: LlmConfig, logService?: LogService): OpenRouterClient {
return {
async complete(request: CompletionRequest): Promise<CompletionResult> {
try {
@@ -72,6 +73,7 @@ export function createOpenRouterClient(config: LlmConfig): OpenRouterClient {
}
}
console.warn(`OpenRouter API error: ${response.status} ${response.statusText}`);
logService?.log('error', 'LLM', `OpenRouter API error: ${response.status} ${response.statusText}`);
return null;
}
@@ -88,6 +90,7 @@ export function createOpenRouterClient(config: LlmConfig): OpenRouterClient {
};
} catch (error) {
console.warn('OpenRouter request failed:', (error as Error).message);
logService?.log('error', 'LLM', `OpenRouter request failed: ${(error as Error).message}`);
return null;
}
},