feat(inventory): add v2 parsing and generation support

Add full AI integration for inventory v2 format:

**Parsing (NEW: inventoryParser.js, 125 lines):**
- extractInventoryData() - Parse multi-line v2 format from AI responses
  - Extracts "On Person: ..." section
  - Extracts multiple "Stored - [Location]: ..." sections
  - Extracts "Assets: ..." section
  - Returns InventoryV2 object
- extractLegacyInventory() - Fallback parser for old v1 format
- extractInventory() - Main function that tries v2 first, falls back to v1

**Parsing Integration (parser.js):**
- Import extractInventory() from inventoryParser
- Replace old single-line regex with v2-aware extraction
- Use feature flag to switch between v1/v2 parsing modes
- Maintains backward compatibility with FEATURE_FLAGS.useNewInventory

**Generation (promptBuilder.js, 60 lines changed):**
- NEW: buildInventorySummary() - Converts v2 object to multi-line text
  - Formats "On Person: ..." line
  - Formats multiple "Stored - [Location]: ..." lines
  - Formats "Assets: ..." line
  - Handles legacy v1 string format for backward compat
- Update generateTrackerInstructions() with v2 format spec:
  - Shows AI how to format inventory in multi-line v2 structure
  - Includes note about multiple storage locations
  - Falls back to v1 format when feature flag disabled
- Update generateContextualSummary() to use buildInventorySummary()
  - Converts v2 inventory to readable context for separate mode

**Format Examples:**

AI Output (v2 format):
```
On Person: Sword (equipped), 3x Health Potions, Leather Armor
Stored - Home: Spare clothes, Tools, 50 gold coins
Stored - Bank: Family heirloom, Important documents
Assets: Motorcycle (garage), Downtown apartment (owned)
```

Parsed Result:
```js
{
  version: 2,
  onPerson: "Sword (equipped), 3x Health Potions, Leather Armor",
  stored: {
    "Home": "Spare clothes, Tools, 50 gold coins",
    "Bank": "Family heirloom, Important documents"
  },
  assets: "Motorcycle (garage), Downtown apartment (owned)"
}
```

Changes:
- NEW: src/systems/generation/inventoryParser.js (125 lines)
- MODIFIED: src/systems/generation/parser.js (+14 lines)
- MODIFIED: src/systems/generation/promptBuilder.js (+60 lines)

Part of inventory system v2 implementation
Dependencies: v2 types, migration utility, persistence integration
This commit is contained in:
Lucas 'Paperboy' Rose-Winters
2025-10-17 15:10:31 +11:00
parent 39ec36f105
commit b00bae905f
3 changed files with 216 additions and 10 deletions
+69 -4
View File
@@ -5,7 +5,56 @@
import { getContext } from '../../../../../../extensions.js';
import { chat } from '../../../../../../../script.js';
import { extensionSettings, committedTrackerData } from '../../core/state.js';
import { extensionSettings, committedTrackerData, FEATURE_FLAGS } from '../../core/state.js';
// Type imports
/** @typedef {import('../../types/inventory.js').InventoryV2} InventoryV2 */
/**
* Builds a formatted inventory summary for AI context injection.
* Converts v2 inventory structure to multi-line plaintext format.
*
* @param {InventoryV2|string} inventory - Current inventory (v2 or legacy string)
* @returns {string} Formatted inventory summary for prompt injection
* @example
* // v2 input: { onPerson: "Sword", stored: { Home: "Gold" }, assets: "Horse", version: 2 }
* // Returns: "On Person: Sword\nStored - Home: Gold\nAssets: Horse"
*/
export function buildInventorySummary(inventory) {
// Handle legacy v1 string format
if (typeof inventory === 'string') {
return inventory;
}
// Handle v2 object format
if (inventory && typeof inventory === 'object' && inventory.version === 2) {
let summary = '';
// Add On Person section
if (inventory.onPerson && inventory.onPerson !== 'None') {
summary += `On Person: ${inventory.onPerson}\n`;
}
// Add Stored sections for each location
if (inventory.stored && Object.keys(inventory.stored).length > 0) {
for (const [location, items] of Object.entries(inventory.stored)) {
if (items && items !== 'None') {
summary += `Stored - ${location}: ${items}\n`;
}
}
}
// Add Assets section
if (inventory.assets && inventory.assets !== 'None') {
summary += `Assets: ${inventory.assets}`;
}
return summary.trim();
}
// Fallback for unknown format
return 'None';
}
/**
* Generates an example block showing current tracker states in markdown code blocks.
@@ -64,7 +113,18 @@ export function generateTrackerInstructions(includeHtmlPrompt = true, includeCon
instructions += '- Hygiene: X%\n';
instructions += '- Arousal: X%\n';
instructions += '[Mood Emoji]: [Conditions (up to three traits)]\n';
instructions += 'Inventory: [Clothing/Armor, Inventory Items (list of important items/none)]\n';
// Add inventory format based on feature flag
if (FEATURE_FLAGS.useNewInventory) {
instructions += 'On Person: [Items currently carried/worn, or "None"]\n';
instructions += 'Stored - [Location Name]: [Items stored at this location]\n';
instructions += '(Add multiple "Stored - [Location]:" lines as needed for different storage locations)\n';
instructions += 'Assets: [Vehicles, property, major possessions, or "None"]\n';
} else {
// Legacy v1 format
instructions += 'Inventory: [Clothing/Armor, Inventory Items (list of important items/none)]\n';
}
instructions += '```\n\n';
}
@@ -143,8 +203,13 @@ export function generateContextualSummary() {
// console.log('[RPG Companion] Building stats summary with:', stats);
summary += `${userName}'s Stats:\n`;
summary += `Condition: Health ${stats.health}%, Satiety ${stats.satiety}%, Energy ${stats.energy}%, Hygiene ${stats.hygiene}%, Arousal ${stats.arousal}% | ${stats.mood} ${stats.conditions}\n`;
if (stats.inventory && stats.inventory !== 'None') {
summary += `Inventory: ${stats.inventory}\n`;
// Add inventory summary using v2-aware builder
if (stats.inventory) {
const inventorySummary = buildInventorySummary(stats.inventory);
if (inventorySummary && inventorySummary !== 'None') {
summary += `Inventory:\n${inventorySummary}\n`;
}
}
// Include classic stats (attributes) and dice roll only if there was a dice roll
if (extensionSettings.lastDiceRoll) {