from flask import Flask, request, jsonify from flask_cors import CORS import sys import os # Add the src/python directory to the path so we can import our modules sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..')) from src.python.vector_store import VectorStore from backend.embedder_wrapper import SyncEmbedder from src.python.config import OPENAI_API_KEY, QDRANT_URL, QDRANT_API_KEY, COLLECTION_NAME import logging # Configure logging logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) app = Flask(__name__) CORS(app, origins=["http://localhost:3000", "http://localhost:5000", "http://127.0.0.1:3000", "http://127.0.0.1:5000"], supports_credentials=True, allow_headers=["Content-Type", "Authorization"]) # Initialize our components try: vector_store = VectorStore() embedder = SyncEmbedder() logger.info("Service initialized successfully") logger.info(f"Connected to Qdrant at: {QDRANT_URL}") except Exception as e: logger.error(f"Failed to initialize services: {str(e)}") raise @app.route('/chat', methods=['POST']) def chat(): """ Chat endpoint that takes user query and returns a response based on RAG Expected JSON format: {"message": "user question"} """ try: data = request.get_json() if not data or 'message' not in data: return jsonify({'error': 'Message field is required'}), 400 user_message = data['message'] # Create embedding for the user message try: query_embedding = embedder.embed_text(user_message) except Exception as e: logger.error(f"Error creating embedding: {str(e)}") return jsonify({'error': f'Error processing your message: {str(e)}'}), 500 # Search for similar documents in the vector store try: similar_docs = vector_store.search_similar(query_embedding, top_k=5) except Exception as e: logger.error(f"Error searching for documents: {str(e)}") return jsonify({'error': f'Error retrieving documents: {str(e)}'}), 500 # Format the retrieved documents as context context = "\n".join([doc['content'] for doc in similar_docs]) # Prepare the prompt for the LLM if context.strip() == "": # If no context is found, let the model respond without it prompt = f""" Please answer the following question. If you don't know the answer, please say so. Question: {user_message} Answer: """ else: prompt = f""" Answer the question based on the context provided. If the answer is not in the context, say "I don't have enough information to answer that question." Context: {context} Question: {user_message} Answer: """ # Use OpenAI API to generate the response try: from openai import OpenAI client = OpenAI(api_key=OPENAI_API_KEY) response = client.chat.completions.create( model="gpt-3.5-turbo", messages=[{"role": "user", "content": prompt}], temperature=0.3, max_tokens=500 ) bot_response = response.choices[0].message.content.strip() except Exception as e: logger.error(f"Error calling LLM: {str(e)}") return jsonify({'error': f'Error generating response: {str(e)}'}), 500 return jsonify({ 'response': bot_response, 'sources': [doc.get('source', '') for doc in similar_docs], 'scores': [doc.get('score', 0.0) for doc in similar_docs], 'retrieved_context': context }) except Exception as e: logger.error(f"Unexpected error in chat endpoint: {str(e)}") return jsonify({'error': str(e)}), 500 @app.route('/health', methods=['GET']) def health(): """Health check endpoint""" return jsonify({'status': 'healthy'}) @app.route('/documents/count', methods=['GET']) def document_count(): """Get the count of documents in the vector store""" try: count = vector_store.get_all_documents_count() return jsonify({'count': count}) except Exception as e: logger.error(f"Error getting document count: {str(e)}") return jsonify({'error': str(e)}), 500 if __name__ == '__main__': # Get port from environment variable, default to 5000 for local development port = int(os.environ.get('PORT', 3000)) app.run(debug=True, host='0.0.0.0', port=port)