Integrate role asset studio into custom world agent flow

This commit is contained in:
2026-04-14 20:16:41 +08:00
parent 0981d6ee1b
commit bc2999ffb9
118 changed files with 31211 additions and 1232 deletions

View File

@@ -0,0 +1,99 @@
import type { CustomWorldRoleAssetSummary } from '../../../packages/shared/src/contracts/customWorldAgent.js';
import {
getRoleAssetSummaryById,
mergeRoleAssetIntoDraftProfile,
} from './customWorldAgentRoleAssetStateService.js';
function toText(value: unknown) {
return typeof value === 'string' ? value.trim() : '';
}
function toRecord(value: unknown) {
return value && typeof value === 'object' && !Array.isArray(value)
? (value as Record<string, unknown>)
: null;
}
function toRecordArray(value: unknown) {
return Array.isArray(value)
? value.filter(
(item): item is Record<string, unknown> =>
Boolean(item) && typeof item === 'object' && !Array.isArray(item),
)
: [];
}
type SyncRoleAssetsPayload = {
roleId: string;
portraitPath: string;
generatedVisualAssetId: string;
generatedAnimationSetId?: string | null;
animationMap?: Record<string, unknown> | null;
};
export type SyncRoleAssetsResult = {
roleId: string;
updatedRole: Record<string, unknown>;
updatedAssetSummary: CustomWorldRoleAssetSummary;
draftProfile: Record<string, unknown>;
};
export class CustomWorldAgentAssetBridgeService {
buildRoleAssetStudioContext(snapshot: unknown, roleId: string) {
const profile = toRecord(snapshot);
if (!profile) {
throw new Error('当前世界草稿为空,无法打开角色资产工坊。');
}
const playableRole = toRecordArray(profile.playableNpcs).find(
(item) => toText(item.id) === roleId,
);
const storyRole = toRecordArray(profile.storyNpcs).find(
(item) => toText(item.id) === roleId,
);
const role = playableRole ?? storyRole;
if (!role) {
throw new Error('未找到目标角色,无法进入角色资产工坊。');
}
const assetSummary = getRoleAssetSummaryById(profile, roleId);
if (!assetSummary) {
throw new Error('未找到目标角色的资产摘要。');
}
return {
roleId,
roleName: toText(role.name) || assetSummary.roleName,
roleKind: playableRole ? ('playable' as const) : ('story' as const),
startFrom:
assetSummary.status === 'missing' ? ('visual' as const) : ('animation' as const),
assetSummary,
};
}
applyRoleAssetPublishResult(
snapshot: unknown,
payload: SyncRoleAssetsPayload,
): SyncRoleAssetsResult {
const profile = toRecord(snapshot);
if (!profile) {
throw new Error('当前世界草稿为空,无法同步角色资产。');
}
const { draftProfile, updatedRole } = mergeRoleAssetIntoDraftProfile(
profile,
payload,
);
const assetSummary = getRoleAssetSummaryById(draftProfile, payload.roleId);
if (!assetSummary) {
throw new Error('角色资产同步后未能生成新的资产摘要。');
}
return {
roleId: payload.roleId,
updatedRole,
updatedAssetSummary: assetSummary,
draftProfile,
};
}
}