1
This commit is contained in:
@@ -9,6 +9,7 @@ import type {
|
||||
NpcRecruitDialogueRequest,
|
||||
PlainTextResponse,
|
||||
} from '../../packages/shared/src/contracts/rpgRuntimeChat';
|
||||
import type { RuntimeStoryAiRequest } from '../../packages/shared/src/contracts/rpgRuntimeStoryState';
|
||||
import type {
|
||||
CustomWorldGenerationProgress,
|
||||
GenerateCustomWorldProfileInput,
|
||||
@@ -32,21 +33,13 @@ import type {
|
||||
TextStreamOptions,
|
||||
} from './aiTypes';
|
||||
import { fetchWithApiAuth, requestJson } from './apiClient';
|
||||
import { type CharacterChatTargetStatus } from './characterChatPrompt';
|
||||
import { type CharacterChatTargetStatus } from './rpgRuntimeChatTypes';
|
||||
import { parseLineListContent } from './llmParsers';
|
||||
|
||||
const RUNTIME_API_BASE = '/api/runtime';
|
||||
|
||||
type LegacyAiModule = typeof import('./ai');
|
||||
|
||||
let legacyAiModulePromise: Promise<LegacyAiModule> | null = null;
|
||||
|
||||
async function loadLegacyAiModule() {
|
||||
if (!legacyAiModulePromise) {
|
||||
legacyAiModulePromise = import('./ai');
|
||||
}
|
||||
|
||||
return legacyAiModulePromise;
|
||||
function getRuntimeSessionIdFromContext(context: StoryGenerationContext) {
|
||||
return context.runtimeSessionId?.trim() || undefined;
|
||||
}
|
||||
|
||||
async function requestPlainText(
|
||||
@@ -169,29 +162,27 @@ export async function generateInitialStory(
|
||||
context: StoryGenerationContext,
|
||||
requestOptions: StoryRequestOptions = {},
|
||||
): Promise<AIResponse> {
|
||||
if (typeof window === 'undefined') {
|
||||
const aiClient = await loadLegacyAiModule();
|
||||
return aiClient.generateInitialStory(
|
||||
world,
|
||||
character,
|
||||
monsters,
|
||||
context,
|
||||
requestOptions,
|
||||
);
|
||||
}
|
||||
const sessionId = getRuntimeSessionIdFromContext(context);
|
||||
const payload: RuntimeStoryAiRequest | Record<string, unknown> = sessionId
|
||||
? {
|
||||
sessionId,
|
||||
clientVersion: context.runtimeActionVersion,
|
||||
requestOptions,
|
||||
}
|
||||
: {
|
||||
worldType: world,
|
||||
character,
|
||||
monsters,
|
||||
context,
|
||||
requestOptions,
|
||||
};
|
||||
|
||||
return requestJson<AIResponse>(
|
||||
`${RUNTIME_API_BASE}/story/initial`,
|
||||
{
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
worldType: world,
|
||||
character,
|
||||
monsters,
|
||||
context,
|
||||
requestOptions,
|
||||
}),
|
||||
body: JSON.stringify(payload),
|
||||
},
|
||||
'剧情开局生成失败',
|
||||
);
|
||||
@@ -206,25 +197,18 @@ export async function generateNextStep(
|
||||
context: StoryGenerationContext,
|
||||
requestOptions: StoryRequestOptions = {},
|
||||
): Promise<AIResponse> {
|
||||
if (typeof window === 'undefined') {
|
||||
const aiClient = await loadLegacyAiModule();
|
||||
return aiClient.generateNextStep(
|
||||
world,
|
||||
character,
|
||||
monsters,
|
||||
history,
|
||||
choice,
|
||||
context,
|
||||
requestOptions,
|
||||
);
|
||||
}
|
||||
|
||||
return requestJson<AIResponse>(
|
||||
`${RUNTIME_API_BASE}/story/continue`,
|
||||
{
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
const sessionId = getRuntimeSessionIdFromContext(context);
|
||||
const payload: RuntimeStoryAiRequest | Record<string, unknown> = sessionId
|
||||
? {
|
||||
sessionId,
|
||||
clientVersion: context.runtimeActionVersion,
|
||||
choice,
|
||||
lastFunctionId: context.lastFunctionId,
|
||||
observeSignsRequested: context.observeSignsRequested,
|
||||
recentActionResult: context.recentActionResult,
|
||||
requestOptions,
|
||||
}
|
||||
: {
|
||||
worldType: world,
|
||||
character,
|
||||
monsters,
|
||||
@@ -232,7 +216,14 @@ export async function generateNextStep(
|
||||
choice,
|
||||
context,
|
||||
requestOptions,
|
||||
}),
|
||||
};
|
||||
|
||||
return requestJson<AIResponse>(
|
||||
`${RUNTIME_API_BASE}/story/continue`,
|
||||
{
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(payload),
|
||||
},
|
||||
'剧情续写失败',
|
||||
);
|
||||
@@ -248,30 +239,25 @@ export async function generateCharacterPanelChatSuggestions(
|
||||
conversationSummary: string,
|
||||
targetStatus: CharacterChatTargetStatus,
|
||||
) {
|
||||
if (typeof window === 'undefined') {
|
||||
const aiClient = await loadLegacyAiModule();
|
||||
return aiClient.generateCharacterPanelChatSuggestions(
|
||||
world,
|
||||
playerCharacter,
|
||||
targetCharacter,
|
||||
storyHistory,
|
||||
context,
|
||||
conversationHistory,
|
||||
conversationSummary,
|
||||
targetStatus,
|
||||
);
|
||||
}
|
||||
|
||||
const payload = {
|
||||
worldType: world,
|
||||
playerCharacter,
|
||||
targetCharacter,
|
||||
storyHistory,
|
||||
context,
|
||||
conversationHistory,
|
||||
conversationSummary,
|
||||
targetStatus,
|
||||
} satisfies CharacterChatSuggestionsRequest;
|
||||
const sessionId = getRuntimeSessionIdFromContext(context);
|
||||
const payload = sessionId
|
||||
? ({
|
||||
sessionId,
|
||||
targetCharacter,
|
||||
conversationHistory,
|
||||
conversationSummary,
|
||||
targetStatus,
|
||||
} satisfies CharacterChatSuggestionsRequest)
|
||||
: ({
|
||||
worldType: world,
|
||||
playerCharacter,
|
||||
targetCharacter,
|
||||
storyHistory,
|
||||
context,
|
||||
conversationHistory,
|
||||
conversationSummary,
|
||||
targetStatus,
|
||||
} satisfies CharacterChatSuggestionsRequest);
|
||||
|
||||
const { text } = await requestPlainText(
|
||||
`${RUNTIME_API_BASE}/chat/character/suggestions`,
|
||||
@@ -291,30 +277,25 @@ export async function generateCharacterPanelChatSummary(
|
||||
previousSummary: string,
|
||||
targetStatus: CharacterChatTargetStatus,
|
||||
) {
|
||||
if (typeof window === 'undefined') {
|
||||
const aiClient = await loadLegacyAiModule();
|
||||
return aiClient.generateCharacterPanelChatSummary(
|
||||
world,
|
||||
playerCharacter,
|
||||
targetCharacter,
|
||||
storyHistory,
|
||||
context,
|
||||
conversationHistory,
|
||||
previousSummary,
|
||||
targetStatus,
|
||||
);
|
||||
}
|
||||
|
||||
const payload = {
|
||||
worldType: world,
|
||||
playerCharacter,
|
||||
targetCharacter,
|
||||
storyHistory,
|
||||
context,
|
||||
conversationHistory,
|
||||
previousSummary,
|
||||
targetStatus,
|
||||
} satisfies CharacterChatSummaryRequest;
|
||||
const sessionId = getRuntimeSessionIdFromContext(context);
|
||||
const payload = sessionId
|
||||
? ({
|
||||
sessionId,
|
||||
targetCharacter,
|
||||
conversationHistory,
|
||||
previousSummary,
|
||||
targetStatus,
|
||||
} satisfies CharacterChatSummaryRequest)
|
||||
: ({
|
||||
worldType: world,
|
||||
playerCharacter,
|
||||
targetCharacter,
|
||||
storyHistory,
|
||||
context,
|
||||
conversationHistory,
|
||||
previousSummary,
|
||||
targetStatus,
|
||||
} satisfies CharacterChatSummaryRequest);
|
||||
|
||||
const { text } = await requestPlainText(
|
||||
`${RUNTIME_API_BASE}/chat/character/summary`,
|
||||
@@ -336,33 +317,27 @@ export async function streamCharacterPanelChatReply(
|
||||
targetStatus: CharacterChatTargetStatus,
|
||||
options: TextStreamOptions = {},
|
||||
) {
|
||||
if (typeof window === 'undefined') {
|
||||
const aiClient = await loadLegacyAiModule();
|
||||
return aiClient.streamCharacterPanelChatReply(
|
||||
world,
|
||||
playerCharacter,
|
||||
targetCharacter,
|
||||
storyHistory,
|
||||
context,
|
||||
conversationHistory,
|
||||
conversationSummary,
|
||||
playerMessage,
|
||||
targetStatus,
|
||||
options,
|
||||
);
|
||||
}
|
||||
|
||||
const payload = {
|
||||
worldType: world,
|
||||
playerCharacter,
|
||||
targetCharacter,
|
||||
storyHistory,
|
||||
context,
|
||||
conversationHistory,
|
||||
conversationSummary,
|
||||
playerMessage,
|
||||
targetStatus,
|
||||
} satisfies CharacterChatReplyRequest;
|
||||
const sessionId = getRuntimeSessionIdFromContext(context);
|
||||
const payload = sessionId
|
||||
? ({
|
||||
sessionId,
|
||||
targetCharacter,
|
||||
conversationHistory,
|
||||
conversationSummary,
|
||||
playerMessage,
|
||||
targetStatus,
|
||||
} satisfies CharacterChatReplyRequest)
|
||||
: ({
|
||||
worldType: world,
|
||||
playerCharacter,
|
||||
targetCharacter,
|
||||
storyHistory,
|
||||
context,
|
||||
conversationHistory,
|
||||
conversationSummary,
|
||||
playerMessage,
|
||||
targetStatus,
|
||||
} satisfies CharacterChatReplyRequest);
|
||||
|
||||
const reply = await requestPlainTextStream(
|
||||
`${RUNTIME_API_BASE}/chat/character/reply/stream`,
|
||||
@@ -383,31 +358,24 @@ export async function streamNpcChatDialogue(
|
||||
resultSummary: string,
|
||||
options: TextStreamOptions = {},
|
||||
) {
|
||||
if (typeof window === 'undefined') {
|
||||
const aiClient = await loadLegacyAiModule();
|
||||
return aiClient.streamNpcChatDialogue(
|
||||
world,
|
||||
character,
|
||||
encounter,
|
||||
monsters,
|
||||
history,
|
||||
context,
|
||||
topic,
|
||||
resultSummary,
|
||||
options,
|
||||
);
|
||||
}
|
||||
|
||||
const payload = {
|
||||
worldType: world,
|
||||
character,
|
||||
encounter,
|
||||
monsters,
|
||||
history,
|
||||
context,
|
||||
topic,
|
||||
resultSummary,
|
||||
} satisfies NpcChatDialogueRequest;
|
||||
const sessionId = getRuntimeSessionIdFromContext(context);
|
||||
const payload = sessionId
|
||||
? ({
|
||||
sessionId,
|
||||
encounter,
|
||||
topic,
|
||||
resultSummary,
|
||||
} satisfies NpcChatDialogueRequest)
|
||||
: ({
|
||||
worldType: world,
|
||||
character,
|
||||
encounter,
|
||||
monsters,
|
||||
history,
|
||||
context,
|
||||
topic,
|
||||
resultSummary,
|
||||
} satisfies NpcChatDialogueRequest);
|
||||
|
||||
const dialogue = await requestPlainTextStream(
|
||||
`${RUNTIME_API_BASE}/chat/npc/dialogue/stream`,
|
||||
@@ -442,14 +410,9 @@ export async function streamNpcChatTurn(
|
||||
npcInitiatesConversation?: boolean;
|
||||
} = {},
|
||||
) {
|
||||
const payload = {
|
||||
worldType: world,
|
||||
character,
|
||||
player: character,
|
||||
const sessionId = getRuntimeSessionIdFromContext(context);
|
||||
const commonChatPayload = {
|
||||
encounter,
|
||||
monsters,
|
||||
history,
|
||||
context,
|
||||
conversationHistory: conversationHistory ?? [],
|
||||
dialogue: conversationHistory ?? [],
|
||||
playerMessage,
|
||||
@@ -457,7 +420,7 @@ export async function streamNpcChatTurn(
|
||||
npcInitiatesConversation: options.npcInitiatesConversation ?? false,
|
||||
questOfferContext: options.questOfferContext
|
||||
? {
|
||||
state: options.questOfferContext.state,
|
||||
state: sessionId ? {} : options.questOfferContext.state,
|
||||
encounter,
|
||||
turnCount: options.questOfferContext.turnCount,
|
||||
}
|
||||
@@ -471,7 +434,21 @@ export async function streamNpcChatTurn(
|
||||
})),
|
||||
}
|
||||
: null,
|
||||
} satisfies NpcChatTurnRequest;
|
||||
};
|
||||
const payload = sessionId
|
||||
? ({
|
||||
sessionId,
|
||||
...commonChatPayload,
|
||||
} satisfies NpcChatTurnRequest)
|
||||
: ({
|
||||
worldType: world,
|
||||
character,
|
||||
player: character,
|
||||
monsters,
|
||||
history,
|
||||
context,
|
||||
...commonChatPayload,
|
||||
} satisfies NpcChatTurnRequest);
|
||||
|
||||
const response = await fetchWithApiAuth(
|
||||
`${RUNTIME_API_BASE}/chat/npc/turn/stream`,
|
||||
@@ -570,31 +547,24 @@ export async function streamNpcRecruitDialogue(
|
||||
recruitSummary: string,
|
||||
options: TextStreamOptions = {},
|
||||
) {
|
||||
if (typeof window === 'undefined') {
|
||||
const aiClient = await loadLegacyAiModule();
|
||||
return aiClient.streamNpcRecruitDialogue(
|
||||
world,
|
||||
character,
|
||||
encounter,
|
||||
monsters,
|
||||
history,
|
||||
context,
|
||||
invitationText,
|
||||
recruitSummary,
|
||||
options,
|
||||
);
|
||||
}
|
||||
|
||||
const payload = {
|
||||
worldType: world,
|
||||
character,
|
||||
encounter,
|
||||
monsters,
|
||||
history,
|
||||
context,
|
||||
invitationText,
|
||||
recruitSummary,
|
||||
} satisfies NpcRecruitDialogueRequest;
|
||||
const sessionId = getRuntimeSessionIdFromContext(context);
|
||||
const payload = sessionId
|
||||
? ({
|
||||
sessionId,
|
||||
encounter,
|
||||
invitationText,
|
||||
recruitSummary,
|
||||
} satisfies NpcRecruitDialogueRequest)
|
||||
: ({
|
||||
worldType: world,
|
||||
character,
|
||||
encounter,
|
||||
monsters,
|
||||
history,
|
||||
context,
|
||||
invitationText,
|
||||
recruitSummary,
|
||||
} satisfies NpcRecruitDialogueRequest);
|
||||
|
||||
const dialogue = await requestPlainTextStream(
|
||||
`${RUNTIME_API_BASE}/chat/npc/recruit/stream`,
|
||||
|
||||
Reference in New Issue
Block a user