Sources in responses

pull/264/head
Alex 1 year ago
parent 577d58c92b
commit 610adcbefc

@ -157,6 +157,10 @@ def complete_stream(question, docsearch, chat_history, api_key):
docs_together = "\n".join([doc.page_content for doc in docs])
p_chat_combine = chat_combine_template.replace("{summaries}", docs_together)
messages_combine = [{"role": "system", "content": p_chat_combine}]
for doc in docs:
data = json.dumps({"type": "source", "doc": doc.page_content})
yield f"data:{data}\n\n"
if len(chat_history) > 1:
tokens_current_history = 0
# count tokens in history
@ -308,6 +312,9 @@ def api_answer():
except Exception:
pass
sources = docsearch.similarity_search(question, k=2)
result['sources'] = [{'title': i.page_content, 'text': i.page_content} for i in sources]
# mock result
# result = {
# "answer": "The answer is 42",

@ -60,6 +60,7 @@ export default function Conversation() {
key={`${index}ANSWER`}
message={query.response}
type={'ANSWER'}
sources={query.sources}
feedback={query.feedback}
handleFeedback={(feedback: FEEDBACK) =>
handleFeedback(query, feedback, index)
@ -83,6 +84,7 @@ export default function Conversation() {
key={`${index}QUESTION`}
message={query.prompt}
type="QUESTION"
sources={query.sources}
></ConversationBubble>
{prepResponseView(query, index)}
</Fragment>

@ -8,6 +8,8 @@ import ReactMarkdown from 'react-markdown';
import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';
import { vscDarkPlus } from 'react-syntax-highlighter/dist/cjs/styles/prism';
const DisableSourceFE = import.meta.env.VITE_DISABLE_SOURCE_FE || false;
const ConversationBubble = forwardRef<
HTMLDivElement,
{
@ -16,12 +18,14 @@ const ConversationBubble = forwardRef<
className?: string;
feedback?: FEEDBACK;
handleFeedback?: (feedback: FEEDBACK) => void;
sources?: { title: string; text: string }[];
}
>(function ConversationBubble(
{ message, type, className, feedback, handleFeedback },
{ message, type, className, feedback, handleFeedback, sources },
ref,
) {
const [showFeedback, setShowFeedback] = useState(false);
const [openSource, setOpenSource] = useState<number | null>(null);
const List = ({
ordered,
children,
@ -37,7 +41,7 @@ const ConversationBubble = forwardRef<
if (type === 'QUESTION') {
bubble = (
<div ref={ref} className={`flex flex-row-reverse self-end ${className}`}>
<Avatar className="mt-4 text-2xl" avatar="🧑‍💻"></Avatar>
<Avatar className="mt-2 text-2xl" avatar="🧑‍💻"></Avatar>
<div className="mr-2 ml-10 flex items-center rounded-3xl bg-blue-1000 p-3.5 text-white">
<ReactMarkdown className="whitespace-pre-wrap break-words">
{message}
@ -49,85 +53,116 @@ const ConversationBubble = forwardRef<
bubble = (
<div
ref={ref}
className={`flex self-start ${className}`}
className={`flex self-start ${className} flex-col`}
onMouseEnter={() => setShowFeedback(true)}
onMouseLeave={() => setShowFeedback(false)}
>
<Avatar className="mt-4 text-2xl" avatar="🦖"></Avatar>
<div
className={`ml-2 mr-5 flex items-center rounded-3xl bg-gray-1000 p-3.5 ${
type === 'ERROR'
? ' rounded-lg border border-red-2000 bg-red-1000 p-2 text-red-3000'
: ''
}`}
>
{type === 'ERROR' && (
<img src={Alert} alt="alert" className="mr-2 inline" />
)}
<ReactMarkdown
className="whitespace-pre-wrap break-words"
components={{
code({ node, inline, className, children, ...props }) {
const match = /language-(\w+)/.exec(className || '');
<div className="flex self-start">
<Avatar className="mt-2 text-2xl" avatar="🦖"></Avatar>
<div
className={`ml-2 mr-5 flex items-center rounded-3xl bg-gray-1000 p-3.5 ${
type === 'ERROR'
? ' rounded-lg border border-red-2000 bg-red-1000 p-2 text-red-3000'
: ''
}`}
>
{type === 'ERROR' && (
<img src={Alert} alt="alert" className="mr-2 inline" />
)}
<ReactMarkdown
className="whitespace-pre-wrap break-words"
components={{
code({ node, inline, className, children, ...props }) {
const match = /language-(\w+)/.exec(className || '');
return !inline && match ? (
<SyntaxHighlighter
PreTag="div"
language={match[1]}
{...props}
style={vscDarkPlus}
>
{String(children).replace(/\n$/, '')}
</SyntaxHighlighter>
) : (
<code className={className ? className : ''} {...props}>
{children}
</code>
);
},
ul({ node, children }) {
return <List>{children}</List>;
},
ol({ node, children }) {
return <List ordered>{children}</List>;
},
}}
return !inline && match ? (
<SyntaxHighlighter
PreTag="div"
language={match[1]}
{...props}
style={vscDarkPlus}
>
{String(children).replace(/\n$/, '')}
</SyntaxHighlighter>
) : (
<code className={className ? className : ''} {...props}>
{children}
</code>
);
},
ul({ node, children }) {
return <List>{children}</List>;
},
ol({ node, children }) {
return <List ordered>{children}</List>;
},
}}
>
{message}
</ReactMarkdown>
</div>
<div
className={`mr-2 flex items-center justify-center ${
feedback === 'LIKE' || (type !== 'ERROR' && showFeedback)
? ''
: 'md:invisible'
}`}
>
{message}
</ReactMarkdown>
</div>
<div
className={`mr-2 flex items-center justify-center ${
feedback === 'LIKE' || (type !== 'ERROR' && showFeedback)
? ''
: 'md:invisible'
}`}
>
<Like
className={`cursor-pointer ${
feedback === 'LIKE'
? 'fill-blue-1000 stroke-blue-1000'
: 'fill-none stroke-gray-4000 hover:fill-gray-4000'
<Like
className={`cursor-pointer ${
feedback === 'LIKE'
? 'fill-blue-1000 stroke-blue-1000'
: 'fill-none stroke-gray-4000 hover:fill-gray-4000'
}`}
onClick={() => handleFeedback?.('LIKE')}
></Like>
</div>
<div
className={`mr-10 flex items-center justify-center ${
feedback === 'DISLIKE' || (type !== 'ERROR' && showFeedback)
? ''
: 'md:invisible'
}`}
onClick={() => handleFeedback?.('LIKE')}
></Like>
>
<Dislike
className={`cursor-pointer ${
feedback === 'DISLIKE'
? 'fill-red-2000 stroke-red-2000'
: 'fill-none stroke-gray-4000 hover:fill-gray-4000'
}`}
onClick={() => handleFeedback?.('DISLIKE')}
></Dislike>
</div>
</div>
<div
className={`mr-10 flex items-center justify-center ${
feedback === 'DISLIKE' || (type !== 'ERROR' && showFeedback)
? ''
: 'md:invisible'
}`}
>
<Dislike
className={`cursor-pointer ${
feedback === 'DISLIKE'
? 'fill-red-2000 stroke-red-2000'
: 'fill-none stroke-gray-4000 hover:fill-gray-4000'
}`}
onClick={() => handleFeedback?.('DISLIKE')}
></Dislike>
<div className="ml-8 mt-2 grid w-1/2 grid-cols-3 gap-2">
{DisableSourceFE
? null
: sources?.map((source, index) => (
<div
key={index}
className="w-26 cursor-pointer rounded-xl border border-gray-200 py-1 px-2 hover:bg-gray-100"
onClick={() =>
setOpenSource(openSource === index ? null : index)
}
>
<p className="truncate text-xs text-gray-500">
{index + 1}. {source.title}
</p>
</div>
))}
</div>
{sources && openSource !== null && sources[openSource] && (
<div className="ml-8 mt-2 w-3/4 rounded-xl bg-blue-200 p-2">
<p className="w-3/4 truncate text-xs text-gray-500">Source:</p>
<div className="rounded-xl border-2 border-gray-200 bg-white p-2">
<p className="text-xs text-gray-500 ">
{sources[openSource].text}
</p>
</div>
</div>
)}
</div>
);
}

@ -51,7 +51,7 @@ export function fetchAnswerApi(
})
.then((data) => {
const result = data.answer;
return { answer: result, query: question, result };
return { answer: result, query: question, result, sources: data.sources };
});
}

@ -16,6 +16,7 @@ export interface Answer {
answer: string;
query: string;
result: string;
sources: { title: string; text: string }[];
}
export interface Query {
@ -23,4 +24,5 @@ export interface Query {
response?: string;
feedback?: FEEDBACK;
error?: string;
sources?: { title: string; text: string }[];
}

@ -28,6 +28,14 @@ export const fetchAnswer = createAsyncThunk<Answer, { question: string }>(
if (data.type === 'end') {
// set status to 'idle'
dispatch(conversationSlice.actions.setStatus('idle'));
} else if (data.type === 'source') {
const result = data.doc;
dispatch(
updateStreamingSource({
index: state.conversation.queries.length - 1,
query: { sources: [{ title: result, text: result }] },
}),
);
} else {
const result = data.answer;
dispatch(
@ -50,7 +58,7 @@ export const fetchAnswer = createAsyncThunk<Answer, { question: string }>(
dispatch(
updateQuery({
index: state.conversation.queries.length - 1,
query: { response: answer.answer },
query: { response: answer.answer, sources: answer.sources },
}),
);
dispatch(conversationSlice.actions.setStatus('idle'));
@ -83,6 +91,17 @@ export const conversationSlice = createSlice({
};
}
},
updateStreamingSource(
state,
action: PayloadAction<{ index: number; query: Partial<Query> }>,
) {
const index = action.payload.index;
if (!state.queries[index].sources) {
state.queries[index].sources = [action.payload.query.sources![0]];
} else {
state.queries[index].sources!.push(action.payload.query.sources![0]);
}
},
updateQuery(
state,
action: PayloadAction<{ index: number; query: Partial<Query> }>,
@ -116,6 +135,10 @@ export const selectQueries = (state: RootState) => state.conversation.queries;
export const selectStatus = (state: RootState) => state.conversation.status;
export const { addQuery, updateQuery, updateStreamingQuery } =
conversationSlice.actions;
export const {
addQuery,
updateQuery,
updateStreamingQuery,
updateStreamingSource,
} = conversationSlice.actions;
export default conversationSlice.reducer;

Loading…
Cancel
Save