From 780f5893de0cc3c53dda00fda7d32c236684dff9 Mon Sep 17 00:00:00 2001 From: ajaythapliyal Date: Sun, 5 Mar 2023 14:25:21 +0530 Subject: [PATCH 1/2] adds api call and introduces model for conversations --- frontend/.env.development | 2 + frontend/.env.production | 1 + frontend/src/conversation/conversationApi.ts | 37 +++++++++++++++---- .../src/conversation/conversationModels.ts | 7 ++++ 4 files changed, 40 insertions(+), 7 deletions(-) create mode 100644 frontend/.env.development create mode 100644 frontend/.env.production diff --git a/frontend/.env.development b/frontend/.env.development new file mode 100644 index 00000000..d728457f --- /dev/null +++ b/frontend/.env.development @@ -0,0 +1,2 @@ +# Please put appropriate value +VITE_API_HOST = https://dev.docsapi.arc53.com \ No newline at end of file diff --git a/frontend/.env.production b/frontend/.env.production new file mode 100644 index 00000000..46e738b1 --- /dev/null +++ b/frontend/.env.production @@ -0,0 +1 @@ +VITE_API_HOST = https://docsapi.arc53.com \ No newline at end of file diff --git a/frontend/src/conversation/conversationApi.ts b/frontend/src/conversation/conversationApi.ts index e48d8368..a7a136c8 100644 --- a/frontend/src/conversation/conversationApi.ts +++ b/frontend/src/conversation/conversationApi.ts @@ -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(); + } + }); } diff --git a/frontend/src/conversation/conversationModels.ts b/frontend/src/conversation/conversationModels.ts index c30977d1..17a3bd13 100644 --- a/frontend/src/conversation/conversationModels.ts +++ b/frontend/src/conversation/conversationModels.ts @@ -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; @@ -16,3 +17,9 @@ export interface Answer { query: string; result: string; } + +export interface Query { + prompt: string; + response?: string; + feedback?: FEEDBACK; +} From 19f807b6c4a93de646d0350ebf445c1ba4cd840c Mon Sep 17 00:00:00 2001 From: ajaythapliyal Date: Sun, 5 Mar 2023 16:29:10 +0530 Subject: [PATCH 2/2] refactors the conversation model from singular messages to queries consisting of prompt and response --- frontend/.env.development | 2 +- frontend/src/conversation/Conversation.tsx | 68 ++++++++++++++----- .../src/conversation/ConversationBubble.tsx | 2 +- .../src/conversation/conversationModels.ts | 3 +- .../src/conversation/conversationSlice.ts | 25 +++---- 5 files changed, 64 insertions(+), 36 deletions(-) diff --git a/frontend/.env.development b/frontend/.env.development index d728457f..d9292fb3 100644 --- a/frontend/.env.development +++ b/frontend/.env.development @@ -1,2 +1,2 @@ # Please put appropriate value -VITE_API_HOST = https://dev.docsapi.arc53.com \ No newline at end of file +VITE_API_HOST = https://docsapi.arc53.com \ No newline at end of file diff --git a/frontend/src/conversation/Conversation.tsx b/frontend/src/conversation/Conversation.tsx index a33a4fe1..62bea48b 100644 --- a/frontend/src/conversation/Conversation.tsx +++ b/frontend/src/conversation/Conversation.tsx @@ -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(); const endMessageRef = useRef(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 = ( + + ); + } else if (query.response) { + responseView = ( + + ); + } + return responseView; + }; + return (
- {messages.length > 0 && ( + {queries.length > 0 && (
- {messages.map((message, index) => { + {queries.map((query, index) => { return ( - + + + {prepResponseView(query, index)} + ); })}
)} - {messages.length === 0 && } + {queries.length === 0 && }
(function ConversationBubble({ message, type, className }, ref) { return ( diff --git a/frontend/src/conversation/conversationModels.ts b/frontend/src/conversation/conversationModels.ts index 17a3bd13..92ed976f 100644 --- a/frontend/src/conversation/conversationModels.ts +++ b/frontend/src/conversation/conversationModels.ts @@ -8,7 +8,7 @@ export interface Message { } export interface ConversationState { - conversation: Message[]; + queries: Query[]; status: Status; } @@ -22,4 +22,5 @@ export interface Query { prompt: string; response?: string; feedback?: FEEDBACK; + error?: string; } diff --git a/frontend/src/conversation/conversationSlice.ts b/frontend/src/conversation/conversationSlice.ts index a822a380..6701bc10 100644 --- a/frontend/src/conversation/conversationSlice.ts +++ b/frontend/src/conversation/conversationSlice.ts @@ -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) { - state.conversation.push(action.payload); + addQuery(state, action: PayloadAction) { + 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; -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;