1
This commit is contained in:
196
server-node/src/services/customWorldAgentMessageTurnService.ts
Normal file
196
server-node/src/services/customWorldAgentMessageTurnService.ts
Normal file
@@ -0,0 +1,196 @@
|
||||
import crypto from 'node:crypto';
|
||||
|
||||
import type {
|
||||
CreatorIntentReadiness,
|
||||
CustomWorldAgentMessage,
|
||||
CustomWorldAgentSessionSnapshot,
|
||||
} from '../../../packages/shared/src/contracts/customWorldAgent.js';
|
||||
import {
|
||||
buildPendingClarifications,
|
||||
evaluateCreatorIntentReadiness,
|
||||
resolveCreatorIntentStage,
|
||||
} from './customWorldAgentClarificationService.js';
|
||||
import {
|
||||
buildAnchorPackFromIntent,
|
||||
buildDraftSummaryFromIntent,
|
||||
buildDraftTitleFromIntent,
|
||||
type CustomWorldCreatorIntentRecord,
|
||||
} from './customWorldAgentIntentExtractionService.js';
|
||||
import type {
|
||||
CustomWorldAgentSessionRecord,
|
||||
CustomWorldAgentSessionStore,
|
||||
} from './customWorldAgentSessionStore.js';
|
||||
import type { CustomWorldAgentSuggestedActionService } from './customWorldAgentSuggestedActionService.js';
|
||||
import { CustomWorldAgentSnapshotBuilder } from './customWorldAgentSnapshotBuilder.js';
|
||||
import {
|
||||
buildCreatorIntentFromEightAnchorContent,
|
||||
buildEightAnchorContentFromCreatorIntent,
|
||||
} from './eightAnchorCompatibilityService.js';
|
||||
import { EightAnchorSingleTurnService } from './eightAnchorSingleTurnService.js';
|
||||
|
||||
function buildDerivedState(
|
||||
intent: CustomWorldCreatorIntentRecord,
|
||||
hasUserInput: boolean,
|
||||
suggestedActionService: CustomWorldAgentSuggestedActionService,
|
||||
) {
|
||||
const readiness = evaluateCreatorIntentReadiness(intent);
|
||||
const pendingClarifications = buildPendingClarifications(intent, readiness);
|
||||
const stage = resolveCreatorIntentStage({
|
||||
hasUserInput,
|
||||
readiness,
|
||||
});
|
||||
|
||||
return {
|
||||
readiness,
|
||||
pendingClarifications,
|
||||
stage,
|
||||
anchorPack: buildAnchorPackFromIntent(intent, {
|
||||
completedKeys: readiness.completedKeys,
|
||||
missingKeys: readiness.missingKeys,
|
||||
}),
|
||||
draftProfile: {
|
||||
title: buildDraftTitleFromIntent(intent),
|
||||
summary: buildDraftSummaryFromIntent(intent),
|
||||
},
|
||||
suggestedActions: suggestedActionService.buildSuggestedActions({
|
||||
stage,
|
||||
isReady: readiness.isReady,
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
export class CustomWorldAgentMessageTurnService {
|
||||
constructor(
|
||||
private readonly sessionStore: CustomWorldAgentSessionStore,
|
||||
private readonly eightAnchorSingleTurnService: EightAnchorSingleTurnService,
|
||||
private readonly suggestedActionService: CustomWorldAgentSuggestedActionService,
|
||||
private readonly snapshotBuilder: CustomWorldAgentSnapshotBuilder,
|
||||
) {}
|
||||
|
||||
async applyMessageTurn(params: {
|
||||
userId: string;
|
||||
sessionId: string;
|
||||
latestUserText: string;
|
||||
quickFillRequested: boolean;
|
||||
relatedOperationId?: string | null;
|
||||
onReplyUpdate?: (text: string) => void;
|
||||
}) {
|
||||
const latestSession = (await this.sessionStore.get(
|
||||
params.userId,
|
||||
params.sessionId,
|
||||
)) as CustomWorldAgentSessionRecord | null;
|
||||
if (!latestSession) {
|
||||
throw new Error('custom world agent session not found');
|
||||
}
|
||||
|
||||
const shouldPreserveDraftStage =
|
||||
(latestSession.stage === 'object_refining' ||
|
||||
latestSession.stage === 'visual_refining') &&
|
||||
latestSession.draftCards.length > 0;
|
||||
|
||||
const assistantTurn = await this.eightAnchorSingleTurnService.streamTurn(
|
||||
{
|
||||
currentTurn: latestSession.currentTurn + 1,
|
||||
progressPercent: latestSession.progressPercent,
|
||||
quickFillRequested: params.quickFillRequested,
|
||||
currentAnchorContent: latestSession.anchorContent,
|
||||
chatHistory: latestSession.messages
|
||||
.filter(
|
||||
(message): message is CustomWorldAgentMessage =>
|
||||
(message.role === 'user' || message.role === 'assistant') &&
|
||||
Boolean(message.text.trim()),
|
||||
)
|
||||
.map((message) => ({
|
||||
role: message.role,
|
||||
content: message.text,
|
||||
})),
|
||||
},
|
||||
{
|
||||
onReplyUpdate: params.onReplyUpdate,
|
||||
},
|
||||
);
|
||||
const nextCreatorIntent = buildCreatorIntentFromEightAnchorContent(
|
||||
assistantTurn.nextAnchorContent,
|
||||
);
|
||||
const progressPercent = Math.max(
|
||||
0,
|
||||
Math.min(100, Math.round(assistantTurn.progressPercent)),
|
||||
);
|
||||
const creatorIntentReadiness: CreatorIntentReadiness =
|
||||
progressPercent >= 100
|
||||
? {
|
||||
isReady: true,
|
||||
completedKeys: [
|
||||
'world_hook',
|
||||
'player_premise',
|
||||
'theme_and_tone',
|
||||
'core_conflict',
|
||||
'relationship_seed',
|
||||
'iconic_element',
|
||||
],
|
||||
missingKeys: [],
|
||||
}
|
||||
: evaluateCreatorIntentReadiness(nextCreatorIntent);
|
||||
const derivedState = buildDerivedState(
|
||||
nextCreatorIntent,
|
||||
true,
|
||||
this.suggestedActionService,
|
||||
);
|
||||
const shouldStayInDraftStage =
|
||||
shouldPreserveDraftStage && progressPercent >= 100;
|
||||
const assistantMessage = {
|
||||
id: `message-${crypto.randomBytes(8).toString('hex')}`,
|
||||
role: 'assistant',
|
||||
kind: 'chat',
|
||||
text: assistantTurn.replyText,
|
||||
createdAt: new Date().toISOString(),
|
||||
relatedOperationId: params.relatedOperationId ?? null,
|
||||
} satisfies CustomWorldAgentMessage;
|
||||
|
||||
await this.sessionStore.replaceDerivedState(
|
||||
params.userId,
|
||||
params.sessionId,
|
||||
this.snapshotBuilder.buildMessageTurnState({
|
||||
latestSession,
|
||||
nextAnchorContent: assistantTurn.nextAnchorContent,
|
||||
progressPercent,
|
||||
replyText: assistantTurn.replyText,
|
||||
nextCreatorIntent,
|
||||
creatorIntentReadiness,
|
||||
derivedDraftProfile: derivedState.draftProfile,
|
||||
derivedPendingClarifications: derivedState.pendingClarifications,
|
||||
derivedStage: derivedState.stage,
|
||||
shouldStayInDraftStage,
|
||||
}),
|
||||
);
|
||||
await this.sessionStore.appendMessage(
|
||||
params.userId,
|
||||
params.sessionId,
|
||||
assistantMessage,
|
||||
);
|
||||
|
||||
return (await this.sessionStore.getSnapshot(
|
||||
params.userId,
|
||||
params.sessionId,
|
||||
)) as CustomWorldAgentSessionSnapshot;
|
||||
}
|
||||
|
||||
deriveInitialSessionState(params: {
|
||||
seedText: string;
|
||||
creatorIntent: CustomWorldCreatorIntentRecord;
|
||||
}) {
|
||||
const anchorContent = buildEightAnchorContentFromCreatorIntent(
|
||||
params.creatorIntent,
|
||||
);
|
||||
const derivedState = buildDerivedState(
|
||||
params.creatorIntent,
|
||||
Boolean(params.seedText),
|
||||
this.suggestedActionService,
|
||||
);
|
||||
|
||||
return {
|
||||
anchorContent,
|
||||
...derivedState,
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user