1
This commit is contained in:
@@ -5,7 +5,11 @@ import type { UpstreamLlmClient } from './llmClient.js';
|
||||
import { CustomWorldAgentFoundationDraftService } from './customWorldAgentFoundationDraftService.js';
|
||||
import { normalizeFoundationDraftProfile } from './customWorldAgentDraftCompiler.js';
|
||||
|
||||
function createFoundationDraftLlmClient(): UpstreamLlmClient {
|
||||
function createFoundationDraftLlmClient(
|
||||
options: {
|
||||
omitStoryOptionalVisualFields?: boolean;
|
||||
} = {},
|
||||
): UpstreamLlmClient {
|
||||
let roleOutlineBatch = 0;
|
||||
let landmarkSeedBatch = 0;
|
||||
let landmarkNetworkBatch = 0;
|
||||
@@ -68,9 +72,15 @@ function createFoundationDraftLlmClient(): UpstreamLlmClient {
|
||||
title: '旧友兼宿敌',
|
||||
role: '沉船商盟引路人',
|
||||
description: '他像旧友,也像最早知道假航灯秘密的人。',
|
||||
visualDescription: '衣角总带着潮水味,像是刚从夜雾里走出来。',
|
||||
actionDescription: '会不断试探玩家到底愿不愿意回到旧航路。',
|
||||
sceneVisualDescription: '总在钟声停下后的空隙里现身。',
|
||||
...(options.omitStoryOptionalVisualFields
|
||||
? {}
|
||||
: {
|
||||
visualDescription:
|
||||
'衣角总带着潮水味,像是刚从夜雾里走出来。',
|
||||
actionDescription:
|
||||
'会不断试探玩家到底愿不愿意回到旧航路。',
|
||||
sceneVisualDescription: '总在钟声停下后的空隙里现身。',
|
||||
}),
|
||||
initialAffinity: 6,
|
||||
relationshipHooks: ['和玩家共享一段无法翻篇的旧灯塔往事'],
|
||||
tags: ['旧友', '宿敌'],
|
||||
@@ -80,9 +90,14 @@ function createFoundationDraftLlmClient(): UpstreamLlmClient {
|
||||
title: '守灯会巡夜官',
|
||||
role: '守灯会前台接口人',
|
||||
description: '她负责把守灯会的怀疑与命令直接压到玩家面前。',
|
||||
visualDescription: '披着潮湿斗篷,眼神总先看灯芯再看人。',
|
||||
actionDescription: '要求玩家立刻证明自己还配站回灯塔。',
|
||||
sceneVisualDescription: '总把巡夜灯举得很高,不给人躲闪空间。',
|
||||
...(options.omitStoryOptionalVisualFields
|
||||
? {}
|
||||
: {
|
||||
visualDescription: '披着潮湿斗篷,眼神总先看灯芯再看人。',
|
||||
actionDescription: '要求玩家立刻证明自己还配站回灯塔。',
|
||||
sceneVisualDescription:
|
||||
'总把巡夜灯举得很高,不给人躲闪空间。',
|
||||
}),
|
||||
initialAffinity: 6,
|
||||
relationshipHooks: ['会逼玩家更早站队'],
|
||||
tags: ['守灯会', '巡夜'],
|
||||
@@ -198,9 +213,15 @@ function createFoundationDraftLlmClient(): UpstreamLlmClient {
|
||||
title: '旧友兼宿敌',
|
||||
role: '沉船商盟引路人',
|
||||
description: '他像旧友,也像最早知道假航灯秘密的人。',
|
||||
visualDescription: '衣角总带着潮水味,像是刚从夜雾里走出来。',
|
||||
actionDescription: '会不断试探玩家到底愿不愿意回到旧航路。',
|
||||
sceneVisualDescription: '总在钟声停下后的空隙里现身。',
|
||||
...(options.omitStoryOptionalVisualFields
|
||||
? {}
|
||||
: {
|
||||
visualDescription:
|
||||
'衣角总带着潮水味,像是刚从夜雾里走出来。',
|
||||
actionDescription:
|
||||
'会不断试探玩家到底愿不愿意回到旧航路。',
|
||||
sceneVisualDescription: '总在钟声停下后的空隙里现身。',
|
||||
}),
|
||||
relationshipHooks: ['和玩家共享一段无法翻篇的旧灯塔往事'],
|
||||
tags: ['旧友', '宿敌'],
|
||||
},
|
||||
@@ -209,9 +230,14 @@ function createFoundationDraftLlmClient(): UpstreamLlmClient {
|
||||
title: '守灯会巡夜官',
|
||||
role: '守灯会前台接口人',
|
||||
description: '她负责把守灯会的怀疑与命令直接压到玩家面前。',
|
||||
visualDescription: '披着潮湿斗篷,眼神总先看灯芯再看人。',
|
||||
actionDescription: '要求玩家立刻证明自己还配站回灯塔。',
|
||||
sceneVisualDescription: '总把巡夜灯举得很高,不给人躲闪空间。',
|
||||
...(options.omitStoryOptionalVisualFields
|
||||
? {}
|
||||
: {
|
||||
visualDescription: '披着潮湿斗篷,眼神总先看灯芯再看人。',
|
||||
actionDescription: '要求玩家立刻证明自己还配站回灯塔。',
|
||||
sceneVisualDescription:
|
||||
'总把巡夜灯举得很高,不给人躲闪空间。',
|
||||
}),
|
||||
relationshipHooks: ['会逼玩家更早站队'],
|
||||
tags: ['守灯会', '巡夜'],
|
||||
},
|
||||
@@ -228,9 +254,15 @@ function createFoundationDraftLlmClient(): UpstreamLlmClient {
|
||||
title: '旧友兼宿敌',
|
||||
role: '沉船商盟引路人',
|
||||
description: '他像旧友,也像最早知道假航灯秘密的人。',
|
||||
visualDescription: '衣角总带着潮水味,像是刚从夜雾里走出来。',
|
||||
actionDescription: '会不断试探玩家到底愿不愿意回到旧航路。',
|
||||
sceneVisualDescription: '总在钟声停下后的空隙里现身。',
|
||||
...(options.omitStoryOptionalVisualFields
|
||||
? {}
|
||||
: {
|
||||
visualDescription:
|
||||
'衣角总带着潮水味,像是刚从夜雾里走出来。',
|
||||
actionDescription:
|
||||
'会不断试探玩家到底愿不愿意回到旧航路。',
|
||||
sceneVisualDescription: '总在钟声停下后的空隙里现身。',
|
||||
}),
|
||||
relationshipHooks: ['和玩家共享一段无法翻篇的旧灯塔往事'],
|
||||
tags: ['旧友', '宿敌'],
|
||||
},
|
||||
@@ -239,9 +271,14 @@ function createFoundationDraftLlmClient(): UpstreamLlmClient {
|
||||
title: '守灯会巡夜官',
|
||||
role: '守灯会前台接口人',
|
||||
description: '她负责把守灯会的怀疑与命令直接压到玩家面前。',
|
||||
visualDescription: '披着潮湿斗篷,眼神总先看灯芯再看人。',
|
||||
actionDescription: '要求玩家立刻证明自己还配站回灯塔。',
|
||||
sceneVisualDescription: '总把巡夜灯举得很高,不给人躲闪空间。',
|
||||
...(options.omitStoryOptionalVisualFields
|
||||
? {}
|
||||
: {
|
||||
visualDescription: '披着潮湿斗篷,眼神总先看灯芯再看人。',
|
||||
actionDescription: '要求玩家立刻证明自己还配站回灯塔。',
|
||||
sceneVisualDescription:
|
||||
'总把巡夜灯举得很高,不给人躲闪空间。',
|
||||
}),
|
||||
relationshipHooks: ['会逼玩家更早站队'],
|
||||
tags: ['守灯会', '巡夜'],
|
||||
},
|
||||
@@ -322,3 +359,49 @@ test('foundation draft service builds draft fields directly from framework inste
|
||||
assert.equal(legacyStoryNpcs[0]?.name, '沈砺');
|
||||
assert.equal(legacyStoryNpcs[0]?.backstory, undefined);
|
||||
});
|
||||
|
||||
test('foundation draft service tolerates missing optional scene role visual fields', async () => {
|
||||
const service = new CustomWorldAgentFoundationDraftService(
|
||||
createFoundationDraftLlmClient({
|
||||
omitStoryOptionalVisualFields: true,
|
||||
}),
|
||||
);
|
||||
|
||||
const draft = await service.generate({
|
||||
creatorIntent: {
|
||||
sourceMode: 'freeform',
|
||||
rawSettingText: '被海雾反复切开的列岛世界。',
|
||||
worldHook: '旧灯塔、假航灯与失控航路重新把列岛撕开。',
|
||||
themeKeywords: ['海岛', '悬疑'],
|
||||
toneDirectives: ['冷峻', '潮湿'],
|
||||
playerPremise: '玩家是被迫返乡的失职守灯人',
|
||||
openingSituation: '开局时正站在即将熄灭的旧灯塔上',
|
||||
coreConflicts: ['守灯会与沉船商盟争夺旧航路解释权'],
|
||||
keyFactions: [],
|
||||
keyCharacters: [],
|
||||
keyLandmarks: [],
|
||||
iconicElements: ['潮雾钟声', '盐火灯塔'],
|
||||
forbiddenDirectives: [],
|
||||
},
|
||||
anchorPack: {
|
||||
creatorIntentSummary: '潮雾、旧灯塔、假航灯和被迫返乡的守灯人。',
|
||||
},
|
||||
});
|
||||
|
||||
const normalized = normalizeFoundationDraftProfile(draft);
|
||||
const legacyResultProfile = (draft as Record<string, unknown>)
|
||||
.legacyResultProfile as Record<string, unknown> | undefined;
|
||||
const legacyStoryNpcs = Array.isArray(legacyResultProfile?.storyNpcs)
|
||||
? (legacyResultProfile?.storyNpcs as Array<Record<string, unknown>>)
|
||||
: [];
|
||||
|
||||
assert.ok(normalized);
|
||||
assert.equal(normalized?.storyNpcs.length, 2);
|
||||
assert.equal(normalized?.storyNpcs[0]?.name, '沈砺');
|
||||
assert.equal(
|
||||
normalized?.storyNpcs[0]?.publicMask,
|
||||
'他像旧友,也像最早知道假航灯秘密的人。',
|
||||
);
|
||||
assert.ok((normalized?.storyNpcs[0]?.currentPressure ?? '').trim());
|
||||
assert.equal(legacyStoryNpcs[0]?.visualDescription, undefined);
|
||||
});
|
||||
|
||||
@@ -61,8 +61,8 @@ function toRecord(value: unknown) {
|
||||
: null;
|
||||
}
|
||||
|
||||
function clampText(value: string, maxLength: number) {
|
||||
const normalized = value.replace(/\s+/gu, ' ').trim();
|
||||
function clampText(value: unknown, maxLength: number) {
|
||||
const normalized = toText(value).replace(/\s+/gu, ' ').trim();
|
||||
if (!normalized) {
|
||||
return '';
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user