feat: migrate runtime backend to node server

This commit is contained in:
victo
2026-04-08 16:41:29 +08:00
parent 9d2fc9e4b8
commit a83841ff2d
70 changed files with 8239 additions and 1561 deletions

View File

@@ -38,6 +38,7 @@ import type {
StoryRequestOptions,
TextStreamOptions,
} from './aiTypes';
import { fetchWithApiAuth } from './apiClient';
import {
buildCharacterPanelChatPrompt,
buildCharacterPanelChatSuggestionPrompt,
@@ -129,6 +130,8 @@ export type {
TextStreamOptions,
} from './aiTypes';
const ENV: Partial<ImportMetaEnv> = import.meta.env ?? {};
type RawOptionItem = {
functionId: string;
actionText?: string;
@@ -139,7 +142,7 @@ type MergeableCustomWorldRoleEntry = {
};
const CUSTOM_WORLD_SCENE_IMAGE_API_BASE_URL =
import.meta.env.VITE_SCENE_IMAGE_PROXY_BASE_URL ||
ENV.VITE_SCENE_IMAGE_PROXY_BASE_URL ||
'/api/custom-world/scene-image';
const CUSTOM_WORLD_JSON_REPAIR_SYSTEM_PROMPT = `你是 JSON 修复器。
你会收到一段本应为单个 JSON 对象的文本。
@@ -155,7 +158,7 @@ const CUSTOM_WORLD_FRAMEWORK_LANDMARK_NETWORK_BATCH_SIZE = 3;
const CUSTOM_WORLD_PLAYABLE_BATCH_SIZE = 3;
const CUSTOM_WORLD_STORY_BATCH_SIZE = 5;
const CUSTOM_WORLD_SCENE_IMAGE_REQUEST_TIMEOUT_MS = (() => {
const rawValue = Number(import.meta.env.VITE_SCENE_IMAGE_REQUEST_TIMEOUT_MS);
const rawValue = Number(ENV.VITE_SCENE_IMAGE_REQUEST_TIMEOUT_MS);
return Number.isFinite(rawValue) && rawValue > 0 ? rawValue : 150000;
})();
@@ -1776,6 +1779,60 @@ async function requestCompletion(
);
}
export async function generateInitialStoryStrict(
world: WorldType,
character: Character,
monsters: SceneHostileNpc[],
context: StoryGenerationContext,
requestOptions: StoryRequestOptions = {},
): Promise<AIResponse> {
return requestCompletion(
buildUserPrompt(
world,
character,
monsters,
[],
context,
undefined,
requestOptions.availableOptions,
requestOptions.optionCatalog,
),
world,
character,
monsters,
context,
requestOptions,
);
}
export async function generateNextStepStrict(
world: WorldType,
character: Character,
monsters: SceneHostileNpc[],
history: StoryMoment[],
choice: string,
context: StoryGenerationContext,
requestOptions: StoryRequestOptions = {},
): Promise<AIResponse> {
return requestCompletion(
buildUserPrompt(
world,
character,
monsters,
history,
context,
choice,
requestOptions.availableOptions,
requestOptions.optionCatalog,
),
world,
character,
monsters,
context,
requestOptions,
);
}
export async function generateCustomWorldSceneImage({
profile,
landmark,
@@ -1794,7 +1851,7 @@ export async function generateCustomWorldSceneImage({
);
try {
const response = await fetch(CUSTOM_WORLD_SCENE_IMAGE_API_BASE_URL, {
const response = await fetchWithApiAuth(CUSTOM_WORLD_SCENE_IMAGE_API_BASE_URL, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({