Files
Genarrative/src/components/rpg-entry/rpgEntryShared.ts
2026-04-25 22:19:04 +08:00

135 lines
4.3 KiB
TypeScript

import type {
CustomWorldAgentMessage,
CustomWorldAgentOperationRecord,
CustomWorldWorkSummary,
} from '../../../packages/shared/src/contracts/customWorldAgent';
import type { CustomWorldLibraryEntry } from '../../../packages/shared/src/contracts/runtime';
import { ApiClientError, isTimeoutError } from '../../services/apiClient';
import { buildCustomWorldCreatorIntentFoundationText } from '../../services/customWorldCreatorIntent';
import type { CustomWorldProfile } from '../../types';
export function resolveRpgEntryErrorMessage(
error: unknown,
fallback: string,
) {
if (isTimeoutError(error)) {
if (//u.test(fallback)) {
return '开启拼图创作工作台超时,请确认运行时后端已启动后重试。';
}
if (//u.test(fallback)) {
return '开启大鱼吃小鱼创作工作台超时,请确认运行时后端已启动后重试。';
}
if (//u.test(fallback)) {
return '开启创作工作台超时,请确认运行时后端已启动后重试。';
}
return '请求超时,请稍后重试。';
}
if (
error instanceof ApiClientError &&
error.status === 401 &&
(error.code === 'UNAUTHORIZED' ||
error.message.includes('Authorization Bearer Token'))
) {
return '当前登录状态已失效,请重新登录后继续。';
}
return error instanceof Error ? error.message : fallback;
}
export function createFailedRpgEntryAgentOperation(params: {
type: CustomWorldAgentOperationRecord['type'];
phaseLabel: string;
error: string;
}): CustomWorldAgentOperationRecord {
return {
operationId: `local-failed-${Date.now()}`,
type: params.type,
status: 'failed',
phaseLabel: params.phaseLabel,
phaseDetail: params.error,
progress: 0,
error: params.error,
updatedAt: new Date().toISOString(),
};
}
export function buildOptimisticRpgEntryAgentMessage(
payload: Pick<CustomWorldAgentMessage, 'id' | 'role' | 'kind' | 'text'>,
): CustomWorldAgentMessage {
return {
...payload,
createdAt: new Date().toISOString(),
relatedOperationId: null,
};
}
export function normalizeRpgEntryAgentBackedProfile(
profile: CustomWorldProfile,
) {
const foundationText = buildCustomWorldCreatorIntentFoundationText(
profile.creatorIntent,
).trim();
if (!foundationText || foundationText === profile.settingText.trim()) {
return profile;
}
return {
...profile,
settingText: foundationText,
} satisfies CustomWorldProfile;
}
export function stringifyRpgEntryAgentBackedProfile(
profile: CustomWorldProfile,
) {
return JSON.stringify(normalizeRpgEntryAgentBackedProfile(profile));
}
export function buildRpgEntryCreationHubFallbackItems(
entries: CustomWorldLibraryEntry<CustomWorldProfile>[],
): CustomWorldWorkSummary[] {
return entries
.filter((entry) => entry.visibility === 'published')
.map((entry) => ({
workId: `fallback:${entry.profileId}`,
sourceType: 'published_profile',
status: 'published',
title: entry.worldName,
subtitle: entry.subtitle || '已发布作品',
summary: entry.summaryText || '继续补完这个世界的设定与游玩入口。',
coverImageSrc: entry.coverImageSrc,
coverRenderMode: 'image',
coverCharacterImageSrcs: [],
updatedAt: entry.updatedAt,
publishedAt: entry.publishedAt,
stage: null,
stageLabel: '已发布',
playableNpcCount: entry.playableNpcCount,
landmarkCount: entry.landmarkCount,
roleVisualReadyCount: 0,
roleAnimationReadyCount: 0,
roleAssetSummaryLabel: null,
sessionId: null,
profileId: entry.profileId,
canResume: false,
canEnterWorld: true,
}));
}
/**
* 兼容创作链工作包已经接入的旧 helper 命名,避免本轮迁移波及其他并行改动。
*/
export const resolveRpgCreationErrorMessage = resolveRpgEntryErrorMessage;
export const createFailedAgentOperation =
createFailedRpgEntryAgentOperation;
export const buildOptimisticAgentMessage =
buildOptimisticRpgEntryAgentMessage;
export const normalizeAgentBackedProfile =
normalizeRpgEntryAgentBackedProfile;
export const stringifyAgentBackedProfile =
stringifyRpgEntryAgentBackedProfile;
export const buildCreationHubFallbackItems =
buildRpgEntryCreationHubFallbackItems;