mirror of
https://github.com/sortedcord/omnia.git
synced 2026-07-22 20:12:48 +05:30
refactor(architect): Use IPromptBuilder Interface for LLMValidator
This commit is contained in:
@@ -9,7 +9,7 @@ export interface ProcessResult extends ValidationResult {
|
||||
}
|
||||
|
||||
export class Architect {
|
||||
private validator: LLMValidator;
|
||||
public validator: LLMValidator;
|
||||
private timeDeltaGenerator: TimeDeltaGenerator;
|
||||
|
||||
constructor(
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
export * from "./llm-validator.js";
|
||||
export * from "./llm-validator-prompt-builder.js";
|
||||
export * from "./architect.js";
|
||||
export * from "./delta.js";
|
||||
|
||||
59
packages/architect/src/llm-validator-prompt-builder.ts
Normal file
59
packages/architect/src/llm-validator-prompt-builder.ts
Normal file
@@ -0,0 +1,59 @@
|
||||
import { WorldState, serializeObjectiveWorldState } from "@omnia/core";
|
||||
import { Intent } from "@omnia/intent";
|
||||
import { hydrateObjective } from "@omnia/voice";
|
||||
import { PromptBreakdown, PromptComponent, IPromptBuilder } from "@omnia/llm";
|
||||
|
||||
/**
|
||||
* Prompt builder for the LLM Validator (World Architect).
|
||||
* Separates prompt generation, structure, and component breakdowns.
|
||||
*/
|
||||
export class LLMValidatorPromptBuilder implements IPromptBuilder<
|
||||
[WorldState, Intent]
|
||||
> {
|
||||
build(worldState: WorldState, intent: Intent): PromptBreakdown {
|
||||
const serializedWorld = serializeObjectiveWorldState(worldState);
|
||||
|
||||
const systemPrompt = `
|
||||
You are the World Architect, a deterministic and objective judge of reality, physics, and narration for a simulation game.
|
||||
Your task is to judge whether a proposed action (Intent) by an actor is physically and logically possible given the current objective state of the world.
|
||||
Exempt dialogue or speech actions from validation (consider them always valid).
|
||||
Enforce logical boundaries such as:
|
||||
- Spatial boundaries (an actor cannot grab an object in another location unless they are there).
|
||||
- Physical boundaries (an actor cannot open a locked drawer without a key or breaking it).
|
||||
- State Boundaries (an actor cannot perform a task if their state doesn't allow them to do so).
|
||||
- State/Attribute constraints.
|
||||
- An actor can perform actions on themselves as long as it follows the boundaries stated above.
|
||||
|
||||
You must respond with a JSON object containing:
|
||||
- "isValid": boolean indicating if the action is possible/allowed.
|
||||
- "reason": a very short explanation of why the action is allowed or denied.
|
||||
`.trim();
|
||||
|
||||
const objectiveContent = hydrateObjective(intent.content, worldState);
|
||||
|
||||
const worldStateSection = `=== CURRENT WORLD STATE ===\nCurrent Time: ${worldState.clock.get().toISOString()}\nEntities & Attributes:\n${serializedWorld}`;
|
||||
const proposedActionSection = `=== PROPOSED ACTION ===\nActor ID: ${intent.actorId}\nType: ${intent.type}\nContent: "${objectiveContent}"\nTarget IDs: ${intent.targetIds.join(", ") || "(None)"}`;
|
||||
|
||||
const userContext = `${worldStateSection}\n\n${proposedActionSection}\n\nDecide if the proposed action is logically valid and physically possible.`;
|
||||
|
||||
const components: PromptComponent[] = [
|
||||
{ label: "System Prompt", type: "system", content: systemPrompt },
|
||||
{
|
||||
label: "Current World State",
|
||||
type: "world",
|
||||
content: worldStateSection,
|
||||
},
|
||||
{
|
||||
label: "Proposed Action",
|
||||
type: "input",
|
||||
content: proposedActionSection,
|
||||
},
|
||||
];
|
||||
|
||||
return {
|
||||
systemPrompt,
|
||||
userContext,
|
||||
components,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
import { z } from "zod";
|
||||
import { WorldState, serializeObjectiveWorldState } from "@omnia/core";
|
||||
import { ILLMProvider } from "@omnia/llm";
|
||||
import { WorldState } from "@omnia/core";
|
||||
import { ILLMProvider, PromptBreakdown } from "@omnia/llm";
|
||||
import { Intent } from "@omnia/intent";
|
||||
import { hydrateObjective } from "@omnia/voice";
|
||||
import { LLMValidatorPromptBuilder } from "./llm-validator-prompt-builder.js";
|
||||
|
||||
export const ValidationResultSchema = z.object({
|
||||
isValid: z.boolean(),
|
||||
@@ -12,7 +12,12 @@ export const ValidationResultSchema = z.object({
|
||||
export type ValidationResult = z.infer<typeof ValidationResultSchema>;
|
||||
|
||||
export class LLMValidator {
|
||||
constructor(private llmProvider: ILLMProvider) {}
|
||||
public lastResult: PromptBreakdown | null = null;
|
||||
private promptBuilder: LLMValidatorPromptBuilder;
|
||||
|
||||
constructor(private llmProvider: ILLMProvider) {
|
||||
this.promptBuilder = new LLMValidatorPromptBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates an action intent against the objective world state.
|
||||
@@ -25,6 +30,8 @@ export class LLMValidator {
|
||||
worldState: WorldState,
|
||||
intent: Intent,
|
||||
): Promise<ValidationResult> {
|
||||
this.lastResult = null;
|
||||
|
||||
// Defensive guard: monologue and thought intents bypass validation.
|
||||
if (intent.type === "monologue" || intent.type === "thought") {
|
||||
return {
|
||||
@@ -42,41 +49,16 @@ export class LLMValidator {
|
||||
};
|
||||
}
|
||||
|
||||
// 1. Serialize the objective world state for the LLM
|
||||
const serializedWorld = serializeObjectiveWorldState(worldState);
|
||||
const { systemPrompt, userContext, components } = this.promptBuilder.build(
|
||||
worldState,
|
||||
intent,
|
||||
);
|
||||
|
||||
// 2. Build the prompts
|
||||
const systemPrompt = `
|
||||
You are the World Architect, a deterministic and objective judge of reality, physics, and narration for a simulation game.
|
||||
Your task is to judge whether a proposed action (Intent) by an actor is physically and logically possible given the current objective state of the world.
|
||||
Exempt dialogue or speech actions from validation (consider them always valid).
|
||||
Enforce logical boundaries such as:
|
||||
- Spatial boundaries (an actor cannot grab an object in another location unless they are there).
|
||||
- Physical boundaries (an actor cannot open a locked drawer without a key or breaking it).
|
||||
- State Boundaries (an actor cannot perform a task if their state doesn't allow them to do so).
|
||||
- State/Attribute constraints.
|
||||
|
||||
You must respond with a JSON object containing:
|
||||
- "isValid": boolean indicating if the action is possible/allowed.
|
||||
- "reason": a concise explanation of why the action is allowed or denied.
|
||||
`.trim();
|
||||
|
||||
const objectiveContent = hydrateObjective(intent.content, worldState);
|
||||
|
||||
const userContext = `
|
||||
=== CURRENT WORLD STATE ===
|
||||
Current Time: ${worldState.clock.get().toISOString()}
|
||||
Entities & Attributes:
|
||||
${serializedWorld}
|
||||
|
||||
=== PROPOSED ACTION ===
|
||||
Actor ID: ${intent.actorId}
|
||||
Type: ${intent.type}
|
||||
Content: "${objectiveContent}"
|
||||
Target IDs: ${intent.targetIds.join(", ") || "(None)"}
|
||||
|
||||
Decide if the proposed action is logically valid and physically possible.
|
||||
`.trim();
|
||||
this.lastResult = {
|
||||
systemPrompt,
|
||||
userContext,
|
||||
components,
|
||||
};
|
||||
|
||||
// structured call via the LLM provider
|
||||
const response = await this.llmProvider.generateStructuredResponse({
|
||||
|
||||
Reference in New Issue
Block a user