fix(web): normalize AI conversation timestamps

Convert Browser Runtime millisecond timestamps at the Web presentation boundary so the shared conversation list keeps its seconds-based contract.
This commit is contained in:
digua committed 2026-08-07 13:40:39 +08:00
1 parent 23bfd7aec4
commit 5c8b5df2d3
3 files changed
+33 -5

No files matched your search

@@ -9,7 +9,7 @@ import ConversationList from '@/components/AIChat/chat/ConversationList.vue'
import SimpleAIChatInput from '@/components/AIChat/input/SimpleAIChatInput.vue'
import { reportRuntimeLog } from '@/services/log-report'
import { useLayoutStore } from '@/stores/layout'
import { toWebAIChatMessage } from './message-mapper'
import { toAIConversationListItem, toWebAIChatMessage } from './message-mapper'
import { isAbortError, resolveSendTarget } from './send-lifecycle'
import { useWebAISettingsStore } from '../../stores/web-ai-settings'
@@ -40,6 +40,7 @@ const displayMessages = computed(() => {
if (streamingMessage.value) result.push(toWebAIChatMessage(streamingMessage.value, true))
return result
})
const conversationListItems = computed(() => conversations.value.map(toAIConversationListItem))
const canRetry = computed(() => messages.value.at(-1)?.role === 'user')
const canRegenerate = computed(() => messages.value.at(-1)?.role === 'assistant')
@@ -376,7 +377,7 @@ function handleSend(payload: { content: string }) {
:session-id="sessionId"
:active-id="conversationId"
:disabled="generating"
:conversations="conversations"
:conversations="conversationListItems"
:loading="loading"
:collapsible="false"
embedded
@@ -392,7 +393,7 @@ function handleSend(payload: { content: string }) {
:session-id="sessionId"
:active-id="conversationId"
:disabled="generating"
:conversations="conversations"
:conversations="conversationListItems"
:loading="loading"
@select="selectConversation"
@create="newConversation"
@@ -1,7 +1,7 @@
import assert from 'node:assert/strict'
import { describe, it } from 'node:test'
import { toWebAIContentBlocks } from './message-mapper'
import { toAIConversationListItem, toWebAIContentBlocks } from './message-mapper'
describe('toWebAIContentBlocks', () => {
it('maps shared runtime blocks to the existing ChatLab message renderer', () => {
@@ -24,3 +24,21 @@ describe('toWebAIContentBlocks', () => {
assert.equal(blocks[1]?.type === 'tool' ? blocks[1].tool.status : null, 'done')
})
})
describe('toAIConversationListItem', () => {
it('normalizes Browser Runtime millisecond timestamps for the shared conversation list', () => {
const createdAt = Date.UTC(2025, 0, 1, 12)
const updatedAt = Date.UTC(2025, 5, 1, 12)
const item = toAIConversationListItem({
id: 'conversation-1',
sessionId: 'session-1',
title: 'Conversation',
createdAt,
updatedAt,
})
assert.equal(item.createdAt, Math.floor(createdAt / 1000))
assert.equal(item.updatedAt, Math.floor(updatedAt / 1000))
})
})
@@ -1,7 +1,16 @@
import type { RuntimeContentBlock, RuntimeMessage } from '@openchatlab/ai-runtime'
import type { RuntimeContentBlock, RuntimeConversation, RuntimeMessage } from '@openchatlab/ai-runtime'
import type { ChatEvidencePayload } from '@openchatlab/core'
import type { AIConversationListItem } from '@/components/AIChat/chat/ConversationList.vue'
import type { ChatMessage, ContentBlock } from '@/stores/aiChat'
export function toAIConversationListItem(conversation: RuntimeConversation): AIConversationListItem {
return {
...conversation,
createdAt: Math.floor(conversation.createdAt / 1000),
updatedAt: Math.floor(conversation.updatedAt / 1000),
}
}
export function toWebAIContentBlocks(blocks: RuntimeContentBlock[] | undefined): ContentBlock[] {
return (blocks ?? []).flatMap((block): ContentBlock[] => {
if (block.type === 'text') return [{ type: 'text', text: block.text }]