mirror of
https://github.com/arc53/DocsGPT
synced 2024-11-19 21:25:39 +00:00
Merge pull request #154 from arc53/feature/feedback-api
Refactors singular Message model to Query model & adds api request to post feedbacks
This commit is contained in:
commit
43ca879f83
2
frontend/.env.development
Normal file
2
frontend/.env.development
Normal file
@ -0,0 +1,2 @@
|
||||
# Please put appropriate value
|
||||
VITE_API_HOST = https://docsapi.arc53.com
|
1
frontend/.env.production
Normal file
1
frontend/.env.production
Normal file
@ -0,0 +1 @@
|
||||
VITE_API_HOST = https://docsapi.arc53.com
|
@ -1,19 +1,20 @@
|
||||
import { useEffect, useRef } from 'react';
|
||||
import { Fragment, useEffect, useRef } from 'react';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import Hero from '../Hero';
|
||||
import { AppDispatch } from '../store';
|
||||
import ConversationBubble from './ConversationBubble';
|
||||
import {
|
||||
addMessage,
|
||||
addQuery,
|
||||
fetchAnswer,
|
||||
selectConversation,
|
||||
selectQueries,
|
||||
selectStatus,
|
||||
} from './conversationSlice';
|
||||
import Send from './../assets/send.svg';
|
||||
import Spinner from './../assets/spinner.svg';
|
||||
import { Query } from './conversationModels';
|
||||
|
||||
export default function Conversation() {
|
||||
const messages = useSelector(selectConversation);
|
||||
const queries = useSelector(selectQueries);
|
||||
const status = useSelector(selectStatus);
|
||||
const dispatch = useDispatch<AppDispatch>();
|
||||
const endMessageRef = useRef<HTMLDivElement>(null);
|
||||
@ -21,34 +22,65 @@ export default function Conversation() {
|
||||
|
||||
useEffect(
|
||||
() => endMessageRef?.current?.scrollIntoView({ behavior: 'smooth' }),
|
||||
[messages],
|
||||
[queries],
|
||||
);
|
||||
|
||||
const handleQuestion = (question: string) => {
|
||||
dispatch(addMessage({ text: question, type: 'QUESTION' }));
|
||||
dispatch(addQuery({ prompt: question }));
|
||||
dispatch(fetchAnswer({ question }));
|
||||
};
|
||||
|
||||
const prepResponseView = (query: Query, index: number) => {
|
||||
let responseView;
|
||||
if (query.error) {
|
||||
responseView = (
|
||||
<ConversationBubble
|
||||
ref={endMessageRef}
|
||||
className={`${index === queries.length - 1 ? 'mb-24' : 'mb-7'}`}
|
||||
key={`${index}ERROR`}
|
||||
message={query.error}
|
||||
type="ERROR"
|
||||
></ConversationBubble>
|
||||
);
|
||||
} else if (query.response) {
|
||||
responseView = (
|
||||
<ConversationBubble
|
||||
ref={endMessageRef}
|
||||
className={`${index === queries.length - 1 ? 'mb-24' : 'mb-7'}`}
|
||||
key={`${index}ANSWER`}
|
||||
message={query.response}
|
||||
type={'ANSWER'}
|
||||
></ConversationBubble>
|
||||
);
|
||||
}
|
||||
return responseView;
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex justify-center p-6">
|
||||
{messages.length > 0 && (
|
||||
{queries.length > 0 && (
|
||||
<div className="mt-20 flex w-10/12 flex-col transition-all md:w-3/4">
|
||||
{messages.map((message, index) => {
|
||||
{queries.map((query, index) => {
|
||||
return (
|
||||
<Fragment key={index}>
|
||||
<ConversationBubble
|
||||
ref={index === messages.length - 1 ? endMessageRef : null}
|
||||
ref={endMessageRef}
|
||||
className={`${
|
||||
index === messages.length - 1 ? 'mb-24' : 'mb-7'
|
||||
index === queries.length - 1 && status === 'loading'
|
||||
? 'mb-24'
|
||||
: 'mb-7'
|
||||
}`}
|
||||
key={index}
|
||||
message={message.text}
|
||||
type={message.type}
|
||||
key={`${index}QUESTION`}
|
||||
message={query.prompt}
|
||||
type="QUESTION"
|
||||
></ConversationBubble>
|
||||
{prepResponseView(query, index)}
|
||||
</Fragment>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
{messages.length === 0 && <Hero className="mt-24 md:mt-52"></Hero>}
|
||||
{queries.length === 0 && <Hero className="mt-24 md:mt-52"></Hero>}
|
||||
<div className="fixed bottom-0 flex w-10/12 flex-col items-end self-center md:w-[50%]">
|
||||
<div className="flex w-full">
|
||||
<div
|
||||
|
@ -8,7 +8,7 @@ const ConversationBubble = forwardRef<
|
||||
{
|
||||
message: string;
|
||||
type: MESSAGE_TYPE;
|
||||
className: string;
|
||||
className?: string;
|
||||
}
|
||||
>(function ConversationBubble({ message, type, className }, ref) {
|
||||
return (
|
||||
|
@ -1,6 +1,8 @@
|
||||
import { Answer } from './conversationModels';
|
||||
import { Answer, FEEDBACK } from './conversationModels';
|
||||
import { Doc } from '../preferences/preferenceApi';
|
||||
|
||||
const apiHost = import.meta.env.VITE_API_HOST || 'https://docsapi.arc53.com';
|
||||
|
||||
export function fetchAnswerApi(
|
||||
question: string,
|
||||
apiKey: string,
|
||||
@ -23,8 +25,6 @@ export function fetchAnswerApi(
|
||||
selectedDocs.model +
|
||||
'/';
|
||||
|
||||
const apiHost = import.meta.env.VITE_API_HOST || 'https://docsapi.arc53.com';
|
||||
|
||||
return fetch(apiHost + '/api/answer', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
@ -51,8 +51,31 @@ export function fetchAnswerApi(
|
||||
});
|
||||
}
|
||||
|
||||
function getRandomInt(min: number, max: number) {
|
||||
min = Math.ceil(min);
|
||||
max = Math.floor(max);
|
||||
return Math.floor(Math.random() * (max - min) + min); // The maximum is exclusive and the minimum is inclusive
|
||||
export function sendFeedback(
|
||||
{
|
||||
prompt,
|
||||
response,
|
||||
}: {
|
||||
prompt: string;
|
||||
response: string;
|
||||
},
|
||||
feedback: FEEDBACK,
|
||||
) {
|
||||
return fetch(`${apiHost}/api/feedback`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
question: prompt,
|
||||
answer: response,
|
||||
feedback: feedback,
|
||||
}),
|
||||
}).then((response) => {
|
||||
if (response.ok) {
|
||||
return Promise.resolve();
|
||||
} else {
|
||||
return Promise.reject();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
export type MESSAGE_TYPE = 'QUESTION' | 'ANSWER' | 'ERROR';
|
||||
export type Status = 'idle' | 'loading' | 'failed';
|
||||
export type FEEDBACK = 'LIKE' | 'DISLIKE';
|
||||
|
||||
export interface Message {
|
||||
text: string;
|
||||
@ -7,7 +8,7 @@ export interface Message {
|
||||
}
|
||||
|
||||
export interface ConversationState {
|
||||
conversation: Message[];
|
||||
queries: Query[];
|
||||
status: Status;
|
||||
}
|
||||
|
||||
@ -16,3 +17,10 @@ export interface Answer {
|
||||
query: string;
|
||||
result: string;
|
||||
}
|
||||
|
||||
export interface Query {
|
||||
prompt: string;
|
||||
response?: string;
|
||||
feedback?: FEEDBACK;
|
||||
error?: string;
|
||||
}
|
||||
|
@ -1,10 +1,10 @@
|
||||
import { createAsyncThunk, createSlice, PayloadAction } from '@reduxjs/toolkit';
|
||||
import store from '../store';
|
||||
import { fetchAnswerApi } from './conversationApi';
|
||||
import { Answer, ConversationState, Message } from './conversationModels';
|
||||
import { Answer, ConversationState, Query } from './conversationModels';
|
||||
|
||||
const initialState: ConversationState = {
|
||||
conversation: [],
|
||||
queries: [],
|
||||
status: 'idle',
|
||||
};
|
||||
|
||||
@ -27,8 +27,8 @@ export const conversationSlice = createSlice({
|
||||
name: 'conversation',
|
||||
initialState,
|
||||
reducers: {
|
||||
addMessage(state, action: PayloadAction<Message>) {
|
||||
state.conversation.push(action.payload);
|
||||
addQuery(state, action: PayloadAction<Query>) {
|
||||
state.queries.push(action.payload);
|
||||
},
|
||||
},
|
||||
extraReducers(builder) {
|
||||
@ -38,27 +38,22 @@ export const conversationSlice = createSlice({
|
||||
})
|
||||
.addCase(fetchAnswer.fulfilled, (state, action) => {
|
||||
state.status = 'idle';
|
||||
state.conversation.push({
|
||||
text: action.payload.answer,
|
||||
type: 'ANSWER',
|
||||
});
|
||||
state.queries[state.queries.length - 1].response =
|
||||
action.payload.answer;
|
||||
})
|
||||
.addCase(fetchAnswer.rejected, (state, action) => {
|
||||
state.status = 'failed';
|
||||
state.conversation.push({
|
||||
text: 'Something went wrong. Please try again later.',
|
||||
type: 'ERROR',
|
||||
});
|
||||
state.queries[state.queries.length - 1].error =
|
||||
'Something went wrong. Please try again later.';
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
type RootState = ReturnType<typeof store.getState>;
|
||||
|
||||
export const selectConversation = (state: RootState) =>
|
||||
state.conversation.conversation;
|
||||
export const selectQueries = (state: RootState) => state.conversation.queries;
|
||||
|
||||
export const selectStatus = (state: RootState) => state.conversation.status;
|
||||
|
||||
export const { addMessage } = conversationSlice.actions;
|
||||
export const { addQuery } = conversationSlice.actions;
|
||||
export default conversationSlice.reducer;
|
||||
|
Loading…
Reference in New Issue
Block a user