mirror of
https://github.com/arc53/DocsGPT.git
synced 2026-02-21 03:41:02 +00:00
Thinking stream (#2276)
* feat: stream thinking tokens * fix: retry bug * fix test
This commit is contained in:
@@ -195,12 +195,21 @@ export const agentPreviewSlice = createSlice({
|
||||
},
|
||||
resendQuery(
|
||||
state,
|
||||
action: PayloadAction<{ index: number; prompt: string; query?: Query }>,
|
||||
action: PayloadAction<{ index: number; prompt: string }>,
|
||||
) {
|
||||
state.queries = [
|
||||
...state.queries.splice(0, action.payload.index),
|
||||
action.payload,
|
||||
];
|
||||
const { index, prompt } = action.payload;
|
||||
if (index < 0 || index >= state.queries.length) return;
|
||||
|
||||
state.queries.splice(index + 1);
|
||||
state.queries[index].prompt = prompt;
|
||||
delete state.queries[index].response;
|
||||
delete state.queries[index].thought;
|
||||
delete state.queries[index].sources;
|
||||
delete state.queries[index].tool_calls;
|
||||
delete state.queries[index].error;
|
||||
delete state.queries[index].structured;
|
||||
delete state.queries[index].schema;
|
||||
delete state.queries[index].feedback;
|
||||
},
|
||||
updateStreamingQuery(
|
||||
state,
|
||||
@@ -309,10 +318,13 @@ export const agentPreviewSlice = createSlice({
|
||||
.addCase(fetchPreviewAnswer.rejected, (state, action) => {
|
||||
if (action.meta.aborted) {
|
||||
state.status = 'idle';
|
||||
return state;
|
||||
return;
|
||||
}
|
||||
state.status = 'failed';
|
||||
state.queries[state.queries.length - 1].error = 'Something went wrong';
|
||||
if (state.queries.length > 0) {
|
||||
state.queries[state.queries.length - 1].error =
|
||||
'Something went wrong';
|
||||
}
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
@@ -120,18 +120,20 @@ export default function Conversation() {
|
||||
if (updated === true) {
|
||||
handleQuestion({ question: question as string, index: indx });
|
||||
} else if (question && status !== 'loading') {
|
||||
if (lastQueryReturnedErr) {
|
||||
if (lastQueryReturnedErr && queries.length > 0) {
|
||||
const retryIndex = queries.length - 1;
|
||||
dispatch(
|
||||
updateQuery({
|
||||
index: queries.length - 1,
|
||||
index: retryIndex,
|
||||
query: {
|
||||
prompt: question,
|
||||
},
|
||||
}),
|
||||
);
|
||||
handleQuestion({
|
||||
question: question,
|
||||
question,
|
||||
isRetry: true,
|
||||
index: retryIndex,
|
||||
});
|
||||
} else {
|
||||
handleQuestion({
|
||||
@@ -152,11 +154,14 @@ export default function Conversation() {
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (queries.length) {
|
||||
queries[queries.length - 1].error && setLastQueryReturnedErr(true);
|
||||
queries[queries.length - 1].response && setLastQueryReturnedErr(false);
|
||||
if (queries.length === 0) {
|
||||
setLastQueryReturnedErr(false);
|
||||
return;
|
||||
}
|
||||
}, [queries[queries.length - 1]]);
|
||||
|
||||
const lastQuery = queries[queries.length - 1];
|
||||
setLastQueryReturnedErr(!!lastQuery.error && !lastQuery.response);
|
||||
}, [queries]);
|
||||
|
||||
return (
|
||||
<div className="flex h-full flex-col justify-end gap-1">
|
||||
|
||||
@@ -24,13 +24,12 @@ type ConversationMessagesProps = {
|
||||
handleQuestion: (params: {
|
||||
question: string;
|
||||
isRetry?: boolean;
|
||||
updated?: boolean | null;
|
||||
indx?: number;
|
||||
index?: number;
|
||||
}) => void;
|
||||
handleQuestionSubmission: (
|
||||
updatedQuestion?: string,
|
||||
updated?: boolean,
|
||||
indx?: number,
|
||||
index?: number,
|
||||
) => void;
|
||||
handleFeedback?: (query: Query, feedback: FEEDBACK, index: number) => void;
|
||||
queries: Query[];
|
||||
@@ -169,7 +168,7 @@ export default function ConversationMessages({
|
||||
handleQuestion({
|
||||
question: questionToRetry,
|
||||
isRetry: true,
|
||||
indx: index,
|
||||
index,
|
||||
});
|
||||
}}
|
||||
aria-label={t('Retry') || 'Retry'}
|
||||
|
||||
@@ -241,12 +241,21 @@ export const conversationSlice = createSlice({
|
||||
},
|
||||
resendQuery(
|
||||
state,
|
||||
action: PayloadAction<{ index: number; prompt: string; query?: Query }>,
|
||||
action: PayloadAction<{ index: number; prompt: string }>,
|
||||
) {
|
||||
state.queries = [
|
||||
...state.queries.splice(0, action.payload.index),
|
||||
action.payload,
|
||||
];
|
||||
const { index, prompt } = action.payload;
|
||||
if (index < 0 || index >= state.queries.length) return;
|
||||
|
||||
state.queries.splice(index + 1);
|
||||
state.queries[index].prompt = prompt;
|
||||
delete state.queries[index].response;
|
||||
delete state.queries[index].thought;
|
||||
delete state.queries[index].sources;
|
||||
delete state.queries[index].tool_calls;
|
||||
delete state.queries[index].error;
|
||||
delete state.queries[index].structured;
|
||||
delete state.queries[index].schema;
|
||||
delete state.queries[index].feedback;
|
||||
},
|
||||
updateStreamingQuery(
|
||||
state,
|
||||
@@ -370,7 +379,7 @@ export const conversationSlice = createSlice({
|
||||
.addCase(fetchAnswer.rejected, (state, action) => {
|
||||
if (action.meta.aborted) {
|
||||
state.status = 'idle';
|
||||
return state;
|
||||
return;
|
||||
}
|
||||
state.status = 'failed';
|
||||
if (state.queries.length > 0) {
|
||||
|
||||
@@ -266,10 +266,13 @@ export const sharedConversationSlice = createSlice({
|
||||
.addCase(fetchSharedAnswer.rejected, (state, action) => {
|
||||
if (action.meta.aborted) {
|
||||
state.status = 'idle';
|
||||
return state;
|
||||
return;
|
||||
}
|
||||
state.status = 'failed';
|
||||
state.queries[state.queries.length - 1].error = 'Something went wrong';
|
||||
if (state.queries.length > 0) {
|
||||
state.queries[state.queries.length - 1].error =
|
||||
'Something went wrong';
|
||||
}
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
@@ -90,12 +90,49 @@ export function getLocalApiKey(): string | null {
|
||||
return key;
|
||||
}
|
||||
|
||||
export function getLocalRecentDocs(sourceDocs?: Doc[] | null): Doc[] | null {
|
||||
const docsString = localStorage.getItem('DocsGPTRecentDocs');
|
||||
const selectedDocs = docsString ? (JSON.parse(docsString) as Doc[]) : null;
|
||||
function parseStoredRecentDocs(docsString: string | null): Doc[] | null {
|
||||
if (!docsString) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!sourceDocs || !selectedDocs || selectedDocs.length === 0) {
|
||||
return selectedDocs;
|
||||
try {
|
||||
const parsedDocs: unknown = JSON.parse(docsString);
|
||||
|
||||
if (Array.isArray(parsedDocs)) {
|
||||
const docs = parsedDocs.filter(
|
||||
(doc): doc is Doc => typeof doc === 'object' && doc !== null,
|
||||
);
|
||||
return docs.length > 0 ? docs : null;
|
||||
}
|
||||
|
||||
if (typeof parsedDocs === 'object' && parsedDocs !== null) {
|
||||
return [parsedDocs as Doc];
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn('Failed to parse DocsGPTRecentDocs from localStorage', error);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
export function getStoredRecentDocs(): Doc[] {
|
||||
const recentDocs = parseStoredRecentDocs(
|
||||
localStorage.getItem('DocsGPTRecentDocs'),
|
||||
);
|
||||
|
||||
if (!recentDocs || recentDocs.length === 0) {
|
||||
localStorage.removeItem('DocsGPTRecentDocs');
|
||||
return [];
|
||||
}
|
||||
|
||||
return recentDocs;
|
||||
}
|
||||
|
||||
export function getLocalRecentDocs(sourceDocs?: Doc[] | null): Doc[] | null {
|
||||
const selectedDocs = getStoredRecentDocs();
|
||||
|
||||
if (!sourceDocs || selectedDocs.length === 0) {
|
||||
return selectedDocs.length > 0 ? selectedDocs : null;
|
||||
}
|
||||
const isDocAvailable = (selected: Doc) => {
|
||||
return sourceDocs.some((source) => {
|
||||
|
||||
@@ -3,6 +3,7 @@ import { configureStore } from '@reduxjs/toolkit';
|
||||
import agentPreviewReducer from './agents/agentPreviewSlice';
|
||||
import { conversationSlice } from './conversation/conversationSlice';
|
||||
import { sharedConversationSlice } from './conversation/sharedConversationSlice';
|
||||
import { getStoredRecentDocs } from './preferences/preferenceApi';
|
||||
import {
|
||||
Preference,
|
||||
prefListenerMiddleware,
|
||||
@@ -13,7 +14,6 @@ import uploadReducer from './upload/uploadSlice';
|
||||
const key = localStorage.getItem('DocsGPTApiKey');
|
||||
const prompt = localStorage.getItem('DocsGPTPrompt');
|
||||
const chunks = localStorage.getItem('DocsGPTChunks');
|
||||
const doc = localStorage.getItem('DocsGPTRecentDocs');
|
||||
const selectedModel = localStorage.getItem('DocsGPTSelectedModel');
|
||||
|
||||
const preloadedState: { preference: Preference } = {
|
||||
@@ -30,7 +30,7 @@ const preloadedState: { preference: Preference } = {
|
||||
{ name: 'strict', id: 'strict', type: 'public' },
|
||||
],
|
||||
chunks: JSON.parse(chunks ?? '2').toString(),
|
||||
selectedDocs: doc !== null ? JSON.parse(doc) : [],
|
||||
selectedDocs: getStoredRecentDocs(),
|
||||
conversations: {
|
||||
data: null,
|
||||
loading: false,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
|
||||
import { createSelector, createSlice, PayloadAction } from '@reduxjs/toolkit';
|
||||
import { RootState } from '../store';
|
||||
|
||||
export interface Attachment {
|
||||
@@ -147,8 +147,10 @@ export const {
|
||||
} = uploadSlice.actions;
|
||||
|
||||
export const selectAttachments = (state: RootState) => state.upload.attachments;
|
||||
export const selectCompletedAttachments = (state: RootState) =>
|
||||
state.upload.attachments.filter((att) => att.status === 'completed');
|
||||
export const selectCompletedAttachments = createSelector(
|
||||
[selectAttachments],
|
||||
(attachments) => attachments.filter((att) => att.status === 'completed'),
|
||||
);
|
||||
export const selectUploadTasks = (state: RootState) => state.upload.tasks;
|
||||
|
||||
export default uploadSlice.reducer;
|
||||
|
||||
Reference in New Issue
Block a user