feat: implement agent preview functionality and enhance conversation handling

This commit is contained in:
Siddhant Rai
2025-04-16 16:26:47 +05:30
parent d80eeb044c
commit e6ed37139a
8 changed files with 400 additions and 162 deletions

View File

@@ -15,6 +15,7 @@ export function handleFetchAnswer(
token_limit: number,
agentId?: string,
attachments?: string[],
save_conversation: boolean = true,
): Promise<
| {
result: any;
@@ -52,6 +53,7 @@ export function handleFetchAnswer(
token_limit: token_limit,
isNoneDoc: selectedDocs === null,
agent_id: agentId,
save_conversation: save_conversation,
};
// Add attachments to payload if they exist
@@ -101,6 +103,7 @@ export function handleFetchAnswerSteaming(
indx?: number,
agentId?: string,
attachments?: string[],
save_conversation: boolean = true,
): Promise<Answer> {
history = history.map((item) => {
return {
@@ -120,6 +123,7 @@ export function handleFetchAnswerSteaming(
isNoneDoc: selectedDocs === null,
index: indx,
agent_id: agentId,
save_conversation: save_conversation,
};
// Add attachments to payload if they exist

View File

@@ -53,4 +53,5 @@ export interface RetrievalPayload {
index?: number;
agent_id?: string;
attachments?: string[];
save_conversation?: boolean;
}

View File

@@ -28,35 +28,157 @@ export function handleAbort() {
export const fetchAnswer = createAsyncThunk<
Answer,
{ question: string; indx?: number }
>('fetchAnswer', async ({ question, indx }, { dispatch, getState }) => {
if (abortController) {
abortController.abort();
}
abortController = new AbortController();
const { signal } = abortController;
{ question: string; indx?: number; isPreview?: boolean }
>(
'fetchAnswer',
async ({ question, indx, isPreview = false }, { dispatch, getState }) => {
if (abortController) abortController.abort();
abortController = new AbortController();
const { signal } = abortController;
let isSourceUpdated = false;
const state = getState() as RootState;
const attachments = state.conversation.attachments?.map((a) => a.id) || [];
let isSourceUpdated = false;
const state = getState() as RootState;
const attachments = state.conversation.attachments?.map((a) => a.id) || [];
const currentConversationId = state.conversation.conversationId;
const conversationIdToSend = isPreview ? null : currentConversationId;
const save_conversation = isPreview ? false : true;
if (state.preference) {
if (API_STREAMING) {
await handleFetchAnswerSteaming(
question,
signal,
state.preference.token,
state.preference.selectedDocs!,
state.conversation.queries,
state.conversation.conversationId,
state.preference.prompt.id,
state.preference.chunks,
state.preference.token_limit,
(event) => {
const data = JSON.parse(event.data);
if (state.preference) {
if (API_STREAMING) {
await handleFetchAnswerSteaming(
question,
signal,
state.preference.token,
state.preference.selectedDocs!,
state.conversation.queries,
conversationIdToSend,
state.preference.prompt.id,
state.preference.chunks,
state.preference.token_limit,
(event) => {
const data = JSON.parse(event.data);
const targetIndex = indx ?? state.conversation.queries.length - 1;
if (data.type === 'end') {
dispatch(conversationSlice.actions.setStatus('idle'));
if (data.type === 'end') {
dispatch(conversationSlice.actions.setStatus('idle'));
if (!isPreview) {
getConversations(state.preference.token)
.then((fetchedConversations) => {
dispatch(setConversations(fetchedConversations));
})
.catch((error) => {
console.error('Failed to fetch conversations: ', error);
});
}
if (!isSourceUpdated) {
dispatch(
updateStreamingSource({
index: targetIndex,
query: { sources: [] },
}),
);
}
} else if (data.type === 'id') {
if (!isPreview) {
dispatch(
updateConversationId({
query: { conversationId: data.id },
}),
);
}
} else if (data.type === 'thought') {
const result = data.thought;
dispatch(
updateThought({
index: targetIndex,
query: { thought: result },
}),
);
} else if (data.type === 'source') {
isSourceUpdated = true;
dispatch(
updateStreamingSource({
index: targetIndex,
query: { sources: data.source ?? [] },
}),
);
} else if (data.type === 'tool_calls') {
dispatch(
updateToolCalls({
index: targetIndex,
query: { tool_calls: data.tool_calls },
}),
);
} else if (data.type === 'error') {
// set status to 'failed'
dispatch(conversationSlice.actions.setStatus('failed'));
dispatch(
conversationSlice.actions.raiseError({
index: targetIndex,
message: data.error,
}),
);
} else {
dispatch(
updateStreamingQuery({
index: targetIndex,
query: { response: data.answer },
}),
);
}
},
indx,
state.preference.selectedAgent?.id,
attachments,
save_conversation,
);
} else {
const answer = await handleFetchAnswer(
question,
signal,
state.preference.token,
state.preference.selectedDocs!,
state.conversation.queries,
state.conversation.conversationId,
state.preference.prompt.id,
state.preference.chunks,
state.preference.token_limit,
state.preference.selectedAgent?.id,
attachments,
save_conversation,
);
if (answer) {
let sourcesPrepped = [];
sourcesPrepped = answer.sources.map((source: { title: string }) => {
if (source && source.title) {
const titleParts = source.title.split('/');
return {
...source,
title: titleParts[titleParts.length - 1],
};
}
return source;
});
const targetIndex = indx ?? state.conversation.queries.length - 1;
dispatch(
updateQuery({
index: targetIndex,
query: {
response: answer.answer,
thought: answer.thought,
sources: sourcesPrepped,
tool_calls: answer.toolCalls,
},
}),
);
if (!isPreview) {
dispatch(
updateConversationId({
query: { conversationId: answer.conversationId },
}),
);
getConversations(state.preference.token)
.then((fetchedConversations) => {
dispatch(setConversations(fetchedConversations));
@@ -64,131 +186,23 @@ export const fetchAnswer = createAsyncThunk<
.catch((error) => {
console.error('Failed to fetch conversations: ', error);
});
if (!isSourceUpdated) {
dispatch(
updateStreamingSource({
index: indx ?? state.conversation.queries.length - 1,
query: { sources: [] },
}),
);
}
} else if (data.type === 'id') {
dispatch(
updateConversationId({
query: { conversationId: data.id },
}),
);
} else if (data.type === 'thought') {
const result = data.thought;
dispatch(
updateThought({
index: indx ?? state.conversation.queries.length - 1,
query: { thought: result },
}),
);
} else if (data.type === 'source') {
isSourceUpdated = true;
dispatch(
updateStreamingSource({
index: indx ?? state.conversation.queries.length - 1,
query: { sources: data.source ?? [] },
}),
);
} else if (data.type === 'tool_calls') {
dispatch(
updateToolCalls({
index: indx ?? state.conversation.queries.length - 1,
query: { tool_calls: data.tool_calls },
}),
);
} else if (data.type === 'error') {
// set status to 'failed'
dispatch(conversationSlice.actions.setStatus('failed'));
dispatch(
conversationSlice.actions.raiseError({
index: indx ?? state.conversation.queries.length - 1,
message: data.error,
}),
);
} else {
const result = data.answer;
dispatch(
updateStreamingQuery({
index: indx ?? state.conversation.queries.length - 1,
query: { response: result },
}),
);
}
},
indx,
state.preference.selectedAgent?.id,
attachments,
);
} else {
const answer = await handleFetchAnswer(
question,
signal,
state.preference.token,
state.preference.selectedDocs!,
state.conversation.queries,
state.conversation.conversationId,
state.preference.prompt.id,
state.preference.chunks,
state.preference.token_limit,
state.preference.selectedAgent?.id,
attachments,
);
if (answer) {
let sourcesPrepped = [];
sourcesPrepped = answer.sources.map((source: { title: string }) => {
if (source && source.title) {
const titleParts = source.title.split('/');
return {
...source,
title: titleParts[titleParts.length - 1],
};
}
return source;
});
dispatch(
updateQuery({
index: indx ?? state.conversation.queries.length - 1,
query: {
response: answer.answer,
thought: answer.thought,
sources: sourcesPrepped,
tool_calls: answer.toolCalls,
},
}),
);
dispatch(
updateConversationId({
query: { conversationId: answer.conversationId },
}),
);
dispatch(conversationSlice.actions.setStatus('idle'));
getConversations(state.preference.token)
.then((fetchedConversations) => {
dispatch(setConversations(fetchedConversations));
})
.catch((error) => {
console.error('Failed to fetch conversations: ', error);
});
dispatch(conversationSlice.actions.setStatus('idle'));
}
}
}
}
return {
conversationId: null,
title: null,
answer: '',
query: question,
result: '',
thought: '',
sources: [],
tool_calls: [],
};
});
return {
conversationId: null,
title: null,
answer: '',
query: question,
result: '',
thought: '',
sources: [],
tool_calls: [],
};
},
);
export const conversationSlice = createSlice({
name: 'conversation',
@@ -293,6 +307,13 @@ export const conversationSlice = createSlice({
) => {
state.attachments = action.payload;
},
resetConversation: (state) => {
state.queries = initialState.queries;
state.status = initialState.status;
state.conversationId = initialState.conversationId;
state.attachments = initialState.attachments;
handleAbort();
},
},
extraReducers(builder) {
builder
@@ -327,5 +348,6 @@ export const {
updateToolCalls,
setConversation,
setAttachments,
resetConversation,
} = conversationSlice.actions;
export default conversationSlice.reducer;