import { Injectable } from "@nestjs/common"; import { PrismaService } from "../prisma/prisma.service.js"; import { EmotionService, dominantState } from "../emotion/emotion.service.js"; import { toResponseStyle } from "../emotion/response-style.js"; import { RelationshipService } from "../relationship/relationship.service.js"; import { KeywordMemoryRetriever } from "../memory/memory-retriever.service.js"; import { WorkingMemoryService } from "../memory/working-memory.service.js"; import { hashToSeed } from "./seeded-random.js"; import type { GenerationContext } from "./types.js"; export interface AssembleOptions { skipMemoryRetrieval?: boolean; seed?: number; now?: Date; } // F-6 上下文組裝器:把人設、當前情緒、檢索到的記憶、關係參數、對話歷史組裝成統一上下文物件。 @Injectable() export class ContextAssemblerService { constructor( private readonly prisma: PrismaService, private readonly emotion: EmotionService, private readonly relationship: RelationshipService, private readonly retriever: KeywordMemoryRetriever, private readonly workingMemory: WorkingMemoryService, ) {} async assemble( characterId: string, userId: string, sessionId: string, userInput: string, options: AssembleOptions = {}, ): Promise { const now = options.now ?? new Date(); const character = await this.prisma.client.character.findUniqueOrThrow({ where: { id: characterId } }); const emotionState = await this.emotion.getState(characterId, now); const dominant = dominantState(emotionState); const style = toResponseStyle(dominant); const { relationship } = await this.relationship.getState(characterId, userId, now); const retrievedMemories = options.skipMemoryRetrieval ? [] : await this.retriever.retrieve(characterId, userInput, { relatedUserId: userId }); const history = (await this.workingMemory.getContext(sessionId)).map((entry) => ({ role: entry.role, content: entry.content, timestamp: entry.timestamp.toISOString(), })); const seed = options.seed ?? hashToSeed(`${sessionId}:${history.length}`); return { character: { id: character.id, personalityArchetype: character.personalityArchetype, speechStyle: character.speechStyle, likesDislikes: character.likesDislikes, basicInfo: character.basicInfo, }, emotion: { dominant, style }, relationship: { intimacy: relationship.intimacy, trust: relationship.trust, stage: relationship.stage, }, retrievedMemories, history, userInput, injectedMemoryIds: retrievedMemories.map((memory) => memory.id), seed, }; } }