feat(frontend): add per-sub-question types and stream state management

Add SubQuestionSources interface for grouped per-sub-question sources. Convert QueryStreamEvent from flat interface to discriminated union with 7 variants including generating_subquestion and completed with sub_question_sources. Add subQuestionSources to QueryStreamState. Update completed event handler to populate subQuestionSources. Make sub_question_sources optional for backward compatibility with old SSE format.

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
Woody 2026-04-26 23:29:38 +08:00
parent 3f50f81bfe
commit 098368bb42
2 changed files with 24 additions and 8 deletions

View File

@ -1,7 +1,7 @@
import React from 'react'
import { QueryClient, QueryClientProvider, useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
import { queryDocument, queryDocumentStream, ingestDocument, listDocuments, listChunks, deleteDocument, deleteChunk, listPromptProfiles, getPromptProfile, activatePromptProfile, updatePrompt, updateAllPrompts, resetPrompts, listQueryHistory, getQueryHistoryDetail, deleteQueryHistory, clearQueryHistory, getHistoryStats } from './api'
import type { QueryRequest, QueryResponse, QueryStreamEvent, SourceMetadata, IngestResponse, DocumentListResponse, ChunkInfo, DeleteResponse, PromptProfileListResponse, PromptSetResponse, PromptUpdateRequest, PromptBatchUpdateRequest, PromptActivateResponse, PromptStatusResponse, QueryHistoryList, QueryHistoryDetail, HistoryStats, HistoryDeleteResponse } from '../types'
import type { QueryRequest, QueryResponse, QueryStreamEvent, SourceMetadata, SubQuestionSources, IngestResponse, DocumentListResponse, ChunkInfo, DeleteResponse, PromptProfileListResponse, PromptSetResponse, PromptUpdateRequest, PromptBatchUpdateRequest, PromptActivateResponse, PromptStatusResponse, QueryHistoryList, QueryHistoryDetail, HistoryStats, HistoryDeleteResponse } from '../types'
import { useState, useCallback, useRef } from 'react'
export const queryClient = new QueryClient()
@ -16,6 +16,7 @@ export interface QueryStreamState {
extractedQuestions: string[] | null
answer: string | null
sources: SourceMetadata[] | null
subQuestionSources: SubQuestionSources[] | null
phase: 'idle' | 'decomposing' | 'retrieving' | 'filtering' | 'generating' | 'completed' | 'error'
error: Error | null
}
@ -25,6 +26,7 @@ export const useQueryDocumentStream = () => {
extractedQuestions: null,
answer: null,
sources: null,
subQuestionSources: null,
phase: 'idle',
error: null,
})
@ -35,6 +37,7 @@ export const useQueryDocumentStream = () => {
extractedQuestions: null,
answer: null,
sources: null,
subQuestionSources: null,
phase: 'decomposing',
error: null,
})
@ -60,11 +63,15 @@ export const useQueryDocumentStream = () => {
case 'generating':
setState(prev => ({ ...prev, phase: 'generating' }))
break
case 'generating_subquestion':
setState(prev => ({ ...prev, phase: 'generating' }))
break
case 'completed':
setState(prev => ({
...prev,
answer: event.answer ?? null,
sources: event.sources ?? null,
subQuestionSources: event.sub_question_sources ?? null,
phase: 'completed',
}))
break
@ -96,6 +103,7 @@ export const useQueryDocumentStream = () => {
extractedQuestions: null,
answer: null,
sources: null,
subQuestionSources: null,
phase: 'idle',
error: null,
})

View File

@ -7,6 +7,12 @@ export interface SourceMetadata {
chunk_file_path: string | null
}
export interface SubQuestionSources {
sub_question_index: number
sub_question_text: string
sources: SourceMetadata[]
}
export interface QueryRequest {
question: string
}
@ -14,16 +20,18 @@ export interface QueryRequest {
export interface QueryResponse {
extracted_questions: string[]
answer: string
sub_question_sources?: SubQuestionSources[]
sources: SourceMetadata[]
}
export interface QueryStreamEvent {
phase: 'decomposed' | 'retrieving' | 'filtering' | 'generating' | 'completed' | 'error'
extracted_questions?: string[]
answer?: string
sources?: SourceMetadata[]
message?: string
}
export type QueryStreamEvent =
| { phase: 'decomposed'; extracted_questions: string[] }
| { phase: 'retrieving' }
| { phase: 'filtering' }
| { phase: 'generating' }
| { phase: 'generating_subquestion'; sub_question_index: number; sub_question_text: string }
| { phase: 'completed'; answer: string; sub_question_sources?: SubQuestionSources[]; sources?: SourceMetadata[] }
| { phase: 'error'; message: string }
export interface IngestResponse {
document_id: string