philipk22 commited on
Commit
b15ca4d
·
1 Parent(s): be4b740

fix app.js and index.html

Browse files
.gitignore CHANGED
@@ -2,5 +2,5 @@ __pycache__/
2
  *.py[cod]
3
  .env
4
  temp_*
5
- *.txt
6
  *.pdf
 
2
  *.py[cod]
3
  .env
4
  temp_*
5
+ note*.txt
6
  *.pdf
Dockerfile CHANGED
@@ -1,15 +1,11 @@
1
- FROM python:3.9-slim
2
 
3
- WORKDIR /app
4
 
5
- # Copy requirements from root directory
6
- COPY requirements.txt .
7
- RUN pip install --no-cache-dir -r requirements.txt
8
 
9
- # Copy backend files
10
- COPY backend/ .
11
 
12
- # Copy frontend files
13
- COPY frontend/dist/ ./static/
14
-
15
- CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "7860"]
 
1
+ FROM python:3.9
2
 
3
+ WORKDIR /code
4
 
5
+ COPY ./requirements.txt /code/requirements.txt
6
+ COPY ./backend /code/backend
7
+ COPY ./frontend/dist /code/frontend/dist
8
 
9
+ RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
 
10
 
11
+ CMD ["uvicorn", "backend.main:app", "--host", "0.0.0.0", "--port", "7860"]
 
 
 
backend/main.py CHANGED
@@ -1,11 +1,17 @@
1
  from fastapi import FastAPI, UploadFile, File, HTTPException
2
  from fastapi.middleware.cors import CORSMiddleware
 
3
  from fastapi.staticfiles import StaticFiles
4
- from fastapi.responses import FileResponse
5
  from pydantic import BaseModel
6
  from typing import List
7
  import os
8
  from dotenv import load_dotenv
 
 
 
 
 
 
9
 
10
  # Import our local utilities instead of aimakerspace
11
  from text_utils import CharacterTextSplitter, TextFileLoader, PDFLoader
@@ -16,10 +22,10 @@ load_dotenv()
16
 
17
  app = FastAPI()
18
 
19
- # Configure CORS
20
  app.add_middleware(
21
  CORSMiddleware,
22
- allow_origins=["*"],
23
  allow_credentials=True,
24
  allow_methods=["*"],
25
  allow_headers=["*"],
@@ -53,11 +59,11 @@ class QueryRequest(BaseModel):
53
  @app.post("/upload")
54
  async def upload_file(file: UploadFile = File(...)):
55
  try:
56
- # Save file temporarily
57
- file_path = f"temp_{file.filename}"
58
- with open(file_path, "wb") as f:
59
  content = await file.read()
60
- f.write(content)
 
61
 
62
  # Process file
63
  loader = PDFLoader(file_path) if file.filename.lower().endswith('.pdf') else TextFileLoader(file_path)
@@ -74,7 +80,7 @@ async def upload_file(file: UploadFile = File(...)):
74
  vector_dbs[session_id] = vector_db
75
 
76
  # Cleanup
77
- os.remove(file_path)
78
 
79
  return {"session_id": session_id, "message": "File processed successfully"}
80
 
@@ -84,11 +90,14 @@ async def upload_file(file: UploadFile = File(...)):
84
  @app.post("/query")
85
  async def query(request: QueryRequest):
86
  try:
 
87
  vector_db = vector_dbs.get(request.session_id)
88
  if not vector_db:
 
89
  raise HTTPException(status_code=404, detail="Session not found")
90
 
91
  # Retrieve context
 
92
  context_list = await vector_db.search_by_text(request.query, k=4)
93
  context_prompt = "\n".join([str(context[0]) for context in context_list])
94
 
@@ -100,9 +109,11 @@ async def query(request: QueryRequest):
100
  )
101
 
102
  # Get response
 
103
  response = await chat_openai.acomplete(
104
  [formatted_system_prompt, formatted_user_prompt]
105
  )
 
106
 
107
  return {
108
  "answer": str(response),
@@ -110,6 +121,7 @@ async def query(request: QueryRequest):
110
  }
111
 
112
  except Exception as e:
 
113
  raise HTTPException(status_code=500, detail=str(e))
114
 
115
  # Optional: Cleanup endpoint
@@ -120,9 +132,14 @@ async def cleanup_session(session_id: str):
120
  return {"message": "Session cleaned up successfully"}
121
  raise HTTPException(status_code=404, detail="Session not found")
122
 
123
- # Serve static files from static directory
124
- app.mount("/static", StaticFiles(directory="static"), name="static")
 
 
 
 
 
 
125
 
126
- @app.get("/")
127
- async def read_root():
128
- return FileResponse('static/index.html')
 
1
  from fastapi import FastAPI, UploadFile, File, HTTPException
2
  from fastapi.middleware.cors import CORSMiddleware
3
+ from fastapi.responses import JSONResponse
4
  from fastapi.staticfiles import StaticFiles
 
5
  from pydantic import BaseModel
6
  from typing import List
7
  import os
8
  from dotenv import load_dotenv
9
+ import tempfile
10
+ import logging
11
+
12
+ # Set up logging
13
+ logging.basicConfig(level=logging.INFO)
14
+ logger = logging.getLogger(__name__)
15
 
16
  # Import our local utilities instead of aimakerspace
17
  from text_utils import CharacterTextSplitter, TextFileLoader, PDFLoader
 
22
 
23
  app = FastAPI()
24
 
25
+ # Configure CORS - update to be more specific
26
  app.add_middleware(
27
  CORSMiddleware,
28
+ allow_origins=["http://localhost:8000", "http://localhost:3000", "http://127.0.0.1:8000", "http://127.0.0.1:3000"],
29
  allow_credentials=True,
30
  allow_methods=["*"],
31
  allow_headers=["*"],
 
59
  @app.post("/upload")
60
  async def upload_file(file: UploadFile = File(...)):
61
  try:
62
+ # Use tempfile instead of direct file writing
63
+ with tempfile.NamedTemporaryFile(delete=False) as temp_file:
 
64
  content = await file.read()
65
+ temp_file.write(content)
66
+ file_path = temp_file.name
67
 
68
  # Process file
69
  loader = PDFLoader(file_path) if file.filename.lower().endswith('.pdf') else TextFileLoader(file_path)
 
80
  vector_dbs[session_id] = vector_db
81
 
82
  # Cleanup
83
+ os.unlink(file_path)
84
 
85
  return {"session_id": session_id, "message": "File processed successfully"}
86
 
 
90
  @app.post("/query")
91
  async def query(request: QueryRequest):
92
  try:
93
+ logger.info(f"Received query request with session_id: {request.session_id}")
94
  vector_db = vector_dbs.get(request.session_id)
95
  if not vector_db:
96
+ logger.error(f"Session not found: {request.session_id}")
97
  raise HTTPException(status_code=404, detail="Session not found")
98
 
99
  # Retrieve context
100
+ logger.info(f"Searching for context with query: {request.query}")
101
  context_list = await vector_db.search_by_text(request.query, k=4)
102
  context_prompt = "\n".join([str(context[0]) for context in context_list])
103
 
 
109
  )
110
 
111
  # Get response
112
+ logger.info("Getting response from OpenAI")
113
  response = await chat_openai.acomplete(
114
  [formatted_system_prompt, formatted_user_prompt]
115
  )
116
+ logger.info(f"Got response: {response}")
117
 
118
  return {
119
  "answer": str(response),
 
121
  }
122
 
123
  except Exception as e:
124
+ logger.error(f"Error processing query: {str(e)}", exc_info=True)
125
  raise HTTPException(status_code=500, detail=str(e))
126
 
127
  # Optional: Cleanup endpoint
 
132
  return {"message": "Session cleaned up successfully"}
133
  raise HTTPException(status_code=404, detail="Session not found")
134
 
135
+ # Keep the root endpoint
136
+ # @app.get("/")
137
+ # async def read_root():
138
+ # return JSONResponse(content={"message": "API is running"})
139
+
140
+ @app.get("/health")
141
+ async def health_check():
142
+ return {"status": "healthy"}
143
 
144
+ # Move this to the end of the file, after all routes
145
+ app.mount("/", StaticFiles(directory="../frontend/dist", html=True), name="static")
 
frontend/dist/index.html CHANGED
@@ -14,17 +14,17 @@
14
  <h2>Upload Document</h2>
15
  <input type="file" id="fileInput" accept=".txt,.pdf">
16
  <button id="uploadButton" onclick="uploadFile()">Upload</button>
17
- <span id="uploadSpinner" class="spinner">Processing...</span>
18
  </div>
19
 
20
- <div id="querySection" class="query-section">
21
  <h2>Ask a Question</h2>
22
  <textarea id="queryInput" placeholder="Enter your question here..."></textarea>
23
  <button id="queryButton" onclick="submitQuery()">Submit Query</button>
24
  <span id="querySpinner" class="spinner">Processing...</span>
25
  </div>
26
 
27
- <div id="responseSection">
28
  <h2>Response</h2>
29
  <div id="answer"></div>
30
  <div class="switch-container">
 
14
  <h2>Upload Document</h2>
15
  <input type="file" id="fileInput" accept=".txt,.pdf">
16
  <button id="uploadButton" onclick="uploadFile()">Upload</button>
17
+ <span id="uploadSpinner" class="spinner" style="display: none;">Processing...</span>
18
  </div>
19
 
20
+ <div class="query-section">
21
  <h2>Ask a Question</h2>
22
  <textarea id="queryInput" placeholder="Enter your question here..."></textarea>
23
  <button id="queryButton" onclick="submitQuery()">Submit Query</button>
24
  <span id="querySpinner" class="spinner">Processing...</span>
25
  </div>
26
 
27
+ <div class="response-section">
28
  <h2>Response</h2>
29
  <div id="answer"></div>
30
  <div class="switch-container">
frontend/dist/js/app.js CHANGED
@@ -28,7 +28,7 @@ async function uploadFile() {
28
  const formData = new FormData();
29
  formData.append('file', file);
30
 
31
- uploadSpinner.style.display = 'inline';
32
 
33
  try {
34
  const response = await fetch('/upload', {
@@ -38,25 +38,23 @@ async function uploadFile() {
38
 
39
  const data = await response.json();
40
  sessionId = data.session_id;
41
- document.getElementById('querySection').style.display = 'block';
42
 
43
  } catch (error) {
44
  alert('Error uploading file: ' + error);
45
  } finally {
46
- uploadSpinner.style.display = 'none';
47
  }
48
  }
49
 
50
  async function submitQuery() {
51
- if (!sessionId) {
52
- alert('Please upload a document first');
 
 
53
  return;
54
  }
55
 
56
- const queryInput = document.getElementById('queryInput');
57
- const querySpinner = document.getElementById('querySpinner');
58
-
59
- querySpinner.style.display = 'inline';
60
 
61
  try {
62
  const response = await fetch('/query', {
@@ -71,13 +69,31 @@ async function submitQuery() {
71
  });
72
 
73
  const data = await response.json();
74
- document.getElementById('answer').textContent = data.answer;
 
 
 
 
 
 
 
 
 
 
 
 
 
75
  document.getElementById('context').innerHTML = data.context.join('<br><br>');
76
 
 
 
 
 
 
77
  } catch (error) {
78
  alert('Error submitting query: ' + error);
79
  } finally {
80
- querySpinner.style.display = 'none';
81
  }
82
  }
83
 
@@ -86,5 +102,18 @@ function toggleContext() {
86
  contextSection.style.display = document.getElementById('contextToggle').checked ? 'block' : 'none';
87
  }
88
 
89
- // Initially hide the query section
90
- document.getElementById('querySection').style.display = 'none';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
28
  const formData = new FormData();
29
  formData.append('file', file);
30
 
31
+ setLoading(true, 'upload');
32
 
33
  try {
34
  const response = await fetch('/upload', {
 
38
 
39
  const data = await response.json();
40
  sessionId = data.session_id;
 
41
 
42
  } catch (error) {
43
  alert('Error uploading file: ' + error);
44
  } finally {
45
+ setLoading(false, 'upload');
46
  }
47
  }
48
 
49
  async function submitQuery() {
50
+ const queryInput = document.getElementById('queryInput');
51
+
52
+ if (!queryInput.value.trim()) {
53
+ alert('Please enter a question');
54
  return;
55
  }
56
 
57
+ setLoading(true, 'query');
 
 
 
58
 
59
  try {
60
  const response = await fetch('/query', {
 
69
  });
70
 
71
  const data = await response.json();
72
+
73
+ // Show response section and its components
74
+ const responseSection = document.querySelector('.response-section');
75
+ responseSection.style.display = 'block';
76
+
77
+ // Update and show answer
78
+ const answerElement = document.getElementById('answer');
79
+ answerElement.textContent = data.answer;
80
+ answerElement.style.display = 'block';
81
+
82
+ // Show switch container
83
+ document.querySelector('.switch-container').style.display = 'block';
84
+
85
+ // Update context
86
  document.getElementById('context').innerHTML = data.context.join('<br><br>');
87
 
88
+ // Show context section if toggle is checked
89
+ if (document.getElementById('contextToggle').checked) {
90
+ document.getElementById('contextSection').style.display = 'block';
91
+ }
92
+
93
  } catch (error) {
94
  alert('Error submitting query: ' + error);
95
  } finally {
96
+ setLoading(false, 'query');
97
  }
98
  }
99
 
 
102
  contextSection.style.display = document.getElementById('contextToggle').checked ? 'block' : 'none';
103
  }
104
 
105
+ // Initialize UI state
106
+ document.addEventListener('DOMContentLoaded', function() {
107
+ // Hide all spinners by default
108
+ const spinners = document.querySelectorAll('.spinner');
109
+ spinners.forEach(spinner => {
110
+ spinner.style.display = 'none';
111
+ });
112
+
113
+ // Show response section but hide context section
114
+ document.querySelector('.response-section').style.display = 'block';
115
+ document.getElementById('contextSection').style.display = 'none';
116
+
117
+ // Show switch container
118
+ document.querySelector('.switch-container').style.display = 'block';
119
+ });
frontend/src/App.js ADDED
@@ -0,0 +1,168 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import React, { useState } from 'react';
2
+ import {
3
+ Container,
4
+ Typography,
5
+ Button,
6
+ TextField,
7
+ Box,
8
+ Paper
9
+ } from '@mui/material';
10
+
11
+ function App() {
12
+ const [file, setFile] = useState(null);
13
+ const [question, setQuestion] = useState('');
14
+ const [response, setResponse] = useState('');
15
+ const [sessionId, setSessionId] = useState('');
16
+ const [error, setError] = useState('');
17
+ const [isProcessing, setIsProcessing] = useState(false);
18
+
19
+ const handleFileUpload = async (event) => {
20
+ const formData = new FormData();
21
+ formData.append('file', event.target.files[0]);
22
+ setError('');
23
+ setIsProcessing(true);
24
+
25
+ try {
26
+ const response = await fetch('http://localhost:8000/upload', {
27
+ method: 'POST',
28
+ body: formData,
29
+ });
30
+ const data = await response.json();
31
+ if (data.session_id) {
32
+ setSessionId(data.session_id);
33
+ console.log('Upload successful, session ID:', data.session_id);
34
+ } else {
35
+ setError('Upload failed: No session ID received');
36
+ console.error('Upload response:', data);
37
+ }
38
+ } catch (error) {
39
+ setError('Upload failed: ' + error.message);
40
+ console.error('Upload error:', error);
41
+ } finally {
42
+ setIsProcessing(false);
43
+ }
44
+ };
45
+
46
+ const handleQuestionSubmit = async () => {
47
+ if (!sessionId) {
48
+ setError('Please upload a document first');
49
+ return;
50
+ }
51
+ setError('');
52
+ setResponse(''); // Clear previous response
53
+
54
+ try {
55
+ console.log('Sending query with session ID:', sessionId);
56
+ const response = await fetch('http://localhost:8000/query', {
57
+ method: 'POST',
58
+ headers: {
59
+ 'Content-Type': 'application/json',
60
+ 'Accept': 'application/json',
61
+ },
62
+ body: JSON.stringify({
63
+ session_id: sessionId,
64
+ query: question
65
+ }),
66
+ });
67
+
68
+ console.log('Response status:', response.status);
69
+ const responseText = await response.text();
70
+ console.log('Raw response:', responseText);
71
+
72
+ if (!response.ok) {
73
+ throw new Error(`HTTP error! status: ${response.status}, body: ${responseText}`);
74
+ }
75
+
76
+ const data = JSON.parse(responseText);
77
+ console.log('Parsed response data:', data);
78
+
79
+ if (data.answer) {
80
+ console.log('Setting response:', data.answer);
81
+ setResponse(data.answer);
82
+ console.log('Response state after setting:', data.answer);
83
+ } else {
84
+ setError('No answer received from the server');
85
+ console.error('Unexpected response format:', data);
86
+ }
87
+ } catch (error) {
88
+ setError('Query failed: ' + error.message);
89
+ console.error('Query error:', error);
90
+ }
91
+ };
92
+
93
+ return (
94
+ <Container maxWidth="md">
95
+ <Box sx={{ my: 4 }}>
96
+ <Typography variant="h4" component="h1" gutterBottom>
97
+ RAG Application
98
+ </Typography>
99
+
100
+ <Paper sx={{ p: 2, mb: 2 }}>
101
+ <Typography variant="h6" gutterBottom>
102
+ Upload Document
103
+ </Typography>
104
+ <Button
105
+ variant="contained"
106
+ component="label"
107
+ disabled={isProcessing}
108
+ >
109
+ {isProcessing ? 'Processing...' : 'Choose File'}
110
+ <input
111
+ type="file"
112
+ hidden
113
+ onChange={handleFileUpload}
114
+ disabled={isProcessing}
115
+ />
116
+ </Button>
117
+ <Typography sx={{ mt: 1, color: isProcessing ? 'info.main' : 'success.main' }}>
118
+ {isProcessing ? 'Processing document...' :
119
+ sessionId ? 'Document uploaded and processed successfully!' :
120
+ 'No document uploaded yet'}
121
+ </Typography>
122
+ </Paper>
123
+
124
+ <Paper sx={{ p: 2, mb: 2 }}>
125
+ <Typography variant="h6" gutterBottom>
126
+ Ask a Question
127
+ </Typography>
128
+ <TextField
129
+ fullWidth
130
+ multiline
131
+ rows={2}
132
+ value={question}
133
+ onChange={(e) => setQuestion(e.target.value)}
134
+ sx={{ mb: 2 }}
135
+ placeholder="Enter your question here..."
136
+ />
137
+ <Button
138
+ variant="contained"
139
+ onClick={handleQuestionSubmit}
140
+ disabled={!sessionId || isProcessing}
141
+ >
142
+ Submit Query
143
+ </Button>
144
+ </Paper>
145
+
146
+ <Paper sx={{ p: 2 }}>
147
+ <Typography variant="h6" gutterBottom>
148
+ Response
149
+ </Typography>
150
+
151
+ <Box sx={{ mb: 2 }}>
152
+ {error && (
153
+ <Typography sx={{ color: 'error.main', mb: 2 }}>
154
+ {error}
155
+ </Typography>
156
+ )}
157
+
158
+ <Typography sx={{ whiteSpace: 'pre-wrap', wordBreak: 'break-word' }}>
159
+ {response || "No response yet. Please upload a document and ask a question."}
160
+ </Typography>
161
+ </Box>
162
+ </Paper>
163
+ </Box>
164
+ </Container>
165
+ );
166
+ }
167
+
168
+ export default App;
requirements.txt CHANGED
@@ -6,5 +6,4 @@ openai
6
  pydantic
7
  pypdf
8
  langchain
9
- faiss-cpu
10
  PyPDF2
 
6
  pydantic
7
  pypdf
8
  langchain
 
9
  PyPDF2