improve streaming error handling
This commit is contained in:
@@ -419,9 +419,9 @@
|
|||||||
|
|
||||||
"@types/prismjs": ["@types/prismjs@1.26.5", "", {}, "sha512-AUZTa7hQ2KY5L7AmtSiqxlhWxb4ina0yd8hNbl4TWuqnv/pFP0nDMb3YrfSBf4hJVGLh2YEIBfKaBW/9UEl6IQ=="],
|
"@types/prismjs": ["@types/prismjs@1.26.5", "", {}, "sha512-AUZTa7hQ2KY5L7AmtSiqxlhWxb4ina0yd8hNbl4TWuqnv/pFP0nDMb3YrfSBf4hJVGLh2YEIBfKaBW/9UEl6IQ=="],
|
||||||
|
|
||||||
"@types/react": ["@types/react@19.0.8", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-9P/o1IGdfmQxrujGbIMDyYaaCykhLKc0NGCtYcECNUr9UAaDe4gwvV9bR6tvd5Br1SG0j+PBpbKr2UYY8CwqSw=="],
|
"@types/react": ["@types/react@19.0.10", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-JuRQ9KXLEjaUNjTWpzuR231Z2WpIwczOkBEIvbHNCzQefFIT0L8IqE6NV6ULLyC1SI/i234JnDoMkfg+RjQj2g=="],
|
||||||
|
|
||||||
"@types/react-dom": ["@types/react-dom@19.0.3", "", { "peerDependencies": { "@types/react": "^19.0.0" } }, "sha512-0Knk+HJiMP/qOZgMyNFamlIjw9OFCsyC2ZbigmEEyXXixgre6IQpm/4V+r3qH4GC1JPvRJKInw+on2rV6YZLeA=="],
|
"@types/react-dom": ["@types/react-dom@19.0.4", "", { "peerDependencies": { "@types/react": "^19.0.0" } }, "sha512-4fSQ8vWFkg+TGhePfUzVmat3eC14TXYSsiiDSLI0dVLsrm9gZFABjPy/Qu6TKgl1tq1Bu1yDsuQgY3A3DOjCcg=="],
|
||||||
|
|
||||||
"@types/react-transition-group": ["@types/react-transition-group@4.4.12", "", { "peerDependencies": { "@types/react": "*" } }, "sha512-8TV6R3h2j7a91c+1DXdJi3Syo69zzIZbz7Lg5tORM5LEJG7X/E6a1V3drRyBRZq7/utz7A+c4OgYLiLcYGHG6w=="],
|
"@types/react-transition-group": ["@types/react-transition-group@4.4.12", "", { "peerDependencies": { "@types/react": "*" } }, "sha512-8TV6R3h2j7a91c+1DXdJi3Syo69zzIZbz7Lg5tORM5LEJG7X/E6a1V3drRyBRZq7/utz7A+c4OgYLiLcYGHG6w=="],
|
||||||
|
|
||||||
|
@@ -59,8 +59,8 @@
|
|||||||
"@tailwindcss/vite": "^4.0.6",
|
"@tailwindcss/vite": "^4.0.6",
|
||||||
"@types/bun": "^1.2.2",
|
"@types/bun": "^1.2.2",
|
||||||
"@types/node": "^22.13.4",
|
"@types/node": "^22.13.4",
|
||||||
"@types/react": "^19.0.8",
|
"@types/react": "^19.0.10",
|
||||||
"@types/react-dom": "^19.0.3",
|
"@types/react-dom": "^19.0.4",
|
||||||
"@types/seedrandom": "^3.0.8",
|
"@types/seedrandom": "^3.0.8",
|
||||||
"@vitejs/plugin-react-swc": "^3.8.0",
|
"@vitejs/plugin-react-swc": "^3.8.0",
|
||||||
"eslint": "^9.20.1",
|
"eslint": "^9.20.1",
|
||||||
|
@@ -212,7 +212,8 @@ export const queryTextStream = async (
|
|||||||
) => {
|
) => {
|
||||||
try {
|
try {
|
||||||
let buffer = ''
|
let buffer = ''
|
||||||
await axiosInstance.post('/query/stream', request, {
|
await axiosInstance
|
||||||
|
.post('/query/stream', request, {
|
||||||
responseType: 'text',
|
responseType: 'text',
|
||||||
headers: {
|
headers: {
|
||||||
Accept: 'application/x-ndjson'
|
Accept: 'application/x-ndjson'
|
||||||
@@ -244,6 +245,9 @@ export const queryTextStream = async (
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
if (onError) onError(errorMessage(error))
|
||||||
|
})
|
||||||
|
|
||||||
// Process any remaining data in the buffer
|
// Process any remaining data in the buffer
|
||||||
if (buffer.trim()) {
|
if (buffer.trim()) {
|
||||||
@@ -266,11 +270,13 @@ export const queryTextStream = async (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const insertText = async (
|
export const insertText = async (text: string): Promise<DocActionResponse> => {
|
||||||
text: string,
|
const response = await axiosInstance.post('/documents/text', { text })
|
||||||
description?: string
|
return response.data
|
||||||
): Promise<DocActionResponse> => {
|
}
|
||||||
const response = await axiosInstance.post('/documents/text', { text, description })
|
|
||||||
|
export const insertTexts = async (texts: string[]): Promise<DocActionResponse> => {
|
||||||
|
const response = await axiosInstance.post('/documents/texts', { texts })
|
||||||
return response.data
|
return response.data
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -29,7 +29,16 @@ export default function DocumentManager() {
|
|||||||
try {
|
try {
|
||||||
const docs = await getDocuments()
|
const docs = await getDocuments()
|
||||||
if (docs && docs.statuses) {
|
if (docs && docs.statuses) {
|
||||||
|
// compose all documents count
|
||||||
|
const numDocuments = Object.values(docs.statuses).reduce(
|
||||||
|
(acc, status) => acc + status.length,
|
||||||
|
0
|
||||||
|
)
|
||||||
|
if (numDocuments > 0) {
|
||||||
setDocs(docs)
|
setDocs(docs)
|
||||||
|
} else {
|
||||||
|
setDocs(null)
|
||||||
|
}
|
||||||
// console.log(docs)
|
// console.log(docs)
|
||||||
} else {
|
} else {
|
||||||
setDocs(null)
|
setDocs(null)
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
import Input from '@/components/ui/Input'
|
import Input from '@/components/ui/Input'
|
||||||
import Button from '@/components/ui/Button'
|
import Button from '@/components/ui/Button'
|
||||||
import { useCallback, useEffect, useRef, useState } from 'react'
|
import { useCallback, useEffect, useRef, useState } from 'react'
|
||||||
import { queryText, queryTextStream, Message } from '@/api/lightrag'
|
import { queryText, queryTextStream, Message as ChatMessage } from '@/api/lightrag'
|
||||||
import { errorMessage } from '@/lib/utils'
|
import { errorMessage } from '@/lib/utils'
|
||||||
import { useSettingsStore } from '@/stores/settings'
|
import { useSettingsStore } from '@/stores/settings'
|
||||||
import { useDebounce } from '@/hooks/useDebounce'
|
import { useDebounce } from '@/hooks/useDebounce'
|
||||||
@@ -9,6 +9,10 @@ import QuerySettings from '@/components/retrieval/QuerySettings'
|
|||||||
|
|
||||||
import { EraserIcon, SendIcon, LoaderIcon } from 'lucide-react'
|
import { EraserIcon, SendIcon, LoaderIcon } from 'lucide-react'
|
||||||
|
|
||||||
|
type Message = ChatMessage & {
|
||||||
|
isError?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
export default function RetrievalTesting() {
|
export default function RetrievalTesting() {
|
||||||
const [messages, setMessages] = useState<Message[]>(
|
const [messages, setMessages] = useState<Message[]>(
|
||||||
() => useSettingsStore.getState().retrievalHistory || []
|
() => useSettingsStore.getState().retrievalHistory || []
|
||||||
@@ -47,13 +51,14 @@ export default function RetrievalTesting() {
|
|||||||
setIsLoading(true)
|
setIsLoading(true)
|
||||||
|
|
||||||
// Create a function to update the assistant's message
|
// Create a function to update the assistant's message
|
||||||
const updateAssistantMessage = (chunk: string) => {
|
const updateAssistantMessage = (chunk: string, isError?: boolean) => {
|
||||||
assistantMessage.content += chunk
|
assistantMessage.content += chunk
|
||||||
setMessages((prev) => {
|
setMessages((prev) => {
|
||||||
const newMessages = [...prev]
|
const newMessages = [...prev]
|
||||||
const lastMessage = newMessages[newMessages.length - 1]
|
const lastMessage = newMessages[newMessages.length - 1]
|
||||||
if (lastMessage.role === 'assistant') {
|
if (lastMessage.role === 'assistant') {
|
||||||
lastMessage.content = assistantMessage.content
|
lastMessage.content = assistantMessage.content
|
||||||
|
lastMessage.isError = isError
|
||||||
}
|
}
|
||||||
return newMessages
|
return newMessages
|
||||||
})
|
})
|
||||||
@@ -65,19 +70,30 @@ export default function RetrievalTesting() {
|
|||||||
...state.querySettings,
|
...state.querySettings,
|
||||||
query: userMessage.content,
|
query: userMessage.content,
|
||||||
conversation_history: prevMessages
|
conversation_history: prevMessages
|
||||||
|
.filter((m) => m.isError !== true)
|
||||||
|
.map((m) => ({ role: m.role, content: m.content }))
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Run query
|
// Run query
|
||||||
if (state.querySettings.stream) {
|
if (state.querySettings.stream) {
|
||||||
await queryTextStream(queryParams, updateAssistantMessage)
|
let errorMessage = ''
|
||||||
|
await queryTextStream(queryParams, updateAssistantMessage, (error) => {
|
||||||
|
errorMessage += error
|
||||||
|
})
|
||||||
|
if (errorMessage) {
|
||||||
|
if (assistantMessage.content) {
|
||||||
|
errorMessage = assistantMessage.content + '\n' + errorMessage
|
||||||
|
}
|
||||||
|
updateAssistantMessage(errorMessage, true)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
const response = await queryText(queryParams)
|
const response = await queryText(queryParams)
|
||||||
updateAssistantMessage(response.response)
|
updateAssistantMessage(response.response)
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
// Handle error
|
// Handle error
|
||||||
updateAssistantMessage(`Error: Failed to get response\n${errorMessage(err)}`)
|
updateAssistantMessage(`Error: Failed to get response\n${errorMessage(err)}`, true)
|
||||||
} finally {
|
} finally {
|
||||||
// Clear loading and add messages to state
|
// Clear loading and add messages to state
|
||||||
setIsLoading(false)
|
setIsLoading(false)
|
||||||
@@ -115,7 +131,11 @@ export default function RetrievalTesting() {
|
|||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className={`max-w-[80%] rounded-lg px-4 py-2 ${
|
className={`max-w-[80%] rounded-lg px-4 py-2 ${
|
||||||
message.role === 'user' ? 'bg-primary text-primary-foreground' : 'bg-muted'
|
message.role === 'user'
|
||||||
|
? 'bg-primary text-primary-foreground'
|
||||||
|
: message.isError
|
||||||
|
? 'bg-red-100 text-red-600 dark:bg-red-950 dark:text-red-400'
|
||||||
|
: 'bg-muted'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<pre className="break-words whitespace-pre-wrap">{message.content}</pre>
|
<pre className="break-words whitespace-pre-wrap">{message.content}</pre>
|
||||||
|
Reference in New Issue
Block a user