diff --git a/packages/architect/package.json b/packages/architect/package.json index a382920..4b815a5 100644 --- a/packages/architect/package.json +++ b/packages/architect/package.json @@ -9,6 +9,7 @@ "dependencies": { "@omnia/core": "workspace:*", "@omnia/llm": "workspace:*", + "@omnia/intent": "workspace:*", "zod": "^4.4.3" } } diff --git a/packages/architect/src/architect.ts b/packages/architect/src/architect.ts index ed2aae3..2ddd690 100644 --- a/packages/architect/src/architect.ts +++ b/packages/architect/src/architect.ts @@ -2,6 +2,7 @@ import { WorldState, SQLiteRepository } from "@omnia/core"; import { LLMValidator, ValidationResult } from "./llm-validator.js"; import { TimeDeltaGenerator, TimeDelta } from "./delta.js"; import { ILLMProvider } from "@omnia/llm"; +import { Intent } from "@omnia/intent"; export interface ProcessResult extends ValidationResult { timeDelta?: TimeDelta; @@ -22,10 +23,9 @@ export class Architect { */ async validateIntent( worldState: WorldState, - actorId: string, - actionIntent: string, + intent: Intent, ): Promise { - return this.validator.validate(worldState, actorId, actionIntent); + return this.validator.validate(worldState, intent); } /** @@ -34,11 +34,10 @@ export class Architect { */ async processIntent( worldState: WorldState, - actorId: string, - actionIntent: string, + intent: Intent, ): Promise { // 1. Validate the intent action - const validation = await this.validateIntent(worldState, actorId, actionIntent); + const validation = await this.validateIntent(worldState, intent); if (!validation.isValid) { return validation; } @@ -46,8 +45,7 @@ export class Architect { // 2. Generate time delta for the valid action const timeDelta = await this.timeDeltaGenerator.generate( worldState, - actorId, - actionIntent, + intent, ); // 3. Apply the time delta to the world state clock diff --git a/packages/architect/src/delta.ts b/packages/architect/src/delta.ts index 96e8b5f..95bc8a4 100644 --- a/packages/architect/src/delta.ts +++ b/packages/architect/src/delta.ts @@ -1,6 +1,7 @@ import { z } from "zod"; import { WorldState, serializeObjectiveWorldState } from "@omnia/core"; import { ILLMProvider } from "@omnia/llm"; +import { Intent } from "@omnia/intent"; export const TimeDeltaSchema = z.object({ minutesToAdvance: z.number().int().nonnegative(), @@ -12,8 +13,7 @@ export type TimeDelta = z.infer; export interface IDeltaGenerator { generate( worldState: WorldState, - actorId: string, - actionIntent: string, + intent: Intent, ): Promise; } @@ -22,8 +22,7 @@ export class TimeDeltaGenerator implements IDeltaGenerator { async generate( worldState: WorldState, - actorId: string, - actionIntent: string, + intent: Intent, ): Promise { const systemPrompt = ` You are the Time Delta Generator for the World Architect. @@ -44,8 +43,11 @@ World Details: ${serializeObjectiveWorldState(worldState)} === ACTION === -Actor ID: ${actorId} -Action: "${actionIntent}" +Actor ID: ${intent.actorId} +Type: ${intent.type} +Description: "${intent.description}" +Original Text: "${intent.originalText}" +Target IDs: ${intent.targetIds.join(", ") || "(None)"} `.trim(); const response = await this.llmProvider.generateStructuredResponse({ diff --git a/packages/architect/src/llm-validator.ts b/packages/architect/src/llm-validator.ts index 5fa2eb0..0f62473 100644 --- a/packages/architect/src/llm-validator.ts +++ b/packages/architect/src/llm-validator.ts @@ -1,6 +1,7 @@ import { z } from "zod"; import { WorldState, serializeObjectiveWorldState } from "@omnia/core"; import { ILLMProvider } from "@omnia/llm"; +import { Intent } from "@omnia/intent"; export const ValidationResultSchema = z.object({ isValid: z.boolean(), @@ -17,14 +18,13 @@ export class LLMValidator { */ async validate( worldState: WorldState, - actorId: string, - actionIntent: string, + intent: Intent, ): Promise { - const actor = worldState.getEntity(actorId); + const actor = worldState.getEntity(intent.actorId); if (!actor) { return { isValid: false, - reason: `Actor entity with ID "${actorId}" does not exist in the world state.`, + reason: `Actor entity with ID "${intent.actorId}" does not exist in the world state.`, }; } @@ -54,8 +54,11 @@ Entities & Attributes: ${serializedWorld} === PROPOSED ACTION === -Actor ID: ${actorId} -Proposed Action: "${actionIntent}" +Actor ID: ${intent.actorId} +Type: ${intent.type} +Description: "${intent.description}" +Original Text: "${intent.originalText}" +Target IDs: ${intent.targetIds.join(", ") || "(None)"} Decide if the proposed action is logically valid and physically possible. `.trim(); diff --git a/packages/architect/tests/architect.test.ts b/packages/architect/tests/architect.test.ts index 9cc5708..7660ad8 100644 --- a/packages/architect/tests/architect.test.ts +++ b/packages/architect/tests/architect.test.ts @@ -3,6 +3,7 @@ import Database from "better-sqlite3"; import { WorldState, Entity, SQLiteRepository } from "@omnia/core"; import { MockLLMProvider } from "@omnia/llm"; import { Architect } from "@omnia/architect"; +import { Intent } from "@omnia/intent"; describe("Architect & LLMValidator Unit Tests (Tier 1)", () => { test("returns valid response when LLM validates intent as successful", async () => { @@ -17,11 +18,15 @@ describe("Architect & LLMValidator Unit Tests (Tier 1)", () => { const llmProvider = new MockLLMProvider([mockResponse]); const architect = new Architect(llmProvider); - const result = await architect.validateIntent( - world, - "alice", - "open the chest and read the scroll", - ); + const intent: Intent = { + type: "action", + originalText: "open the chest and read the scroll", + description: "Open the chest and read the scroll", + actorId: "alice", + targetIds: [], + }; + + const result = await architect.validateIntent(world, intent); expect(result.isValid).toBe(true); expect(result.reason).toBe( @@ -42,11 +47,15 @@ describe("Architect & LLMValidator Unit Tests (Tier 1)", () => { const llmProvider = new MockLLMProvider([mockResponse]); const architect = new Architect(llmProvider); - const result = await architect.validateIntent( - world, - "bob", - "unlock the gate and escape", - ); + const intent: Intent = { + type: "action", + originalText: "unlock the gate and escape", + description: "Unlock the gate and escape", + actorId: "bob", + targetIds: [], + }; + + const result = await architect.validateIntent(world, intent); expect(result.isValid).toBe(false); expect(result.reason).toBe("Bob does not have the key to the iron gate."); @@ -59,11 +68,15 @@ describe("Architect & LLMValidator Unit Tests (Tier 1)", () => { const llmProvider = new MockLLMProvider([]); // No mock responses because it shouldn't be called const architect = new Architect(llmProvider); - const result = await architect.validateIntent( - world, - "ghost", - "haunt the mansion", - ); + const intent: Intent = { + type: "action", + originalText: "haunt the mansion", + description: "Haunt the mansion", + actorId: "ghost", + targetIds: [], + }; + + const result = await architect.validateIntent(world, intent); expect(result.isValid).toBe(false); expect(result.reason).toContain( @@ -93,11 +106,15 @@ describe("TimeDeltaGenerator & Architect.processIntent Unit Tests (Tier 1)", () const architect = new Architect(llmProvider, repo); - const result = await architect.processIntent( - world, - "alice", - "pick the lock of the wooden chest", - ); + const intent: Intent = { + type: "action", + originalText: "pick the lock of the wooden chest", + description: "Pick the lock of the wooden chest", + actorId: "alice", + targetIds: [], + }; + + const result = await architect.processIntent(world, intent); // Assert results expect(result.isValid).toBe(true); @@ -131,11 +148,15 @@ describe("TimeDeltaGenerator & Architect.processIntent Unit Tests (Tier 1)", () const architect = new Architect(llmProvider, repo); - const result = await architect.processIntent( - world, - "bob", - "run away", - ); + const intent: Intent = { + type: "action", + originalText: "run away", + description: "Run away", + actorId: "bob", + targetIds: [], + }; + + const result = await architect.processIntent(world, intent); expect(result.isValid).toBe(false); expect(result.reason).toBe("Bob is bound by chains."); diff --git a/packages/architect/tsconfig.json b/packages/architect/tsconfig.json index ecba433..66a91d3 100644 --- a/packages/architect/tsconfig.json +++ b/packages/architect/tsconfig.json @@ -7,6 +7,7 @@ "include": ["src"], "references": [ { "path": "../core" }, - { "path": "../llm" } + { "path": "../llm" }, + { "path": "../intent" } ] } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 887c0d5..ba46a6c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -302,6 +302,9 @@ importers: '@omnia/core': specifier: workspace:* version: link:../core + '@omnia/intent': + specifier: workspace:* + version: link:../intent '@omnia/llm': specifier: workspace:* version: link:../llm