diff --git a/packages/stage-ui/src/stores/chat/context-providers/datetime.ts b/packages/stage-ui/src/stores/chat/context-providers/datetime.ts index 8f08ccd4a..ecc6fe116 100644 --- a/packages/stage-ui/src/stores/chat/context-providers/datetime.ts +++ b/packages/stage-ui/src/stores/chat/context-providers/datetime.ts @@ -18,5 +18,14 @@ export function createDatetimeContext(): ContextMessage { strategy: ContextUpdateStrategy.ReplaceSelf, text: `Current datetime: ${now.toISOString()} (${now.toLocaleString()})`, createdAt: Date.now(), + metadata: { + source: { + id: DATETIME_CONTEXT_ID, + kind: 'plugin', + plugin: { + id: 'airi:system:datetime', + }, + }, + }, } } diff --git a/packages/stage-ui/src/stores/mods/api/context-bridge.ts b/packages/stage-ui/src/stores/mods/api/context-bridge.ts index 75e560c86..25d503a0a 100644 --- a/packages/stage-ui/src/stores/mods/api/context-bridge.ts +++ b/packages/stage-ui/src/stores/mods/api/context-bridge.ts @@ -1,7 +1,7 @@ import type { ChatProvider } from '@xsai-ext/providers/utils' import type { UserMessage } from '@xsai/shared-chat' -import type { ChatStreamEvent, ContextMessage } from '../../../types/chat' +import type { ChatStreamEvent, ChatStreamEventContext, ContextMessage } from '../../../types/chat' import { isStageTamagotchi, isStageWeb } from '@proj-airi/stage-shared' import { useBroadcastChannel } from '@vueuse/core' @@ -19,6 +19,20 @@ import { useConsciousnessStore } from '../../modules/consciousness' import { useProvidersStore } from '../../providers' import { useModsServerChannelStore } from './channel-server' +export function normalizeContextSnapshot>(contexts: C): C { + return { + ...contexts, + contexts: Object.fromEntries( + Object + .entries(toRaw(contexts.contexts)) + .map(([key, ctx]) => [ + key, + ctx.map(c => toRaw(c)), + ]), + ), + } +} + export const useContextBridgeStore = defineStore('mods:api:context-bridge', () => { const mutex = new Mutex() @@ -156,49 +170,49 @@ export const useContextBridgeStore = defineStore('mods:api:context-bridge', () = if (isProcessingRemoteStream) return - broadcastStreamEvent({ type: 'before-compose', message, sessionId: chatSession.activeSessionId, context: structuredClone(toRaw(context)) }) + broadcastStreamEvent({ type: 'before-compose', message, sessionId: chatSession.activeSessionId, context: structuredClone(normalizeContextSnapshot(context)) }) }), chatOrchestrator.onAfterMessageComposed(async (message, context) => { if (isProcessingRemoteStream) return - broadcastStreamEvent({ type: 'after-compose', message, sessionId: chatSession.activeSessionId, context: structuredClone(toRaw(context)) }) + broadcastStreamEvent({ type: 'after-compose', message, sessionId: chatSession.activeSessionId, context: structuredClone(normalizeContextSnapshot(context)) }) }), chatOrchestrator.onBeforeSend(async (message, context) => { if (isProcessingRemoteStream) return - broadcastStreamEvent({ type: 'before-send', message, sessionId: chatSession.activeSessionId, context: structuredClone(toRaw(context)) }) + broadcastStreamEvent({ type: 'before-send', message, sessionId: chatSession.activeSessionId, context: structuredClone(normalizeContextSnapshot(context)) }) }), chatOrchestrator.onAfterSend(async (message, context) => { if (isProcessingRemoteStream) return - broadcastStreamEvent({ type: 'after-send', message, sessionId: chatSession.activeSessionId, context: structuredClone(toRaw(context)) }) + broadcastStreamEvent({ type: 'after-send', message, sessionId: chatSession.activeSessionId, context: structuredClone(normalizeContextSnapshot(context)) }) }), chatOrchestrator.onTokenLiteral(async (literal, context) => { if (isProcessingRemoteStream) return - broadcastStreamEvent({ type: 'token-literal', literal, sessionId: chatSession.activeSessionId, context: structuredClone(toRaw(context)) }) + broadcastStreamEvent({ type: 'token-literal', literal, sessionId: chatSession.activeSessionId, context: structuredClone(normalizeContextSnapshot(context)) }) }), chatOrchestrator.onTokenSpecial(async (special, context) => { if (isProcessingRemoteStream) return - broadcastStreamEvent({ type: 'token-special', special, sessionId: chatSession.activeSessionId, context: structuredClone(toRaw(context)) }) + broadcastStreamEvent({ type: 'token-special', special, sessionId: chatSession.activeSessionId, context: structuredClone(normalizeContextSnapshot(context)) }) }), chatOrchestrator.onStreamEnd(async (context) => { if (isProcessingRemoteStream) return - broadcastStreamEvent({ type: 'stream-end', sessionId: chatSession.activeSessionId, context: structuredClone(toRaw(context)) }) + broadcastStreamEvent({ type: 'stream-end', sessionId: chatSession.activeSessionId, context: structuredClone(normalizeContextSnapshot(context)) }) }), chatOrchestrator.onAssistantResponseEnd(async (message, context) => { if (isProcessingRemoteStream) return - broadcastStreamEvent({ type: 'assistant-end', message, sessionId: chatSession.activeSessionId, context: structuredClone(toRaw(context)) }) + broadcastStreamEvent({ type: 'assistant-end', message, sessionId: chatSession.activeSessionId, context: structuredClone(normalizeContextSnapshot(context)) }) }), chatOrchestrator.onAssistantMessage(async (message, _messageText, context) => { diff --git a/packages/stage-ui/src/utils/event-source.ts b/packages/stage-ui/src/utils/event-source.ts index b6190877a..f3ea7cf16 100644 --- a/packages/stage-ui/src/utils/event-source.ts +++ b/packages/stage-ui/src/utils/event-source.ts @@ -3,7 +3,6 @@ import type { MetadataEventSource } from '@proj-airi/server-sdk' interface EventSourcePayload { source?: string metadata?: { source?: MetadataEventSource } - eventMetadata?: { source?: MetadataEventSource } } function formatMetadataSource(source?: MetadataEventSource) { @@ -18,8 +17,7 @@ function formatMetadataSource(source?: MetadataEventSource) { export function getEventSourceKey(event: EventSourcePayload, fallback = 'unknown') { return ( - formatMetadataSource(event.eventMetadata?.source) - ?? formatMetadataSource(event.metadata?.source) + formatMetadataSource(event.metadata?.source) ?? event.source ?? fallback )