mirror of
https://github.com/hellodigua/ChatLab.git
synced 2026-10-25 17:54:34 +08:00
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:
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 }]
|
||||
|
||||
Reference in new issue
Block a user