Spaces:
Paused
Paused
File size: 6,921 Bytes
8f07f94 a08d304 a546f7a f62f25d 3b468ed 8f2cddf 3b468ed 8f2cddf 3b468ed 50356ed 8f07f94 8f2cddf 3b468ed 8f07f94 50356ed 8f2cddf 50356ed f62f25d 50356ed f62f25d 50356ed 3c8c9ec 50356ed f62f25d 50356ed f62f25d 50356ed b4f4511 50356ed 8f2cddf 8cf9ff5 50356ed 8cf9ff5 8f2cddf 8cf9ff5 8f2cddf 8cf9ff5 8f2cddf 3c8c9ec 50356ed 8f2cddf 50356ed 8f2cddf 8f07f94 f62f25d 50356ed 8f07f94 50356ed f62f25d 8f07f94 a294dd2 f62f25d a294dd2 50356ed f62f25d 50356ed fc1ab54 50356ed 8f07f94 50356ed 8f07f94 552deeb fc1ab54 50356ed 552deeb f62f25d 552deeb 50356ed f62f25d 50356ed 552deeb 50356ed fc1ab54 50356ed b1805d6 8f07f94 50356ed 8f07f94 3b468ed 50356ed 7fabd5f 50356ed 7fabd5f 3b468ed 7fabd5f 50356ed f62f25d 50356ed 7fabd5f 50356ed 7fabd5f d4f700d 50356ed | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 | import os
import tempfile
import logging
from typing import List, Dict
from langchain.embeddings import OpenAIEmbeddings
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.vectorstores import Chroma
from langchain.chains import ConversationalRetrievalChain
from langchain.document_loaders import PyPDFLoader
from langchain.chat_models import ChatOpenAI
from langchain.prompts import ChatPromptTemplate, SystemMessagePromptTemplate, HumanMessagePromptTemplate
from langchain.schema import Document
from langchain.memory import ConversationBufferMemory
import chainlit as cl
from chainlit.types import AskFileResponse
# Configure logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
# Set Chainlit config directory
os.environ["CHAINLIT_CONFIG_DIR"] = tempfile.gettempdir()
# App configuration
APP_CONFIG = {
"model_name": "gpt-4o",
"temperature": 0,
"chunk_size": 2000, # Increased for book-length content
"chunk_overlap": 200,
"max_file_size_mb": 100, # Increased for larger books
"max_output_tokens": 4096,
"embedding_model": "text-embedding-3-large"
}
# Initialize text splitter with more sophisticated settings
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=APP_CONFIG["chunk_size"],
chunk_overlap=APP_CONFIG["chunk_overlap"],
length_function=len,
separators=["\n\n", "\n", " ", ""] # Prioritize splitting on paragraph breaks
)
# System template
system_template = """You are GPT-4o, an advanced AI model with multimodal capabilities and high intelligence. Use the following pieces of context to answer the user's question.
If you don't know the answer, just say that you don't know, don't try to make up an answer.
ALWAYS return a "SOURCES" part in your answer.
The "SOURCES" part should be a reference to the source of the document from which you got your answer.
If the user greets with phrases like Hi, hello, How are you, etc., reply accordingly as well.
Example of your response should be:
The answer is foo
SOURCES: xyz
Begin!
----------------
{context}"""
# Create prompt template
messages = [
SystemMessagePromptTemplate.from_template(system_template),
HumanMessagePromptTemplate.from_template("{question}")
]
prompt = ChatPromptTemplate.from_messages(messages)
def process_file(file: AskFileResponse) -> List[Dict[str, str]]:
"""Process uploaded PDF file and return list of text chunks with metadata."""
with tempfile.NamedTemporaryFile(mode="wb", delete=False, suffix=".pdf") as temp_file:
temp_file.write(file.content)
temp_file_path = temp_file.name
loader = PyPDFLoader(temp_file_path)
pages = loader.load_and_split()
chunks_with_metadata = []
for i, page in enumerate(pages):
chunks = text_splitter.split_text(page.page_content)
for j, chunk in enumerate(chunks):
chunks_with_metadata.append({
"content": chunk,
"metadata": {
"page": i + 1,
"chunk": j + 1,
"source": f"page_{i+1}_chunk_{j+1}"
}
})
return chunks_with_metadata
@cl.on_chat_start
async def start():
await cl.Message(f"Welcome! Chat session started using {APP_CONFIG['model_name']} for text generation and {APP_CONFIG['embedding_model']} for embeddings. This combination is optimized for processing and querying book-length content. You can now upload a PDF of a book to begin!").send()
files = await cl.AskFileMessage(
content="Please upload a PDF file of a book to begin!",
accept=["application/pdf"],
max_size_mb=APP_CONFIG["max_file_size_mb"],
timeout=3600,
).send()
if not files:
await cl.Message("No file uploaded. Please try again.").send()
return
file = files[0]
await cl.Message(f"Starting to process `{file.name}`. This may take a few minutes...").send()
try:
# Process the file
await cl.Message("Reading and splitting the document...").send()
chunks_with_metadata = process_file(file)
await cl.Message("Creating embeddings and setting up the retrieval system...").send()
embeddings = OpenAIEmbeddings(model=APP_CONFIG["embedding_model"])
docsearch = await cl.make_async(Chroma.from_documents)(
[Document(page_content=chunk["content"], metadata=chunk["metadata"]) for chunk in chunks_with_metadata],
embeddings
)
memory = ConversationBufferMemory(
memory_key="chat_history",
output_key="answer",
return_messages=True,
)
await cl.Message("Finalizing the chat model...").send()
chain = ConversationalRetrievalChain.from_llm(
ChatOpenAI(
model_name=APP_CONFIG["model_name"],
temperature=APP_CONFIG["temperature"],
max_tokens=APP_CONFIG["max_output_tokens"],
streaming=True
),
chain_type="stuff",
retriever=docsearch.as_retriever(search_kwargs={"k": 3}),
memory=memory,
return_source_documents=True,
combine_docs_chain_kwargs={"prompt": prompt}
)
cl.user_session.set("chain", chain)
await cl.Message(content=f"Processing `{file.name}` complete. You can now ask questions about the book!").send()
except Exception as e:
error_msg = f"An error occurred while processing the file: {str(e)}"
logger.error(error_msg)
await cl.Message(content=error_msg).send()
@cl.on_message
async def main(message: cl.Message):
"""Handle user messages and generate responses."""
try:
chain = cl.user_session.get("chain")
cb = cl.AsyncLangchainCallbackHandler()
res = await chain.acall(message.content, callbacks=[cb])
answer = res["answer"]
source_documents = res["source_documents"]
text_elements = []
for source_doc in source_documents:
source_name = f"Page {source_doc.metadata['page']}, Chunk {source_doc.metadata['chunk']}"
text_elements.append(cl.Text(content=source_doc.page_content, name=source_name))
source_names = [text_el.name for text_el in text_elements]
if source_names:
answer += f"\nSources: {', '.join(source_names)}"
else:
answer += "\nNo sources found"
await cl.Message(content=answer, elements=text_elements).send()
except Exception as e:
error_msg = f"An error occurred: {str(e)}"
logger.error(error_msg)
await cl.Message(content=error_msg).send()
if __name__ == "__main__":
logger.info(f"Starting Chainlit application with model: {APP_CONFIG['model_name']} and embedding model: {APP_CONFIG['embedding_model']}")
cl.run() |