Update utils.py
Browse files
utils.py
CHANGED
@@ -30,7 +30,219 @@ logging.basicConfig(
|
|
30 |
format="%(asctime)s [%(levelname)s] [%(filename)s:%(lineno)d] %(message)s",
|
31 |
)
|
32 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
33 |
|
|
|
|
|
|
|
|
|
|
|
34 |
def markdown_to_html_with_syntax_highlight(md_str):
|
35 |
def replacer(match):
|
36 |
lang = match.group(1) or "text"
|
@@ -218,10 +430,6 @@ shared_state = State()
|
|
218 |
|
219 |
|
220 |
|
221 |
-
|
222 |
-
|
223 |
-
|
224 |
-
|
225 |
def is_stop_word_or_prefix(s: str, stop_words: list) -> bool:
|
226 |
for stop_word in stop_words:
|
227 |
if s.endswith(stop_word):
|
|
|
30 |
format="%(asctime)s [%(levelname)s] [%(filename)s:%(lineno)d] %(message)s",
|
31 |
)
|
32 |
|
33 |
+
##################################################
|
34 |
+
#RAG Hilfsfunktionen - Dokumenten bearbeiten für Vektorstore
|
35 |
+
##################################################
|
36 |
+
##################################################
|
37 |
+
# Funktion, um für einen best. File-typ ein directory-loader zu definieren
|
38 |
+
def create_directory_loader(file_type, directory_path):
|
39 |
+
#verscheidene Dokument loaders:
|
40 |
+
loaders = {
|
41 |
+
'.pdf': PyPDFLoader,
|
42 |
+
'.word': UnstructuredWordDocumentLoader,
|
43 |
+
}
|
44 |
+
return DirectoryLoader(
|
45 |
+
path=directory_path,
|
46 |
+
glob=f"**/*{file_type}",
|
47 |
+
loader_cls=loaders[file_type],
|
48 |
+
)
|
49 |
+
################################################
|
50 |
+
#die Inhalte splitten, um in Vektordatenbank entsprechend zu laden als Splits
|
51 |
+
def document_loading_splitting():
|
52 |
+
global splittet
|
53 |
+
##############################
|
54 |
+
# Document loading
|
55 |
+
docs = []
|
56 |
+
|
57 |
+
# kreiere einen DirectoryLoader für jeden file type
|
58 |
+
pdf_loader = create_directory_loader('.pdf', './chroma/pdf')
|
59 |
+
word_loader = create_directory_loader('.word', './chroma/word')
|
60 |
+
|
61 |
+
|
62 |
+
# Load the files
|
63 |
+
pdf_documents = pdf_loader.load()
|
64 |
+
word_documents = word_loader.load()
|
65 |
+
|
66 |
+
#alle zusammen in docs...
|
67 |
+
docs.extend(pdf_documents)
|
68 |
+
docs.extend(word_documents)
|
69 |
+
|
70 |
+
#andere loader...
|
71 |
+
# Load PDF
|
72 |
+
loader = PyPDFLoader(PDF_URL)
|
73 |
+
docs.extend(loader.load())
|
74 |
+
# Load Web
|
75 |
+
loader = WebBaseLoader(WEB_URL)
|
76 |
+
docs.extend(loader.load())
|
77 |
+
# Load YouTube
|
78 |
+
loader = GenericLoader(YoutubeAudioLoader([YOUTUBE_URL_1,YOUTUBE_URL_2], PATH_WORK + YOUTUBE_DIR), OpenAIWhisperParser())
|
79 |
+
docs.extend(loader.load())
|
80 |
+
################################
|
81 |
+
# Document splitting
|
82 |
+
text_splitter = RecursiveCharacterTextSplitter(chunk_overlap = 150, chunk_size = 1500)
|
83 |
+
splits = text_splitter.split_documents(docs)
|
84 |
+
|
85 |
+
#nur bei erster Anfrage mit "choma" wird gesplittet...
|
86 |
+
splittet = True
|
87 |
+
return splits
|
88 |
+
|
89 |
+
###########################################
|
90 |
+
#Chroma DB die splits ablegen - vektorisiert...
|
91 |
+
def document_storage_chroma(splits):
|
92 |
+
#OpenAi embeddings----------------------------------
|
93 |
+
Chroma.from_documents(documents = splits, embedding = OpenAIEmbeddings(disallowed_special = ()), persist_directory = PATH_WORK + CHROMA_DIR)
|
94 |
+
|
95 |
+
#HF embeddings--------------------------------------
|
96 |
+
#Chroma.from_documents(documents = splits, embedding = HuggingFaceEmbeddings(model_name="sentence-transformers/all-mpnet-base-v2", model_kwargs={"device": "cpu"}, encode_kwargs={'normalize_embeddings': False}), persist_directory = PATH_WORK + CHROMA_DIR)
|
97 |
+
|
98 |
+
#Mongo DB die splits ablegen - vektorisiert...
|
99 |
+
def document_storage_mongodb(splits):
|
100 |
+
MongoDBAtlasVectorSearch.from_documents(documents = splits,
|
101 |
+
embedding = OpenAIEmbeddings(disallowed_special = ()),
|
102 |
+
collection = MONGODB_COLLECTION,
|
103 |
+
index_name = MONGODB_INDEX_NAME)
|
104 |
+
############################################
|
105 |
+
#dokumente in chroma db vektorisiert ablegen können - die Db vorbereiten daüfur
|
106 |
+
def document_retrieval_chroma(llm, prompt):
|
107 |
+
#OpenAI embeddings -------------------------------
|
108 |
+
embeddings = OpenAIEmbeddings()
|
109 |
+
|
110 |
+
#HF embeddings -----------------------------------
|
111 |
+
#Alternative Embedding - für Vektorstore, um Ähnlichkeitsvektoren zu erzeugen - die ...InstructEmbedding ist sehr rechenaufwendig
|
112 |
+
#embeddings = HuggingFaceInstructEmbeddings(model_name="sentence-transformers/all-MiniLM-L6-v2", model_kwargs={"device": "cpu"})
|
113 |
+
#etwas weniger rechenaufwendig:
|
114 |
+
#embeddings = HuggingFaceEmbeddings(model_name="sentence-transformers/all-mpnet-base-v2", model_kwargs={"device": "cpu"}, encode_kwargs={'normalize_embeddings': False})
|
115 |
+
|
116 |
+
#ChromaDb um die embedings zu speichern
|
117 |
+
db = Chroma(embedding_function = embeddings, persist_directory = PATH_WORK + CHROMA_DIR)
|
118 |
+
return db
|
119 |
+
|
120 |
+
############################################
|
121 |
+
#dokumente in chroma db vektorisiert ablegen können - die Db vorbereiten daüfur
|
122 |
+
#zweite Variante, passend zu rag_chain2 für generate_text_mit_bild- ohne llm vorher festlegen zu müssen
|
123 |
+
def document_retrieval_chroma2():
|
124 |
+
#OpenAI embeddings -------------------------------
|
125 |
+
embeddings = OpenAIEmbeddings()
|
126 |
+
|
127 |
+
#HF embeddings -----------------------------------
|
128 |
+
#Alternative Embedding - für Vektorstore, um Ähnlichkeitsvektoren zu erzeugen - die ...InstructEmbedding ist sehr rechenaufwendig
|
129 |
+
#embeddings = HuggingFaceInstructEmbeddings(model_name="sentence-transformers/all-MiniLM-L6-v2", model_kwargs={"device": "cpu"})
|
130 |
+
#etwas weniger rechenaufwendig:
|
131 |
+
#embeddings = HuggingFaceEmbeddings(model_name="sentence-transformers/all-mpnet-base-v2", model_kwargs={"device": "cpu"}, encode_kwargs={'normalize_embeddings': False})
|
132 |
+
#oder einfach ohne Langchain:
|
133 |
+
#embeddings = SentenceTransformerEmbeddings(model_name="all-MiniLM-L6-v2")
|
134 |
+
|
135 |
+
#ChromaDb um die embedings zu speichern
|
136 |
+
db = Chroma(embedding_function = embeddings, persist_directory = PATH_WORK + CHROMA_DIR)
|
137 |
+
print ("Chroma DB bereit ...................")
|
138 |
+
|
139 |
+
return db
|
140 |
+
|
141 |
+
###########################################
|
142 |
+
#dokumente in mongo db vektorisiert ablegen können - die Db vorbereiten daüfür
|
143 |
+
def document_retrieval_mongodb(llm, prompt):
|
144 |
+
db = MongoDBAtlasVectorSearch.from_connection_string(MONGODB_URI,
|
145 |
+
MONGODB_DB_NAME + "." + MONGODB_COLLECTION_NAME,
|
146 |
+
OpenAIEmbeddings(disallowed_special = ()),
|
147 |
+
index_name = MONGODB_INDEX_NAME)
|
148 |
+
return db
|
149 |
+
|
150 |
+
###############################################
|
151 |
+
#Langchain anlegen
|
152 |
+
###############################################
|
153 |
+
#langchain nutzen, um prompt an LLM zu leiten - llm und prompt sind austauschbar
|
154 |
+
def llm_chain(llm, prompt):
|
155 |
+
llm_chain = LLMChain(llm = llm, prompt = LLM_CHAIN_PROMPT)
|
156 |
+
result = llm_chain.run({"question": prompt})
|
157 |
+
return result
|
158 |
+
|
159 |
+
#############################################
|
160 |
+
#langchain nutzen, um prompt an llm zu leiten, aber vorher in der VektorDB suchen, um passende splits zum Prompt hinzuzufügen
|
161 |
+
def rag_chain(llm, prompt, db):
|
162 |
+
rag_chain = RetrievalQA.from_chain_type(llm,
|
163 |
+
chain_type_kwargs = {"prompt": RAG_CHAIN_PROMPT},
|
164 |
+
retriever = db.as_retriever(search_kwargs = {"k": 3}),
|
165 |
+
return_source_documents = True)
|
166 |
+
result = rag_chain({"query": prompt})
|
167 |
+
return result["result"]
|
168 |
+
|
169 |
+
############################################
|
170 |
+
# rag_chain Alternative für RAg mit Bild-Upload, da hier das llm so nicht genutzt werden kann und der prompt mit den RAG Erweiterungen anders übergeben wird
|
171 |
+
#langchain nutzen, um prompt an llm zu leiten, aber vorher in der VektorDB suchen, um passende splits zum Prompt hinzuzufügen
|
172 |
+
#prompt mit RAG!!!
|
173 |
+
def rag_chain2(prompt, db, k=3):
|
174 |
+
rag_template = "Nutze die folgenden Kontext Teile am Ende, um die Frage zu beantworten . " + template + "Frage: " + prompt + "Kontext Teile: "
|
175 |
+
retrieved_chunks = db.similarity_search(prompt, k)
|
176 |
+
|
177 |
+
neu_prompt = rag_template
|
178 |
+
for i, chunk in enumerate(retrieved_chunks):
|
179 |
+
neu_prompt += f"{i+1}. {chunk}\n"
|
180 |
+
|
181 |
+
return neu_prompt
|
182 |
+
|
183 |
+
###################################################
|
184 |
+
#Prompts mit History erzeugen für verschiednee Modelle
|
185 |
+
###################################################
|
186 |
+
#Funktion, die einen Prompt mit der history zusammen erzeugt - allgemein
|
187 |
+
def generate_prompt_with_history(text, history, max_length=4048):
|
188 |
+
#prompt = "The following is a conversation between a human and an AI assistant named Baize (named after a mythical creature in Chinese folklore). Baize is an open-source AI assistant developed by UCSD and Sun Yat-Sen University. The human and the AI assistant take turns chatting. Human statements start with [|Human|] and AI assistant statements start with [|AI|]. The AI assistant always provides responses in as much detail as possible, and in Markdown format. The AI assistant always declines to engage with topics, questions and instructions related to unethical, controversial, or sensitive issues. Complete the transcript in exactly that format.\n[|Human|]Hello!\n[|AI|]Hi!"
|
189 |
+
#prompt = "Das folgende ist eine Unterhaltung in deutsch zwischen einem Menschen und einem KI-Assistenten, der Baize genannt wird. Baize ist ein open-source KI-Assistent, der von UCSD entwickelt wurde. Der Mensch und der KI-Assistent chatten abwechselnd miteinander in deutsch. Die Antworten des KI Assistenten sind immer so ausführlich wie möglich und in Markdown Schreibweise und in deutscher Sprache. Wenn nötig übersetzt er sie ins Deutsche. Die Antworten des KI-Assistenten vermeiden Themen und Antworten zu unethischen, kontroversen oder sensiblen Themen. Die Antworten sind immer sehr höflich formuliert..\n[|Human|]Hallo!\n[|AI|]Hi!"
|
190 |
+
prompt=""
|
191 |
+
history = ["\n{}\n{}".format(x[0],x[1]) for x in history]
|
192 |
+
history.append("\n{}\n".format(text))
|
193 |
+
history_text = ""
|
194 |
+
flag = False
|
195 |
+
for x in history[::-1]:
|
196 |
+
history_text = x + history_text
|
197 |
+
flag = True
|
198 |
+
print ("Prompt: ..........................")
|
199 |
+
print(prompt+history_text)
|
200 |
+
if flag:
|
201 |
+
return prompt+history_text
|
202 |
+
else:
|
203 |
+
return None
|
204 |
+
|
205 |
+
##############################################
|
206 |
+
#Prompt und History für OPenAi Schnittstelle
|
207 |
+
def generate_prompt_with_history_openai(prompt, history):
|
208 |
+
history_openai_format = []
|
209 |
+
for human, assistant in history:
|
210 |
+
history_openai_format.append({"role": "user", "content": human })
|
211 |
+
history_openai_format.append({"role": "assistant", "content":assistant})
|
212 |
+
|
213 |
+
history_openai_format.append({"role": "user", "content": prompt})
|
214 |
+
print("openai history und prompt................")
|
215 |
+
print(history_openai_format)
|
216 |
+
return history_openai_format
|
217 |
+
|
218 |
+
#############################################
|
219 |
+
#Prompt und History für Hugging Face Schnittstelle
|
220 |
+
def generate_prompt_with_history_hf(prompt, history):
|
221 |
+
history_transformer_format = history + [[prompt, ""]]
|
222 |
+
#stop = StopOnTokens()
|
223 |
+
|
224 |
+
messages = "".join(["".join(["\n<human>:"+item[0], "\n<bot>:"+item[1]]) #curr_system_message +
|
225 |
+
for item in history_transformer_format])
|
226 |
+
|
227 |
+
##############################################
|
228 |
+
#Prompt und History für Langchain Schnittstelle
|
229 |
+
def generate_prompt_with_history_langchain(prompt, history):
|
230 |
+
history_langchain_format = []
|
231 |
+
for human, ai in history:
|
232 |
+
history_langchain_format.append(HumanMessage(content=human))
|
233 |
+
history_langchain_format.append(AIMessage(content=ai))
|
234 |
+
history_langchain_format.append(HumanMessage(content=prompt))
|
235 |
+
|
236 |
+
return history_langchain_format
|
237 |
+
|
238 |
+
|
239 |
+
|
240 |
|
241 |
+
|
242 |
+
|
243 |
+
########################################################
|
244 |
+
#Ausgabe im Chatbot aufhübschen...
|
245 |
+
########################################################
|
246 |
def markdown_to_html_with_syntax_highlight(md_str):
|
247 |
def replacer(match):
|
248 |
lang = match.group(1) or "text"
|
|
|
430 |
|
431 |
|
432 |
|
|
|
|
|
|
|
|
|
433 |
def is_stop_word_or_prefix(s: str, stop_words: list) -> bool:
|
434 |
for stop_word in stop_words:
|
435 |
if s.endswith(stop_word):
|