diff --git a/web/app/components/rag-pipeline/components/chunk-card-list/chunk-card.tsx b/web/app/components/rag-pipeline/components/chunk-card-list/chunk-card.tsx index 9891fd9a47..106084846c 100644 --- a/web/app/components/rag-pipeline/components/chunk-card-list/chunk-card.tsx +++ b/web/app/components/rag-pipeline/components/chunk-card-list/chunk-card.tsx @@ -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 ( { ) } - 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) => { )}
{contentElement}
+ {summaryElement && } ) } diff --git a/web/app/components/rag-pipeline/components/chunk-card-list/index.spec.tsx b/web/app/components/rag-pipeline/components/chunk-card-list/index.spec.tsx index e665cf134e..8a15193d2e 100644 --- a/web/app/components/rag-pipeline/components/chunk-card-list/index.spec.tsx +++ b/web/app/components/rag-pipeline/components/chunk-card-list/index.spec.tsx @@ -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 => ({ 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 = {}): 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( , @@ -196,7 +196,7 @@ describe('ChunkCard', () => { , @@ -218,7 +218,7 @@ describe('ChunkCard', () => { , @@ -234,7 +234,7 @@ describe('ChunkCard', () => { , @@ -250,7 +250,7 @@ describe('ChunkCard', () => { render( , @@ -268,7 +268,7 @@ describe('ChunkCard', () => { render( , @@ -283,7 +283,7 @@ describe('ChunkCard', () => { render( , @@ -299,7 +299,7 @@ describe('ChunkCard', () => { , @@ -317,7 +317,7 @@ describe('ChunkCard', () => { render( , @@ -332,7 +332,7 @@ describe('ChunkCard', () => { render( , @@ -347,7 +347,7 @@ describe('ChunkCard', () => { render( , @@ -366,7 +366,7 @@ describe('ChunkCard', () => { , @@ -380,7 +380,7 @@ describe('ChunkCard', () => { , @@ -395,7 +395,7 @@ describe('ChunkCard', () => { const { rerender } = render( , @@ -408,7 +408,7 @@ describe('ChunkCard', () => { rerender( , @@ -424,7 +424,7 @@ describe('ChunkCard', () => { const { rerender } = render( , @@ -458,7 +458,7 @@ describe('ChunkCard', () => { , @@ -495,7 +495,7 @@ describe('ChunkCard', () => { render( , @@ -510,7 +510,7 @@ describe('ChunkCard', () => { render( , @@ -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( { expect(screen.getByText('Initial chunk')).toBeInTheDocument() // Act - update chunks - const updatedChunks = createGeneralChunks(['Updated chunk']) + const updatedChunks = createGeneralChunks([{ content: 'Updated chunk' }]) rerender( { 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( { 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( diff --git a/web/app/components/rag-pipeline/components/chunk-card-list/index.tsx b/web/app/components/rag-pipeline/components/chunk-card-list/index.tsx index 3b1f2533b4..6bd69bc46e 100644 --- a/web/app/components/rag-pipeline/components/chunk-card-list/index.tsx +++ b/web/app/components/rag-pipeline/components/chunk-card-list/index.tsx @@ -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} /> diff --git a/web/app/components/rag-pipeline/components/chunk-card-list/types.ts b/web/app/components/rag-pipeline/components/chunk-card-list/types.ts index 0a5e594b47..331f17d17c 100644 --- a/web/app/components/rag-pipeline/components/chunk-card-list/types.ts +++ b/web/app/components/rag-pipeline/components/chunk-card-list/types.ts @@ -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 } diff --git a/web/app/components/rag-pipeline/components/panel/test-run/index.spec.tsx b/web/app/components/rag-pipeline/components/panel/test-run/index.spec.tsx index 93423f9e10..972468588a 100644 --- a/web/app/components/rag-pipeline/components/panel/test-run/index.spec.tsx +++ b/web/app/components/rag-pipeline/components/panel/test-run/index.spec.tsx @@ -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) diff --git a/web/app/components/rag-pipeline/components/panel/test-run/result/result-preview/utils.ts b/web/app/components/rag-pipeline/components/panel/test-run/result/result-preview/utils.ts index 9ac0de4d48..0e62941053 100644 --- a/web/app/components/rag-pipeline/components/panel/test-run/result/result-preview/utils.ts +++ b/web/app/components/rag-pipeline/components/panel/test-run/result/result-preview/utils.ts @@ -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[] }