refactor(gui): Use structured logging over string parsing

This commit is contained in:
rhit-lid2
2026-07-19 16:08:55 +05:30
parent c2926261a1
commit f8977a14c6
6 changed files with 112 additions and 52 deletions

View File

@@ -29,24 +29,37 @@ export function PromptModal({ entry, onClose }: PromptModalProps) {
systemPrompt: string,
userContext: string,
inputTokens: number,
sectionsObj?: {
worldInfo: string;
memoryLedger: string;
cognitiveBuffer: string;
},
) => {
const recentHeader = "=== COGNITIVE BUFFER ===";
const ledgerHeader = "=== MEMORY LEDGER ===";
const recentIdx = userContext.indexOf(recentHeader);
let worldStr = userContext;
let worldStr = "";
let recentStr = "";
let ledgerStr = "";
if (recentIdx !== -1) {
worldStr = userContext.substring(0, recentIdx).trim();
const rest = userContext.substring(recentIdx).trim();
const ledgerIdx = rest.indexOf(ledgerHeader);
if (ledgerIdx !== -1) {
recentStr = rest.substring(0, ledgerIdx).trim();
ledgerStr = rest.substring(ledgerIdx).trim();
} else {
recentStr = rest;
if (sectionsObj) {
worldStr = sectionsObj.worldInfo;
recentStr = sectionsObj.cognitiveBuffer;
ledgerStr = sectionsObj.memoryLedger;
} else {
const recentHeader = "=== COGNITIVE BUFFER ===";
const ledgerHeader = "=== MEMORY LEDGER ===";
const recentIdx = userContext.indexOf(recentHeader);
worldStr = userContext;
if (recentIdx !== -1) {
worldStr = userContext.substring(0, recentIdx).trim();
const rest = userContext.substring(recentIdx).trim();
const ledgerIdx = rest.indexOf(ledgerHeader);
if (ledgerIdx !== -1) {
recentStr = rest.substring(0, ledgerIdx).trim();
ledgerStr = rest.substring(ledgerIdx).trim();
} else {
recentStr = rest;
}
}
}
@@ -144,6 +157,7 @@ export function PromptModal({ entry, onClose }: PromptModalProps) {
entry.rawPrompt.systemPrompt,
entry.rawPrompt.userContext,
entry.usage.inputTokens,
entry.rawPrompt.sections,
)
: null;
const decoderBreakdown =

View File

@@ -20,6 +20,11 @@ export interface LogEntry {
rawPrompt?: {
systemPrompt: string;
userContext: string;
sections?: {
worldInfo: string;
memoryLedger: string;
cognitiveBuffer: string;
};
};
usage?: {
inputTokens: number;

View File

@@ -165,6 +165,11 @@ export async function processNpcTurn(
narrativeProse: result.narrativeProse,
intents: [],
timestamp: worldState.clock.get().toISOString(),
rawPrompt: {
systemPrompt: result.systemPrompt || "",
userContext: result.userContext || "",
sections: result.promptComponents,
},
};
if (
@@ -175,10 +180,6 @@ export async function processNpcTurn(
session.actorProvider.lastCalls[
session.actorProvider.lastCalls.length - 1
];
entry.rawPrompt = {
systemPrompt: actorCall.systemPrompt,
userContext: actorCall.userContext,
};
entry.usage = actorCall.usage;
}
@@ -243,8 +244,9 @@ export async function executePlayerAction(
intents: [],
timestamp: worldState.clock.get().toISOString(),
rawPrompt: {
systemPrompt: ctx.systemPrompt,
userContext: ctx.userContext,
systemPrompt: result.systemPrompt || ctx.systemPrompt,
userContext: result.userContext || ctx.userContext,
sections: result.promptComponents,
},
};

View File

@@ -54,16 +54,27 @@ export class ActorPromptBuilder {
private ledgerLimit = 5,
) {}
/**
* Assembles the system prompt and user context for a given entity.
*/
/**
* Assembles the system prompt and user context for a given entity.
*/
build(
worldState: WorldState,
entity: Entity,
): { systemPrompt: string; userContext: string } {
): {
systemPrompt: string;
userContext: string;
sections: {
worldInfo: string;
memoryLedger: string;
cognitiveBuffer: string;
};
} {
const systemPrompt = this.buildSystemPrompt();
const userContext = this.buildUserContext(worldState, entity);
return { systemPrompt, userContext };
const { userContext, sections } = this.buildUserContext(worldState, entity);
return { systemPrompt, userContext, sections };
}
private buildSystemPrompt(): string {
@@ -84,23 +95,28 @@ Guidelines:
- Stay strictly within what your character knows. Do not invent knowledge that doesn't exist or act on it.
- You are limited by just your memory. If your memory is limited, then that's all you can remember. If you do make stuff up then that's lying. Which is allowed, but remember that you're lying
- Only describe your character's own actions, spoken words, and internal reactions. Do NOT narrate the environment or your surroundings, or other characters' actions.
- Be clear about who or what you are interacting with.
".
`.trim();
}
private buildUserContext(worldState: WorldState, entity: Entity): string {
const sections: string[] = [];
private buildUserContext(
worldState: WorldState,
entity: Entity,
): {
userContext: string;
sections: {
worldInfo: string;
memoryLedger: string;
cognitiveBuffer: string;
};
} {
const now = worldState.clock.get();
// --- Subjective present time ---
sections.push(
`=== CURRENT MOMENT ===\nIt is ${now.toISOString()} right now.`,
);
// --- Subjective world state (self + perceived entities + co-location) ---
sections.push(
`=== THE WORLD AS YOU PERCEIVE IT ===\n${serializeSubjectiveWorldState(worldState, entity.id)}`,
);
// --- Subjective present time & world state ---
const momentStr = `=== CURRENT MOMENT ===\nIt is ${now.toISOString()} right now.`;
const perceivedStr = `=== THE WORLD AS YOU PERCEIVE IT ===\n${serializeSubjectiveWorldState(worldState, entity.id)}`;
const worldInfo = `${momentStr}\n\n${perceivedStr}`;
// Fetch recent buffer entries once
let recentEntries: BufferEntry[] = [];
@@ -112,16 +128,6 @@ Guidelines:
}
}
// --- Cognitive Buffer ---
const memorySection = this.buildCognitiveBufferSection(
entity,
recentEntries,
now,
);
if (memorySection) {
sections.push(memorySection);
}
// --- Recalled Memory Ledger ---
const ledgerSection = this.buildLedgerSection(
worldState,
@@ -129,11 +135,30 @@ Guidelines:
recentEntries,
now,
);
if (ledgerSection) {
sections.push(ledgerSection);
}
const memoryLedger = ledgerSection || "";
return sections.join("\n\n");
// --- Cognitive Buffer ---
const memorySection = this.buildCognitiveBufferSection(
entity,
recentEntries,
now,
);
const cognitiveBuffer = memorySection || "";
// Assemble final user context
const parts: string[] = [worldInfo];
if (memoryLedger) parts.push(memoryLedger);
if (cognitiveBuffer) parts.push(cognitiveBuffer);
const userContext = parts.join("\n\n");
return {
userContext,
sections: {
worldInfo,
memoryLedger,
cognitiveBuffer,
},
};
}
private buildCognitiveBufferSection(

View File

@@ -54,6 +54,13 @@ export interface ActorTurnResult {
narrativeProse: string;
/** The decoded intent sequence (split/classified from the prose). */
intents: IntentSequence;
systemPrompt?: string;
userContext?: string;
promptComponents?: {
worldInfo: string;
memoryLedger: string;
cognitiveBuffer: string;
};
}
/**
@@ -116,7 +123,7 @@ export class ActorAgent {
);
}
const { systemPrompt, userContext } = this.promptBuilder.build(
const { systemPrompt, userContext, sections } = this.promptBuilder.build(
worldState,
entity,
);
@@ -145,6 +152,9 @@ export class ActorAgent {
return {
narrativeProse,
intents,
systemPrompt,
userContext,
promptComponents: sections,
};
}
}

View File

@@ -128,14 +128,18 @@ describe("Talking Room Demo Scenario Test (Tier 1)", () => {
// 7. Assert initial pre-seeded memories
const alphaMemories = bufferRepo.listForOwner(alphaId);
expect(alphaMemories).toHaveLength(2);
expect(alphaMemories).toHaveLength(3);
expect(alphaMemories[0].id).toBe("ab3f29d2-cf11-4111-9a99-b13c126d123e");
expect(alphaMemories[0].intent.type).toBe("monologue");
expect(alphaMemories[0].intent.content).toContain("jail");
expect(alphaMemories[1].id).toBe("zz3f29d2-as11-9811-9a99-b13c126d123e");
expect(alphaMemories[1].id).toBe("10ak29d2-as11-9811-9a99-b13c126d123e");
expect(alphaMemories[1].intent.type).toBe("action");
expect(alphaMemories[1].intent.content).toContain("sleep");
expect(alphaMemories[1].intent.content).toContain("another man");
expect(alphaMemories[2].id).toBe("zz3f29d2-as11-9811-9a99-b13c126d123e");
expect(alphaMemories[2].intent.type).toBe("action");
expect(alphaMemories[2].intent.content).toContain("sleep");
const betaMemories = bufferRepo.listForOwner(betaId);
expect(betaMemories).toHaveLength(1);