From 74845aed64fa87f7819dae410c280c91022dca95 Mon Sep 17 00:00:00 2001 From: Alex Date: Thu, 18 May 2023 14:27:13 +0100 Subject: [PATCH 1/8] history init --- application/app.py | 25 +++++++++++-------- frontend/src/conversation/conversationApi.ts | 3 ++- .../src/conversation/conversationSlice.ts | 1 + 3 files changed, 18 insertions(+), 11 deletions(-) diff --git a/application/app.py b/application/app.py index d68c5b93..fa56c1d4 100644 --- a/application/app.py +++ b/application/app.py @@ -23,6 +23,7 @@ from langchain.prompts.chat import ( ChatPromptTemplate, SystemMessagePromptTemplate, HumanMessagePromptTemplate, + AIMessagePromptTemplate, ) from pymongo import MongoClient from werkzeug.utils import secure_filename @@ -107,6 +108,8 @@ def run_async_chain(chain, question, chat_history): result["answer"] = answer return result + + @celery.task(bind=True) def ingest(self, directory, formats, name_job, filename, user): @@ -164,16 +167,6 @@ def api_answer(): 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") - q_prompt = PromptTemplate(input_variables=["context", "question"], template=template_quest, template_format="jinja2") if settings.LLM_NAME == "openai_chat": @@ -182,6 +175,18 @@ def api_answer(): SystemMessagePromptTemplate.from_template(chat_combine_template), HumanMessagePromptTemplate.from_template("{question}") ] + if history: + tokens_current_history = 0 + tokens_max_history = 1000 + #count tokens in history + for i in history: + if "prompt" in i and "response" in i: + tokens_batch = llm.get_num_tokens(i["prompt"]) + llm.get_num_tokens(i["response"]) + if tokens_current_history + tokens_batch < tokens_max_history: + tokens_current_history += tokens_batch + messages_combine.append(HumanMessagePromptTemplate.from_template(i["prompt"])) + messages_combine.append(SystemMessagePromptTemplate.from_template(i["response"])) + p_chat_combine = ChatPromptTemplate.from_messages(messages_combine) elif settings.LLM_NAME == "openai": llm = OpenAI(openai_api_key=api_key, temperature=0) diff --git a/frontend/src/conversation/conversationApi.ts b/frontend/src/conversation/conversationApi.ts index c7320342..4d5bdfb7 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, + history: Array = [], ): Promise { let namePath = selectedDocs.name; if (selectedDocs.language === namePath) { @@ -37,7 +38,7 @@ export function fetchAnswerApi( question: question, api_key: apiKey, embeddings_key: apiKey, - history: localStorage.getItem('chatHistory'), + history: history, active_docs: docPath, }), }) diff --git a/frontend/src/conversation/conversationSlice.ts b/frontend/src/conversation/conversationSlice.ts index c728b9e0..a822c9bd 100644 --- a/frontend/src/conversation/conversationSlice.ts +++ b/frontend/src/conversation/conversationSlice.ts @@ -19,6 +19,7 @@ export const fetchAnswer = createAsyncThunk< question, state.preference.apiKey, state.preference.selectedDocs!, + state.conversation.queries, ); return answer; }); From bc9f1c17ed80ff22fcd34189b71d9c708d84c07f Mon Sep 17 00:00:00 2001 From: Alex Date: Thu, 18 May 2023 18:42:23 +0100 Subject: [PATCH 2/8] History Co-Authored-By: riccardofresi <89981746+riccardofresi@users.noreply.github.com> --- application/app.py | 6 +++--- docker-compose.yaml | 8 -------- 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/application/app.py b/application/app.py index fa56c1d4..013b8708 100644 --- a/application/app.py +++ b/application/app.py @@ -177,7 +177,7 @@ def api_answer(): ] if history: tokens_current_history = 0 - tokens_max_history = 1000 + tokens_max_history = 500 #count tokens in history for i in history: if "prompt" in i and "response" in i: @@ -185,7 +185,7 @@ def api_answer(): if tokens_current_history + tokens_batch < tokens_max_history: tokens_current_history += tokens_batch messages_combine.append(HumanMessagePromptTemplate.from_template(i["prompt"])) - messages_combine.append(SystemMessagePromptTemplate.from_template(i["response"])) + messages_combine.append(AIMessagePromptTemplate.from_template(i["response"])) p_chat_combine = ChatPromptTemplate.from_messages(messages_combine) elif settings.LLM_NAME == "openai": @@ -213,7 +213,7 @@ def api_answer(): result = run_async_chain(chain, question, chat_history) else: qa_chain = load_qa_chain(llm=llm, chain_type="map_reduce", - combine_prompt=c_prompt, question_prompt=q_prompt) + combine_prompt=chat_combine_template, question_prompt=q_prompt) chain = VectorDBQA(combine_documents_chain=qa_chain, vectorstore=docsearch, k=3) result = chain({"query": question}) diff --git a/docker-compose.yaml b/docker-compose.yaml index 1052b614..4a97fbf6 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -1,14 +1,6 @@ version: "3.9" services: - frontend: - build: ./frontend - environment: - - VITE_API_HOST=http://localhost:5001 - ports: - - "5173:5173" - depends_on: - - backend backend: build: ./application From ba9c50524915680620b2667dc74c6c17b640c6c6 Mon Sep 17 00:00:00 2001 From: Alex Date: Thu, 18 May 2023 18:45:15 +0100 Subject: [PATCH 3/8] accidentaly deleted frontend container --- docker-compose.yaml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/docker-compose.yaml b/docker-compose.yaml index 4a97fbf6..703ed4e7 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -1,6 +1,14 @@ version: "3.9" services: + frontend: + build: ./frontend + environment: + - VITE_API_HOST=http://localhost:5001 + ports: + - "5173:5173" + depends_on: + - backend backend: build: ./application From 6a68b6319215c56e9bcbb92dd1647f73eee80571 Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 19 May 2023 13:09:41 +0100 Subject: [PATCH 4/8] history fix --- application/app.py | 11 +++++------ application/prompts/chat_combine_prompt.txt | 1 + 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/application/app.py b/application/app.py index 013b8708..7c12721f 100644 --- a/application/app.py +++ b/application/app.py @@ -171,13 +171,10 @@ def api_answer(): template_format="jinja2") if settings.LLM_NAME == "openai_chat": llm = ChatOpenAI(openai_api_key=api_key) # optional parameter: model_name="gpt-4" - messages_combine = [ - SystemMessagePromptTemplate.from_template(chat_combine_template), - HumanMessagePromptTemplate.from_template("{question}") - ] + messages_combine = [SystemMessagePromptTemplate.from_template(chat_combine_template)] if history: tokens_current_history = 0 - tokens_max_history = 500 + tokens_max_history = 1000 #count tokens in history for i in history: if "prompt" in i and "response" in i: @@ -186,7 +183,9 @@ def api_answer(): tokens_current_history += tokens_batch messages_combine.append(HumanMessagePromptTemplate.from_template(i["prompt"])) messages_combine.append(AIMessagePromptTemplate.from_template(i["response"])) - + messages_combine.append(HumanMessagePromptTemplate.from_template("{question}")) + import sys + print(messages_combine, file=sys.stderr) p_chat_combine = ChatPromptTemplate.from_messages(messages_combine) elif settings.LLM_NAME == "openai": llm = OpenAI(openai_api_key=api_key, temperature=0) diff --git a/application/prompts/chat_combine_prompt.txt b/application/prompts/chat_combine_prompt.txt index 295a0b72..2f9a61c9 100644 --- a/application/prompts/chat_combine_prompt.txt +++ b/application/prompts/chat_combine_prompt.txt @@ -1,5 +1,6 @@ You are a DocsGPT, friendly and helpful AI assistant by Arc53 that provides help with documents. You give thorough answers with code examples if possible. Use the following pieces of context to help answer the users question. If its not relevant to the question, provide friendly responses. +You have access to chat history, and can use it to help answer the question. When using code examples, use the following format: ```(language) (code) From da5d62cc1caaa1fb46fc211d5c1a03a95797fd52 Mon Sep 17 00:00:00 2001 From: Nazih Kalo Date: Fri, 19 May 2023 10:29:18 -0700 Subject: [PATCH 5/8] updating the bulk ingest file metadata to account for parsers that output lists --- scripts/parser/file/bulk.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/scripts/parser/file/bulk.py b/scripts/parser/file/bulk.py index 8a963104..8b5bd406 100644 --- a/scripts/parser/file/bulk.py +++ b/scripts/parser/file/bulk.py @@ -1,8 +1,5 @@ """Simple reader that reads files of different formats from a directory.""" import logging -from pathlib import Path -from typing import Callable, Dict, List, Optional, Union - from parser.file.base import BaseReader from parser.file.base_parser import BaseParser from parser.file.docs_parser import DocxParser, PDFParser @@ -12,6 +9,8 @@ from parser.file.markdown_parser import MarkdownParser from parser.file.rst_parser import RstParser from parser.file.tabular_parser import PandasCSVParser from parser.schema.base import Document +from pathlib import Path +from typing import Callable, Dict, List, Optional, Union DEFAULT_FILE_EXTRACTOR: Dict[str, BaseParser] = { ".pdf": PDFParser(), @@ -151,10 +150,15 @@ class SimpleDirectoryReader(BaseReader): data = f.read() if isinstance(data, List): data_list.extend(data) + if self.file_metadata is not None: + for _ in range(len(data)): + metadata_list.append(self.file_metadata(str(input_file))) else: data_list.append(str(data)) - if self.file_metadata is not None: - metadata_list.append(self.file_metadata(str(input_file))) + if self.file_metadata is not None: + metadata_list.append(self.file_metadata(str(input_file))) + + if concatenate: return [Document("\n".join(data_list))] From 0b78480977052d88e8945ce50a714339f9f93c43 Mon Sep 17 00:00:00 2001 From: Alex Date: Thu, 25 May 2023 15:14:47 +0100 Subject: [PATCH 6/8] init --- application/app.py | 17 +++++++++++++++++ application/requirements.txt | 6 ++++-- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/application/app.py b/application/app.py index 7c12721f..a88a1bec 100644 --- a/application/app.py +++ b/application/app.py @@ -27,6 +27,7 @@ from langchain.prompts.chat import ( ) from pymongo import MongoClient from werkzeug.utils import secure_filename +from langchain.llms import GPT4All from core.settings import settings from error import bad_request @@ -195,6 +196,9 @@ def api_answer(): llm = HuggingFaceHub(repo_id="bigscience/bloom", huggingfacehub_api_token=api_key) elif settings.LLM_NAME == "cohere": llm = Cohere(model="command-xlarge-nightly", cohere_api_key=api_key) + elif settings.LLM_NAME == "gpt4all": + llm = GPT4All(model="/Users/alextu/Library/Application Support/nomic.ai/GPT4All/", + backend='gpt4all-j-v1.3-groovy') else: raise ValueError("unknown LLM model") @@ -210,6 +214,19 @@ def api_answer(): # result = chain({"question": question, "chat_history": chat_history}) # generate async with async generate method result = run_async_chain(chain, question, chat_history) + elif settings.LLM_NAME == "gpt4all": + question_generator = LLMChain(llm=llm, prompt=CONDENSE_QUESTION_PROMPT) + doc_chain = load_qa_chain(llm, chain_type="map_reduce", combine_prompt=p_chat_combine) + chain = ConversationalRetrievalChain( + retriever=docsearch.as_retriever(k=2), + question_generator=question_generator, + combine_docs_chain=doc_chain, + ) + chat_history = [] + # result = chain({"question": question, "chat_history": chat_history}) + # generate async with async generate method + result = run_async_chain(chain, question, chat_history) + else: qa_chain = load_qa_chain(llm=llm, chain_type="map_reduce", combine_prompt=chat_combine_template, question_prompt=q_prompt) diff --git a/application/requirements.txt b/application/requirements.txt index 7f737e32..af976297 100644 --- a/application/requirements.txt +++ b/application/requirements.txt @@ -26,7 +26,8 @@ ecdsa==0.18.0 entrypoints==0.4 faiss-cpu==1.7.3 filelock==3.9.0 -Flask==2.3.2 +Flask==2.2.3 +Flask-Cors==3.0.10 frozenlist==1.3.3 geojson==2.5.0 greenlet==2.0.2 @@ -39,7 +40,8 @@ Jinja2==3.1.2 jmespath==1.0.1 joblib==1.2.0 kombu==5.2.4 -langchain==0.0.126 +langchain==0.0.179 +loguru==0.6.0 lxml==4.9.2 MarkupSafe==2.1.2 marshmallow==3.19.0 From ffaa22c49bc545c9438aee9e3bd5c605a28668d3 Mon Sep 17 00:00:00 2001 From: Alex Date: Thu, 25 May 2023 16:40:11 +0100 Subject: [PATCH 7/8] reverse history order to use latest history firts Co-Authored-By: Pavel <32868631+pabik@users.noreply.github.com> --- application/app.py | 1 + 1 file changed, 1 insertion(+) diff --git a/application/app.py b/application/app.py index 7c12721f..55b6152d 100644 --- a/application/app.py +++ b/application/app.py @@ -176,6 +176,7 @@ def api_answer(): tokens_current_history = 0 tokens_max_history = 1000 #count tokens in history + history.reverse() for i in history: if "prompt" in i and "response" in i: tokens_batch = llm.get_num_tokens(i["prompt"]) + llm.get_num_tokens(i["response"]) From aaa1249a41215fa5a5400832bad9143df4175ed4 Mon Sep 17 00:00:00 2001 From: Alex Date: Thu, 25 May 2023 19:33:37 +0100 Subject: [PATCH 8/8] model fix + env var --- application/app.py | 3 +-- application/core/settings.py | 1 + application/requirements.txt | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/application/app.py b/application/app.py index a88a1bec..14916d06 100644 --- a/application/app.py +++ b/application/app.py @@ -197,8 +197,7 @@ def api_answer(): elif settings.LLM_NAME == "cohere": llm = Cohere(model="command-xlarge-nightly", cohere_api_key=api_key) elif settings.LLM_NAME == "gpt4all": - llm = GPT4All(model="/Users/alextu/Library/Application Support/nomic.ai/GPT4All/", - backend='gpt4all-j-v1.3-groovy') + llm = GPT4All(model=settings.MODEL_PATH) else: raise ValueError("unknown LLM model") diff --git a/application/core/settings.py b/application/core/settings.py index fa654ed5..3c0672da 100644 --- a/application/core/settings.py +++ b/application/core/settings.py @@ -9,6 +9,7 @@ class Settings(BaseSettings): CELERY_BROKER_URL: str = "redis://localhost:6379/0" CELERY_RESULT_BACKEND: str = "redis://localhost:6379/1" MONGO_URI: str = "mongodb://localhost:27017/docsgpt" + MODEL_PATH: str = "./models/gpt4all-model.bin" API_URL: str = "http://localhost:5001" # backend url for celery worker diff --git a/application/requirements.txt b/application/requirements.txt index af976297..4d9c1b0d 100644 --- a/application/requirements.txt +++ b/application/requirements.txt @@ -31,6 +31,7 @@ Flask-Cors==3.0.10 frozenlist==1.3.3 geojson==2.5.0 greenlet==2.0.2 +gpt4all==0.1.7 hub==3.0.1 huggingface-hub==0.12.1 humbug==0.2.8