HimanshuGoyal2004 commited on
Commit
15c8fbf
Β·
1 Parent(s): fcbf7d5

back to original code

Browse files
Files changed (3) hide show
  1. Dockerfile +12 -56
  2. app.py +88 -180
  3. requirements.txt +8 -19
Dockerfile CHANGED
@@ -1,68 +1,24 @@
1
- # Use Python 3.9 slim image for better compatibility
2
- FROM python:3.9-slim
3
 
4
- # Set environment variables to prevent Python from buffering stdout/stderr
5
- ENV PYTHONDONTWRITEBYTECODE=1
6
- ENV PYTHONUNBUFFERED=1
7
 
8
- # Set the working directory
9
  WORKDIR /app
10
 
11
- # Install system dependencies that might be needed
12
- RUN apt-get update && apt-get install -y \
13
- gcc \
14
- g++ \
15
- curl \
16
- && rm -rf /var/lib/apt/lists/*
17
-
18
- # Create NLTK data directory with proper permissions
19
- RUN mkdir -p /usr/local/nltk_data && chmod 755 /usr/local/nltk_data
20
- ENV NLTK_DATA=/usr/local/nltk_data
21
-
22
- # Copy requirements first for better Docker layer caching
23
- COPY requirements.txt .
24
 
25
- # Install Python dependencies with specific flags for stability
26
- RUN pip install --no-cache-dir --upgrade pip && \
27
- pip install --no-cache-dir -r requirements.txt
28
 
29
- # Download all potentially needed NLTK data during build
30
- # This ensures we have permissions and avoids runtime download issues
31
- RUN python -c "import nltk; \
32
- nltk.download('punkt', download_dir='/usr/local/nltk_data', quiet=True); \
33
- nltk.download('punkt_tab', download_dir='/usr/local/nltk_data', quiet=True); \
34
- nltk.download('stopwords', download_dir='/usr/local/nltk_data', quiet=True); \
35
- nltk.download('averaged_perceptron_tagger', download_dir='/usr/local/nltk_data', quiet=True); \
36
- print('NLTK data download completed successfully')"
37
 
38
- # Verify NLTK data was downloaded correctly
39
- RUN python -c "import nltk; \
40
- try: \
41
- nltk.data.find('tokenizers/punkt'); \
42
- print('NLTK punkt tokenizer found successfully'); \
43
- except LookupError: \
44
- print('Warning: NLTK punkt tokenizer not found'); \
45
- exit(1)"
46
-
47
- # Copy application code
48
- COPY app.py .
49
-
50
- # Create a non-root user for security but ensure they can access NLTK data
51
- RUN useradd -m -u 1000 appuser && \
52
- chown -R appuser:appuser /app && \
53
- chmod -R 755 /usr/local/nltk_data
54
- USER appuser
55
-
56
- # Expose the port
57
  EXPOSE 7860
58
 
59
- # Set Gradio environment variables
60
  ENV GRADIO_SERVER_NAME="0.0.0.0"
61
- ENV GRADIO_SERVER_PORT="7860"
62
-
63
- # Health check to ensure the service is running
64
- HEALTHCHECK --interval=30s --timeout=30s --start-period=60s --retries=3 \
65
- CMD curl -f http://localhost:7860/ || exit 1
66
 
67
- # Run the application
68
  CMD ["python", "app.py"]
 
1
+ # Dockerfile
 
2
 
3
+ # Use the official Python image with the desired version
4
+ FROM python:3.9-slim
 
5
 
6
+ # Set the working directory inside the container
7
  WORKDIR /app
8
 
9
+ # Copy the requirements file to the working directory
10
+ COPY requirements.txt /app
 
 
 
 
 
 
 
 
 
 
 
11
 
12
+ # Install the dependencies
13
+ RUN pip install --no-cache-dir -r requirements.txt
 
14
 
15
+ # Copy the rest of the application code to the working directory
16
+ COPY app.py /app
 
 
 
 
 
 
17
 
18
+ # Expose the port that Gradio will run on (default is 7860)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
19
  EXPOSE 7860
20
 
 
21
  ENV GRADIO_SERVER_NAME="0.0.0.0"
 
 
 
 
 
22
 
23
+ # Command to run your application
24
  CMD ["python", "app.py"]
app.py CHANGED
@@ -1,63 +1,32 @@
1
  import os
2
  import gradio as gr
3
-
4
- # Handle NLTK setup early with proper error handling
5
- try:
6
- import nltk
7
- # Ensure NLTK data is available, try to download if missing
8
- try:
9
- nltk.data.find('tokenizers/punkt')
10
- except LookupError:
11
- print("NLTK punkt tokenizer not found, attempting to download...")
12
- try:
13
- nltk.download('punkt', quiet=True)
14
- nltk.download('punkt_tab', quiet=True)
15
- except Exception as e:
16
- print(f"Warning: Could not download NLTK data: {e}")
17
- print("This may cause issues with text processing")
18
- except ImportError:
19
- print("NLTK not available, continuing without it")
20
-
21
- # Now import LlamaIndex components
22
  from llama_index.core import SimpleDirectoryReader, VectorStoreIndex
23
  from llama_index.embeddings.cohere import CohereEmbedding
24
  from llama_index.llms.groq import Groq
25
  from llama_parse import LlamaParse
26
 
27
- # API keys validation with clearer error messages
 
 
 
28
  llama_cloud_key = os.environ.get("LLAMA_CLOUD_API_KEY")
29
  groq_key = os.environ.get("GROQ_API_KEY")
30
  cohere_key = os.environ.get("COHERE_API_KEY")
 
 
 
 
31
 
32
- if not llama_cloud_key:
33
- raise ValueError("LLAMA_CLOUD_API_KEY environment variable is required")
34
- if not groq_key:
35
- raise ValueError("GROQ_API_KEY environment variable is required")
36
- if not cohere_key:
37
- raise ValueError("COHERE_API_KEY environment variable is required")
38
-
39
- # Model configuration
40
  llm_model_name = "llama3-70b-8192"
41
  embed_model_name = "embed-english-v3.0"
42
 
43
  # Global variable for the vector index
44
  vector_index = None
45
 
46
- # Initialize components with error handling
47
- try:
48
- # Initialize the parser
49
- parser = LlamaParse(api_key=llama_cloud_key, result_type="markdown")
50
-
51
- # Initialize the Cohere embedding model
52
- embed_model = CohereEmbedding(api_key=cohere_key, model_name=embed_model_name)
53
-
54
- # Initialize the LLM
55
- llm = Groq(model=llm_model_name, api_key=groq_key)
56
-
57
- print("All models initialized successfully")
58
- except Exception as e:
59
- print(f"Error initializing models: {e}")
60
- raise
61
 
62
  # Define file extractor with various common extensions
63
  file_extractor = {
@@ -76,169 +45,108 @@ file_extractor = {
76
  ".svg": parser,
77
  }
78
 
 
 
 
 
 
 
 
 
79
  def load_files(file_path: str):
80
- """Process uploaded files and create vector index"""
81
  global vector_index
82
-
83
  if not file_path:
84
  return "No file path provided. Please upload a file."
85
 
86
- # Validate file extension
87
- valid_extensions = list(file_extractor.keys())
88
- if not any(file_path.lower().endswith(ext) for ext in valid_extensions):
89
- valid_exts_str = ', '.join(valid_extensions)
90
- return f"Unsupported file type. Supported types: {valid_exts_str}"
 
 
 
 
 
91
 
92
- try:
93
- # Load and process document
94
- print(f"Processing file: {file_path}")
95
- document = SimpleDirectoryReader(
96
- input_files=[file_path],
97
- file_extractor=file_extractor
98
- ).load_data()
99
-
100
- # Create vector index
101
- vector_index = VectorStoreIndex.from_documents(
102
- document,
103
- embed_model=embed_model
104
- )
105
-
106
- filename = os.path.basename(file_path)
107
- success_msg = f"βœ… Successfully processed: {filename}"
108
- print(success_msg)
109
- return success_msg
110
-
111
- except Exception as e:
112
- error_msg = f"❌ Error processing file: {str(e)}"
113
- print(error_msg)
114
- return error_msg
115
 
 
116
  def respond(message, history):
117
- """Generate responses based on the uploaded document"""
118
  global vector_index
119
-
120
  if vector_index is None:
121
- yield "⚠️ Please upload and process a document first."
122
- return
123
-
124
- if not message.strip():
125
- yield "⚠️ Please enter a question."
126
  return
127
 
128
  try:
129
- # Create query engine
130
  query_engine = vector_index.as_query_engine(streaming=True, llm=llm)
131
  streaming_response = query_engine.query(message)
132
 
133
- # Stream the response
134
  partial_text = ""
135
  for token in streaming_response.response_gen:
136
  partial_text += token
 
137
  yield partial_text
138
-
139
  except Exception as e:
140
- error_msg = f"❌ Error generating response: {str(e)}"
141
- print(error_msg)
142
- yield error_msg
143
 
 
144
  def clear_state():
145
- """Clear all application state"""
146
  global vector_index
147
  vector_index = None
148
- return [None, "State cleared. Ready for new document.", None]
149
-
150
- # Create the Gradio interface with better error handling
151
- def create_interface():
152
- """Create and configure the Gradio interface"""
 
 
 
 
 
 
 
 
153
 
154
- # Use a more conservative theme configuration
155
- theme = gr.themes.Soft(
156
- primary_hue="blue",
157
- secondary_hue="slate",
158
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
159
 
160
- # Create the interface
161
- with gr.Blocks(theme=theme, title="Document Q&A") as demo:
162
- gr.Markdown("# πŸ€–πŸ“ƒ Document Q&A Assistant")
163
- gr.Markdown("Upload a document and ask questions about its content!")
164
-
165
- with gr.Row():
166
- with gr.Column(scale=1, min_width=300):
167
- gr.Markdown("### πŸ“ Document Upload")
168
-
169
- file_input = gr.File(
170
- file_count="single",
171
- type="filepath",
172
- label="Choose Document",
173
- file_types=[".pdf", ".docx", ".doc", ".txt", ".csv", ".xlsx", ".pptx", ".html"]
174
- )
175
-
176
- status_output = gr.Textbox(
177
- label="πŸ“Š Status",
178
- interactive=False,
179
- value="Ready to process documents..."
180
- )
181
-
182
- with gr.Row():
183
- process_btn = gr.Button(
184
- "πŸ”„ Process Document",
185
- variant="primary",
186
- scale=2
187
- )
188
- clear_btn = gr.Button("πŸ—‘οΈ Clear All", scale=1)
189
-
190
- with gr.Column(scale=3):
191
- gr.Markdown("### πŸ’¬ Chat Interface")
192
-
193
- # Use the older ChatInterface syntax for better compatibility
194
- chatbot_interface = gr.ChatInterface(
195
- fn=respond,
196
- chatbot=gr.Chatbot(
197
- height=500,
198
- label="Conversation",
199
- show_copy_button=True
200
- ),
201
- textbox=gr.Textbox(
202
- placeholder="Ask questions about your document...",
203
- container=False,
204
- scale=7
205
- ),
206
- submit_btn="Send",
207
- retry_btn="πŸ”„ Retry",
208
- undo_btn="β†Ά Undo",
209
- clear_btn="πŸ—‘οΈ Clear Chat"
210
- )
211
-
212
- # Wire up the event handlers
213
- process_btn.click(
214
- fn=load_files,
215
- inputs=[file_input],
216
- outputs=[status_output]
217
- )
218
-
219
- clear_btn.click(
220
- fn=clear_state,
221
- outputs=[file_input, status_output, chatbot_interface.chatbot],
222
- queue=False
223
- )
224
-
225
- return demo
226
 
227
- # Main execution
228
  if __name__ == "__main__":
229
- try:
230
- demo = create_interface()
231
- print("Starting Gradio interface...")
232
-
233
- # Launch with more conservative settings
234
- demo.launch(
235
- server_name="0.0.0.0",
236
- server_port=7860,
237
- share=True,
238
- show_error=True, # This helps with debugging
239
- quiet=False # Show startup messages
240
- )
241
-
242
- except Exception as e:
243
- print(f"Failed to start application: {e}")
244
- raise
 
1
  import os
2
  import gradio as gr
3
+ from dotenv import load_dotenv
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4
  from llama_index.core import SimpleDirectoryReader, VectorStoreIndex
5
  from llama_index.embeddings.cohere import CohereEmbedding
6
  from llama_index.llms.groq import Groq
7
  from llama_parse import LlamaParse
8
 
9
+ # Load variables from .env file
10
+ load_dotenv()
11
+
12
+ # API keys
13
  llama_cloud_key = os.environ.get("LLAMA_CLOUD_API_KEY")
14
  groq_key = os.environ.get("GROQ_API_KEY")
15
  cohere_key = os.environ.get("COHERE_API_KEY")
16
+ if not (llama_cloud_key and groq_key and cohere_key):
17
+ raise ValueError(
18
+ "API Keys not found! Ensure they are passed to the Docker container."
19
+ )
20
 
21
+ # models name
 
 
 
 
 
 
 
22
  llm_model_name = "llama3-70b-8192"
23
  embed_model_name = "embed-english-v3.0"
24
 
25
  # Global variable for the vector index
26
  vector_index = None
27
 
28
+ # Initialize the parser
29
+ parser = LlamaParse(api_key=llama_cloud_key, result_type="markdown")
 
 
 
 
 
 
 
 
 
 
 
 
 
30
 
31
  # Define file extractor with various common extensions
32
  file_extractor = {
 
45
  ".svg": parser,
46
  }
47
 
48
+ # Initialize the Cohere embedding model
49
+ embed_model = CohereEmbedding(api_key=cohere_key, model_name=embed_model_name)
50
+
51
+ # Initialize the LLM
52
+ llm = Groq(model="llama3-70b-8192", api_key=groq_key)
53
+
54
+
55
+ # File processing function
56
  def load_files(file_path: str):
 
57
  global vector_index
 
58
  if not file_path:
59
  return "No file path provided. Please upload a file."
60
 
61
+ valid_extensions = ', '.join(file_extractor.keys())
62
+ if not any(file_path.endswith(ext) for ext in file_extractor):
63
+ return f"The parser can only parse the following file types: {valid_extensions}"
64
+
65
+ document = SimpleDirectoryReader(input_files=[file_path], file_extractor=file_extractor).load_data()
66
+ vector_index = VectorStoreIndex.from_documents(document, embed_model=embed_model)
67
+
68
+ print(f"Parsing completed for: {file_path}")
69
+ filename = os.path.basename(file_path)
70
+ return f"Ready to provide responses based on: {filename}"
71
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
72
 
73
+ # Respond function
74
  def respond(message, history):
 
75
  global vector_index
 
76
  if vector_index is None:
77
+ yield "Please upload a file first to begin the chat."
 
 
 
 
78
  return
79
 
80
  try:
81
+ # Create a stateless query engine for each response
82
  query_engine = vector_index.as_query_engine(streaming=True, llm=llm)
83
  streaming_response = query_engine.query(message)
84
 
85
+ # Stream the text response
86
  partial_text = ""
87
  for token in streaming_response.response_gen:
88
  partial_text += token
89
+ # Yield an empty string to cleanup the message textbox and the updated conversation history
90
  yield partial_text
 
91
  except Exception as e:
92
+ print(f"An error occurred during chat: {e}")
93
+ yield "An error occurred while processing your request. Please try again."
94
+
95
 
96
+ # Clear function
97
  def clear_state():
 
98
  global vector_index
99
  vector_index = None
100
+ return [None, None, None]
101
+
102
+
103
+ # UI Setup
104
+ with gr.Blocks(
105
+ theme=gr.themes.Monochrome(
106
+ primary_hue="indigo",
107
+ secondary_hue="blue",
108
+ font=[gr.themes.GoogleFont("Inter"), "system-ui", "sans-serif"],
109
+ ),
110
+ css="footer {visibility: hidden}",
111
+ ) as demo:
112
+ gr.Markdown("# Document Q&A πŸ€–πŸ“ƒ")
113
 
114
+ with gr.Row():
115
+ with gr.Column(scale=1, min_width=300):
116
+ gr.Markdown("### Controls")
117
+ file_input = gr.File(
118
+ file_count="single", type="filepath", label="Upload Document"
119
+ )
120
+ output = gr.Textbox(label="Status", interactive=False)
121
+ with gr.Row():
122
+ btn = gr.Button("1. Process Document", variant="primary", scale=2)
123
+ clear = gr.Button("Clear All", scale=1)
124
+
125
+ with gr.Column(scale=3):
126
+ chatbot = gr.ChatInterface(
127
+ fn=respond,
128
+ chatbot=gr.Chatbot(
129
+ height=500,
130
+ label="Chat Window",
131
+ ),
132
+ textbox=gr.Textbox(
133
+ placeholder="2. Ask questions about the document here...",
134
+ container=False,
135
+ scale=7,
136
+ ),
137
+ submit_btn="Ask",
138
+ show_progress="full",
139
+ )
140
+
141
+ # Set up Gradio interactions
142
+ btn.click(fn=load_files, inputs=file_input, outputs=output)
143
 
144
+ clear.click(
145
+ fn=clear_state, # Use the clear_state function
146
+ outputs=[file_input, output, chatbot],
147
+ queue=False
148
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
149
 
150
+ # Launch the demo
151
  if __name__ == "__main__":
152
+ demo.launch()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
requirements.txt CHANGED
@@ -1,19 +1,8 @@
1
- # Pin Gradio to a stable version that works well with LlamaIndex
2
- gradio>=4.0.0,<5.0.0
3
-
4
- # Pin LlamaIndex core and related packages to compatible versions
5
- llama-index>=0.9.0,<0.11.0
6
- llama-parse>=0.4.0,<0.5.0
7
- llama-index-llms-groq>=0.1.0,<0.2.0
8
- llama-index-embeddings-cohere>=0.1.0,<0.2.0
9
-
10
- # Pin PyTorch to avoid potential conflicts
11
- torch>=2.0.0,<2.2.0
12
- transformers>=4.30.0,<5.0.0
13
-
14
- # Add explicit dependencies that might be causing issues
15
- pydantic>=2.0.0,<3.0.0
16
- fastapi>=0.100.0,<1.0.0
17
-
18
- # Explicitly include NLTK with a compatible version
19
- nltk>=3.8,<4.0
 
1
+ gradio
2
+ python-dotenv
3
+ llama-index
4
+ llama-parse
5
+ llama-index-llms-groq
6
+ llama-index-embeddings-cohere
7
+ torch
8
+ transformers