135 lines
4.3 KiB
TypeScript
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;
|