From ff2e79fe7b65b0f79abbe84479fef496bb950c73 Mon Sep 17 00:00:00 2001 From: Alex Date: Thu, 18 May 2023 23:52:59 +0100 Subject: [PATCH 1/7] streaming experiments --- application/app.py | 17 ++++++- frontend/src/conversation/conversationApi.ts | 42 ++++++++--------- .../src/conversation/conversationSlice.ts | 46 +++++++++++++++---- 3 files changed, 70 insertions(+), 35 deletions(-) diff --git a/application/app.py b/application/app.py index d68c5b9..0307201 100644 --- a/application/app.py +++ b/application/app.py @@ -9,7 +9,7 @@ import dotenv import requests from celery import Celery from celery.result import AsyncResult -from flask import Flask, request, render_template, send_from_directory, jsonify +from flask import Flask, request, render_template, send_from_directory, jsonify, Response from langchain import FAISS from langchain import VectorDBQA, HuggingFaceHub, Cohere, OpenAI from langchain.chains import LLMChain, ConversationalRetrievalChain @@ -120,6 +120,21 @@ def home(): embeddings_choice=settings.EMBEDDINGS_NAME) +def complete_stream(input): + import time + for i in range(10): + data = json.dumps({"answer": i}) + #data = {"answer": str(i)} + yield f"data: {data}\n\n" + time.sleep(0.05) + # send data.type = "end" to indicate that the stream has ended as json + data = json.dumps({"type": "end"}) + yield f"data: {data}\n\n" +@app.route("/stream", methods=['POST', 'GET']) +def stream(): + return Response(complete_stream("hi"), mimetype='text/event-stream') + + @app.route("/api/answer", methods=["POST"]) def api_answer(): data = request.get_json() diff --git a/frontend/src/conversation/conversationApi.ts b/frontend/src/conversation/conversationApi.ts index c732034..6c3ff03 100644 --- a/frontend/src/conversation/conversationApi.ts +++ b/frontend/src/conversation/conversationApi.ts @@ -7,6 +7,7 @@ export function fetchAnswerApi( question: string, apiKey: string, selectedDocs: Doc, + onEvent: (event: MessageEvent) => void, ): Promise { let namePath = selectedDocs.name; if (selectedDocs.language === namePath) { @@ -28,30 +29,23 @@ export function fetchAnswerApi( '/'; } - return fetch(apiHost + '/api/answer', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ - question: question, - api_key: apiKey, - embeddings_key: apiKey, - history: localStorage.getItem('chatHistory'), - active_docs: docPath, - }), - }) - .then((response) => { - if (response.ok) { - return response.json(); - } else { - Promise.reject(response); - } - }) - .then((data) => { - const result = data.answer; - return { answer: result, query: question, result }; - }); + return new Promise((resolve, reject) => { + const url = new URL(apiHost + '/stream'); + url.searchParams.append('question', question); + url.searchParams.append('api_key', apiKey); + url.searchParams.append('embeddings_key', apiKey); + url.searchParams.append('history', localStorage.getItem('chatHistory')); + url.searchParams.append('active_docs', docPath); + + const eventSource = new EventSource(url.href); + + eventSource.onmessage = onEvent; + + eventSource.onerror = (error) => { + console.log('Connection failed.'); + eventSource.close(); + }; + }); } export function sendFeedback( diff --git a/frontend/src/conversation/conversationSlice.ts b/frontend/src/conversation/conversationSlice.ts index c728b9e..04dd0be 100644 --- a/frontend/src/conversation/conversationSlice.ts +++ b/frontend/src/conversation/conversationSlice.ts @@ -1,7 +1,8 @@ import { createAsyncThunk, createSlice, PayloadAction } from '@reduxjs/toolkit'; import store from '../store'; import { fetchAnswerApi } from './conversationApi'; -import { Answer, ConversationState, Query } from './conversationModels'; +import { ConversationState, Query } from './conversationModels'; +import { Dispatch } from 'react'; const initialState: ConversationState = { queries: [], @@ -9,18 +10,35 @@ const initialState: ConversationState = { }; export const fetchAnswer = createAsyncThunk< - Answer, + void, { question: string }, - { state: RootState } ->('fetchAnswer', async ({ question }, { getState }) => { + { dispatch: Dispatch; state: RootState } +>('fetchAnswer', ({ question }, { dispatch, getState }) => { const state = getState(); - const answer = await fetchAnswerApi( + fetchAnswerApi( question, state.preference.apiKey, state.preference.selectedDocs!, + (event) => { + const data = JSON.parse(event.data); + console.log(data); + + // check if the 'end' event has been received + if (data.type === 'end') { + // set status to 'idle' + dispatch(conversationSlice.actions.setStatus('idle')); + } else { + const result = JSON.stringify(data.answer); + dispatch( + updateQuery({ + index: state.conversation.queries.length - 1, + query: { response: result }, + }), + ); + } + }, ); - return answer; }); export const conversationSlice = createSlice({ @@ -35,10 +53,18 @@ export const conversationSlice = createSlice({ action: PayloadAction<{ index: number; query: Partial }>, ) { const index = action.payload.index; - state.queries[index] = { - ...state.queries[index], - ...action.payload.query, - }; + if (action.payload.query.response) { + state.queries[index].response = + (state.queries[index].response || '') + action.payload.query.response; + } else { + state.queries[index] = { + ...state.queries[index], + ...action.payload.query, + }; + } + }, + setStatus(state, action: PayloadAction) { + state.status = action.payload; }, }, extraReducers(builder) { From c3af8a77afe1fea3879acfeddae1492450e99a1e Mon Sep 17 00:00:00 2001 From: Alex Date: Mon, 29 May 2023 17:55:43 +0100 Subject: [PATCH 2/7] working streams --- application/app.py | 37 ++++++++++++++----- .../src/conversation/conversationSlice.ts | 3 +- 2 files changed, 30 insertions(+), 10 deletions(-) diff --git a/application/app.py b/application/app.py index 0307201..8f30dcf 100644 --- a/application/app.py +++ b/application/app.py @@ -5,6 +5,7 @@ import json import os import traceback +import openai import dotenv import requests from celery import Celery @@ -119,20 +120,38 @@ def home(): return render_template("index.html", api_key_set=api_key_set, llm_choice=settings.LLM_NAME, embeddings_choice=settings.EMBEDDINGS_NAME) - -def complete_stream(input): - import time - for i in range(10): - data = json.dumps({"answer": i}) - #data = {"answer": str(i)} - yield f"data: {data}\n\n" - time.sleep(0.05) +def complete_stream(question): + import sys + openai.api_key = settings.API_KEY + docsearch = FAISS.load_local("", OpenAIEmbeddings(openai_api_key=settings.EMBEDDINGS_KEY)) + docs = docsearch.similarity_search(question, k=2) + # join all page_content together with a newline + docs_together = "\n".join([doc.page_content for doc in docs]) + + # swap {summaries} in chat_combine_template with the summaries from the docs + p_chat_combine = chat_combine_template.replace("{summaries}", docs_together) + completion = openai.ChatCompletion.create(model="gpt-3.5-turbo", messages=[ + {"role": "system", "content": p_chat_combine}, + {"role": "user", "content": question}, + ], stream=True, max_tokens=1000, temperature=0) + + for line in completion: + print(line, file=sys.stderr) + if 'content' in line['choices'][0]['delta']: + # check if the delta contains content + data = json.dumps({"answer": str(line['choices'][0]['delta']['content'])}) + yield f"data: {data}\n\n" # send data.type = "end" to indicate that the stream has ended as json data = json.dumps({"type": "end"}) yield f"data: {data}\n\n" @app.route("/stream", methods=['POST', 'GET']) def stream(): - return Response(complete_stream("hi"), mimetype='text/event-stream') + #data = request.get_json() + #question = data["question"] + # get parameter from url question + question = request.args.get('question') + #question = "Hi" + return Response(complete_stream(question), mimetype='text/event-stream') @app.route("/api/answer", methods=["POST"]) diff --git a/frontend/src/conversation/conversationSlice.ts b/frontend/src/conversation/conversationSlice.ts index 04dd0be..743f2af 100644 --- a/frontend/src/conversation/conversationSlice.ts +++ b/frontend/src/conversation/conversationSlice.ts @@ -29,7 +29,7 @@ export const fetchAnswer = createAsyncThunk< // set status to 'idle' dispatch(conversationSlice.actions.setStatus('idle')); } else { - const result = JSON.stringify(data.answer); + const result = data.answer; dispatch( updateQuery({ index: state.conversation.queries.length - 1, @@ -53,6 +53,7 @@ export const conversationSlice = createSlice({ action: PayloadAction<{ index: number; query: Partial }>, ) { const index = action.payload.index; + console.log('updating query'); if (action.payload.query.response) { state.queries[index].response = (state.queries[index].response || '') + action.payload.query.response; From d2358c399dde51832a543f4d737c938486454556 Mon Sep 17 00:00:00 2001 From: Alex Date: Tue, 30 May 2023 19:43:06 +0100 Subject: [PATCH 3/7] working version --- application/app.py | 14 +-- frontend/src/conversation/conversationApi.ts | 52 +++++++++++ .../src/conversation/conversationSlice.ts | 91 +++++++++++++------ 3 files changed, 117 insertions(+), 40 deletions(-) diff --git a/application/app.py b/application/app.py index 8f30dcf..5e823fa 100644 --- a/application/app.py +++ b/application/app.py @@ -136,7 +136,6 @@ def complete_stream(question): ], stream=True, max_tokens=1000, temperature=0) for line in completion: - print(line, file=sys.stderr) if 'content' in line['choices'][0]['delta']: # check if the delta contains content data = json.dumps({"answer": str(line['choices'][0]['delta']['content'])}) @@ -197,16 +196,9 @@ def api_answer(): elif settings.EMBEDDINGS_NAME == "cohere_medium": docsearch = FAISS.load_local(vectorstore, CohereEmbeddings(cohere_api_key=embeddings_key)) - # create a prompt template - if history: - history = json.loads(history) - template_temp = template_hist.replace("{historyquestion}", history[0]).replace("{historyanswer}", - history[1]) - c_prompt = PromptTemplate(input_variables=["summaries", "question"], template=template_temp, - template_format="jinja2") - else: - c_prompt = PromptTemplate(input_variables=["summaries", "question"], template=template, - template_format="jinja2") + + c_prompt = PromptTemplate(input_variables=["summaries", "question"], template=template, + template_format="jinja2") q_prompt = PromptTemplate(input_variables=["context", "question"], template=template_quest, template_format="jinja2") diff --git a/frontend/src/conversation/conversationApi.ts b/frontend/src/conversation/conversationApi.ts index 6c3ff03..98cfd6f 100644 --- a/frontend/src/conversation/conversationApi.ts +++ b/frontend/src/conversation/conversationApi.ts @@ -4,6 +4,58 @@ import { Doc } from '../preferences/preferenceApi'; const apiHost = import.meta.env.VITE_API_HOST || 'https://docsapi.arc53.com'; export function fetchAnswerApi( + question: string, + apiKey: string, + selectedDocs: Doc, + history: Array = [], +): Promise { + let namePath = selectedDocs.name; + if (selectedDocs.language === namePath) { + namePath = '.project'; + } + + let docPath = 'default'; + if (selectedDocs.location === 'local') { + docPath = 'local' + '/' + selectedDocs.name + '/'; + } else if (selectedDocs.location === 'remote') { + docPath = + selectedDocs.language + + '/' + + namePath + + '/' + + selectedDocs.version + + '/' + + selectedDocs.model + + '/'; + } + + return fetch(apiHost + '/api/answer', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + question: question, + api_key: apiKey, + embeddings_key: apiKey, + history: history, + active_docs: docPath, + }), + }) + .then((response) => { + if (response.ok) { + return response.json(); + } else { + Promise.reject(response); + } + }) + .then((data) => { + const result = data.answer; + return { answer: result, query: question, result }; + }); +} + +export function fetchAnswerSteaming( question: string, apiKey: string, selectedDocs: Doc, diff --git a/frontend/src/conversation/conversationSlice.ts b/frontend/src/conversation/conversationSlice.ts index 743f2af..20e3e4b 100644 --- a/frontend/src/conversation/conversationSlice.ts +++ b/frontend/src/conversation/conversationSlice.ts @@ -1,7 +1,7 @@ import { createAsyncThunk, createSlice, PayloadAction } from '@reduxjs/toolkit'; import store from '../store'; -import { fetchAnswerApi } from './conversationApi'; -import { ConversationState, Query } from './conversationModels'; +import { fetchAnswerApi, fetchAnswerSteaming } from './conversationApi'; +import { Answer, ConversationState, Query } from './conversationModels'; import { Dispatch } from 'react'; const initialState: ConversationState = { @@ -9,36 +9,58 @@ const initialState: ConversationState = { status: 'idle', }; +const API_STREAMING = import.meta.env.API_STREAMING || false; + export const fetchAnswer = createAsyncThunk< - void, + Answer | void, { question: string }, - { dispatch: Dispatch; state: RootState } ->('fetchAnswer', ({ question }, { dispatch, getState }) => { + { dispatch: Dispatch; state: RootState } +>('fetchAnswer', async ({ question }, { dispatch, getState }) => { const state = getState(); - fetchAnswerApi( - question, - state.preference.apiKey, - state.preference.selectedDocs!, - (event) => { - const data = JSON.parse(event.data); - console.log(data); + if (API_STREAMING) { + fetchAnswerSteaming( + question, + state.preference.apiKey, + state.preference.selectedDocs!, + (event) => { + const data = JSON.parse(event.data); + console.log(data); - // check if the 'end' event has been received - if (data.type === 'end') { - // set status to 'idle' - dispatch(conversationSlice.actions.setStatus('idle')); - } else { - const result = data.answer; - dispatch( - updateQuery({ - index: state.conversation.queries.length - 1, - query: { response: result }, - }), - ); - } - }, - ); + // check if the 'end' event has been received + if (data.type === 'end') { + // set status to 'idle' + dispatch(conversationSlice.actions.setStatus('idle')); + } else { + const result = data.answer; + dispatch( + updateStreamingQuery({ + index: state.conversation.queries.length - 1, + query: { response: result }, + }), + ); + } + }, + ); + } else { + const answer = await fetchAnswerApi( + question, + state.preference.apiKey, + state.preference.selectedDocs!, + state.conversation.queries, + ); + dispatch( + // conversationSlice.actions.addQuery({ + // question: question, + // response: answer, + // }), + updateQuery({ + index: state.conversation.queries.length - 1, + query: { response: answer.answer }, + }), + ); + dispatch(conversationSlice.actions.setStatus('idle')); + } }); export const conversationSlice = createSlice({ @@ -48,7 +70,7 @@ export const conversationSlice = createSlice({ addQuery(state, action: PayloadAction) { state.queries.push(action.payload); }, - updateQuery( + updateStreamingQuery( state, action: PayloadAction<{ index: number; query: Partial }>, ) { @@ -64,6 +86,16 @@ export const conversationSlice = createSlice({ }; } }, + updateQuery( + state, + action: PayloadAction<{ index: number; query: Partial }>, + ) { + const index = action.payload.index; + state.queries[index] = { + ...state.queries[index], + ...action.payload.query, + }; + }, setStatus(state, action: PayloadAction) { state.status = action.payload; }, @@ -92,5 +124,6 @@ export const selectQueries = (state: RootState) => state.conversation.queries; export const selectStatus = (state: RootState) => state.conversation.status; -export const { addQuery, updateQuery } = conversationSlice.actions; +export const { addQuery, updateQuery, updateStreamingQuery } = + conversationSlice.actions; export default conversationSlice.reducer; From 8380858a82a41eca833f634e05516d7c9ee991bd Mon Sep 17 00:00:00 2001 From: Alex Date: Tue, 30 May 2023 20:00:41 +0100 Subject: [PATCH 4/7] some fixes --- frontend/src/conversation/conversationApi.ts | 2 +- .../src/conversation/conversationSlice.ts | 86 +++++++++---------- 2 files changed, 44 insertions(+), 44 deletions(-) diff --git a/frontend/src/conversation/conversationApi.ts b/frontend/src/conversation/conversationApi.ts index 98cfd6f..29a3956 100644 --- a/frontend/src/conversation/conversationApi.ts +++ b/frontend/src/conversation/conversationApi.ts @@ -46,7 +46,7 @@ export function fetchAnswerApi( if (response.ok) { return response.json(); } else { - Promise.reject(response); + return Promise.reject(new Error(response.statusText)); } }) .then((data) => { diff --git a/frontend/src/conversation/conversationSlice.ts b/frontend/src/conversation/conversationSlice.ts index 20e3e4b..ad30381 100644 --- a/frontend/src/conversation/conversationSlice.ts +++ b/frontend/src/conversation/conversationSlice.ts @@ -9,57 +9,57 @@ const initialState: ConversationState = { status: 'idle', }; -const API_STREAMING = import.meta.env.API_STREAMING || false; +const API_STREAMING = import.meta.env.API_STREAMING || true; export const fetchAnswer = createAsyncThunk< - Answer | void, + Answer, { question: string }, { dispatch: Dispatch; state: RootState } >('fetchAnswer', async ({ question }, { dispatch, getState }) => { const state = getState(); - if (API_STREAMING) { - fetchAnswerSteaming( - question, - state.preference.apiKey, - state.preference.selectedDocs!, - (event) => { - const data = JSON.parse(event.data); - console.log(data); + if (state.preference) { + if (API_STREAMING) { + fetchAnswerSteaming( + question, + state.preference.apiKey, + state.preference.selectedDocs!, + (event) => { + const data = JSON.parse(event.data); + console.log(data); - // check if the 'end' event has been received - if (data.type === 'end') { - // set status to 'idle' - dispatch(conversationSlice.actions.setStatus('idle')); - } else { - const result = data.answer; - dispatch( - updateStreamingQuery({ - index: state.conversation.queries.length - 1, - query: { response: result }, - }), - ); - } - }, - ); - } else { - const answer = await fetchAnswerApi( - question, - state.preference.apiKey, - state.preference.selectedDocs!, - state.conversation.queries, - ); - dispatch( - // conversationSlice.actions.addQuery({ - // question: question, - // response: answer, - // }), - updateQuery({ - index: state.conversation.queries.length - 1, - query: { response: answer.answer }, - }), - ); - dispatch(conversationSlice.actions.setStatus('idle')); + // check if the 'end' event has been received + if (data.type === 'end') { + // set status to 'idle' + dispatch(conversationSlice.actions.setStatus('idle')); + } else { + const result = data.answer; + dispatch( + updateStreamingQuery({ + index: state.conversation.queries.length - 1, + query: { response: result }, + }), + ); + } + }, + ); + } else { + const answer = await fetchAnswerApi( + question, + state.preference.apiKey, + state.preference.selectedDocs!, + state.conversation.queries, + ); + if (answer) { + dispatch( + updateQuery({ + index: state.conversation.queries.length - 1, + query: { response: answer.answer }, + }), + ); + dispatch(conversationSlice.actions.setStatus('idle')); + } + } } }); From 20c877f75bbb9a05eec028fe54bd051306ad1d73 Mon Sep 17 00:00:00 2001 From: Alex Date: Wed, 31 May 2023 15:42:17 +0100 Subject: [PATCH 5/7] working fe --- application/app.py | 1 + .../src/conversation/conversationSlice.ts | 104 +++++++++--------- 2 files changed, 50 insertions(+), 55 deletions(-) diff --git a/application/app.py b/application/app.py index 5e823fa..789f329 100644 --- a/application/app.py +++ b/application/app.py @@ -149,6 +149,7 @@ def stream(): #question = data["question"] # get parameter from url question question = request.args.get('question') + history = request.args.get('history') #question = "Hi" return Response(complete_stream(question), mimetype='text/event-stream') diff --git a/frontend/src/conversation/conversationSlice.ts b/frontend/src/conversation/conversationSlice.ts index ad30381..713f964 100644 --- a/frontend/src/conversation/conversationSlice.ts +++ b/frontend/src/conversation/conversationSlice.ts @@ -1,67 +1,66 @@ import { createAsyncThunk, createSlice, PayloadAction } from '@reduxjs/toolkit'; import store from '../store'; import { fetchAnswerApi, fetchAnswerSteaming } from './conversationApi'; -import { Answer, ConversationState, Query } from './conversationModels'; -import { Dispatch } from 'react'; +import { Answer, ConversationState, Query, Status } from './conversationModels'; const initialState: ConversationState = { queries: [], status: 'idle', }; -const API_STREAMING = import.meta.env.API_STREAMING || true; +const API_STREAMING = import.meta.env.API_STREAMING || false; -export const fetchAnswer = createAsyncThunk< - Answer, - { question: string }, - { dispatch: Dispatch; state: RootState } ->('fetchAnswer', async ({ question }, { dispatch, getState }) => { - const state = getState(); +export const fetchAnswer = createAsyncThunk( + 'fetchAnswer', + async ({ question }, { dispatch, getState }) => { + const state = getState() as RootState; - if (state.preference) { - if (API_STREAMING) { - fetchAnswerSteaming( - question, - state.preference.apiKey, - state.preference.selectedDocs!, - (event) => { - const data = JSON.parse(event.data); - console.log(data); + if (state.preference) { + if (API_STREAMING) { + fetchAnswerSteaming( + question, + state.preference.apiKey, + state.preference.selectedDocs!, + (event) => { + const data = JSON.parse(event.data); + console.log(data); - // check if the 'end' event has been received - if (data.type === 'end') { - // set status to 'idle' - dispatch(conversationSlice.actions.setStatus('idle')); - } else { - const result = data.answer; - dispatch( - updateStreamingQuery({ - index: state.conversation.queries.length - 1, - query: { response: result }, - }), - ); - } - }, - ); - } else { - const answer = await fetchAnswerApi( - question, - state.preference.apiKey, - state.preference.selectedDocs!, - state.conversation.queries, - ); - if (answer) { - dispatch( - updateQuery({ - index: state.conversation.queries.length - 1, - query: { response: answer.answer }, - }), + // check if the 'end' event has been received + if (data.type === 'end') { + // set status to 'idle' + dispatch(conversationSlice.actions.setStatus('idle')); + } else { + const result = data.answer; + dispatch( + updateStreamingQuery({ + index: state.conversation.queries.length - 1, + query: { response: result }, + }), + ); + } + }, ); - dispatch(conversationSlice.actions.setStatus('idle')); + } else { + const answer = await fetchAnswerApi( + question, + state.preference.apiKey, + state.preference.selectedDocs!, + state.conversation.queries, + ); + if (answer) { + dispatch( + updateQuery({ + index: state.conversation.queries.length - 1, + query: { response: answer.answer }, + }), + ); + dispatch(conversationSlice.actions.setStatus('idle')); + } } } - } -}); + return { answer: '', query: question, result: '' }; + }, +); export const conversationSlice = createSlice({ name: 'conversation', @@ -96,7 +95,7 @@ export const conversationSlice = createSlice({ ...action.payload.query, }; }, - setStatus(state, action: PayloadAction) { + setStatus(state, action: PayloadAction) { state.status = action.payload; }, }, @@ -105,11 +104,6 @@ export const conversationSlice = createSlice({ .addCase(fetchAnswer.pending, (state) => { state.status = 'loading'; }) - .addCase(fetchAnswer.fulfilled, (state, action) => { - state.status = 'idle'; - state.queries[state.queries.length - 1].response = - action.payload.answer; - }) .addCase(fetchAnswer.rejected, (state, action) => { state.status = 'failed'; state.queries[state.queries.length - 1].error = From fae3f55010d3d82ce4b18d199bf6de8f34efc8c2 Mon Sep 17 00:00:00 2001 From: Alex Date: Wed, 31 May 2023 17:44:20 +0100 Subject: [PATCH 6/7] Working streaming --- application/app.py | 76 ++++++++++++------- docker-compose.yaml | 1 + .../src/conversation/conversationSlice.ts | 7 +- 3 files changed, 50 insertions(+), 34 deletions(-) diff --git a/application/app.py b/application/app.py index 789f329..0b0bb14 100644 --- a/application/app.py +++ b/application/app.py @@ -108,6 +108,31 @@ def run_async_chain(chain, question, chat_history): result["answer"] = answer return result +def get_vectorstore(data): + if "active_docs" in data: + if data["active_docs"].split("/")[0] == "local": + if data["active_docs"].split("/")[1] == "default": + vectorstore = "" + else: + vectorstore = "indexes/" + data["active_docs"] + else: + vectorstore = "vectors/" + data["active_docs"] + if data['active_docs'] == "default": + vectorstore = "" + else: + vectorstore = "" + return vectorstore + +def get_docsearch(vectorstore, embeddings_key): + if settings.EMBEDDINGS_NAME == "openai_text-embedding-ada-002": + docsearch = FAISS.load_local(vectorstore, OpenAIEmbeddings(openai_api_key=embeddings_key)) + elif settings.EMBEDDINGS_NAME == "huggingface_sentence-transformers/all-mpnet-base-v2": + docsearch = FAISS.load_local(vectorstore, HuggingFaceHubEmbeddings()) + elif settings.EMBEDDINGS_NAME == "huggingface_hkunlp/instructor-large": + docsearch = FAISS.load_local(vectorstore, HuggingFaceInstructEmbeddings()) + elif settings.EMBEDDINGS_NAME == "cohere_medium": + docsearch = FAISS.load_local(vectorstore, CohereEmbeddings(cohere_api_key=embeddings_key)) + return docsearch @celery.task(bind=True) def ingest(self, directory, formats, name_job, filename, user): @@ -120,10 +145,9 @@ def home(): return render_template("index.html", api_key_set=api_key_set, llm_choice=settings.LLM_NAME, embeddings_choice=settings.EMBEDDINGS_NAME) -def complete_stream(question): +def complete_stream(question, docsearch, chat_history, api_key): import sys - openai.api_key = settings.API_KEY - docsearch = FAISS.load_local("", OpenAIEmbeddings(openai_api_key=settings.EMBEDDINGS_KEY)) + openai.api_key = api_key docs = docsearch.similarity_search(question, k=2) # join all page_content together with a newline docs_together = "\n".join([doc.page_content for doc in docs]) @@ -145,13 +169,28 @@ def complete_stream(question): yield f"data: {data}\n\n" @app.route("/stream", methods=['POST', 'GET']) def stream(): - #data = request.get_json() - #question = data["question"] # get parameter from url question question = request.args.get('question') history = request.args.get('history') + # check if active_docs is set + + if not api_key_set: + api_key = request.args.get("api_key") + else: + api_key = settings.API_KEY + if not embeddings_key_set: + embeddings_key = request.args.get("embeddings_key") + else: + embeddings_key = settings.EMBEDDINGS_KEY + if "active_docs" in request.args: + vectorstore = get_vectorstore({"active_docs": request.args.get("active_docs")}) + else: + vectorstore = "" + docsearch = get_docsearch(vectorstore, embeddings_key) + + #question = "Hi" - return Response(complete_stream(question), mimetype='text/event-stream') + return Response(complete_stream(question, docsearch, chat_history= history, api_key=api_key), mimetype='text/event-stream') @app.route("/api/answer", methods=["POST"]) @@ -172,31 +211,10 @@ def api_answer(): # use try and except to check for exception try: # check if the vectorstore is set - if "active_docs" in data: - if data["active_docs"].split("/")[0] == "local": - if data["active_docs"].split("/")[1] == "default": - vectorstore = "" - else: - vectorstore = "indexes/" + data["active_docs"] - else: - vectorstore = "vectors/" + data["active_docs"] - if data['active_docs'] == "default": - vectorstore = "" - else: - vectorstore = "" - print(vectorstore) - # vectorstore = "outputs/inputs/" + vectorstore = get_vectorstore(data) # loading the index and the store and the prompt template # Note if you have used other embeddings than OpenAI, you need to change the embeddings - if settings.EMBEDDINGS_NAME == "openai_text-embedding-ada-002": - docsearch = FAISS.load_local(vectorstore, OpenAIEmbeddings(openai_api_key=embeddings_key)) - elif settings.EMBEDDINGS_NAME == "huggingface_sentence-transformers/all-mpnet-base-v2": - docsearch = FAISS.load_local(vectorstore, HuggingFaceHubEmbeddings()) - elif settings.EMBEDDINGS_NAME == "huggingface_hkunlp/instructor-large": - docsearch = FAISS.load_local(vectorstore, HuggingFaceInstructEmbeddings()) - elif settings.EMBEDDINGS_NAME == "cohere_medium": - docsearch = FAISS.load_local(vectorstore, CohereEmbeddings(cohere_api_key=embeddings_key)) - + docsearch = get_docsearch(vectorstore, embeddings_key) c_prompt = PromptTemplate(input_variables=["summaries", "question"], template=template, template_format="jinja2") diff --git a/docker-compose.yaml b/docker-compose.yaml index 1052b61..584dea0 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -5,6 +5,7 @@ services: build: ./frontend environment: - VITE_API_HOST=http://localhost:5001 + - VITE_API_STREAMING=$VITE_API_STREAMING ports: - "5173:5173" depends_on: diff --git a/frontend/src/conversation/conversationSlice.ts b/frontend/src/conversation/conversationSlice.ts index 713f964..37a7b0e 100644 --- a/frontend/src/conversation/conversationSlice.ts +++ b/frontend/src/conversation/conversationSlice.ts @@ -8,22 +8,20 @@ const initialState: ConversationState = { status: 'idle', }; -const API_STREAMING = import.meta.env.API_STREAMING || false; +const API_STREAMING = import.meta.env.VITE_API_STREAMING === 'true'; export const fetchAnswer = createAsyncThunk( 'fetchAnswer', async ({ question }, { dispatch, getState }) => { const state = getState() as RootState; - if (state.preference) { if (API_STREAMING) { - fetchAnswerSteaming( + await fetchAnswerSteaming( question, state.preference.apiKey, state.preference.selectedDocs!, (event) => { const data = JSON.parse(event.data); - console.log(data); // check if the 'end' event has been received if (data.type === 'end') { @@ -74,7 +72,6 @@ export const conversationSlice = createSlice({ action: PayloadAction<{ index: number; query: Partial }>, ) { const index = action.payload.index; - console.log('updating query'); if (action.payload.query.response) { state.queries[index].response = (state.queries[index].response || '') + action.payload.query.response; From e6bccaaf4ece6eec171fbf632571555d6f08a8c9 Mon Sep 17 00:00:00 2001 From: Alex Date: Wed, 31 May 2023 22:20:47 +0100 Subject: [PATCH 7/7] Update app.py --- application/app.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/application/app.py b/application/app.py index fa92cb8..d0ecb3b 100644 --- a/application/app.py +++ b/application/app.py @@ -150,7 +150,6 @@ def home(): embeddings_choice=settings.EMBEDDINGS_NAME) def complete_stream(question, docsearch, chat_history, api_key): - import sys openai.api_key = api_key docs = docsearch.similarity_search(question, k=2) # join all page_content together with a newline @@ -194,7 +193,8 @@ def stream(): #question = "Hi" - return Response(complete_stream(question, docsearch, chat_history= history, api_key=api_key), mimetype='text/event-stream') + return Response(complete_stream(question, docsearch, + chat_history= history, api_key=api_key), mimetype='text/event-stream') @app.route("/api/answer", methods=["POST"])