This commit is contained in:
2026-04-10 15:37:02 +08:00
parent 161cd32277
commit f19e482c8f
233 changed files with 43987 additions and 5127 deletions

View File

@@ -1,17 +1,29 @@
import type { QuestGenerationRequest } from '../../../packages/shared/src/contracts/story.js';
import {
QUEST_INTIMACY_LEVELS,
QUEST_NARRATIVE_TYPES,
QUEST_OBJECTIVE_KINDS,
QUEST_REWARD_THEMES,
QUEST_URGENCY_LEVELS,
} from '../../../packages/shared/src/contracts/story.js';
import { parseJsonResponseText } from '../../../packages/shared/src/llm/parsers.js';
import {
buildFallbackQuestIntent,
compileQuestIntentToQuest,
evaluateQuestOpportunity,
} from '../../../src/data/questFlow.js';
import { parseJsonResponseText } from '../../../src/services/llmParsers.js';
import { buildQuestGenerationContextFromState } from '../../../src/services/questDirector.js';
import { buildQuestIntentPrompt, QUEST_INTENT_SYSTEM_PROMPT } from '../../../src/services/questPrompt.js';
import type { QuestIntent, QuestPreviewRequest } from '../../../src/services/questTypes.js';
import type { GameState } from '../../../src/types/game.js';
import type { Encounter } from '../../../src/types/scene.js';
import type { QuestLogEntry } from '../../../src/types/story.js';
buildQuestGenerationContextFromState,
buildQuestIntentPrompt,
QUEST_INTENT_SYSTEM_PROMPT,
} from '../bridges/legacyQuestRuntimeBridge.js';
import type { UpstreamLlmClient } from './llmClient.js';
type QuestPreviewRequest = Parameters<typeof evaluateQuestOpportunity>[0];
type QuestIntent = ReturnType<typeof buildFallbackQuestIntent>;
type QuestGenerationInput = Parameters<typeof buildQuestGenerationContextFromState>[0];
type QuestGenerationState = QuestGenerationInput['state'];
type QuestGenerationEncounter = QuestGenerationInput['encounter'];
type QuestLogEntry = ReturnType<typeof compileQuestIntentToQuest>;
function coerceString(value: unknown, fallback: string) {
return typeof value === 'string' && value.trim() ? value.trim() : fallback;
}
@@ -41,7 +53,7 @@ function sanitizeQuestIntent(rawIntent: unknown, fallback: QuestIntent): QuestIn
summary: coerceString(intent.summary, fallback.summary),
narrativeType:
typeof intent.narrativeType === 'string' &&
['bounty', 'escort', 'investigation', 'retrieval', 'relationship', 'trial'].includes(intent.narrativeType)
QUEST_NARRATIVE_TYPES.includes(intent.narrativeType)
? (intent.narrativeType as QuestIntent['narrativeType'])
: fallback.narrativeType,
dramaticNeed: coerceString(intent.dramaticNeed, fallback.dramaticNeed),
@@ -51,29 +63,20 @@ function sanitizeQuestIntent(rawIntent: unknown, fallback: QuestIntent): QuestIn
recommendedObjectiveKinds: coerceStringArray(
intent.recommendedObjectiveKinds,
fallback.recommendedObjectiveKinds,
).filter((kind) =>
[
'defeat_hostile_npc',
'inspect_treasure',
'spar_with_npc',
'talk_to_npc',
'reach_scene',
'deliver_item',
].includes(kind),
) as QuestIntent['recommendedObjectiveKinds'],
).filter((kind) => QUEST_OBJECTIVE_KINDS.includes(kind)) as QuestIntent['recommendedObjectiveKinds'],
urgency:
typeof intent.urgency === 'string' &&
['low', 'medium', 'high'].includes(intent.urgency)
QUEST_URGENCY_LEVELS.includes(intent.urgency)
? (intent.urgency as QuestIntent['urgency'])
: fallback.urgency,
intimacy:
typeof intent.intimacy === 'string' &&
['transactional', 'cooperative', 'trust_based'].includes(intent.intimacy)
QUEST_INTIMACY_LEVELS.includes(intent.intimacy)
? (intent.intimacy as QuestIntent['intimacy'])
: fallback.intimacy,
rewardTheme:
typeof intent.rewardTheme === 'string' &&
['currency', 'resource', 'relationship', 'intel', 'rare_item'].includes(intent.rewardTheme)
QUEST_REWARD_THEMES.includes(intent.rewardTheme)
? (intent.rewardTheme as QuestIntent['rewardTheme'])
: fallback.rewardTheme,
followupHooks: coerceStringArray(intent.followupHooks, fallback.followupHooks),
@@ -82,10 +85,7 @@ function sanitizeQuestIntent(rawIntent: unknown, fallback: QuestIntent): QuestIn
export async function generateQuestForNpcEncounter(
llmClient: UpstreamLlmClient,
params: {
state: GameState;
encounter: Encounter;
},
params: QuestGenerationRequest<QuestGenerationState, QuestGenerationEncounter>,
): Promise<QuestLogEntry | null> {
const { state, encounter } = params;
const issuerNpcId = encounter.id ?? encounter.npcName;
@@ -95,7 +95,7 @@ export async function generateQuestForNpcEncounter(
roleText: encounter.context,
scene: state.currentScenePreset,
worldType: state.worldType,
currentQuests: state.quests.map((quest: QuestLogEntry) => ({
currentQuests: state.quests.map((quest) => ({
id: quest.id,
issuerNpcId: quest.issuerNpcId,
status: quest.status,