File size: 3,717 Bytes
ccd3265
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
# Add this import
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

    # Parse PDF with Docling
    converter = DocumentConverter()
    result = converter.convert(file_path)
    markdown_content = result.document.export_to_markdown()

    # Save markdown temporarily
    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

    # Load and split documents
    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()

    # Rebuild QA chain with current LLM
    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'."

    # If vectorstore and retriever are already set (PDF uploaded), rebuild qa_chain
    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)