feat(interpreter): implement hybrid intent resolution with LLM and deterministic fallback
- Added new contracts for intent interpretation, including InterpreterOutput and ResolverMode. - Implemented deterministic intent resolver with clarity checks for ambiguous references and empty input. - Developed LLM intent resolver that communicates with an external model, handling JSON responses and fallback clarifications. - Created an interpretTurn function to manage intent resolution based on the selected resolver mode. - Introduced validation for interpreter output to ensure integrity before processing actions. - Established a turn manager to orchestrate turn processing, including action validation and world state mutation. - Added integration tests to verify the functionality of the new intent resolution system. Co-authored-by: Copilot <copilot@github.com>
This commit is contained in:
@@ -11,6 +11,7 @@ export function createDefaultRulebook(worldId: string): SceneRulebook {
|
||||
return {
|
||||
id: DEFAULT_RULEBOOK_ID,
|
||||
worldId,
|
||||
version: 1,
|
||||
name: "Default Rulebook",
|
||||
description: "Built-in scene rules for the CharacterGarden engine. Edit freely — the engine re-evaluates on every turn.",
|
||||
createdAt: now,
|
||||
@@ -27,23 +28,47 @@ export function createDefaultRulebook(worldId: string): SceneRulebook {
|
||||
enabled: true,
|
||||
checks: [
|
||||
{
|
||||
id: "take_target_exists",
|
||||
description: "Target entity must exist in the world",
|
||||
condition: { op: "entityExists", role: "target" },
|
||||
id: "take_target_exists_or_actor_can_create",
|
||||
description: "Target must exist, or actor must be authorized to create it when createIfMissing is true",
|
||||
condition: {
|
||||
op: "or",
|
||||
conditions: [
|
||||
{ op: "entityExists", role: "target" },
|
||||
{
|
||||
op: "and",
|
||||
conditions: [
|
||||
{ op: "actionMetadataEq", key: "createIfMissing", value: true },
|
||||
{ op: "actorIdIn", allowedIds: ["player"] },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
failReason: "target_not_found",
|
||||
failMessage: "Target '{target.id}' does not exist.",
|
||||
failMessage: "Target '{target.id}' does not exist, and actor '{actor.id}' is not allowed to create missing items.",
|
||||
},
|
||||
{
|
||||
id: "take_same_location",
|
||||
description: "Actor and target must be in the same location",
|
||||
condition: { op: "sameLocation", roleA: "actor", roleB: "target" },
|
||||
description: "If target exists, actor and target must be in the same location",
|
||||
condition: {
|
||||
op: "or",
|
||||
conditions: [
|
||||
{ op: "not", condition: { op: "entityExists", role: "target" } },
|
||||
{ op: "sameLocation", roleA: "actor", roleB: "target" },
|
||||
],
|
||||
},
|
||||
failReason: "not_in_same_location",
|
||||
failMessage: "Target '{target.id}' is not in the same location as '{actor.id}'.",
|
||||
},
|
||||
{
|
||||
id: "take_takeable",
|
||||
description: "Target must have takeable attribute set to true",
|
||||
condition: { op: "eq", role: "target", attribute: "takeable", value: true },
|
||||
description: "If target exists, it must have takeable attribute set to true",
|
||||
condition: {
|
||||
op: "or",
|
||||
conditions: [
|
||||
{ op: "not", condition: { op: "entityExists", role: "target" } },
|
||||
{ op: "eq", role: "target", attribute: "takeable", value: true },
|
||||
],
|
||||
},
|
||||
failReason: "not_takeable",
|
||||
failMessage: "Target '{target.id}' cannot be taken.",
|
||||
},
|
||||
@@ -242,6 +267,41 @@ export function createDefaultRulebook(worldId: string): SceneRulebook {
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
{
|
||||
actionType: "transfer",
|
||||
enabled: true,
|
||||
checks: [
|
||||
{
|
||||
id: "transfer_recipient_exists",
|
||||
description: "Recipient must exist in the world",
|
||||
condition: { op: "entityExists", role: "target" },
|
||||
failReason: "target_not_found",
|
||||
failMessage: "Recipient '{target.id}' does not exist.",
|
||||
},
|
||||
{
|
||||
id: "transfer_recipient_character",
|
||||
description: "Recipient must be a character",
|
||||
condition: { op: "entityType", role: "target", requiredType: "character" },
|
||||
failReason: "target_not_character",
|
||||
failMessage: "Recipient '{target.id}' is not a character.",
|
||||
},
|
||||
{
|
||||
id: "transfer_same_location",
|
||||
description: "Actor and recipient must be in the same location",
|
||||
condition: { op: "sameLocation", roleA: "actor", roleB: "target" },
|
||||
failReason: "not_in_same_location",
|
||||
failMessage: "Recipient '{target.id}' is not in the same location as '{actor.id}'.",
|
||||
},
|
||||
{
|
||||
id: "transfer_actor_holds_item",
|
||||
description: "Actor must currently hold the specified item in inventory",
|
||||
condition: { op: "itemInInventory", itemMetadataKey: "itemId", holderRole: "actor" },
|
||||
failReason: "item_not_in_inventory",
|
||||
failMessage: "Actor '{actor.id}' is not holding the requested item.",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user