import requests import os, sys, json import gradio as gr import time import re import io #from PIL import Image, ImageDraw, ImageOps, ImageFont #import base64 import tempfile import asyncio from concurrent.futures import ThreadPoolExecutor from PyPDF2 import PdfReader, PdfWriter #from langchain.chains import LLMChain, RetrievalQA from langchain_community.document_loaders import PyPDFLoader, UnstructuredWordDocumentLoader, DirectoryLoader from langchain_community.document_loaders.blob_loaders.youtube_audio import YoutubeAudioLoader #from langchain.document_loaders import GenericLoader #from langchain.schema import AIMessage, HumanMessage #from langchain_community.llms import HuggingFaceHub from langchain_huggingface import HuggingFaceEndpoint #from langchain_community.llms import HuggingFaceEndPoints from langchain_huggingface import HuggingFaceEmbeddings #from langchain_community.llms import HuggingFaceTextGenInference #from langchain_community.embeddings import HuggingFaceInstructEmbeddings, HuggingFaceEmbeddings, HuggingFaceBgeEmbeddings, HuggingFaceInferenceAPIEmbeddings #from langchain.prompts import PromptTemplate #from langchain.chains import Runnable................................. from langchain.text_splitter import RecursiveCharacterTextSplitter from langchain_community.vectorstores import Chroma from chromadb.errors import InvalidDimensionException from langchain_community.llms.huggingface_pipeline import HuggingFacePipeline from transformers import pipeline from huggingface_hub import InferenceApi from utils import * from beschreibungen import * #Konstanten #Validieren des PW ANTI_BOT_PW = os.getenv("VALIDATE_PW") ############################### #HF Authentifizierung HUGGINGFACEHUB_API_TOKEN = os.getenv("HF_READ") os.environ["HUGGINGFACEHUB_API_TOKEN"] = HUGGINGFACEHUB_API_TOKEN HEADERS = {"Authorization": f"Bearer {HUGGINGFACEHUB_API_TOKEN}"} # Hugging Face Token direkt im Code setzen hf_token = os.getenv("HF_READ") #max Anzahl der zurückgelieferten Dokumente ANZAHL_DOCS = 5 PATH_WORK = "." CHROMA_DIR = "chroma/demo" CHROMA_DIR_PDF = 'chroma/demo/pdf' CHROMA_DIR_WORD = 'chroma/demo/word' #CHROMA_EXCEL = './chroma/demo/excel' #DOCS_DIR = "chroma/demo" ########################################### # Alternativen, um HF Modelle in der rAG Chain einzusetzen ########################################### ####################################### #1. Alternative: HuggingFace Model name-------------------------------- #MODEL_NAME_HF = "HuggingFaceH4/zephyr-7b-alpha" #"t5-small" #"meta-llama/Meta-Llama-3-8B-Instruct" #"mistralai/Mistral-7B-Instruct-v0.3" #"microsoft/Phi-3-mini-4k-instruct" #"HuggingFaceH4/zephyr-7b-alpha" ############################################ #2. Alternative_ HuggingFace Reop ID-------------------------------- #repo_id = "meta-llama/Llama-2-13b-chat-hf" #repo_id = "HuggingFaceH4/zephyr-7b-alpha" #das Modell ist echt gut!!! Vom MIT #repo_id = "TheBloke/Yi-34B-Chat-GGUF" #repo_id = "meta-llama/Llama-2-70b-chat-hf" #repo_id = "tiiuae/falcon-40b" #repo_id = "Vicuna-33b" #repo_id = "alexkueck/ChatBotLI2Klein" #repo_id = "mistralai/Mistral-7B-v0.1" #repo_id = "internlm/internlm-chat-7b" #repo_id = "Qwen/Qwen-7B" #repo_id = "Salesforce/xgen-7b-8k-base" #repo_id = "Writer/camel-5b-hf" #repo_id = "databricks/dolly-v2-3b" #repo_id = "google/flan-t5-xxl" #repo_id = "mistralai/Mixtral-8x7B-Instruct-v0.1" #repo_id = "abacusai/Smaug-72B-v0.1" ########################################### #3. Alternative: HF API - URL #API_URL = "https://api-inference.huggingface.co/models/Falconsai/text_summarization" ############################################### #globale Variablen ############################################## #Filepath zu temp Folder (temp) mit File von ausgewähltem chatverlauf file_path_download = "" ################################################ # Erstellen des Vektorstores ################################################ def create_vectorstore(): global vektordatenbank, SPLIT_TO_ORIGINAL_MAPPING, ORIGINAL_SPLITS, PREPROCESSED_SPLITS # Splits zu allen Dokumenten in den Verzeichnissen erstellen PREPROCESSED_SPLITS, SPLIT_TO_ORIGINAL_MAPPING, ORIGINAL_SPLITS = document_loading_splitting() if PREPROCESSED_SPLITS: print("Vektordatenbank neu .....................") # Vektordatenbank zu den Splits erstellen vektordatenbank = document_storage_chroma(PREPROCESSED_SPLITS) # Speichern der Splits und Metadaten save_splits(PREPROCESSED_SPLITS, ORIGINAL_SPLITS) save_split_to_original_mapping(SPLIT_TO_ORIGINAL_MAPPING) #falls Vektorstore vorhanden: neu laden!!!!!!!!!!!!!!!!! def load_vectorstore_and_mapping(): global vektordatenbank, SPLIT_TO_ORIGINAL_MAPPING, ORIGINAL_SPLITS, PREPROCESSED_SPLITS preprocessed_splits, original_splits = load_splits() mapping = load_split_to_original_mapping() if preprocessed_splits is not None and original_splits is not None and mapping is not None: # Vektordatenbank zu den Splits erstellen vektordatenbank = document_storage_chroma(preprocessed_splits) SPLIT_TO_ORIGINAL_MAPPING = mapping ORIGINAL_SPLITS = original_splits PREPROCESSED_SPLITS = preprocessed_splits else: #fehler beim laden -> Vektorstore neu zusammensetzen create_vectorstore() ########################################### # Beim Start der Anwendung - Vektorstore laden!!!! ########################################### print("Vektorstore laden.........................") #die Variablen: vektordatenbank, PREPROCESSED_SPLITS, ORGINAL_SPLITS und das Mapping werden neu gesetzt global!!!! load_vectorstore_and_mapping() ################################################# ################################################# #Funktionen zur Verarbeitung ################################################ ############################################## #wenn löschen Button geklickt def clear_all(history, uploaded_file_paths, chats): dic_history = {schluessel: wert for schluessel, wert in history} #später wird die summary auf 50 tokens verkürzt, um die Anfrage nicht so teuer werden zu lassen #summary wird gebraucht für die Anfrage beim NN, um eine Überschrift des Eintrages zu generieren summary = "\n\n".join(f'{schluessel}: \n {wert}' for schluessel, wert in dic_history.items()) #falls file mit summay für download existiert hat: das zunächst löschen #cleanup(file_path_download) #noch nicht im Einsatz, aber hier werden alle Chats einer Sitzung gespeichert #den aktuellen Chatverlauf zum Download bereitstellen: if chats != {} : id_neu = len(chats)+1 chats[id_neu]= summary else: chats[0]= summary #Eine Überschrift zu dem jeweiligen Chatverlauf finden - abhängig vom Inhalt #file_path_download = save_and_download(summary) #headers, payload = process_chatverlauf(summary, MODEL_NAME, OAI_API_KEY) #response = requests.post("https://api.openai.com/v1/chat/completions", headers=headers, json=payload) #als json ausgeben #data = response.json() # Den "content" auswählen, da dort die Antwort der Ki enthalten ist #result = data['choices'][0]['message']['content'] #worte = result.split() #if len(worte) > 2: #file_path_download = "data/" + str(len(chats)) + "_Chatverlauf.pdf" #else: #file_path_download = "data/" + str(len(chats)) + "_" + result + ".pdf" #erstellePdf(file_path_download, result, dic_history) #die session variable in gradio erweitern und alle fliepath neu in das gr.File hochladen #uploaded_file_paths= uploaded_file_paths + [file_path_download] return None, gr.Image(visible=False), uploaded_file_paths, [], gr.File(uploaded_file_paths, label="Download-Chatverläufe", visible=True, file_count="multiple", interactive = False), chats #wenn löschen Button geklickt def clear_all3(history): #die session variable in gradio erweitern und alle fliepath neu in das gr.File hochladen uploaded_file_paths= "" return None, gr.Image(visible=False), [], ############################################## #History - die Frage oder das File eintragen... #in history_file ist ein file gespeichert, falls voher im Verlauf schon ein File hochgeladen wurde. #wird ein neuer File hochgeladen, so wird history_fiel dadurch ersetzt def add_text(chatbot, history, prompt, file, file_history): if (file == None): chatbot = chatbot +[(prompt, None)] else: file_history = file if (prompt == ""): chatbot=chatbot + [((file.name,), "Prompt fehlt!")] else: chatbot = chatbot +[("Hochgeladenes Dokument: "+ get_filename(file) +"\n" + prompt, None)] return chatbot, history, prompt, file, file_history, gr.Image(visible = False), "" ############################################ #nach dem Upload soll das zusätzliche Fenster mit dem image drinnen angezeigt werden def file_anzeigen(file): ext = analyze_file(file) if (ext == "png" or ext == "PNG" or ext == "jpg" or ext == "jpeg" or ext == "JPG" or ext == "JPEG"): return gr.Image(width=47, visible=True, interactive = False, height=47, min_width=47, show_label=False, show_share_button=False, show_download_button=False, scale = 0.5), file, file else: return gr.Image(width=47, visible=True, interactive = False, height=47, min_width=47, show_label=False, show_share_button=False, show_download_button=False, scale = 0.5), "data/file.png", file def file_loeschen(): return None, gr.Image(visible = False) ############################################ #wenn 'Stop' Button geklickt, dann Message dazu und das Eingabe-Fenster leeren def cancel_outputing(): reset_textbox() return "Stop Done" def reset_textbox(): return gr.update(value=""),"" #################################################### #aus einem Text-Prompt die Antwort von KI bekommen def generate_text (prompt, chatbot, history, retriever, top_p=0.6, temperature=0.2, max_new_tokens=4048, max_context_length_tokens=2048, repetition_penalty=1.3, top_k=35): if (prompt == ""): raise gr.Error("Prompt ist erforderlich.") try: ######################################### #Prompt mit History Daten zusammenstellen: #Prompt an history anhängen und einen Text daraus machen #history_text_und_prompt = generate_prompt_with_history(prompt, history) #oder an Hugging Face -------------------------- print("HF Anfrage.......................") #zusätzliche Dokumenten Splits aus DB zum Prompt hinzufügen (aus VektorDB - Chroma oder Mongo DB) ############################################## #Verschiedene Alternativen als llm übergeben an die rag-chain ############################################# #0. Alternative - repo ID # Verwenden Sie die Inference Api von huggingface_hub #llm = InferenceApi(repo_id, token=hf_token) #result = rag_chain(llm, history_text_und_prompt, retriever) ############################################## #1.Alternative mit Inference API ung HF EndPoint # Erstelle eine HuggingFaceEndPoints-Instanz mit den entsprechenden Endpunkt-Parametern """ llm = HuggingFaceEndpoint( endpoint_url=f"https://api-inference.huggingface.co/models/{MODEL_NAME_HF}", api_key=hf_token, temperature=0.5, max_length=1024, top_k=top_k, top_p=top_p, repetition_penalty=repetition_penalty ) result = rag_chain(llm, history_text_und_prompt, retriever) ############################################# #2. Alternative: mit API_URL #result = rag_chain(API_URL, history_text_und_prompt, retriever) ############################################# #3.te Alternative für pipeline # Erstelle eine Pipeline mit den gewünschten Parametern #llm = pipeline("text-generation", model=MODEL_NAME_HF, config={"temperature": 0.5, "max_length": 1024, "num_return_sequences": 1, "top_k": top_k, "top_p": top_p, "repetition_penalty": repetition_penalty}, trust_remote_code=True) #llm = pipeline("summarization", model=MODEL_NAME_HF, trust_remote_code=True) #result = rag_chain(llm, history_text_und_prompt, retriever) """ result = rag_chain_simpel(prompt, retriever) except Exception as e: raise gr.Error(e) return result, False ############################################################## #Eingaben der GUI verarbeiten def generate_auswahl(prompt_in, file, file_history, chatbot, history, anzahl_docs=4, top_p=0.6, temperature=0.5, max_new_tokens=4048, max_context_length_tokens=2048, repetition_penalty=1.3,top_k=5, validate=False): global vektordatenbank, SPLIT_TO_ORIGINAL_MAPPING #nur wenn man sich validiert hat, kann die Anwendung los legen if (validate and not prompt_in == "" and not prompt_in == None): # Vektorstore initialisieren #falls schon ein File hochgeladen wurde, ist es in history_file gespeichert - falls ein neues File hochgeladen wurde, wird es anschließend neu gesetzt neu_file = file_history #prompt normalisieren bevor er an die KIs geht prompt = preprocess_text(prompt_in) if vektordatenbank is None: print("db neu aufbauen!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!1") #Splits zu allen Dokumenten in den Verzeichnissen erstellen #vektordatenbank, SPLIT_TO_ORIGINAL_MAPPING werden aktualisiert create_vectorstore() if vektordatenbank: #Retriever erstellen, um die relevanten Slpits zu einem Prompt zu suchen.... (retrieven) retriever = vektordatenbank.as_retriever(search_kwargs = {"k": ANZAHL_DOCS}) #kein Bild hochgeladen -> auf Text antworten... status = "Antwort der Vektordatenbank" results, status = generate_text(prompt, chatbot, history, retriever, top_p=0.6, temperature=0.5, max_new_tokens=4048, max_context_length_tokens=2048, repetition_penalty=1.3, top_k=3) # Überprüfen, ob relevante Dokumente gefunden wurden if results['relevant_docs']: # in results sind die preprocessed Splits enthalten, dargestellt werden sollen die originalen: relevant_docs_org = [] for result in results['relevant_docs']: split_id = result.get("metadata", {}).get("split_id") if split_id: try: original_split = SPLIT_TO_ORIGINAL_MAPPING[split_id] relevant_docs_org.append(original_split) except Exception as e: print(f"Fehler beim Laden des Mappings...................: {str(e)}") else: # Keine relevanten Dokumente gefunden status = "Keine relevanten Dokumente gefunden." relevant_docs_org = [] relevant_docs = extract_document_info(relevant_docs_org) #Ergebnisse für history und chatbot zusammenstellen summary = str(results['answer']) + "\n\n" summary += " ".join([ '