| 1 | import fs from 'node:fs'; |
| 2 | import path from 'node:path'; |
| 3 | |
| 4 | import { test, expect } from '@playwright/test'; |
| 5 | import { testSetup } from './frontent-test-utils.js'; |
| 6 | import { serverDirectory } from '../../src/server-directory.js'; |
| 7 | |
| 8 | test.describe('MacroStoryString', () => { |
| 9 | test.beforeEach(testSetup.awaitST); |
| 10 | |
| 11 | /** @type {any[]} */ |
| 12 | const defaultContextPresets = []; |
| 13 | |
| 14 | test.beforeAll(() => { |
| 15 | const contextPresetsPath = path.join(serverDirectory, 'default', 'content', 'presets', 'context'); |
| 16 | const files = fs.readdirSync(contextPresetsPath).filter(f => path.extname(f).toLowerCase() === '.json'); |
| 17 | for (const file of files) { |
| 18 | const fullPath = path.join(contextPresetsPath, file); |
| 19 | const fileContent = fs.readFileSync(fullPath, 'utf-8'); |
| 20 | const preset = JSON.parse(fileContent); |
| 21 | defaultContextPresets.push(preset); |
| 22 | } |
| 23 | }); |
| 24 | |
| 25 | test('should produce equivalent story strings with new macro engine', async ({ page }) => { |
| 26 | const output = await page.evaluate(async ([defaultContextPresets]) => { |
| 27 | const { substituteParams, extension_prompt_types } = await import('./script.js'); |
| 28 | const { power_user, renderStoryString } = await import('./scripts/power-user.js'); |
| 29 | |
| 30 | power_user.experimental_macro_engine = true; |
| 31 | |
| 32 | const context = { |
| 33 | description: 'character description', |
| 34 | personality: 'character personality', |
| 35 | persona: 'persona details', |
| 36 | scenario: 'scenario setup', |
| 37 | system: 'system instructions', |
| 38 | char: 'character name', |
| 39 | user: 'user name', |
| 40 | wiBefore: 'world info before', |
| 41 | wiAfter: 'world info after', |
| 42 | loreBefore: 'lore before', |
| 43 | loreAfter: 'lore after', |
| 44 | anchorBefore: 'before anchor text', |
| 45 | anchorAfter: 'after anchor text', |
| 46 | mesExamples: 'example messages', |
| 47 | mesExamplesRaw: 'raw example messages', |
| 48 | }; |
| 49 | |
| 50 | const customInstructSettings = { |
| 51 | enabled: false, |
| 52 | }; |
| 53 | |
| 54 | const customContextSettings = { |
| 55 | story_string_position: extension_prompt_types.IN_PROMPT, |
| 56 | }; |
| 57 | |
| 58 | const result = []; |
| 59 | |
| 60 | function getMacroStoryString(templateString) { |
| 61 | let output = substituteParams(templateString, { name1Override: context.user, name2Override: context.char, replaceCharacterCard: true, dynamicMacros: context }); |
| 62 | output = output.replace(/^\n+/, ''); |
| 63 | if (output.length > 0 && !output.endsWith('\n')) { |
| 64 | output += '\n'; |
| 65 | } |
| 66 | return output; |
| 67 | } |
| 68 | |
| 69 | for (const template of defaultContextPresets) { |
| 70 | const classicStoryString = renderStoryString(context, { customStoryString: template.story_string, customContextSettings, customInstructSettings }); |
| 71 | const macroStoryString = getMacroStoryString(template.story_string); |
| 72 | result.push({ name: template.name, classicStoryString, macroStoryString }); |
| 73 | } |
| 74 | |
| 75 | return result; |
| 76 | }, [defaultContextPresets]); |
| 77 | |
| 78 | for (const { classicStoryString, macroStoryString, name } of output) { |
| 79 | expect(macroStoryString, `Mismatch in template: ${name}`).toBe(classicStoryString); |
| 80 | } |
| 81 | }); |
| 82 | }); |