File size: 12,319 Bytes
2d8b8bf
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
from dotenv import load_dotenv
load_dotenv()
import os
import chainlit as cl
from llama_index.core import Settings
from llama_index.core import VectorStoreIndex, StorageContext
from llama_index.core.node_parser import SentenceSplitter
from llama_index.vector_stores.milvus import MilvusVectorStore
from llama_index.embeddings.nvidia import NVIDIAEmbedding
from llama_index.llms.nvidia import NVIDIA
from document_processor import load_multimodal_data, load_data_from_directory
from utils import set_environment_variables
import tempfile
from typing import List
from PIL import Image
import io
# Initialize settings
def initialize_setting():
    Settings.embed_model = NVIDIAEmbedding(model="nvidia/nv-embedqa-e5-v5", truncate="END")
    Settings.llm = NVIDIA(model="meta/llama-3.1-70b-instruct")
    Settings.text_splitter = SentenceSplitter(chunk_size=600)

# Create index from documents
def create_index(documents):
    vector_store = MilvusVectorStore(
            token="db_341eca982e73331:Dr8+SXGsfb3Kp4/8",
            host = "https://in03-341eca982e73331.serverless.gcp-us-west1.cloud.zilliz.com",
            port = 19530,
            dim = 1024
    )
    storage_context = StorageContext.from_defaults(vector_store=vector_store)
    return VectorStoreIndex.from_documents(documents, storage_context=storage_context)

async def process_uploaded_files(files: List[cl.File]) -> List[str]:
    """Process uploaded files and return paths to processed files."""
    temp_dir = tempfile.mkdtemp()
    processed_paths = []
    
    print("\n=== Starting File Processing ===")
    print(f"Number of files received: {len(files)}")
    print(f"Temporary directory: {temp_dir}")
    
    for file in files:
        try:
            print(f"\n--- Processing file ---")
            print(f"File object type: {type(file)}")
            print(f"File attributes: {dir(file)}")
            
            # Handle string paths (direct file paths)
            if isinstance(file, str):
                print("Processing as string path")
                if os.path.exists(file):
                    file_name = os.path.basename(file)
                    file_extension = os.path.splitext(file_name)[1].lower()
                    temp_path = os.path.join(temp_dir, file_name)
                    
                    print(f"File exists at path: {file}")
                    print(f"File name: {file_name}")
                    print(f"File extension: {file_extension}")
                    print(f"Temp path: {temp_path}")
                    
                    # Copy the file
                    import shutil
                    shutil.copy2(file, temp_path)
                    
                    if file_extension in ['.png', '.jpg', '.jpeg', '.gif', '.bmp']:
                        await cl.Message(
                            content=f"πŸ“Έ Received image: {file_name}",
                            elements=[cl.Image(path=file, name=file_name, display="inline")]
                        ).send()
                    else:
                        await cl.Message(
                            content=f"πŸ“„ Received file: {file_name}"
                        ).send()
                    
                    processed_paths.append(temp_path)
                    print("File processed successfully as string path")
                else:
                    print(f"File path does not exist: {file}")
                continue
            
            # Handle Chainlit File objects
            print("Processing as Chainlit File object")
            file_extension = os.path.splitext(file.name)[1].lower()
            temp_path = os.path.join(temp_dir, file.name)
            
            print(f"File name: {file.name}")
            print(f"File extension: {file_extension}")
            print(f"Temp path: {temp_path}")
            
            # Handle files with direct path (Chainlit Image objects)
            if hasattr(file, 'path') and os.path.exists(file.path):
                print(f"File has path attribute: {file.path}")
                # For Chainlit Image objects, copy the file
                import shutil
                shutil.copy2(file.path, temp_path)
                print("File copied successfully")
                
                if file_extension in ['.png', '.jpg', '.jpeg', '.gif', '.bmp']:
                    print("Processing as image file")
                    await cl.Message(
                        content=f"πŸ“Έ Received image: {file.name}",
                        elements=[cl.Image(path=file.path, name=file.name, display="inline")]
                    ).send()
                else:
                    print("Processing as non-image file")
                    await cl.Message(
                        content=f"πŸ“„ Received file: {file.name}"
                    ).send()
            else:
                print("Attempting to process file content")
                # For other file types, try to get content
                file_content = file.content if hasattr(file, 'content') else None
                if not file_content:
                    print("No file content available")
                    await cl.Message(
                        content=f"⚠️ Warning: Could not access content for {file.name}"
                    ).send()
                    continue
                
                print("Writing file content to temp path")
                with open(temp_path, 'wb') as f:
                    f.write(file_content)
                
                await cl.Message(
                    content=f"πŸ“„ Received file: {file.name}"
                ).send()
            
            processed_paths.append(temp_path)
            print("File processed successfully")
            
        except Exception as e:
            print(f"Error processing file: {str(e)}")
            print(f"Error type: {type(e)}")
            import traceback
            print(f"Traceback: {traceback.format_exc()}")
            await cl.Message(
                content=f"❌ Error processing file: {str(e)}"
            ).send()
            continue
    
    print("\n=== File Processing Summary ===")
    print(f"Total files processed: {len(processed_paths)}")
    print(f"Processed paths: {processed_paths}")
    return processed_paths

@cl.on_chat_start
async def start():
    """Initialize the chat session."""
    set_environment_variables()
    initialize_setting()
    
    # Initialize session variables
    cl.user_session.set('index', None)
    cl.user_session.set('temp_dir', tempfile.mkdtemp())
    
    # Send welcome message
    await cl.Message(
        content="πŸ‘‹ Welcome! You can:\n"
                "1. Upload images or documents using the paperclip icon\n"
                "2. Ask questions about the uploaded content\n"
                "3. Get detailed analysis including text extraction and scene descriptions"
    ).send()

@cl.on_message
async def main(message: cl.Message):
    """Handle incoming messages and files."""
    
    print("\n=== Starting Message Processing ===")
    print(f"Message type: {type(message)}")
    print(f"Message content: {message.content}")
    print(f"Message elements: {message.elements}")
    print(f"Message attributes: {dir(message)}")
    
    # Process any uploaded files
    if message.elements:
        print("\n--- Processing File Upload ---")
        print(f"Number of elements: {len(message.elements)}")
        print(f"Elements types: {[type(elem) for elem in message.elements]}")
        
        try:
            # Process uploaded files
            print("Starting file processing...")
            processed_paths = await process_uploaded_files(message.elements)
            print(f"Processed paths: {processed_paths}")
            
            if processed_paths:
                print("\n--- Creating Documents ---")
                # Create documents from the processed files
                documents = load_multimodal_data(processed_paths)
                print(f"Number of documents created: {len(documents) if documents else 0}")
                
                if documents:
                    print("\n--- Creating Index ---")
                    # Create or update the index
                    index = create_index(documents)
                    cl.user_session.set('index', index)
                    print("Index created and stored in session")
                    
                    await cl.Message(
                        content="βœ… Files processed successfully! You can now ask questions about the content."
                    ).send()
                else:
                    print("No documents were created")
                    await cl.Message(
                        content="⚠️ No documents were created from the uploaded files."
                    ).send()
                return
            else:
                print("No files were processed successfully")
                await cl.Message(
                    content="⚠️ No files were successfully processed."
                ).send()
                return
                
        except Exception as e:
            print(f"\n!!! Error in file processing !!!")
            print(f"Error type: {type(e)}")
            print(f"Error message: {str(e)}")
            import traceback
            print(f"Traceback: {traceback.format_exc()}")
            await cl.Message(
                content=f"❌ Error processing files: {str(e)}"
            ).send()
            return
    
    # Handle text queries
    if message.content:
        print("\n--- Processing Text Query ---")
        print(f"Query content: {message.content}")
        
        index = cl.user_session.get('index')
        print(f"Index exists: {index is not None}")
        
        if index is None:
            print("No index found in session")
            await cl.Message(
                content="⚠️ Please upload some files first before asking questions."
            ).send()
            return
        
        try:
            print("Creating query engine...")
            # Create message placeholder for streaming
            msg = cl.Message(content="")
            await msg.send()
            
            # Process the query
            query_engine = index.as_query_engine(similarity_top_k=20)
            print("Executing query...")
            response = query_engine.query(message.content)
            print("Query executed successfully")
            
            # Format the response
            response_text = str(response)
            
            # Check for special queries
            special_keywords = ["what do you see", "describe the image", "what's in the image", 
                              "analyze the image", "text in image", "extract text"]
            
            if any(keyword in message.content.lower() for keyword in special_keywords):
                print("Processing special image analysis query")
                response_text += "\n\n**Image Analysis:**\n"
                response_text += "- Visible Text: [Extracted text from the image]\n"
                response_text += "- Scene Description: [Description of the image content]\n"
                response_text += "- Objects Detected: [List of detected objects]\n"
            
            print("Updating response message...")
            # Update the message with the final response
            msg.content=response_text
            await msg.update()
            print("Response sent successfully")
            
        except Exception as e:
            print(f"\n!!! Error in query processing !!!")
            print(f"Error type: {type(e)}")
            print(f"Error message: {str(e)}")
            import traceback
            print(f"Traceback: {traceback.format_exc()}")
            await cl.Message(
                content=f"❌ Error processing query: {str(e)}"
            ).send()

    print("\n=== Message Processing Complete ===\n")

@cl.on_stop
def on_stop():
    """Clean up resources when the chat session ends."""
    # Clean up temporary directory
    temp_dir = cl.user_session.get('temp_dir')
    if temp_dir and os.path.exists(temp_dir):
        try:
            import shutil
            shutil.rmtree(temp_dir)
        except Exception:
            pass

if __name__ == "__main__":
    cl.run()