artifactStore.test.ts 2.7 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950
  1. import { describe, expect, it } from 'vitest'
  2. import type { ChatMessage, SessionSummary } from '@shared/types'
  3. import { extractArtifacts } from '@/utils/artifacts'
  4. const session: SessionSummary = {
  5. id: 'session-1', profile: 'yaoyao', source: 'web', title: '产物会话', messageCount: 4,
  6. toolCallCount: 1, startedAt: 1, updatedAt: 5,
  7. }
  8. function row(id: string, role: ChatMessage['role'], content: string, extras: Partial<ChatMessage> = {}): ChatMessage {
  9. return { id, sessionId: session.id, profile: session.profile, role, content, timestamp: 2, stage: 'settled', ...extras }
  10. }
  11. describe('artifact extractor', () => {
  12. it('extracts assistant markdown with readable link titles and ignores user rows', () => {
  13. const artifacts = extractArtifacts(session, [
  14. row('user', 'user', '[不要收集](https://private.example/user)'),
  15. row('assistant', 'assistant', '查看 [发布说明](https://example.com/releases/v1) 和 ![图](/tmp/chart.png)'),
  16. ])
  17. expect(artifacts.map(item => item.value)).toEqual(expect.arrayContaining(['https://example.com/releases/v1', '/tmp/chart.png']))
  18. expect(artifacts.find(item => item.kind === 'link')?.label).toBe('发布说明')
  19. expect(artifacts.find(item => item.kind === 'image')?.label).toBe('chart.png')
  20. })
  21. it('preserves structured attachment metadata for preview', () => {
  22. const artifacts = extractArtifacts(session, [row('assistant', 'assistant', '', {
  23. attachments: [{ id: 'attachment-1', name: 'demo.mp4', mimeType: 'video/mp4', size: 10, path: '/tmp/demo.mp4', kind: 'file' }],
  24. })])
  25. expect(artifacts[0]).toMatchObject({ kind: 'file', value: '/tmp/demo.mp4', mimeType: 'video/mp4' })
  26. expect(artifacts[0].attachment?.id).toBe('attachment-1')
  27. })
  28. it('extracts hinted tool result paths and deduplicates within a session', () => {
  29. const artifacts = extractArtifacts(session, [row('tool', 'tool', '{"output_path":"/tmp/result.pdf"}', {
  30. toolCalls: [{ id: 'tool-1', name: 'write', status: 'completed', result: { output_path: '/tmp/result.pdf' } }],
  31. })])
  32. expect(artifacts).toHaveLength(1)
  33. expect(artifacts[0]).toMatchObject({ kind: 'file', value: '/tmp/result.pdf', label: 'result.pdf' })
  34. })
  35. it('keeps the same artifact path when it was produced by distinct messages', () => {
  36. const artifacts = extractArtifacts(session, [
  37. row('assistant-1', 'assistant', '[第一版](/tmp/result.pdf)'),
  38. row('assistant-2', 'assistant', '[第二版](/tmp/result.pdf)', { timestamp: 3 }),
  39. ])
  40. expect(artifacts).toHaveLength(2)
  41. expect(artifacts.map(item => item.messageId)).toEqual(['assistant-1', 'assistant-2'])
  42. expect(new Set(artifacts.map(item => item.id)).size).toBe(2)
  43. })
  44. })