fix: pipeline run panel summary

This commit is contained in:
zxhlyh 2026-01-15 18:02:19 +08:00
parent 1126a2aa95
commit f02adc26e5
6 changed files with 95 additions and 66 deletions

View File

@ -1,10 +1,11 @@
import type { QAChunk } from './types'
import type { GeneralChunk, ParentChildChunk, QAChunk } from './types'
import type { ParentMode } from '@/models/datasets'
import * as React from 'react'
import { useMemo } from 'react'
import { useTranslation } from 'react-i18next'
import Dot from '@/app/components/datasets/documents/detail/completed/common/dot'
import SegmentIndexTag from '@/app/components/datasets/documents/detail/completed/common/segment-index-tag'
import SummaryLabel from '@/app/components/datasets/documents/detail/completed/common/summary-label'
import { PreviewSlice } from '@/app/components/datasets/formatted-text/flavours/preview-slice'
import { ChunkingMode } from '@/models/datasets'
import { formatNumber } from '@/utils/format'
@ -14,7 +15,7 @@ import { QAItemType } from './types'
type ChunkCardProps = {
chunkType: ChunkingMode
parentMode?: ParentMode
content: string | string[] | QAChunk
content: ParentChildChunk | QAChunk | GeneralChunk
positionId?: string | number
wordCount: number
}
@ -33,7 +34,7 @@ const ChunkCard = (props: ChunkCardProps) => {
const contentElement = useMemo(() => {
if (chunkType === ChunkingMode.parentChild) {
return (content as string[]).map((child, index) => {
return (content as ParentChildChunk).child_contents.map((child, index) => {
const indexForLabel = index + 1
return (
<PreviewSlice
@ -57,7 +58,17 @@ const ChunkCard = (props: ChunkCardProps) => {
)
}
return content as string
return (content as GeneralChunk).content
}, [content, chunkType])
const summaryElement = useMemo(() => {
if (chunkType === ChunkingMode.parentChild) {
return (content as ParentChildChunk).parent_content.summary
}
if (chunkType === ChunkingMode.text) {
return (content as GeneralChunk).summary
}
return null
}, [content, chunkType])
return (
@ -73,6 +84,7 @@ const ChunkCard = (props: ChunkCardProps) => {
</div>
)}
<div className="body-md-regular text-text-secondary">{contentElement}</div>
{summaryElement && <SummaryLabel summary={summaryElement} />}
</div>
)
}

View File

@ -10,19 +10,19 @@ import { QAItemType } from './types'
// Test Data Factories
// =============================================================================
const createGeneralChunks = (overrides: string[] = []): GeneralChunks => {
const createGeneralChunks = (overrides: GeneralChunks = []): GeneralChunks => {
if (overrides.length > 0)
return overrides
return [
'This is the first chunk of text content.',
'This is the second chunk with different content.',
'Third chunk here with more text.',
{ content: 'This is the first chunk of text content.' },
{ content: 'This is the second chunk with different content.' },
{ content: 'Third chunk here with more text.' },
]
}
const createParentChildChunk = (overrides: Partial<ParentChildChunk> = {}): ParentChildChunk => ({
child_contents: ['Child content 1', 'Child content 2'],
parent_content: 'This is the parent content that contains the children.',
parent_content: { content: 'This is the parent content that contains the children.' },
parent_mode: 'paragraph',
...overrides,
})
@ -32,7 +32,7 @@ const createParentChildChunks = (overrides: Partial<ParentChildChunks> = {}): Pa
createParentChildChunk(),
createParentChildChunk({
child_contents: ['Another child 1', 'Another child 2', 'Another child 3'],
parent_content: 'Another parent content here.',
parent_content: { content: 'Another parent content here.' },
}),
],
parent_mode: 'paragraph',
@ -152,7 +152,7 @@ describe('ChunkCard', () => {
render(
<ChunkCard
chunkType={ChunkingMode.text}
content="This is plain text content."
content={createGeneralChunks()[0]}
wordCount={27}
positionId={1}
/>,
@ -196,7 +196,7 @@ describe('ChunkCard', () => {
<ChunkCard
chunkType={ChunkingMode.parentChild}
parentMode="paragraph"
content={childContents}
content={createParentChildChunk({ child_contents: childContents })}
wordCount={50}
positionId={1}
/>,
@ -218,7 +218,7 @@ describe('ChunkCard', () => {
<ChunkCard
chunkType={ChunkingMode.parentChild}
parentMode="paragraph"
content={['Child content']}
content={createParentChildChunk({ child_contents: ['Child content'] })}
wordCount={13}
positionId={1}
/>,
@ -234,7 +234,7 @@ describe('ChunkCard', () => {
<ChunkCard
chunkType={ChunkingMode.parentChild}
parentMode="full-doc"
content={['Child content']}
content={createParentChildChunk({ child_contents: ['Child content'] })}
wordCount={13}
positionId={1}
/>,
@ -250,7 +250,7 @@ describe('ChunkCard', () => {
render(
<ChunkCard
chunkType={ChunkingMode.text}
content="Text content"
content={createGeneralChunks()[0]}
wordCount={12}
positionId={5}
/>,
@ -268,7 +268,7 @@ describe('ChunkCard', () => {
render(
<ChunkCard
chunkType={ChunkingMode.text}
content="Some content"
content={createGeneralChunks()[0]}
wordCount={1234}
positionId={1}
/>,
@ -283,7 +283,7 @@ describe('ChunkCard', () => {
render(
<ChunkCard
chunkType={ChunkingMode.text}
content="Some content"
content={createGeneralChunks()[0]}
wordCount={100}
positionId={1}
/>,
@ -299,7 +299,7 @@ describe('ChunkCard', () => {
<ChunkCard
chunkType={ChunkingMode.parentChild}
parentMode="full-doc"
content={['Child']}
content={createParentChildChunk({ child_contents: ['Child'] })}
wordCount={500}
positionId={1}
/>,
@ -317,7 +317,7 @@ describe('ChunkCard', () => {
render(
<ChunkCard
chunkType={ChunkingMode.text}
content="Content"
content={createGeneralChunks()[0]}
wordCount={7}
positionId={42}
/>,
@ -332,7 +332,7 @@ describe('ChunkCard', () => {
render(
<ChunkCard
chunkType={ChunkingMode.text}
content="Content"
content={createGeneralChunks()[0]}
wordCount={7}
positionId="99"
/>,
@ -347,7 +347,7 @@ describe('ChunkCard', () => {
render(
<ChunkCard
chunkType={ChunkingMode.text}
content="Content"
content={createGeneralChunks()[0]}
wordCount={7}
positionId={3}
/>,
@ -366,7 +366,7 @@ describe('ChunkCard', () => {
<ChunkCard
chunkType={ChunkingMode.parentChild}
parentMode="paragraph"
content={['Child']}
content={createParentChildChunk({ child_contents: ['Child'] })}
wordCount={5}
positionId={1}
/>,
@ -380,7 +380,7 @@ describe('ChunkCard', () => {
<ChunkCard
chunkType={ChunkingMode.parentChild}
parentMode="full-doc"
content={['Child']}
content={createParentChildChunk({ child_contents: ['Child'] })}
wordCount={5}
positionId={1}
/>,
@ -395,7 +395,7 @@ describe('ChunkCard', () => {
const { rerender } = render(
<ChunkCard
chunkType={ChunkingMode.text}
content="Initial content"
content={createGeneralChunks()[0]}
wordCount={15}
positionId={1}
/>,
@ -408,7 +408,7 @@ describe('ChunkCard', () => {
rerender(
<ChunkCard
chunkType={ChunkingMode.text}
content="Updated content"
content={createGeneralChunks()[0]}
wordCount={15}
positionId={1}
/>,
@ -424,7 +424,7 @@ describe('ChunkCard', () => {
const { rerender } = render(
<ChunkCard
chunkType={ChunkingMode.text}
content="Text content"
content={createGeneralChunks()[0]}
wordCount={12}
positionId={1}
/>,
@ -458,7 +458,7 @@ describe('ChunkCard', () => {
<ChunkCard
chunkType={ChunkingMode.parentChild}
parentMode="paragraph"
content={[]}
content={createParentChildChunk({ child_contents: [] })}
wordCount={0}
positionId={1}
/>,
@ -495,7 +495,7 @@ describe('ChunkCard', () => {
render(
<ChunkCard
chunkType={ChunkingMode.text}
content={longContent}
content={createGeneralChunks()[0]}
wordCount={10000}
positionId={1}
/>,
@ -510,7 +510,7 @@ describe('ChunkCard', () => {
render(
<ChunkCard
chunkType={ChunkingMode.text}
content=""
content={createGeneralChunks()[0]}
wordCount={0}
positionId={1}
/>,
@ -546,9 +546,9 @@ describe('ChunkCardList', () => {
)
// Assert
expect(screen.getByText(chunks[0])).toBeInTheDocument()
expect(screen.getByText(chunks[1])).toBeInTheDocument()
expect(screen.getByText(chunks[2])).toBeInTheDocument()
expect(screen.getByText(chunks[0].content)).toBeInTheDocument()
expect(screen.getByText(chunks[1].content)).toBeInTheDocument()
expect(screen.getByText(chunks[2].content)).toBeInTheDocument()
})
it('should render parent-child chunks correctly', () => {
@ -594,7 +594,10 @@ describe('ChunkCardList', () => {
describe('Memoization - chunkList', () => {
it('should extract chunks from GeneralChunks for text mode', () => {
// Arrange
const chunks: GeneralChunks = ['Chunk 1', 'Chunk 2']
const chunks: GeneralChunks = [
{ content: 'Chunk 1' },
{ content: 'Chunk 2' },
]
// Act
render(
@ -653,7 +656,7 @@ describe('ChunkCardList', () => {
it('should update chunkList when chunkInfo changes', () => {
// Arrange
const initialChunks = createGeneralChunks(['Initial chunk'])
const initialChunks = createGeneralChunks([{ content: 'Initial chunk' }])
const { rerender } = render(
<ChunkCardList
@ -666,7 +669,7 @@ describe('ChunkCardList', () => {
expect(screen.getByText('Initial chunk')).toBeInTheDocument()
// Act - update chunks
const updatedChunks = createGeneralChunks(['Updated chunk'])
const updatedChunks = createGeneralChunks([{ content: 'Updated chunk' }])
rerender(
<ChunkCardList
chunkType={ChunkingMode.text}
@ -684,7 +687,7 @@ describe('ChunkCardList', () => {
describe('Word Count Calculation', () => {
it('should calculate word count for text chunks using string length', () => {
// Arrange - "Hello" has 5 characters
const chunks = createGeneralChunks(['Hello'])
const chunks = createGeneralChunks([{ content: 'Hello' }])
// Act
render(
@ -703,7 +706,7 @@ describe('ChunkCardList', () => {
const chunks = createParentChildChunks({
parent_child_chunks: [
createParentChildChunk({
parent_content: 'Parent', // 6 characters
parent_content: { content: 'Parent' }, // 6 characters
child_contents: ['Child'],
}),
],
@ -747,7 +750,11 @@ describe('ChunkCardList', () => {
describe('Position ID', () => {
it('should assign 1-based position IDs to chunks', () => {
// Arrange
const chunks = createGeneralChunks(['First', 'Second', 'Third'])
const chunks = createGeneralChunks([
{ content: 'First' },
{ content: 'Second' },
{ content: 'Third' },
])
// Act
render(
@ -768,7 +775,7 @@ describe('ChunkCardList', () => {
describe('Custom className', () => {
it('should apply custom className to container', () => {
// Arrange
const chunks = createGeneralChunks(['Test'])
const chunks = createGeneralChunks([{ content: 'Test' }])
// Act
const { container } = render(
@ -785,7 +792,7 @@ describe('ChunkCardList', () => {
it('should merge custom className with default classes', () => {
// Arrange
const chunks = createGeneralChunks(['Test'])
const chunks = createGeneralChunks([{ content: 'Test' }])
// Act
const { container } = render(
@ -805,7 +812,7 @@ describe('ChunkCardList', () => {
it('should render without className prop', () => {
// Arrange
const chunks = createGeneralChunks(['Test'])
const chunks = createGeneralChunks([{ content: 'Test' }])
// Act
const { container } = render(
@ -860,7 +867,7 @@ describe('ChunkCardList', () => {
it('should not use parentMode for text type', () => {
// Arrange
const chunks = createGeneralChunks(['Text'])
const chunks = createGeneralChunks([{ content: 'Text' }])
// Act
render(
@ -937,7 +944,7 @@ describe('ChunkCardList', () => {
it('should handle single item in chunks', () => {
// Arrange
const chunks = createGeneralChunks(['Single chunk'])
const chunks = createGeneralChunks([{ content: 'Single chunk' }])
// Act
render(
@ -954,7 +961,7 @@ describe('ChunkCardList', () => {
it('should handle large number of chunks', () => {
// Arrange
const chunks = Array.from({ length: 100 }, (_, i) => `Chunk number ${i + 1}`)
const chunks = Array.from({ length: 100 }, (_, i) => ({ content: `Chunk number ${i + 1}` }))
// Act
render(
@ -975,8 +982,11 @@ describe('ChunkCardList', () => {
describe('Key Generation', () => {
it('should generate unique keys for chunks', () => {
// Arrange - chunks with same content
const chunks = createGeneralChunks(['Same content', 'Same content', 'Same content'])
const chunks = createGeneralChunks([
{ content: 'Same content' },
{ content: 'Same content' },
{ content: 'Same content' },
])
// Act
const { container } = render(
<ChunkCardList
@ -1006,9 +1016,9 @@ describe('ChunkCardList Integration', () => {
it('should render complete text chunking workflow', () => {
// Arrange
const textChunks = createGeneralChunks([
'First paragraph of the document.',
'Second paragraph with more information.',
'Final paragraph concluding the content.',
{ content: 'First paragraph of the document.' },
{ content: 'Second paragraph with more information.' },
{ content: 'Final paragraph concluding the content.' },
])
// Act
@ -1037,7 +1047,7 @@ describe('ChunkCardList Integration', () => {
const parentChildChunks = createParentChildChunks({
parent_child_chunks: [
{
parent_content: 'Main section about React components and their lifecycle.',
parent_content: { content: 'Main section about React components and their lifecycle.' },
child_contents: [
'React components are building blocks.',
'Lifecycle methods control component behavior.',
@ -1104,7 +1114,7 @@ describe('ChunkCardList Integration', () => {
describe('Type Switching', () => {
it('should handle switching from text to QA type', () => {
// Arrange
const textChunks = createGeneralChunks(['Text content'])
const textChunks = createGeneralChunks([{ content: 'Text content' }])
const qaChunks = createQAChunks()
const { rerender } = render(
@ -1132,7 +1142,7 @@ describe('ChunkCardList Integration', () => {
it('should handle switching from text to parent-child type', () => {
// Arrange
const textChunks = createGeneralChunks(['Simple text'])
const textChunks = createGeneralChunks([{ content: 'Simple text' }])
const parentChildChunks = createParentChildChunks()
const { rerender } = render(

View File

@ -1,4 +1,4 @@
import type { ChunkInfo, GeneralChunks, ParentChildChunk, ParentChildChunks, QAChunk, QAChunks } from './types'
import type { ChunkInfo, GeneralChunk, GeneralChunks, ParentChildChunk, ParentChildChunks, QAChunk, QAChunks } from './types'
import type { ParentMode } from '@/models/datasets'
import { useMemo } from 'react'
import { ChunkingMode } from '@/models/datasets'
@ -21,13 +21,13 @@ export const ChunkCardList = (props: ChunkCardListProps) => {
if (chunkType === ChunkingMode.parentChild)
return (chunkInfo as ParentChildChunks).parent_child_chunks
return (chunkInfo as QAChunks).qa_chunks
}, [chunkInfo])
}, [chunkInfo, chunkType])
const getWordCount = (seg: string | ParentChildChunk | QAChunk) => {
const getWordCount = (seg: GeneralChunk | ParentChildChunk | QAChunk) => {
if (chunkType === ChunkingMode.parentChild)
return (seg as ParentChildChunk).parent_content.length
return (seg as ParentChildChunk).parent_content.content?.length
if (chunkType === ChunkingMode.text)
return (seg as string).length
return (seg as GeneralChunk).content.length
return (seg as QAChunk).question.length + (seg as QAChunk).answer.length
}
@ -41,7 +41,7 @@ export const ChunkCardList = (props: ChunkCardListProps) => {
key={`${chunkType}-${index}`}
chunkType={chunkType}
parentMode={parentMode}
content={chunkType === ChunkingMode.parentChild ? (seg as ParentChildChunk).child_contents : (seg as string | QAChunk)}
content={seg}
wordCount={wordCount}
positionId={index + 1}
/>

View File

@ -1,8 +1,11 @@
export type GeneralChunks = string[]
export type GeneralChunk = {
content: string
summary?: string
}
export type GeneralChunks = GeneralChunk[]
export type ParentChildChunk = {
child_contents: string[]
parent_content: string
parent_content: GeneralChunk
parent_mode: string
}

View File

@ -1,8 +1,8 @@
import type { GeneralChunks } from '@/app/components/rag-pipeline/components/chunk-card-list/types'
import type { WorkflowRunningData } from '@/app/components/workflow/types'
import { fireEvent, render, screen, waitFor } from '@testing-library/react'
import { WorkflowRunningStatus } from '@/app/components/workflow/types'
import { ChunkingMode } from '@/models/datasets'
import Header from './header'
// Import components after mocks
import TestRunPanel from './index'
@ -836,7 +836,7 @@ describe('formatPreviewChunks', () => {
it('should limit to RAG_PIPELINE_PREVIEW_CHUNK_NUM chunks', () => {
const manyChunks = Array.from({ length: 10 }, (_, i) => `chunk${i}`)
const outputs = createMockGeneralOutputs(manyChunks)
const result = formatPreviewChunks(outputs) as string[]
const result = formatPreviewChunks(outputs) as GeneralChunks
// RAG_PIPELINE_PREVIEW_CHUNK_NUM is mocked to 5
expect(result).toHaveLength(5)

View File

@ -1,24 +1,28 @@
import type { ChunkInfo, GeneralChunks, ParentChildChunks, QAChunks } from '../../../../chunk-card-list/types'
import type { ChunkInfo, GeneralChunk, GeneralChunks, ParentChildChunks, QAChunks } from '../../../../chunk-card-list/types'
import type { ParentMode } from '@/models/datasets'
import { RAG_PIPELINE_PREVIEW_CHUNK_NUM } from '@/config'
import { ChunkingMode } from '@/models/datasets'
type GeneralChunkPreview = {
content: string
summary?: string
}
const formatGeneralChunks = (outputs: any) => {
const chunkInfo: GeneralChunks = []
const chunks = outputs.preview as GeneralChunkPreview[]
chunks.slice(0, RAG_PIPELINE_PREVIEW_CHUNK_NUM).forEach((chunk) => {
chunkInfo.push(chunk.content)
chunkInfo.push({
content: chunk.content,
summary: chunk.summary,
})
})
return chunkInfo
}
type ParentChildChunkPreview = {
content: string
content: GeneralChunk
child_chunks: string[]
}