mirror of
https://github.com/sortedcord/omnia.git
synced 2026-07-22 20:12:48 +05:30
feat: Wire up intent and architect packages together
- Added "@omnia/intent": "workspace:*" dependency to package.json. - Referenced ../intent in the project references of tsconfig.json. - Updated validate in llm-validator.ts to accept intent:Intent instead of actorId and actionIntent strings - Promoted the LLM prompt's action details block to include the structured fields of the intent (type , description , originalText , targetIds ), passing this context to the validator.) - Updated the IDeltaGenerator interface and TimeDeltaGenerator 's generate signature in delta.ts to accept intent: Intent . - Refined the LLM prompt's action details block in TimeDeltaGenerator to utilize the structured structured Intent object. - Updated all validation and execution mock calls in architect.test.ts to construct and pass valid Intent objects.
This commit is contained in:
@@ -9,6 +9,7 @@
|
||||
"dependencies": {
|
||||
"@omnia/core": "workspace:*",
|
||||
"@omnia/llm": "workspace:*",
|
||||
"@omnia/intent": "workspace:*",
|
||||
"zod": "^4.4.3"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<ValidationResult> {
|
||||
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<ProcessResult> {
|
||||
// 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
|
||||
|
||||
@@ -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<typeof TimeDeltaSchema>;
|
||||
export interface IDeltaGenerator<T> {
|
||||
generate(
|
||||
worldState: WorldState,
|
||||
actorId: string,
|
||||
actionIntent: string,
|
||||
intent: Intent,
|
||||
): Promise<T>;
|
||||
}
|
||||
|
||||
@@ -22,8 +22,7 @@ export class TimeDeltaGenerator implements IDeltaGenerator<TimeDelta> {
|
||||
|
||||
async generate(
|
||||
worldState: WorldState,
|
||||
actorId: string,
|
||||
actionIntent: string,
|
||||
intent: Intent,
|
||||
): Promise<TimeDelta> {
|
||||
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({
|
||||
|
||||
@@ -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<ValidationResult> {
|
||||
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();
|
||||
|
||||
@@ -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.");
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
"include": ["src"],
|
||||
"references": [
|
||||
{ "path": "../core" },
|
||||
{ "path": "../llm" }
|
||||
{ "path": "../llm" },
|
||||
{ "path": "../intent" }
|
||||
]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user