1
This commit is contained in:
@@ -0,0 +1,157 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import test from 'node:test';
|
||||
|
||||
import { generateCustomWorldEntity } from './customWorldEntityGenerationService.js';
|
||||
import type { UpstreamLlmClient } from './llmClient.js';
|
||||
|
||||
function createProfile() {
|
||||
return {
|
||||
name: '裂潮边城',
|
||||
settingText: '裂潮重新逼近边城,旧封桥令也被重新翻出。',
|
||||
summary: '一座在裂潮与旧案之间摇摇欲坠的边城。',
|
||||
tone: '紧绷、克制、暗流涌动',
|
||||
playerGoal: '查清封桥旧令背后的真正操盘者',
|
||||
playableNpcs: [
|
||||
{
|
||||
id: 'playable-1',
|
||||
name: '沈砺',
|
||||
title: '灰炬向导',
|
||||
role: '边路同行者',
|
||||
description: '熟悉裂潮边路的向导。',
|
||||
visualDescription: '灰斗篷和旧路标是他最显眼的识别点。',
|
||||
actionDescription: '先试探风向,再用短弓牵制。',
|
||||
sceneVisualDescription: '他常在旧边路哨点出现。',
|
||||
backstory: '曾在旧撤离线里失去整支同行队。',
|
||||
personality: '谨慎寡言。',
|
||||
motivation: '想查清旧撤离线再次失控的原因。',
|
||||
combatStyle: '短弓牵制后贴近补刀。',
|
||||
initialAffinity: 18,
|
||||
relationshipHooks: ['旧撤离线'],
|
||||
tags: ['裂潮', '向导'],
|
||||
},
|
||||
],
|
||||
storyNpcs: [
|
||||
{
|
||||
id: 'story-1',
|
||||
name: '梁砺',
|
||||
title: '断桥巡守',
|
||||
role: '巡守',
|
||||
description: '守着旧桥与哨火的人。',
|
||||
visualDescription: '披着旧制巡守外袍,枪柄磨损很重。',
|
||||
actionDescription: '先立枪封路,再逼近压线。',
|
||||
sceneVisualDescription: '多出现在断桥和潮湿石阶附近。',
|
||||
backstory: '旧案爆发时,他是最后一个封桥的人。',
|
||||
personality: '直接、警觉。',
|
||||
motivation: '不想再让封桥旧案被人利用。',
|
||||
combatStyle: '长枪压线。',
|
||||
initialAffinity: 6,
|
||||
relationshipHooks: ['断桥'],
|
||||
tags: ['巡守'],
|
||||
},
|
||||
],
|
||||
landmarks: [
|
||||
{
|
||||
id: 'landmark-1',
|
||||
name: '旧潮栈桥',
|
||||
description: '裂潮来时最先响起铁索声的旧栈桥。',
|
||||
visualDescription: '铁索、旧桩和盐雾一起压在栈桥上。',
|
||||
dangerLevel: 'medium',
|
||||
sceneNpcIds: ['story-1'],
|
||||
connections: [],
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
test('generateCustomWorldEntity returns role-side visual descriptions from the same model response', async () => {
|
||||
const llmClient = {
|
||||
requestMessageContent: async () =>
|
||||
JSON.stringify({
|
||||
playableNpc: {
|
||||
name: '顾潮音',
|
||||
title: '潮港校灯人',
|
||||
role: '边港同行者',
|
||||
description: '在港区高处替玩家校正风向与路标的人。',
|
||||
visualDescription:
|
||||
'深蓝防潮外套压着风痕,腰侧悬着校灯尺和短刃,整个人像常年站在高处看潮线的人。',
|
||||
actionDescription:
|
||||
'先借高处观察和校灯信号牵制敌人,再突然贴近切断退路。',
|
||||
sceneVisualDescription:
|
||||
'他第一次出现的高台边缘挂着潮湿风旗,脚下是被盐雾浸白的木板和仍亮着的旧校灯。',
|
||||
backstory: '曾负责港区夜航校灯,后被卷进旧案。',
|
||||
personality: '沉稳、寡言、观察细。',
|
||||
motivation: '想在港区秩序彻底失控前找到还能守住的线。',
|
||||
combatStyle: '高差观察后快速切入。',
|
||||
initialAffinity: 24,
|
||||
relationshipHooks: ['夜航校灯', '旧港案'],
|
||||
tags: ['港区', '校灯'],
|
||||
publicSummary: '港区里很少有人比他更熟悉夜里的风向。',
|
||||
chapterTeasers: ['他盯风向比盯人更久。', '旧港案在他身上没过去。', '他一直在等某个信号。', '他还藏着最后一次校灯记录。'],
|
||||
chapterContents: ['他总先校风向。', '旧港案改变了他的站位。', '他真正守的是港区里还没断的线。', '最后那份校灯记录能指向操盘者。'],
|
||||
skills: [
|
||||
{ name: '校灯试探', summary: '先用灯信号试探敌我位置。', style: '起手压制' },
|
||||
{ name: '斜坡切入', summary: '借高差快速贴近改线。', style: '机动周旋' },
|
||||
{ name: '潮线封口', summary: '看准潮线后一口气断掉退路。', style: '爆发终结' },
|
||||
],
|
||||
initialItems: [
|
||||
{ name: '校灯尺', category: '武器', quantity: 1, rarity: 'rare', description: '兼具校灯与近战功能。', tags: ['港区'] },
|
||||
{ name: '旧港图片', category: '专属物品', quantity: 1, rarity: 'rare', description: '记着他自己的旧线路。', tags: ['旧案'] },
|
||||
{ name: '潮雾止血包', category: '消耗品', quantity: 2, rarity: 'uncommon', description: '港区常备。', tags: ['补给'] },
|
||||
],
|
||||
},
|
||||
}),
|
||||
} as UpstreamLlmClient;
|
||||
|
||||
const result = await generateCustomWorldEntity(llmClient, {
|
||||
profile: createProfile(),
|
||||
kind: 'playable',
|
||||
});
|
||||
|
||||
assert.equal(result.kind, 'playable');
|
||||
assert.equal(
|
||||
result.entity.visualDescription,
|
||||
'深蓝防潮外套压着风痕,腰侧悬着校灯尺和短刃,整个人像常年站在高处看潮线的人。',
|
||||
);
|
||||
assert.equal(
|
||||
result.entity.actionDescription,
|
||||
'先借高处观察和校灯信号牵制敌人,再突然贴近切断退路。',
|
||||
);
|
||||
assert.equal(
|
||||
result.entity.sceneVisualDescription,
|
||||
'他第一次出现的高台边缘挂着潮湿风旗,脚下是被盐雾浸白的木板和仍亮着的旧校灯。',
|
||||
);
|
||||
});
|
||||
|
||||
test('generateCustomWorldEntity returns landmark visual descriptions from the same model response', async () => {
|
||||
const llmClient = {
|
||||
requestMessageContent: async () =>
|
||||
JSON.stringify({
|
||||
landmark: {
|
||||
name: '回潮观测台',
|
||||
description: '能俯瞰旧港和裂潮边缘的新观测点。',
|
||||
visualDescription:
|
||||
'观测台立在湿冷高处,铁质风标、旧灯架和被潮气侵白的石阶一起构成了压迫感很强的前景。',
|
||||
dangerLevel: 'high',
|
||||
sceneNpcNames: ['梁砺'],
|
||||
connections: [
|
||||
{
|
||||
targetLandmarkName: '旧潮栈桥',
|
||||
relativePosition: 'forward',
|
||||
summary: '沿风雨走廊可直接回到旧潮栈桥',
|
||||
},
|
||||
],
|
||||
},
|
||||
}),
|
||||
} as UpstreamLlmClient;
|
||||
|
||||
const result = await generateCustomWorldEntity(llmClient, {
|
||||
profile: createProfile(),
|
||||
kind: 'landmark',
|
||||
});
|
||||
|
||||
assert.equal(result.kind, 'landmark');
|
||||
assert.equal(
|
||||
result.entity.visualDescription,
|
||||
'观测台立在湿冷高处,铁质风标、旧灯架和被潮气侵白的石阶一起构成了压迫感很强的前景。',
|
||||
);
|
||||
});
|
||||
Reference in New Issue
Block a user