import gradio as gr from openai import OpenAI import tiktoken from os import getenv as os_getenv from json import loads as json_loads from pathlib import Path import fitz MODEL = 'gpt-4-turbo' PRICE_PER_M = 10.00 LIMIT = 125000 # some space for answer api_key = os_getenv("OPENAI_API_KEY") client = OpenAI(api_key=api_key) def new_state(): return gr.State({ "prompt": "", }) def get_prompt(books, question = None): prompt = ( f"Read the following books.\n" + f"Each book may have some pages at the beggining with data about the book, an index, or table of content, etc. " + f"Pages may have a header and/or a footer. Consider all this maybe present." + f"For each book, please answer, all below in the suggested format and also answer all the questions at the end in detail, if present.\n" f"Answer in the language of the book:\n"+ f"**Title**: ...\n" f"**Author**: ...\n" f"**Chapter Names**: ...\n" f"**Characters**: \n" f"**Detailed Summary of the whole book**: \n" ) prompt += f"{books}\n" return prompt def chat(message, history, files, state): history_openai_format = [] prompt = state["prompt"] if not message: if len(history) > 0: gr.Error("You sent an empty question. It's expensive, don't do it") return '' if not prompt: gr.Error("First upload a book") return '' if (not history): if message: prompt += f"**Questions**:{message}" state["prompt"] = prompt message = prompt for human, assistant in history: if not history_openai_format: history_openai_format.append({"role": "user", "content": prompt}) elif human: history_openai_format.append({"role": "user", "content": human }) if assistant: history_openai_format.append({"role": "assistant", "content":assistant}) if message: history_openai_format.append({"role": "user", "content": message}) # return f"hola {len(history)}" # # def no(): response = client.chat.completions.create( model=MODEL, messages= history_openai_format, temperature=1.0, stream=True) partial_message = "" for chunk in response: if chunk.choices[0].delta.content is not None: partial_message = partial_message + chunk.choices[0].delta.content yield partial_message def get_text(filename): answer = "" suffix = Path(filename).suffix if suffix in [".pdf"]: for i,page in enumerate(fitz.open(filename)): answer += f"\n### Page #{i+1}\n{page.get_text()}\n" elif suffix in [".txt"]: answer = open(filename).read() return answer def files_ready(filenames, state): encoder = encoding = tiktoken.encoding_for_model('gpt-4-turbo') books = '' for i, name in enumerate(filenames): books += f"\n## Document #{i+1}\nName: {Path(name).name}\n" books += get_text(name) prompt = get_prompt(books) tokens = len(encoder.encode(prompt)) cost = tokens * PRICE_PER_M / 1000000 * 2 # * 2 is too much for an answer state["prompt"] = prompt if tokens > LIMIT: raise gr.Error(f"Book is too long. It's {tokens} tokens long and can't be more than {LIMIT}.") return len(prompt), tokens, f"${cost}", state def files_changed(filenames, state): if filenames: return "-", "-", "-", state else: return 0, 0, "$0", new_state() with gr.Blocks(title="Book summarization and more") as demo: state = new_state() with gr.Row(): files = gr.Files(file_types=["txt","doc","docx","pdf"] ) with gr.Column(): letters = gr.Text("0", label="Letters (with spaces)") tokens = gr.Text("0", label="Tokens") cost = gr.Text("0", label="Cost") chat = gr.ChatInterface( fn=chat, title="Summarization and more", additional_inputs=[files, state], multimodal=False) other = gr.Button(interactive=False) files.upload(files_ready, [files, state], [letters, tokens, cost, state]) files.change(files_changed, [files, state], [letters, tokens, cost, state]) auth=os_getenv("APP_USERS", "null") auth=json_loads(auth) demo.launch(auth=auth)