diff --git a/.refactory.tags.cache.v3/cache.db b/.refactory.tags.cache.v3/cache.db new file mode 100644 index 0000000..b61236b Binary files /dev/null and b/.refactory.tags.cache.v3/cache.db differ diff --git a/.refactory.tags.cache.v3/cache.db-shm b/.refactory.tags.cache.v3/cache.db-shm new file mode 100644 index 0000000..53a7578 Binary files /dev/null and b/.refactory.tags.cache.v3/cache.db-shm differ diff --git a/.refactory.tags.cache.v3/cache.db-wal b/.refactory.tags.cache.v3/cache.db-wal new file mode 100644 index 0000000..d0af28a Binary files /dev/null and b/.refactory.tags.cache.v3/cache.db-wal differ diff --git "a/0_\360\237\224\214API_KEY.py" "b/0_\360\237\224\214API_KEY.py" index 112f3af..1746677 100644 --- "a/0_\360\237\224\214API_KEY.py" +++ "b/0_\360\237\224\214API_KEY.py" @@ -1,35 +1,126 @@ -import streamlit as st -from streamlit_extras.switch_page_button import switch_page -import os -import time -import tempfile -import openai -from llama_index.core import VectorStoreIndex, SimpleDirectoryReader, ServiceContext, set_global_service_context -from llama_index.llms.openai import OpenAI -from functions import sidebar_stuff1 - - -st.set_page_config(page_title="Talk to PDF", page_icon=":robot_face:", layout="wide") -st.title("Talk to your PDF ๐Ÿค– ๐Ÿ“‘๏ธ") - - -st.write("#### Enter your OpenAI api key below :") -api_key = st.text_input("Enter your OpenAI API key (https://platform.openai.com/account/api-keys)", type="password") -st.session_state['api_key'] = api_key - -if not api_key : - st.sidebar.warning("โš ๏ธ Please enter OpenAI API key") -else: - openai.api_key = api_key - -submit = st.button("Submit",use_container_width=True) -if submit: - st.sidebar.success("โœ… API key entered successfully") - time.sleep(1.5) - switch_page('upload pdf') -sidebar_stuff1() - - - - - +""" +API Key Configuration Page for Talk to PDF Application + +This module handles the OpenAI API key configuration and validation for the Talk to PDF application. +It provides a user interface for API key input and manages the transition to the PDF upload page +once the key is successfully validated. +""" + +import time +from typing import Optional + +import openai +import streamlit as st +from streamlit_extras.switch_page_button import switch_page + +from functions import sidebar_stuff1 +from llama_index.core import ( + VectorStoreIndex, + SimpleDirectoryReader, + ServiceContext, + set_global_service_context +) +from llama_index.llms.openai import OpenAI + +# Configuration Constants +PAGE_TITLE = "Talk to PDF" +PAGE_ICON = ":robot_face:" +API_KEY_PLACEHOLDER = "Enter your OpenAI API key (https://platform.openai.com/account/api-keys)" +SUCCESS_MESSAGE = "โœ… API key entered successfully" +WARNING_MESSAGE = "โš ๏ธ Please enter OpenAI API key" +REDIRECT_DELAY = 1.5 + +class APIKeyManager: + """Manages the OpenAI API key configuration and validation.""" + + @staticmethod + def validate_api_key(api_key: str) -> bool: + """ + Validates the provided OpenAI API key. + + Args: + api_key: The API key to validate + + Returns: + bool: True if the key is not empty, False otherwise + """ + return bool(api_key and api_key.strip()) + + @staticmethod + def save_api_key(api_key: str) -> None: + """ + Saves the API key to session state and configures OpenAI. + + Args: + api_key: The API key to save + """ + st.session_state['api_key'] = api_key + openai.api_key = api_key + +class PageConfig: + """Handles page configuration and UI setup.""" + + @staticmethod + def setup_page() -> None: + """Configures the Streamlit page settings.""" + st.set_page_config( + page_title=PAGE_TITLE, + page_icon=PAGE_ICON, + layout="wide" + ) + st.title("Talk to your PDF ๐Ÿค– ๐Ÿ“‘๏ธ") + + @staticmethod + def render_api_key_input() -> Optional[str]: + """ + Renders the API key input field. + + Returns: + Optional[str]: The entered API key or None + """ + st.write("#### Enter your OpenAI api key below :") + return st.text_input( + API_KEY_PLACEHOLDER, + type="password" + ) + + @staticmethod + def render_submit_button() -> bool: + """ + Renders the submit button. + + Returns: + bool: True if button is clicked, False otherwise + """ + return st.button("Submit", use_container_width=True) + +def main() -> None: + """Main function to run the API key configuration page.""" + + # Initialize page configuration + page_config = PageConfig() + page_config.setup_page() + + # Initialize API key manager + api_manager = APIKeyManager() + + # Render API key input + api_key = page_config.render_api_key_input() + + # Handle API key validation + if not api_manager.validate_api_key(api_key): + st.sidebar.warning(WARNING_MESSAGE) + else: + api_manager.save_api_key(api_key) + + # Handle form submission + if page_config.render_submit_button() and api_key: + st.sidebar.success(SUCCESS_MESSAGE) + time.sleep(REDIRECT_DELAY) + switch_page('upload pdf') + + # Render sidebar content + sidebar_stuff1() + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/LEARN.md b/LEARN.md index f12fa99..10231c7 100644 --- a/LEARN.md +++ b/LEARN.md @@ -1,29 +1,103 @@ # Talk to PDF ๐Ÿค– ๐Ÿ“‘๏ธ -This is the README file for the "Talk to PDF" code. The code is written in Python and uses the Streamlit library to create an interactive web application. The application allows users to ask questions about the content of a PDF file using natural language and receive instant answers powered by an AI question-answering system. +[![Python 3.11](https://img.shields.io/badge/python-3.11-blue.svg)](https://www.python.org/downloads/) +[![Streamlit](https://img.shields.io/badge/Streamlit-1.38.0-FF4B4B.svg)](https://streamlit.io) +[![OpenAI](https://img.shields.io/badge/OpenAI-1.43.0-412991.svg)](https://openai.com) +[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) -## Usage +An intelligent document interaction system that enables natural language conversations with PDF documents using AI-powered question-answering capabilities. -1. **Step 1: Enter your OpenAI API key** - - Open the application and find the "Step 1: Enter your OpenAI API key" section. - - Obtain an OpenAI API key from the OpenAI platform if you don't have one. - - Enter your API key in the text input field. - - Click the "Submit" button to set the OpenAI API key. +## ๐ŸŒŸ Features -2. **Step 2: Upload your PDF** - - In the "Step 2: Upload your PDF" section, click the "Browse Files" button. - - Select a PDF file from your device to upload. - - Wait for the PDF to finish uploading. +- Natural language interaction with PDF documents +- Real-time AI-powered question answering +- Context-aware conversation handling +- Support for multiple PDF documents +- Customizable AI model parameters +- User-friendly web interface +- Streaming responses for better user experience -3. **Ask a question** - - Once the PDF is uploaded, you will see an input box labeled "Ask a question." - - Enter your question about the PDF content in the input box. +## ๐Ÿ“‹ Requirements -4. **Get the answer** - - Click the "Ask" button to submit your question. - - The application will use the uploaded PDF and the question to generate a response using the AI question-answering system. - - The response will be displayed on the screen. +- Python 3.11 or higher +- OpenAI API key +- Modern web browser +- Internet connection -Feel free to reach out to the author, [@Obelisk_1531](https://twitter.com/Obelisk_1531), for any questions or feedback. +## ๐Ÿš€ Quick Start -Enjoy interacting with your PDFs using natural language! ๐Ÿš€๐Ÿ“„ +### Installation + +1. Clone the repository: +```bash +git clone https://github.com/yourusername/talk-to-pdf.git +cd talk-to-pdf +``` + +2. Install dependencies: +```bash +pip install -r requirements.txt +``` + +### Usage Guide + +1. **API Key Setup** + - Launch the application + - Navigate to the API key configuration section + - Enter your OpenAI API key (Get one from [OpenAI Platform](https://platform.openai.com/account/api-keys)) + - Click "Submit" to save your configuration + +2. **Document Upload** + - Go to the PDF upload section + - Click "Browse Files" or drag and drop your PDF + - Wait for the document processing to complete + - Multiple PDFs are supported + +3. **Interactive Chat** + - Type your question in the chat input box + - Click "Send" or press Enter + - View the AI-generated response + - Continue the conversation naturally + +4. **Advanced Options** + - Adjust model temperature for response variety + - Choose between different GPT models + - Configure chat context settings + +## ๐Ÿ› ๏ธ Technical Architecture + +- **Frontend**: Streamlit web application +- **Backend**: Python with LlamaIndex +- **AI Engine**: OpenAI GPT models +- **Document Processing**: PDF parsing and vector indexing +- **Storage**: Vector store for document embeddings + +## ๐Ÿค Contributing + +Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change. + +1. Fork the repository +2. Create your feature branch (`git checkout -b feature/AmazingFeature`) +3. Commit your changes (`git commit -m 'Add some AmazingFeature'`) +4. Push to the branch (`git push origin feature/AmazingFeature`) +5. Open a Pull Request + +## ๐Ÿ“ License + +This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details. + +## ๐Ÿ”— Contact + +Author: Kaushal - [@holy_kau](https://twitter.com/holy_kau) + +Project Link: [https://github.com/yourusername/talk-to-pdf](https://github.com/yourusername/talk-to-pdf) + +## ๐Ÿ™ Acknowledgments + +- OpenAI for providing the GPT models +- Streamlit team for the amazing web framework +- LlamaIndex for document processing capabilities + +--- + +Made with โค๏ธ by Kaushal | [Support my work](https://www.buymeacoffee.com/kaushal.ai) \ No newline at end of file diff --git a/README.md b/README.md index c73ae80..64ae9e0 100644 --- a/README.md +++ b/README.md @@ -1,146 +1,60 @@ -# Talk to PDF ๐Ÿค–๐Ÿ“‘ +# Talk to PDF ๐Ÿค– ๐Ÿ“‘ +[![Streamlit App](https://static.streamlit.io/badges/streamlit_badge_black_white.svg)](https://talk-to-pdf.streamlit.app/) +[![Python 3.11](https://img.shields.io/badge/python-3.11-blue.svg)](https://www.python.org/downloads/) +[![MIT License](https://img.shields.io/badge/License-MIT-green.svg)](https://choosealicense.com/licenses/mit/) +## ๐Ÿ“‹ Table of Contents -[![Streamlit App](https://static.streamlit.io/badges/streamlit_badge_black_white.svg)](https://talk-to-pdf.streamlit.app/) +- [Overview](#overview) +- [Features](#features) +- [Architecture](#architecture) +- [Installation](#installation) +- [Usage](#usage) +- [Technical Details](#technical-details) +- [Contributing](#contributing) +- [License](#license) +- [Contact](#contact) -## Project Summary +## ๐Ÿ” Overview -Talk to PDF is an innovative web application that enables natural language interaction with PDF documents through an AI-powered interface. The project leverages cutting-edge technologies including OpenAI's language models and LlamaIndex for document processing to create a seamless question-answering system for PDF content. +Talk to PDF is an innovative web application that enables natural language interaction with PDF documents through an AI-powered interface. The application leverages OpenAI's language models and LlamaIndex for document processing to create a seamless question-answering system for PDF content. + +### Key Components -The application follows a multi-page architecture built on Streamlit, with three primary components: 1. API Configuration Interface 2. Document Upload and Processing System 3. Interactive Chat Interface -The system processes uploaded PDFs through a sophisticated pipeline that includes document indexing, vector storage creation, and context-aware response generation. This architecture enables users to extract information from PDFs through natural conversation rather than manual searching. - -### Technical Architecture - -The application is structured using a modular approach with clear separation of concerns: - -```python -talk_to_pdf/ -โ”œโ”€โ”€ 0_๐Ÿ”ŒAPI_KEY.py # API configuration entry point -โ”œโ”€โ”€ functions.py # Shared utility functions -โ””โ”€โ”€ pages/ - โ”œโ”€โ”€ 1_๐Ÿ“‘UPLOAD_PDF.py # Document processing - โ””โ”€โ”€ 2_๐Ÿ’ฌCHAT_WITH_PDF.py # Chat interface -``` - -The system utilizes Streamlit's session state management for maintaining application state and LlamaIndex for document processing and retrieval operations. This architecture ensures efficient document handling and responsive user interactions. - -## Key Features +## โœจ Features ### 1. Intelligent PDF Processing - -The application implements advanced PDF processing capabilities using LlamaIndex and OpenAI's embedding models. The document processing pipeline includes: - -```python -def load_document(uploaded_files): - temp_dir = tempfile.TemporaryDirectory() - for file in uploaded_files: - temp_filepath = os.path.join(temp_dir.name, file.name) - with open(temp_filepath, "wb") as f: - f.write(file.getvalue()) - reader = SimpleDirectoryReader(input_dir=temp_dir.name) - docs = reader.load_data() - return docs -``` - -This implementation ensures efficient document handling while maintaining document integrity and security. +- Advanced document indexing using LlamaIndex +- Efficient vector storage implementation +- Secure document handling +- Multiple PDF file support ### 2. Context-Aware Question Answering - -The system employs a sophisticated chat engine that maintains conversation context and generates accurate responses: - -```python -custom_prompt = PromptTemplate("""\ -Given a conversation (between Human and Assistant) and a follow up message from Human, \ -rewrite the message to be a standalone question that captures all relevant context \ -from the conversation. -""") - -chat_engine = CondenseQuestionChatEngine.from_defaults( - query_engine=query_engine, - condense_question_prompt=custom_prompt, - chat_history=custom_chat_history -) -``` - -This feature enables natural conversation flow while maintaining context accuracy throughout the interaction. +- Natural language understanding +- Conversation context maintenance +- Accurate response generation +- Custom prompt templates ### 3. Real-Time Response Streaming +- Immediate feedback system +- Progressive response generation +- Efficient token handling +- Smooth user experience -The application implements streaming responses for improved user experience: - -```python -def conversational_chat(query): - streaming_response = chat_engine.stream_chat(query) - response_tokens = [] - for token in streaming_response.response_gen: - response_tokens.append(token) - return ''.join(response_tokens) -``` - -This implementation provides immediate feedback while processing complex queries. - -### 4. Flexible Model Selection - -Users can customize their experience by selecting different language models and adjusting response parameters: - -```python -model_name = st.selectbox("Select the model you want to use", - ("gpt-3.5-turbo","gpt-4")) -temperature = st.slider("Set temperature", 0.1, 1.0, 0.5, 0.1) -``` - -## Benefits - -### 1. Enhanced Document Analysis Efficiency - -The application significantly reduces the time required to extract information from PDF documents through: -- Instant access to document content through natural language queries -- Context-aware responses that understand document structure -- Efficient document indexing for quick retrieval of relevant information - -### 2. User-Friendly Interface - -The application provides several usability advantages: -- Progressive disclosure of functionality through a step-by-step interface -- Clear visual feedback for all operations -- Intuitive chat-based interaction model -- Customizable response parameters for different use cases +### 4. Customizable Experience +- Multiple language model options (GPT-3.5, GPT-4) +- Adjustable response parameters +- Temperature control for output variation +- Flexible conversation settings -### 3. Technical Advantages +## ๐Ÿ— Architecture -The implementation offers several technical benefits: -- Scalable architecture supporting multiple document formats -- Efficient memory management through temporary file handling -- Secure document processing with proper cleanup -- Modular design enabling easy feature additions and modifications - -### 4. Integration Capabilities - -The application's architecture facilitates easy integration with existing systems through: -- Clear API-based communication -- Standardized document processing pipeline -- Modular component structure -- Session-based state management - -The Talk to PDF project represents a significant advancement in document interaction technology, combining sophisticated AI capabilities with a user-friendly interface to create a powerful tool for document analysis and information extraction. - -# Talk to PDF - Architectural Overview - -## System Architecture - -The Talk to PDF application implements a modern, modular architecture built on the Streamlit framework, leveraging OpenAI's language models and LlamaIndex for document processing. The system follows a three-tier architecture pattern with clear separation of concerns: - -1. **Presentation Layer**: Streamlit-based user interface -2. **Processing Layer**: Document indexing and query processing -3. **Integration Layer**: OpenAI API integration and vector storage - -### Architecture Diagram +### System Components ```mermaid graph TD @@ -153,234 +67,53 @@ graph TD G --> E ``` -## Core Components - -### 1. API Key Configuration Module - -The API key configuration module (`0_๐Ÿ”ŒAPI_KEY.py`) serves as the entry point for the application, implementing a secure way to handle OpenAI API credentials: - -```python -def handle_api_key(): - api_key = st.text_input("Enter your OpenAI API key", type="password") - st.session_state['api_key'] = api_key - if not api_key: - st.sidebar.warning("โš ๏ธ Please enter OpenAI API key") - else: - openai.api_key = api_key -``` - -This component features: -- Secure password-masked input -- Session state persistence -- Automatic validation -- Seamless navigation flow - -### 2. Document Processing Engine - -The document processing engine (`pages/1_๐Ÿ“‘UPLOAD_PDF.py`) handles PDF upload and indexing operations. It utilizes LlamaIndex for efficient document processing: - -```python -def query_engine(docs, model_name, temperature): - llm = OpenAI(model=model_name, temperature=temperature) - with st.spinner("Indexing document..."): - index = VectorStoreIndex.from_documents(docs, llm=llm) - with st.spinner("Creating query engine..."): - query_engine = index.as_query_engine() - return query_engine -``` - -Key features include: -- Multiple PDF file support -- Automatic document indexing -- Vector store creation -- Configurable model parameters - -### 3. Chat Interface System - -The chat interface (`pages/2_๐Ÿ’ฌCHAT_WITH_PDF.py`) implements an interactive conversation system with context-aware responses: - -```python -custom_prompt = PromptTemplate("""\ -Given a conversation (between Human and Assistant) and a follow up message from Human, \ -rewrite the message to be a standalone question that captures all relevant context \ -from the conversation. - - -{chat_history} - - -{question} - - -""") -``` - -Notable features: -- Streaming responses -- Chat history management -- Context-aware question processing -- Custom prompt templates - -## Data Flow Architecture - -### 1. Input Processing Flow - -The application implements a sequential data flow pattern: - -1. **API Key Validation** - - User inputs API key - - System validates and stores in session state - - Enables access to document processing - -2. **Document Processing Pipeline** - - PDF upload triggers document reader - - Content extraction and preprocessing - - Vector index generation - - Storage in session state - -3. **Query Processing Chain** - - User input captured - - Context integration - - Query reformation - - Response generation and streaming - -### 2. State Management - -The application utilizes Streamlit's session state for persistent data management: - -```python -if 'history' not in st.session_state: - st.session_state['history'] = [] -if 'generated' not in st.session_state: - st.session_state['generated'] = ["Hello! Ask me anything about the uploaded document ๐Ÿค—"] -if 'past' not in st.session_state: - st.session_state['past'] = ["Hey! ๐Ÿ‘‹"] -``` - -Key state components: -- API key storage -- Document index persistence -- Chat history management -- Query engine state +### Technology Stack -## Technical Implementation Details +#### Core Technologies +- **Python 3.11**: Primary development language +- **Streamlit 1.38.0**: Web application framework +- **OpenAI API 1.43.0**: Language processing engine +- **LlamaIndex 0.11.3**: Document processing system -### 1. Vector Store Implementation +#### Key Dependencies +- **streamlit-chat**: Chat interface components +- **streamlit-extras**: UI utilities +- **PyPDF**: PDF processing +- **Vector storage**: Document indexing -The system uses LlamaIndex's VectorStoreIndex for efficient document querying: +## ๐Ÿ“ฅ Installation -```python -def load_document(uploaded_files): - temp_dir = tempfile.TemporaryDirectory() - for file in uploaded_files: - temp_filepath = os.path.join(temp_dir.name, file.name) - with open(temp_filepath, "wb") as f: - f.write(file.getvalue()) - reader = SimpleDirectoryReader(input_dir=temp_dir.name) - docs = reader.load_data() - return docs +1. Clone the repository: +```bash +git clone https://github.com/yourusername/talk-to-pdf.git +cd talk-to-pdf ``` -### 2. Chat Engine Configuration - -The chat engine implements a custom configuration for context-aware responses: - -```python -chat_engine = CondenseQuestionChatEngine.from_defaults( - query_engine=query_engine, - condense_question_prompt=custom_prompt, - chat_history=custom_chat_history -) +2. Install dependencies: +```bash +pip install -r requirements.txt ``` -## Architectural Decisions and Rationale - -### 1. Technology Choices - -- **Streamlit**: Selected for rapid development and interactive UI capabilities -- **LlamaIndex**: Chosen for efficient document processing and vector storage -- **OpenAI Integration**: Provides powerful language understanding capabilities - -### 2. Design Patterns - -The application implements several key design patterns: - -1. **Modular Architecture** - - Separate pages for distinct functionality - - Centralized utility functions - - Clear component boundaries - -2. **State Management Pattern** - - Session-based state persistence - - Centralized state management - - Clear state initialization - -3. **Stream Processing Pattern** - - Real-time response streaming - - Asynchronous document processing - - Progressive UI updates - -This architecture ensures scalability, maintainability, and a smooth user experience while maintaining robust security and performance characteristics. - -# Component Breakdown: Talk to PDF System - -## API Configuration Module - -The API Configuration module serves as the initial entry point for the Talk to PDF application, handling OpenAI API key validation and storage. This component is crucial for enabling the AI-powered functionality throughout the application. - -### Primary Functions - -1. **API Key Input and Validation** - - Provides a secure input interface for users to enter their OpenAI API key - - Validates the key format and stores it in the session state - - Manages the transition to the PDF upload page upon successful configuration - -### Implementation Details - -The module is implemented in `0_๐Ÿ”ŒAPI_KEY.py` and utilizes Streamlit's session state management for persistent storage. Key features include: - -```python -st.set_page_config(page_title="Talk to PDF", page_icon=":robot_face:", layout="wide") -st.title("Talk to your PDF ๐Ÿค– ๐Ÿ“‘๏ธ") - -api_key = st.text_input("Enter your OpenAI API key", type="password") -st.session_state['api_key'] = api_key - -if not api_key: - st.sidebar.warning("โš ๏ธ Please enter OpenAI API key") -else: - openai.api_key = api_key +3. Set up environment: +```bash +cp .env.example .env +# Add your OpenAI API key to .env file ``` -The module implements secure key storage using password-masked input and provides immediate feedback through the sidebar. Upon successful key submission, it triggers a page transition: +## ๐Ÿš€ Usage -```python -submit = st.button("Submit", use_container_width=True) -if submit: - st.sidebar.success("โœ… API key entered successfully") - time.sleep(1.5) - switch_page('upload pdf') +1. Start the application: +```bash +streamlit run 0_๐Ÿ”ŒAPI_KEY.py ``` -## Document Processing Module - -The Document Processing module handles PDF file uploads, document indexing, and vector store creation. This component transforms raw PDF documents into queryable knowledge bases. - -### Primary Functions +2. Enter your OpenAI API key +3. Upload PDF document(s) +4. Start asking questions about your documents -1. **Document Upload Handling** - - Manages file uploads through Streamlit's file uploader - - Validates PDF file format - - Creates temporary storage for document processing +## ๐Ÿ”ง Technical Details -2. **Document Indexing** - - Processes PDF content using LlamaIndex - - Creates vector embeddings for efficient querying - - Establishes the query engine for chat functionality - -### Implementation Details - -Located in `pages/1_๐Ÿ“‘UPLOAD_PDF.py`, the module implements sophisticated document processing: +### Document Processing Pipeline ```python def load_document(uploaded_files): @@ -389,50 +122,15 @@ def load_document(uploaded_files): temp_filepath = os.path.join(temp_dir.name, file.name) with open(temp_filepath, "wb") as f: f.write(file.getvalue()) - reader = SimpleDirectoryReader(input_dir=temp_dir.name) docs = reader.load_data() return docs ``` -The indexing process utilizes OpenAI's language models for creating searchable document representations: +### Chat Engine Implementation ```python -def query_engine(docs, model_name, temperature): - llm = OpenAI(model=model_name, temperature=temperature) - Settings.llm = llm - with st.spinner("Indexing document..."): - index = VectorStoreIndex.from_documents(docs, llm=llm) - with st.spinner("Creating query engine..."): - query_engine = index.as_query_engine() - - st.session_state['index'] = index - st.session_state['query_engine'] = query_engine - return query_engine -``` - -## Chat Interface Module - -The Chat Interface module provides an interactive environment for users to query their PDF documents using natural language. This component handles the conversation flow and response generation. - -### Primary Functions - -1. **Chat Management** - - Maintains conversation history - - Handles user input processing - - Manages response streaming and display - -2. **Context-Aware Question Answering** - - Reformulates questions to maintain context - - Generates relevant responses using the query engine - - Streams responses for better user experience - -### Implementation Details - -Implemented in `pages/2_๐Ÿ’ฌCHAT_WITH_PDF.py`, the module uses a custom prompt template for context-aware responses: - -```python -custom_prompt = PromptTemplate("""\ +custom_prompt = PromptTemplate(""" Given a conversation (between Human and Assistant) and a follow up message from Human, \ rewrite the message to be a standalone question that captures all relevant context \ from the conversation. @@ -445,146 +143,43 @@ from the conversation. """) -``` - -The chat engine implementation includes streaming capabilities for real-time response generation: - -```python -def conversational_chat(query): - streaming_response = chat_engine.stream_chat(query) - response_tokens = [] - for token in streaming_response.response_gen: - response_tokens.append(token) - return ''.join(response_tokens) -``` - -### Component Interactions - -The three modules work together in a sequential flow: - -1. The API Configuration module initializes the OpenAI client and enables AI functionality -2. The Document Processing module uses the configured API to create document indices -3. The Chat Interface module leverages both the API configuration and document indices to provide interactive question-answering capabilities - -This architecture ensures a smooth user experience while maintaining separation of concerns and modularity in the codebase. - -## Error Handling and State Management - -Each component implements robust error handling and state management: - -- API Configuration validates keys and provides clear feedback -- Document Processing includes upload validation and processing status indicators -- Chat Interface maintains conversation state and handles streaming errors gracefully - -The application uses Streamlit's session state for persistent storage across components, ensuring a seamless user experience throughout the interaction flow. - -# Technology Stack Documentation - Talk to PDF - -## Core Technologies - -### Python -- **Version**: 3.11 (specified in devcontainer.json) -- **Role**: Primary programming language for the application -- **Justification**: Python was chosen for its extensive machine learning and NLP libraries, excellent web framework support through Streamlit, and seamless integration with OpenAI's APIs. The language's readability and extensive package ecosystem make it ideal for rapid development of AI-powered applications. - -### Streamlit -- **Version**: 1.38.0 -- **Role**: Web application framework and user interface -- **Justification**: Streamlit provides a rapid development environment for data-focused applications with minimal frontend code. Its built-in components and session state management make it perfect for creating interactive AI applications. - -Example usage from `0_๐Ÿ”ŒAPI_KEY.py`: -```python -st.set_page_config(page_title="Talk to PDF", page_icon=":robot_face:", layout="wide") -st.title("Talk to your PDF ๐Ÿค– ๐Ÿ“‘๏ธ") -api_key = st.text_input("Enter your OpenAI API key", type="password") -``` -### OpenAI Integration -- **Version**: 1.43.0 -- **Role**: Natural language processing and question answering -- **Justification**: OpenAI's GPT models provide state-of-the-art natural language understanding and generation capabilities, essential for accurate PDF content analysis and question answering. - -Implementation example from `functions.py`: -```python -def query_engine(docs, model_name, temperature): - llm = OpenAI(model=model_name, temperature=temperature) - Settings.llm = llm - index = VectorStoreIndex.from_documents(docs, llm=llm) - query_engine = index.as_query_engine() - return query_engine +chat_engine = CondenseQuestionChatEngine.from_defaults( + query_engine=query_engine, + condense_question_prompt=custom_prompt, + chat_history=custom_chat_history +) ``` -## Document Processing Stack +### State Management -### LlamaIndex -- **Version**: 0.11.3 -- **Role**: Document indexing and retrieval system -- **Justification**: LlamaIndex provides sophisticated document processing capabilities with built-in support for various document types and vector storage systems. It seamlessly integrates with OpenAI's embeddings for efficient document querying. - -Key components: -- `VectorStoreIndex`: Document indexing and retrieval -- `SimpleDirectoryReader`: PDF file processing -- `CondenseQuestionChatEngine`: Context-aware question answering - -Example implementation: ```python -from llama_index.core import VectorStoreIndex, SimpleDirectoryReader -reader = SimpleDirectoryReader(input_dir=temp_dir.name) -docs = reader.load_data() -index = VectorStoreIndex.from_documents(docs, llm=llm) -``` - -### PyPDF -- **Version**: 4.3.1 -- **Role**: PDF file processing and text extraction -- **Justification**: Provides robust PDF parsing capabilities with support for various PDF formats and structures. - -## UI Components and Extensions - -### Streamlit Extensions -- **streamlit-chat**: Version 0.1.1 - Provides chat interface components -- **streamlit-extras**: Version 0.4.7 - Additional UI utilities -- **streamlit-faker**: Version 0.0.3 - Test data generation -- **markdownlit**: Version 0.0.7 - Enhanced markdown rendering - -These extensions enhance the base Streamlit functionality with specialized components for chat interfaces and improved user experience. - -## Dependency Management - -### Requirements Management -The project uses `pip` and `requirements.txt` for dependency management. The requirements file is compiled using `pip-compile`, ensuring reproducible builds across environments. - -Key dependencies are organized into categories: -- Core dependencies (Python packages) -- UI components (Streamlit and extensions) -- AI/ML libraries (OpenAI, LlamaIndex) -- Utility packages (typing, packaging, etc.) - -Installation process: -```bash -pip install -r requirements.txt +if 'history' not in st.session_state: + st.session_state['history'] = [] +if 'generated' not in st.session_state: + st.session_state['generated'] = ["Hello! Ask me anything about the uploaded document ๐Ÿค—"] +if 'past' not in st.session_state: + st.session_state['past'] = ["Hey! ๐Ÿ‘‹"] ``` -### Version Control -Dependencies are strictly versioned to ensure consistency: -```txt -streamlit==1.38.0 -openai==1.43.0 -llama-index==0.11.3 -pypdf==4.3.1 -``` +## ๐Ÿค Contributing +1. Fork the repository +2. Create your feature branch (`git checkout -b feature/AmazingFeature`) +3. Commit your changes (`git commit -m 'Add some AmazingFeature'`) +4. Push to the branch (`git push origin feature/AmazingFeature`) +5. Open a Pull Request -## ๐Ÿ“ License +## ๐Ÿ“„ License -This project is [MIT](https://choosealicense.com/licenses/mit/) licensed. +This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details. ## ๐Ÿ“ฌ Contact -Author - [Kaushal](https://www.linkedin.com/in/kaushal-powar-a52b1a159/) +Author: [Kaushal](https://www.linkedin.com/in/kaushal-powar-a52b1a159/) Project Link: [https://github.com/yourusername/talk-to-pdf](https://github.com/yourusername/talk-to-pdf) --- -Enjoy interacting with your PDFs using natural language! ๐Ÿš€๐Ÿ“„ +Built with โค๏ธ by Kaushal | [Twitter](https://twitter.com/holy_kau) \ No newline at end of file diff --git a/functions.py b/functions.py index 771efe9..9916e33 100644 --- a/functions.py +++ b/functions.py @@ -1,202 +1,232 @@ +""" +Utility functions for the Talk to PDF application. +Handles UI components, document processing, and query engine setup. +""" + +import logging +import os +import tempfile +from dataclasses import dataclass +from pathlib import Path +from typing import List, Optional + import streamlit as st from streamlit.components.v1 import html from streamlit_extras.switch_page_button import switch_page import openai -import os - -from llama_index.core import VectorStoreIndex, SimpleDirectoryReader, Settings, set_global_service_context +from llama_index.core import ( + VectorStoreIndex, + SimpleDirectoryReader, + Settings, + Document +) from llama_index.llms.openai import OpenAI from llama_index.embeddings.openai import OpenAIEmbedding from llama_index.core.text_splitter import TokenTextSplitter from llama_index.core.indices.prompt_helper import PromptHelper -import tempfile - -def sidebar_stuff1(): - html_temp = """ -
- -
- """ - - - button = """ - """ +# Configure logging +logging.basicConfig(level=logging.INFO) +logger = logging.getLogger(__name__) + +# UI Constants +HTML_TEMPLATE = """ +
+
+""" + +SUPPORT_BUTTON = """ + +""" + +BUTTON_STYLE = """ + +""" + +@dataclass +class SidebarConfig: + """Configuration for sidebar content.""" + title: str + content: str + show_api_key_info: bool = False + show_model_info: bool = False + show_chat_info: bool = False + +def create_sidebar(config: SidebarConfig) -> None: + """ + Creates and configures the sidebar with specified content. + + Args: + config: SidebarConfig object containing sidebar configuration + """ with st.sidebar: - st.markdown(""" - # โ— About - "Talk to PDF" is an app that allows users to ask questions about the content of a PDF file using Natural Language Processing. - - The app uses a question-answering system powered by OpenAI's GPT ๐Ÿ”ฅ to provide accurate and relevant answers to the your queries. """) - - st.markdown(html_temp.format("rgba(55, 53, 47, 0.16)"), unsafe_allow_html=True) - st.markdown(""" - # โ— Get started - ใƒปPaste your OpenAI API key. (click on the link to get your API key) - - """) - st.markdown(html_temp.format("rgba(55, 53, 47, 0.16)"), unsafe_allow_html=True) - - - st.markdown(""" - Made by [Kaushal](https://twitter.com/holy_kau) - """) - html(button, height=70, width=220) + st.markdown("# โ— About") st.markdown( - """ - - """, - unsafe_allow_html=True, + '"Talk to PDF" is an app that allows users to ask questions about ' + 'the content of a PDF file using Natural Language Processing.\n\n' + 'The app uses a question-answering system powered by OpenAI\'s GPT ๐Ÿ”ฅ ' + 'to provide accurate and relevant answers to your queries.' ) - - -def sidebar_stuff2(): - html_temp = """ -
- -
- """ - - - button = """ - """ - with st.sidebar: - - st.markdown(html_temp.format("rgba(55, 53, 47, 0.16)"), unsafe_allow_html=True) - st.markdown(""" - - ใƒปChoose your model (gpt-3.5-turbo or gpt-4) - ใƒปAdjust the temperature according to your needs - + st.markdown(HTML_TEMPLATE.format("rgba(55, 53, 47, 0.16)"), unsafe_allow_html=True) - (It controls the randomness of the model's output. A higher temperature (e.g., 1.0) makes the output more diverse and random, while a lower temperature (e.g., 0.5) makes the output more focused and deterministic.) - - ใƒปUpload a PDF file and ask questions about its content - - """) - st.markdown(html_temp.format("rgba(55, 53, 47, 0.16)"), unsafe_allow_html=True) - - - st.markdown(""" - Made by [Kaushal](https://twitter.com/holy_kau) - """) - html(button, height=70, width=220) - st.markdown( - """ - - """, - unsafe_allow_html=True, - ) - - -def sidebar_stuff3(): - html_temp = """ -
- -
- """ - - - button = """ - """ - with st.sidebar: - - st.markdown(html_temp.format("rgba(55, 53, 47, 0.16)"), unsafe_allow_html=True) - st.markdown(""" - - ใƒปAsk questions about your documents content + if config.show_api_key_info: + st.markdown( + "# โ— Get started\n" + "ใƒปPaste your OpenAI API key. (click on the link to get your API key)" + ) - ใƒปGet instant answers to your questions - - """) - st.markdown(html_temp.format("rgba(55, 53, 47, 0.16)"), unsafe_allow_html=True) + if config.show_model_info: + st.markdown( + "ใƒปChoose your model (gpt-3.5-turbo or gpt-4)\n" + "ใƒปAdjust the temperature according to your needs\n\n" + "(It controls the randomness of the model's output. A higher temperature " + "makes the output more diverse and random, while a lower temperature " + "makes the output more focused and deterministic.)\n\n" + "ใƒปUpload a PDF file and ask questions about its content" + ) + + if config.show_chat_info: + st.markdown( + "ใƒปAsk questions about your documents content\n" + "ใƒปGet instant answers to your questions" + ) + + st.markdown(HTML_TEMPLATE.format("rgba(55, 53, 47, 0.16)"), unsafe_allow_html=True) + st.markdown("Made by [Kaushal](https://twitter.com/holy_kau)") + + html(SUPPORT_BUTTON, height=70, width=220) + st.markdown(BUTTON_STYLE, unsafe_allow_html=True) +# Alias functions for backwards compatibility +sidebar_stuff1 = lambda: create_sidebar(SidebarConfig(title="About", content="", show_api_key_info=True)) +sidebar_stuff2 = lambda: create_sidebar(SidebarConfig(title="About", content="", show_model_info=True)) +sidebar_stuff3 = lambda: create_sidebar(SidebarConfig(title="About", content="", show_chat_info=True)) - st.markdown(""" - Made by [Kaushal](https://twitter.com/holy_kau) - """) - html(button, height=70, width=220) - st.markdown( - """ - - """, - unsafe_allow_html=True, - ) +def save_document(doc, documents_folder: Path) -> str: + """ + Saves uploaded document to the specified folder. - -def save_file(doc): - fn = os.path.basename(doc.name) - # check if documents_folder exists in the directory - if not os.path.exists(documents_folder): - # if documents_folder does not exist then making the directory - os.makedirs(documents_folder) - # open read and write the file into the server - open(documents_folder + '/' + fn, 'wb').write(doc.read()) - # Check for the current filename, If new filename - # clear the previous cached vectors and update the filename - # with current name - if st.session_state.get('file_name'): - if st.session_state.file_name != fn: + Args: + doc: Uploaded document file + documents_folder: Path to save documents + + Returns: + str: Filename of saved document + + Raises: + IOError: If file operations fail + """ + try: + filename = Path(doc.name).name + documents_folder.mkdir(exist_ok=True) + + file_path = documents_folder / filename + with open(file_path, 'wb') as f: + f.write(doc.read()) + + # Update session state + if st.session_state.get('file_name') != filename: st.cache_resource.clear() - st.session_state['file_name'] = fn - else: - st.session_state['file_name'] = fn - - return fn - - -def remove_file(file_path): - # Remove the file from the Document folder once - # vectors are created - if os.path.isfile(documents_folder + '/' + file_path): - os.remove(documents_folder + '/' + file_path) - - - -def query_engine(docs, model_name, temperature): - llm = OpenAI(model=model_name, temperature=temperature) - #file_name = st.session_state["tmp_file"] - #service_context = ServiceContext.from_defaults(llm=llm) - Settings.llm = llm - with st.spinner("Indexing document..."): - index = VectorStoreIndex.from_documents(docs, llm=llm) - print("index created : ", index) - with st.spinner("Creating query engine..."): - query_engine = index.as_query_engine() - print("query engine created ") - - st.session_state['index'] = index - st.session_state['query_engine'] = query_engine - switch_page('chat with pdf') - return query_engine - -def load_document(uploaded_files): - temp_dir = tempfile.TemporaryDirectory() - for file in uploaded_files: - temp_filepath = os.path.join(temp_dir.name, file.name) - with open(temp_filepath, "wb") as f: - f.write(file.getvalue()) + st.session_state['file_name'] = filename + + return filename + + except Exception as e: + logger.error(f"Error saving document: {e}") + raise IOError(f"Failed to save document: {e}") - reader = SimpleDirectoryReader(input_dir=temp_dir.name) - docs = reader.load_data() - print(docs) - return docs +def remove_document(file_path: Path) -> None: + """ + Removes document file from filesystem. + + Args: + file_path: Path to document to remove + """ + try: + if file_path.is_file(): + file_path.unlink() + except Exception as e: + logger.error(f"Error removing document: {e}") + +def setup_query_engine(docs: List[Document], model_name: str, temperature: float): + """ + Sets up the query engine with the specified model and documents. + + Args: + docs: List of documents to index + model_name: Name of OpenAI model to use + temperature: Temperature parameter for model + + Returns: + Query engine instance + """ + try: + llm = OpenAI(model=model_name, temperature=temperature) + Settings.llm = llm + + with st.spinner("Indexing document..."): + index = VectorStoreIndex.from_documents(docs, llm=llm) + logger.info("Document indexing completed") + + with st.spinner("Creating query engine..."): + query_engine = index.as_query_engine() + logger.info("Query engine created") + + # Store in session state + st.session_state['index'] = index + st.session_state['query_engine'] = query_engine + + switch_page('chat with pdf') + return query_engine + + except Exception as e: + logger.error(f"Error setting up query engine: {e}") + st.error("Failed to setup query engine. Please try again.") + raise + +def load_documents(uploaded_files) -> List[Document]: + """ + Loads documents from uploaded files. + + Args: + uploaded_files: List of uploaded file objects + + Returns: + List of loaded Document objects + """ + try: + with tempfile.TemporaryDirectory() as temp_dir: + temp_dir_path = Path(temp_dir) + + # Save uploaded files to temp directory + for file in uploaded_files: + temp_file_path = temp_dir_path / file.name + temp_file_path.write_bytes(file.getvalue()) + + # Load documents + reader = SimpleDirectoryReader(input_dir=temp_dir) + docs = reader.load_data() + logger.info(f"Loaded {len(docs)} documents") + return docs + + except Exception as e: + logger.error(f"Error loading documents: {e}") + st.error("Failed to load documents. Please try again.") + raise \ No newline at end of file diff --git "a/pages/1_\360\237\223\221UPLOAD_PDF.py" "b/pages/1_\360\237\223\221UPLOAD_PDF.py" index b7fe680..70da985 100644 --- "a/pages/1_\360\237\223\221UPLOAD_PDF.py" +++ "b/pages/1_\360\237\223\221UPLOAD_PDF.py" @@ -1,20 +1,115 @@ +""" +PDF Upload and Model Configuration Page + +This module handles the PDF file upload interface and model configuration settings +for the Talk to PDF application. It provides a user interface for selecting the +OpenAI model, temperature settings, and uploading PDF files for processing. +""" + import os -from functions import sidebar_stuff2, query_engine, save_file, remove_file, load_document +from typing import List, Optional, Tuple import tempfile import streamlit as st from streamlit_extras.switch_page_button import switch_page -sidebar_stuff2() +from functions import ( + sidebar_stuff2, + query_engine, + save_file, + remove_file, + load_document +) + +# Constants +SUPPORTED_MODELS = ("gpt-3.5-turbo", "gpt-4") +DEFAULT_TEMPERATURE = 0.5 +TEMPERATURE_MIN = 0.1 +TEMPERATURE_MAX = 1.0 +TEMPERATURE_STEP = 0.1 +def initialize_page() -> None: + """Initialize the page layout and sidebar.""" + sidebar_stuff2() -model_name = st.selectbox("Select the model you want to use",("gpt-3.5-turbo","gpt-4")) -temperature = st.slider("Set temperature", 0.1, 1.0, 0.5,0.1) -pdf_file = st.file_uploader( - "Unleash the power of AI to have a conversation with your PDFs and uncover new insights, all with a single uploadโฌ‡๏ธ ",type=['pdf'], accept_multiple_files=True) +def get_model_settings() -> Tuple[str, float]: + """ + Get model configuration settings from user input. + + Returns: + Tuple[str, float]: Selected model name and temperature setting + """ + model_name = st.selectbox( + "Select the model you want to use", + options=SUPPORTED_MODELS, + help="Choose between GPT-3.5 Turbo (faster, cheaper) or GPT-4 (more capable)" + ) + + temperature = st.slider( + "Set temperature", + min_value=TEMPERATURE_MIN, + max_value=TEMPERATURE_MAX, + value=DEFAULT_TEMPERATURE, + step=TEMPERATURE_STEP, + help="Higher values make output more creative, lower values make it more focused" + ) + + return model_name, temperature -if pdf_file : - reader = load_document(uploaded_files=pdf_file) - query_engine = query_engine(reader, model_name, temperature) -else: +def handle_file_upload() -> Optional[List]: + """ + Handle PDF file upload process. + + Returns: + Optional[List]: List of processed documents if upload successful, None otherwise + """ + uploaded_files = st.file_uploader( + "Unleash the power of AI to have a conversation with your PDFs " + "and uncover new insights, all with a single uploadโฌ‡๏ธ", + type=['pdf'], + accept_multiple_files=True, + help="Upload one or more PDF files to analyze" + ) + + if not uploaded_files: st.error("Please upload a PDF file") + return None + + try: + documents = load_document(uploaded_files=uploaded_files) + return documents + except Exception as e: + st.error(f"Error processing PDF file: {str(e)}") + return None + +def main(): + """Main function to run the PDF upload and model configuration page.""" + initialize_page() + + # Get model settings + model_name, temperature = get_model_settings() + + # Handle file upload + documents = handle_file_upload() + + # Process documents if available + if documents: + try: + # Initialize query engine with selected settings + engine = query_engine( + docs=documents, + model_name=model_name, + temperature=temperature + ) + + # Store engine in session state and proceed + st.session_state['query_engine'] = engine + + except Exception as e: + st.error( + "Error initializing query engine. Please check your settings " + f"and try again: {str(e)}" + ) + +if __name__ == "__main__": + main() \ No newline at end of file diff --git "a/pages/2_\360\237\222\254CHAT_WITH_PDF.py" "b/pages/2_\360\237\222\254CHAT_WITH_PDF.py" index ca556f9..ab3c128 100644 --- "a/pages/2_\360\237\222\254CHAT_WITH_PDF.py" +++ "b/pages/2_\360\237\222\254CHAT_WITH_PDF.py" @@ -1,19 +1,24 @@ +""" +Chat interface implementation for the Talk to PDF application. +Handles conversation management, prompt generation, and response streaming. +""" + import streamlit as st from streamlit_chat import message +from typing import List, Optional +from dataclasses import dataclass from functions import sidebar_stuff3 -from llama_index.core.prompts import PromptTemplate +from llama_index.core.prompts import PromptTemplate from llama_index.core.chat_engine.condense_question import CondenseQuestionChatEngine from llama_index.core.llms import ChatMessage, MessageRole -sidebar_stuff3() - -query_engine=st.session_state['query_engine'] -index = st.session_state['index'] +# Constants +DEFAULT_GREETING = "Hello! Ask me anything about the uploaded document ๐Ÿค—" +DEFAULT_USER_GREETING = "Hey! ๐Ÿ‘‹" +QUERY_PLACEHOLDER = "What is this document about?" -#from llama_index.memory import ChatMemoryBuffer - -#memory = ChatMemoryBuffer.from_defaults(token_limit=1500) -custom_prompt = PromptTemplate("""\ +# Custom prompt template +CHAT_PROMPT_TEMPLATE = """\ Given a conversation (between Human and Assistant) and a follow up message from Human, \ rewrite the message to be a standalone question that captures all relevant context \ from the conversation. @@ -25,64 +30,121 @@ {question} -""") - -custom_chat_history = [ - ChatMessage( - role=MessageRole.USER, - content='Hello assistant, given is a document. Please answer the question by understanding the context and information of the document. Use your own knowledge and understanding to answer the question.' - ), - ChatMessage( - role=MessageRole.ASSISTANT, - content='Okay, sounds good.' - ) -] - -query_engine = st.session_state['query_engine'] -chat_engine = CondenseQuestionChatEngine.from_defaults( - query_engine=query_engine, - condense_question_prompt=custom_prompt, - chat_history=custom_chat_history -) - -response = chat_engine.chat("Hello!") -def conversational_chat(query): - streaming_response = chat_engine.stream_chat(query) - response_tokens = [] - for token in streaming_response.response_gen: - response_tokens.append(token) - return ''.join(response_tokens) - -# Initialize session state variables -if 'history' not in st.session_state: - st.session_state['history'] = [] - -if 'generated' not in st.session_state: - st.session_state['generated'] = ["Hello! Ask me anything about the uploaded document ๐Ÿค—"] - -if 'past' not in st.session_state: - st.session_state['past'] = ["Hey! ๐Ÿ‘‹"] - -# Containers for chat history and user input -response_container = st.container() -container = st.container() - -# User input form -with container: - with st.form(key='my_form', clear_on_submit=True): - user_input = st.text_input("Query:", placeholder="What is this document about?", key='input') - submit_button = st.form_submit_button(label='Send') +""" + +@dataclass +class ChatState: + """Manages chat session state""" + history: List[str] + generated: List[str] + past: List[str] + +class PDFChatInterface: + """Handles PDF chat functionality and UI rendering""" + + def __init__(self): + self.query_engine = st.session_state.get('query_engine') + self.index = st.session_state.get('index') + self.chat_engine = self._initialize_chat_engine() + self.state = self._initialize_state() - # Handle user input and generate response - if submit_button and user_input: - output = conversational_chat(user_input) - st.session_state['past'].append(user_input) - st.session_state['generated'].append(output) - -# Display chat history -if st.session_state['generated']: - with response_container: - for i in range(len(st.session_state['generated'])): - message(st.session_state["past"][i], is_user=True, key=str(i) + '_user', avatar_style="big-ears",seed="missy") - message(st.session_state["generated"][i], key=str(i)) -#st.markdown(response) + def _initialize_chat_engine(self) -> CondenseQuestionChatEngine: + """Initialize the chat engine with custom prompt and history""" + custom_prompt = PromptTemplate(CHAT_PROMPT_TEMPLATE) + + initial_chat_history = [ + ChatMessage( + role=MessageRole.USER, + content='Hello assistant, given is a document. Please answer the question by understanding the context and information of the document. Use your own knowledge and understanding to answer the question.' + ), + ChatMessage( + role=MessageRole.ASSISTANT, + content='Okay, sounds good.' + ) + ] + + return CondenseQuestionChatEngine.from_defaults( + query_engine=self.query_engine, + condense_question_prompt=custom_prompt, + chat_history=initial_chat_history + ) + + def _initialize_state(self) -> ChatState: + """Initialize or retrieve chat session state""" + if 'history' not in st.session_state: + st.session_state['history'] = [] + if 'generated' not in st.session_state: + st.session_state['generated'] = [DEFAULT_GREETING] + if 'past' not in st.session_state: + st.session_state['past'] = [DEFAULT_USER_GREETING] + + return ChatState( + history=st.session_state['history'], + generated=st.session_state['generated'], + past=st.session_state['past'] + ) + + def _handle_chat_response(self, user_input: str) -> str: + """Process user input and generate response""" + try: + streaming_response = self.chat_engine.stream_chat(user_input) + response_tokens = [] + + # Stream response tokens + for token in streaming_response.response_gen: + response_tokens.append(token) + + return ''.join(response_tokens) + except Exception as e: + st.error(f"Error generating response: {str(e)}") + return "I apologize, but I encountered an error processing your request." + + def render_chat_interface(self): + """Render the chat interface components""" + # Create containers for chat layout + response_container = st.container() + input_container = st.container() + + # Render input form + with input_container: + with st.form(key='chat_form', clear_on_submit=True): + user_input = st.text_input( + "Query:", + placeholder=QUERY_PLACEHOLDER, + key='input' + ) + submit_button = st.form_submit_button(label='Send') + + if submit_button and user_input: + output = self._handle_chat_response(user_input) + self.state.past.append(user_input) + self.state.generated.append(output) + + # Update session state + st.session_state['past'] = self.state.past + st.session_state['generated'] = self.state.generated + + # Display chat history + if self.state.generated: + with response_container: + for i in range(len(self.state.generated)): + message( + self.state.past[i], + is_user=True, + key=f"{i}_user", + avatar_style="big-ears", + seed="missy" + ) + message(self.state.generated[i], key=str(i)) + +def main(): + """Main entry point for the chat interface""" + # Initialize sidebar + sidebar_stuff3() + + # Initialize and render chat interface + chat_interface = PDFChatInterface() + chat_interface.render_chat_interface() + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index aef2009..24723e4 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,420 +1,146 @@ -# -# This file is autogenerated by pip-compile with Python 3.11 -# by the following command: -# -# pip-compile requirements.in -# -aiohappyeyeballs==2.4.0 - # via aiohttp -aiohttp==3.10.5 - # via - # llama-index-core - # llama-index-legacy -aiosignal==1.3.1 - # via aiohttp -altair==5.4.1 - # via streamlit -annotated-types==0.7.0 - # via pydantic -anyio==4.4.0 - # via - # httpx - # openai -attrs==24.2.0 - # via - # aiohttp - # jsonschema - # referencing -beautifulsoup4==4.12.3 - # via - # favicon - # llama-index-readers-file -blinker==1.8.2 - # via streamlit -cachetools==5.5.0 - # via streamlit -certifi==2024.8.30 - # via - # httpcore - # httpx - # requests -charset-normalizer==3.3.2 - # via requests -click==8.1.7 - # via - # nltk - # streamlit -contourpy==1.3.0 - # via matplotlib -cycler==0.12.1 - # via matplotlib -dataclasses-json==0.6.7 - # via - # llama-index-core - # llama-index-legacy -deprecated==1.2.14 - # via - # llama-index-core - # llama-index-legacy -dirtyjson==1.0.8 - # via - # llama-index-core - # llama-index-legacy -distro==1.9.0 - # via openai -entrypoints==0.4 - # via streamlit-extras -faker==28.1.0 - # via streamlit-faker -favicon==0.7.0 - # via markdownlit -fonttools==4.53.1 - # via matplotlib -frozenlist==1.4.1 - # via - # aiohttp - # aiosignal -fsspec==2024.6.1 - # via - # llama-index-core - # llama-index-legacy -gitdb==4.0.11 - # via gitpython -gitpython==3.1.43 - # via streamlit -greenlet==3.0.3 - # via sqlalchemy -h11==0.14.0 - # via httpcore -htbuilder==0.6.2 - # via - # markdownlit - # st-annotated-text - # streamlit-extras -httpcore==1.0.5 - # via httpx -httpx==0.27.2 - # via - # llama-cloud - # llama-index-core - # llama-index-legacy - # openai -idna==3.8 - # via - # anyio - # httpx - # requests - # yarl -jinja2==3.1.4 - # via - # altair - # pydeck - # streamlit-camera-input-live - # streamlit-image-coordinates - # streamlit-keyup -jiter==0.5.0 - # via openai -joblib==1.4.2 - # via nltk -jsonschema==4.23.0 - # via altair -jsonschema-specifications==2023.12.1 - # via jsonschema -kiwisolver==1.4.5 - # via matplotlib -llama-cloud==0.0.15 - # via llama-index-indices-managed-llama-cloud -llama-index==0.11.3 - # via -r requirements.in -llama-index-agent-openai==0.3.0 - # via - # llama-index - # llama-index-program-openai -llama-index-cli==0.3.0 - # via llama-index -llama-index-core==0.11.3 - # via - # llama-index - # llama-index-agent-openai - # llama-index-cli - # llama-index-embeddings-openai - # llama-index-indices-managed-llama-cloud - # llama-index-llms-openai - # llama-index-multi-modal-llms-openai - # llama-index-program-openai - # llama-index-question-gen-openai - # llama-index-readers-file - # llama-index-readers-llama-parse - # llama-parse -llama-index-embeddings-openai==0.2.3 - # via - # llama-index - # llama-index-cli -llama-index-indices-managed-llama-cloud==0.3.0 - # via llama-index -llama-index-legacy==0.9.48.post3 - # via llama-index -llama-index-llms-openai==0.2.0 - # via - # llama-index - # llama-index-agent-openai - # llama-index-cli - # llama-index-multi-modal-llms-openai - # llama-index-program-openai - # llama-index-question-gen-openai -llama-index-multi-modal-llms-openai==0.2.0 - # via llama-index -llama-index-program-openai==0.2.0 - # via - # llama-index - # llama-index-question-gen-openai -llama-index-question-gen-openai==0.2.0 - # via llama-index -llama-index-readers-file==0.2.0 - # via llama-index -llama-index-readers-llama-parse==0.2.0 - # via llama-index -llama-parse==0.5.1 - # via llama-index-readers-llama-parse -lxml==5.3.0 - # via markdownlit -markdown==3.7 - # via - # markdownlit - # pymdown-extensions -markdown-it-py==3.0.0 - # via rich -markdownlit==0.0.7 - # via streamlit-extras -markupsafe==2.1.5 - # via jinja2 -marshmallow==3.22.0 - # via dataclasses-json -matplotlib==3.9.2 - # via streamlit-faker -mdurl==0.1.2 - # via markdown-it-py -more-itertools==10.4.0 - # via htbuilder -multidict==6.0.5 - # via - # aiohttp - # yarl -mypy-extensions==1.0.0 - # via typing-inspect -narwhals==1.6.0 - # via altair -nest-asyncio==1.6.0 - # via - # llama-index-core - # llama-index-legacy -networkx==3.3 - # via - # llama-index-core - # llama-index-legacy -nltk==3.9.1 - # via - # llama-index - # llama-index-core - # llama-index-legacy -numpy==1.26.4 - # via - # contourpy - # llama-index-core - # llama-index-legacy - # matplotlib - # pandas - # pyarrow - # pydeck - # streamlit -openai==1.43.0 - # via - # -r requirements.in - # llama-index-agent-openai - # llama-index-embeddings-openai - # llama-index-legacy - # llama-index-llms-openai -packaging==24.1 - # via - # altair - # marshmallow - # matplotlib - # plotly - # streamlit -pandas==2.2.2 - # via - # llama-index-legacy - # llama-index-readers-file - # streamlit -pillow==10.4.0 - # via - # llama-index-core - # matplotlib - # streamlit -plotly==5.24.0 - # via streamlit-extras -prometheus-client==0.20.0 - # via streamlit-extras -protobuf==5.28.0 - # via - # streamlit - # streamlit-extras -pyarrow==17.0.0 - # via streamlit -pydantic==2.8.2 - # via - # llama-cloud - # llama-index-core - # openai -pydantic-core==2.20.1 - # via pydantic -pydeck==0.9.1 - # via streamlit -pygments==2.18.0 - # via rich -pymdown-extensions==10.9 - # via markdownlit -pyparsing==3.1.4 - # via matplotlib -pypdf==4.3.1 - # via - # -r requirements.in - # llama-index-readers-file -python-dateutil==2.9.0.post0 - # via - # faker - # matplotlib - # pandas -pytz==2024.1 - # via pandas -pyyaml==6.0.2 - # via - # llama-index-core - # pymdown-extensions -referencing==0.35.1 - # via - # jsonschema - # jsonschema-specifications -regex==2024.7.24 - # via - # nltk - # tiktoken -requests==2.32.3 - # via - # favicon - # llama-index-core - # llama-index-legacy - # streamlit - # tiktoken -rich==13.8.0 - # via streamlit -rpds-py==0.20.0 - # via - # jsonschema - # referencing -six==1.16.0 - # via python-dateutil -smmap==5.0.1 - # via gitdb -sniffio==1.3.1 - # via - # anyio - # httpx - # openai -soupsieve==2.6 - # via beautifulsoup4 -sqlalchemy[asyncio]==2.0.32 - # via - # llama-index-core - # llama-index-legacy -st-annotated-text==4.0.1 - # via streamlit-extras -st-theme==1.2.3 - # via streamlit-extras -streamlit==1.38.0 - # via - # -r requirements.in - # markdownlit - # st-theme - # streamlit-camera-input-live - # streamlit-card - # streamlit-chat - # streamlit-embedcode - # streamlit-extras - # streamlit-faker - # streamlit-image-coordinates - # streamlit-keyup - # streamlit-toggle-switch - # streamlit-vertical-slider -streamlit-camera-input-live==0.2.0 - # via streamlit-extras -streamlit-card==1.0.2 - # via streamlit-extras -streamlit-chat==0.1.1 - # via -r requirements.in -streamlit-embedcode==0.1.2 - # via streamlit-extras -streamlit-extras==0.4.7 - # via - # -r requirements.in - # markdownlit - # streamlit-faker -streamlit-faker==0.0.3 - # via streamlit-extras -streamlit-image-coordinates==0.1.9 - # via streamlit-extras -streamlit-keyup==0.2.4 - # via streamlit-extras -streamlit-toggle-switch==1.0.2 - # via streamlit-extras -streamlit-vertical-slider==2.5.5 - # via streamlit-extras -striprtf==0.0.26 - # via llama-index-readers-file -tenacity==8.5.0 - # via - # llama-index-core - # llama-index-legacy - # plotly - # streamlit -tiktoken==0.7.0 - # via - # llama-index-core - # llama-index-legacy -toml==0.10.2 - # via streamlit -tornado==6.4.1 - # via streamlit -tqdm==4.66.5 - # via - # llama-index-core - # nltk - # openai -typing-extensions==4.12.2 - # via - # altair - # llama-index-core - # llama-index-legacy - # openai - # pydantic - # pydantic-core - # sqlalchemy - # streamlit - # typing-inspect -typing-inspect==0.9.0 - # via - # dataclasses-json - # llama-index-core - # llama-index-legacy -tzdata==2024.1 - # via pandas -urllib3==2.2.2 - # via requests -validators==0.33.0 - # via streamlit-extras -wrapt==1.16.0 - # via - # deprecated - # llama-index-core -yarl==1.9.6 - # via aiohttp +# +# This file is autogenerated by pip-compile with Python 3.11 +# by the following command: +# +# pip-compile requirements.in +# + +# Core Dependencies +openai==1.43.0 +llama-index==0.11.3 +pypdf==4.3.1 + +# Web Framework & UI +streamlit==1.38.0 +streamlit-chat==0.1.1 +streamlit-extras==0.4.7 +streamlit-camera-input-live==0.2.0 +streamlit-card==1.0.2 +streamlit-embedcode==0.1.2 +streamlit-faker==0.0.3 +streamlit-image-coordinates==0.1.9 +streamlit-keyup==0.2.4 +streamlit-toggle-switch==1.0.2 +streamlit-vertical-slider==2.5.5 + +# LlamaIndex Components +llama-index-core==0.11.3 +llama-index-agent-openai==0.3.0 +llama-index-cli==0.3.0 +llama-index-embeddings-openai==0.2.3 +llama-index-indices-managed-llama-cloud==0.3.0 +llama-index-legacy==0.9.48.post3 +llama-index-llms-openai==0.2.0 +llama-index-multi-modal-llms-openai==0.2.0 +llama-index-program-openai==0.2.0 +llama-index-question-gen-openai==0.2.0 +llama-index-readers-file==0.2.0 +llama-index-readers-llama-parse==0.2.0 + +# Data Processing & ML +numpy==1.26.4 +pandas==2.2.2 +matplotlib==3.9.2 +nltk==3.9.1 +tiktoken==0.7.0 + +# HTTP & API +aiohttp==3.10.5 +httpx==0.27.2 +requests==2.32.3 +urllib3==2.2.2 + +# UI Components & Styling +altair==5.4.1 +plotly==5.24.0 +pillow==10.4.0 +rich==13.8.0 +markdownlit==0.0.7 +st-annotated-text==4.0.1 +st-theme==1.2.3 + +# Utilities +pydantic==2.8.2 +sqlalchemy[asyncio]==2.0.32 +typing-extensions==4.12.2 +pyyaml==6.0.2 +tenacity==8.5.0 +tqdm==4.66.5 + +# Dependencies of Dependencies +aiohappyeyeballs==2.4.0 +aiosignal==1.3.1 +annotated-types==0.7.0 +anyio==4.4.0 +attrs==24.2.0 +beautifulsoup4==4.12.3 +blinker==1.8.2 +cachetools==5.5.0 +certifi==2024.8.30 +charset-normalizer==3.3.2 +click==8.1.7 +contourpy==1.3.0 +cycler==0.12.1 +dataclasses-json==0.6.7 +deprecated==1.2.14 +dirtyjson==1.0.8 +distro==1.9.0 +entrypoints==0.4 +faker==28.1.0 +favicon==0.7.0 +fonttools==4.53.1 +frozenlist==1.4.1 +fsspec==2024.6.1 +gitdb==4.0.11 +gitpython==3.1.43 +greenlet==3.0.3 +h11==0.14.0 +htbuilder==0.6.2 +httpcore==1.0.5 +idna==3.8 +jinja2==3.1.4 +jiter==0.5.0 +joblib==1.4.2 +jsonschema==4.23.0 +jsonschema-specifications==2023.12.1 +kiwisolver==1.4.5 +llama-cloud==0.0.15 +llama-parse==0.5.1 +lxml==5.3.0 +markdown==3.7 +markdown-it-py==3.0.0 +markupsafe==2.1.5 +marshmallow==3.22.0 +mdurl==0.1.2 +more-itertools==10.4.0 +multidict==6.0.5 +mypy-extensions==1.0.0 +narwhals==1.6.0 +nest-asyncio==1.6.0 +networkx==3.3 +packaging==24.1 +prometheus-client==0.20.0 +protobuf==5.28.0 +pyarrow==17.0.0 +pydantic-core==2.20.1 +pydeck==0.9.1 +pygments==2.18.0 +pymdown-extensions==10.9 +pyparsing==3.1.4 +python-dateutil==2.9.0.post0 +pytz==2024.1 +referencing==0.35.1 +regex==2024.7.24 +rpds-py==0.20.0 +six==1.16.0 +smmap==5.0.1 +sniffio==1.3.1 +soupsieve==2.6 +striprtf==0.0.26 +toml==0.10.2 +tornado==6.4.1 +typing-inspect==0.9.0 +tzdata==2024.1 +validators==0.33.0 +wrapt==1.16.0 +yarl==1.9.6 \ No newline at end of file