|
|
|
|
|
import tempfile |
|
|
from dotenv import load_dotenv |
|
|
import os |
|
|
from docling.document_converter import DocumentConverter |
|
|
from langchain_community.document_loaders import TextLoader |
|
|
from langchain.text_splitter import CharacterTextSplitter |
|
|
from langchain.vectorstores import FAISS |
|
|
from langchain.embeddings import HuggingFaceEmbeddings |
|
|
from langchain_openai import ChatOpenAI |
|
|
from langchain_community.chat_models import ChatAnthropic |
|
|
from langchain.chains import RetrievalQA |
|
|
import gradio as gr |
|
|
import tempfile |
|
|
|
|
|
|
|
|
llm = None |
|
|
qa_chain = None |
|
|
vectorstore = None |
|
|
retriever = None |
|
|
|
|
|
def process_pdf(file_path): |
|
|
global vectorstore, retriever, qa_chain |
|
|
|
|
|
|
|
|
converter = DocumentConverter() |
|
|
result = converter.convert(file_path) |
|
|
markdown_content = result.document.export_to_markdown() |
|
|
|
|
|
|
|
|
with tempfile.NamedTemporaryFile(delete=False, suffix=".md", mode="w", encoding="utf-8") as tmp_md: |
|
|
tmp_md.write(markdown_content) |
|
|
tmp_md_path = tmp_md.name |
|
|
|
|
|
|
|
|
loader = TextLoader(tmp_md_path) |
|
|
documents = loader.load() |
|
|
|
|
|
splitter = CharacterTextSplitter(chunk_size=1000, chunk_overlap=200) |
|
|
docs = splitter.split_documents(documents) |
|
|
|
|
|
embeddings = HuggingFaceEmbeddings(model_name="BAAI/bge-small-en-v1.5") |
|
|
vectorstore = FAISS.from_documents(docs, embeddings) |
|
|
retriever = vectorstore.as_retriever() |
|
|
|
|
|
|
|
|
if llm is not None: |
|
|
qa_chain = RetrievalQA.from_chain_type(llm=llm, chain_type="stuff", retriever=retriever) |
|
|
|
|
|
def setup_chain(api_key, provider): |
|
|
global llm, qa_chain |
|
|
|
|
|
if provider.lower() == "anthropic": |
|
|
os.environ["ANTHROPIC_API_KEY"] = api_key |
|
|
llm = ChatAnthropic(model_name="claude-3-sonnet-20240229") |
|
|
elif provider.lower() == "openai": |
|
|
os.environ["OPENAI_API_KEY"] = api_key |
|
|
llm = ChatOpenAI(model_name="gpt-4o") |
|
|
else: |
|
|
return "Unsupported provider. Please select 'openai' or 'anthropic'." |
|
|
|
|
|
|
|
|
global vectorstore, retriever |
|
|
if retriever is not None: |
|
|
qa_chain = RetrievalQA.from_chain_type(llm=llm, chain_type="stuff", retriever=retriever) |
|
|
|
|
|
return "API key and provider set successfully. You can now upload a PDF and ask questions." |
|
|
|
|
|
def answer_question(user_input): |
|
|
if qa_chain is None: |
|
|
return "Please upload a PDF and set your API key and provider first." |
|
|
if user_input.strip() == "": |
|
|
return "Please enter a question." |
|
|
response = qa_chain.run(user_input) |
|
|
return response |
|
|
|
|
|
import gradio as gr |
|
|
|
|
|
with gr.Blocks() as iface: |
|
|
gr.Markdown("# PDF Chat App with Docling and LangChain\nAsk questions directly from your PDF document.") |
|
|
|
|
|
with gr.Row(): |
|
|
api_key_input = gr.Textbox(label="API Key", type="password", placeholder="Enter your API Key") |
|
|
provider_input = gr.Dropdown(choices=["openai", "anthropic"], label="Provider", value="openai") |
|
|
set_api_button = gr.Button("Set API Key and Provider") |
|
|
|
|
|
api_status = gr.Textbox(label="Status", interactive=False) |
|
|
|
|
|
pdf_uploader = gr.File(label="Upload PDF", file_types=[".pdf"]) |
|
|
|
|
|
question_input = gr.Textbox(lines=2, placeholder="Ask a question about the PDF...") |
|
|
ask_button = gr.Button("Ask") |
|
|
answer_output = gr.Textbox(label="Answer", interactive=False) |
|
|
|
|
|
set_api_button.click(fn=setup_chain, inputs=[api_key_input, provider_input], outputs=api_status) |
|
|
pdf_uploader.change(fn=process_pdf, inputs=pdf_uploader, outputs=api_status) |
|
|
ask_button.click(fn=answer_question, inputs=question_input, outputs=answer_output) |
|
|
|
|
|
iface.launch(share=True) |
|
|
|