Codegeass321 commited on
Commit
7036bcd
·
1 Parent(s): 8bf7eee
Files changed (3) hide show
  1. README.md +5 -6
  2. api.py +49 -16
  3. app.py +27 -22
README.md CHANGED
@@ -19,14 +19,13 @@ pinned: false
19
  - `.env.example` — Example environment variables
20
 
21
  ## Port Configuration
22
- - **FastAPI**: Port 8000 (internal)
23
- - **Gradio**: Port 7860 (default for Hugging Face Spaces)
24
 
25
  ## API Endpoints
26
- - **Upload Documents**: `https://codegeass321-backendserver-8000.hf.space/upload`
27
- - **Ask Questions**: `https://codegeass321-backendserver-8000.hf.space/ask`
28
- - **Check Status**: `https://codegeass321-backendserver-8000.hf.space/status`
29
- - **API Documentation**: `https://codegeass321-backendserver-8000.hf.space/docs`
30
 
31
  ## Running Locally
32
  ```sh
 
19
  - `.env.example` — Example environment variables
20
 
21
  ## Port Configuration
22
+ - Single port (7860) with FastAPI mounted at the `/api` path
 
23
 
24
  ## API Endpoints
25
+ - **Upload Documents**: `https://codegeass321-backendserver.hf.space/api/upload`
26
+ - **Ask Questions**: `https://codegeass321-backendserver.hf.space/api/ask`
27
+ - **Check Status**: `https://codegeass321-backendserver.hf.space/api/status`
28
+ - **API Documentation**: `https://codegeass321-backendserver.hf.space/api/docs`
29
 
30
  ## Running Locally
31
  ```sh
api.py CHANGED
@@ -22,6 +22,14 @@ from utils import (
22
 
23
  load_dotenv()
24
 
 
 
 
 
 
 
 
 
25
  app = FastAPI()
26
 
27
  # Define the specific origins that are allowed to make requests to your API
@@ -47,6 +55,15 @@ client = authenticate()
47
  store = {"value": None}
48
 
49
 
 
 
 
 
 
 
 
 
 
50
  @app.options("/upload")
51
  async def options_upload():
52
  return JSONResponse(
@@ -67,9 +84,9 @@ async def upload(files: List[UploadFile] = File(...)):
67
  "Access-Control-Allow-Headers": "Content-Type, Authorization",
68
  }
69
  try:
70
- print(f"Upload request received with {len(files)} files")
71
  for i, file in enumerate(files):
72
- print(f"File {i+1}: {file.filename}, content_type: {file.content_type}")
73
 
74
  if not files:
75
  return JSONResponse(
@@ -79,17 +96,17 @@ async def upload(files: List[UploadFile] = File(...)):
79
  )
80
 
81
  # Explicitly clear memory before processing new files
82
- print("Clearing previous vector store from memory...")
83
  store["value"] = None
84
  gc.collect()
85
- print("Memory cleared.")
86
 
87
- print("Starting document processing...")
88
  try:
89
  raw_docs = load_documents_gradio(files)
90
- print(f"Documents loaded: {len(raw_docs)} documents")
91
  except Exception as doc_error:
92
- print(f"Error loading documents: {doc_error}")
93
  return JSONResponse(
94
  content={"status": "error", "message": f"Error loading documents: {str(doc_error)}"},
95
  status_code=500,
@@ -103,24 +120,24 @@ async def upload(files: List[UploadFile] = File(...)):
103
  headers=headers
104
  )
105
 
106
- print("Documents loaded. Splitting documents...")
107
  try:
108
  chunks = split_documents(raw_docs)
109
- print(f"Documents split into {len(chunks)} chunks")
110
  except Exception as split_error:
111
- print(f"Error splitting documents: {split_error}")
112
  return JSONResponse(
113
  content={"status": "error", "message": f"Error splitting documents: {str(split_error)}"},
114
  status_code=500,
115
  headers=headers
116
  )
117
 
118
- print("Documents split. Building vector store...")
119
  try:
120
  store["value"] = build_vectorstore(chunks)
121
- print("Vector store built successfully.")
122
  except Exception as vector_error:
123
- print(f"Error building vector store: {vector_error}")
124
  return JSONResponse(
125
  content={"status": "error", "message": f"Error building vector store: {str(vector_error)}"},
126
  status_code=500,
@@ -134,8 +151,8 @@ async def upload(files: List[UploadFile] = File(...)):
134
  except Exception as e:
135
  import traceback
136
  error_trace = traceback.format_exc()
137
- print(f"An error occurred during upload: {e}")
138
- print(f"Traceback: {error_trace}")
139
  return JSONResponse(
140
  content={"status": "error", "message": f"An internal server error occurred: {str(e)}"},
141
  status_code=500,
@@ -147,8 +164,10 @@ async def ask(
147
  text: Optional[str] = Form(None),
148
  audio: Optional[UploadFile] = File(None)
149
  ):
 
150
  transcribed = None
151
  if store["value"] is None:
 
152
  return JSONResponse({"status": "error", "message": "Please upload and process a document first."}, status_code=400)
153
  if text and text.strip():
154
  query = text.strip()
@@ -169,13 +188,21 @@ async def ask(
169
  except Exception as e:
170
  return JSONResponse({"status": "error", "message": f"Audio decode failed: {str(e)}"}, status_code=400)
171
  else:
 
172
  return JSONResponse({"status": "error", "message": "Please provide a question by typing or speaking."}, status_code=400)
 
 
 
173
  if store["value"]["chunks"] <= 50:
174
  top_chunks = retrieve_context(query, store["value"])
175
  else:
176
  top_chunks = retrieve_context_approx(query, store["value"])
 
177
  prompt = build_prompt(top_chunks, query)
178
  answer = ask_gemini(prompt, client)
 
 
 
179
  return {"status": "success", "answer": answer.strip(), "transcribed": transcribed}
180
 
181
  @app.get("/status")
@@ -185,11 +212,13 @@ async def status():
185
  import sys
186
  import psutil
187
 
 
 
188
  # Get memory info
189
  process = psutil.Process(os.getpid())
190
  memory_info = process.memory_info()
191
 
192
- return {
193
  "status": "ok",
194
  "message": "Server is running",
195
  "google_api_key_set": bool(os.environ.get("GOOGLE_API_KEY")),
@@ -207,3 +236,7 @@ async def status():
207
  "SYSTEM": os.environ.get("SYSTEM")
208
  }
209
  }
 
 
 
 
 
22
 
23
  load_dotenv()
24
 
25
+ # Configure logging
26
+ import logging
27
+ logging.basicConfig(
28
+ level=logging.INFO,
29
+ format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
30
+ )
31
+ logger = logging.getLogger(__name__)
32
+
33
  app = FastAPI()
34
 
35
  # Define the specific origins that are allowed to make requests to your API
 
55
  store = {"value": None}
56
 
57
 
58
+ @app.get("/")
59
+ async def root():
60
+ """Root endpoint that redirects to status."""
61
+ logger.info("Root endpoint called")
62
+ return {
63
+ "message": "API is running. Use /status, /upload, or /ask endpoints."
64
+ }
65
+
66
+
67
  @app.options("/upload")
68
  async def options_upload():
69
  return JSONResponse(
 
84
  "Access-Control-Allow-Headers": "Content-Type, Authorization",
85
  }
86
  try:
87
+ logger.info(f"Upload request received with {len(files)} files")
88
  for i, file in enumerate(files):
89
+ logger.info(f"File {i+1}: {file.filename}, content_type: {file.content_type}")
90
 
91
  if not files:
92
  return JSONResponse(
 
96
  )
97
 
98
  # Explicitly clear memory before processing new files
99
+ logger.info("Clearing previous vector store from memory...")
100
  store["value"] = None
101
  gc.collect()
102
+ logger.info("Memory cleared.")
103
 
104
+ logger.info("Starting document processing...")
105
  try:
106
  raw_docs = load_documents_gradio(files)
107
+ logger.info(f"Documents loaded: {len(raw_docs)} documents")
108
  except Exception as doc_error:
109
+ logger.error(f"Error loading documents: {doc_error}")
110
  return JSONResponse(
111
  content={"status": "error", "message": f"Error loading documents: {str(doc_error)}"},
112
  status_code=500,
 
120
  headers=headers
121
  )
122
 
123
+ logger.info("Documents loaded. Splitting documents...")
124
  try:
125
  chunks = split_documents(raw_docs)
126
+ logger.info(f"Documents split into {len(chunks)} chunks")
127
  except Exception as split_error:
128
+ logger.error(f"Error splitting documents: {split_error}")
129
  return JSONResponse(
130
  content={"status": "error", "message": f"Error splitting documents: {str(split_error)}"},
131
  status_code=500,
132
  headers=headers
133
  )
134
 
135
+ logger.info("Documents split. Building vector store...")
136
  try:
137
  store["value"] = build_vectorstore(chunks)
138
+ logger.info("Vector store built successfully.")
139
  except Exception as vector_error:
140
+ logger.error(f"Error building vector store: {vector_error}")
141
  return JSONResponse(
142
  content={"status": "error", "message": f"Error building vector store: {str(vector_error)}"},
143
  status_code=500,
 
151
  except Exception as e:
152
  import traceback
153
  error_trace = traceback.format_exc()
154
+ logger.error(f"An error occurred during upload: {e}")
155
+ logger.error(f"Traceback: {error_trace}")
156
  return JSONResponse(
157
  content={"status": "error", "message": f"An internal server error occurred: {str(e)}"},
158
  status_code=500,
 
164
  text: Optional[str] = Form(None),
165
  audio: Optional[UploadFile] = File(None)
166
  ):
167
+ logger.info(f"Ask endpoint called: text={bool(text)}, audio={bool(audio)}")
168
  transcribed = None
169
  if store["value"] is None:
170
+ logger.warning("Ask called but no document is loaded")
171
  return JSONResponse({"status": "error", "message": "Please upload and process a document first."}, status_code=400)
172
  if text and text.strip():
173
  query = text.strip()
 
188
  except Exception as e:
189
  return JSONResponse({"status": "error", "message": f"Audio decode failed: {str(e)}"}, status_code=400)
190
  else:
191
+ logger.warning("Ask called with no text or audio")
192
  return JSONResponse({"status": "error", "message": "Please provide a question by typing or speaking."}, status_code=400)
193
+
194
+ logger.info(f"Processing query: {query[:100]}...")
195
+
196
  if store["value"]["chunks"] <= 50:
197
  top_chunks = retrieve_context(query, store["value"])
198
  else:
199
  top_chunks = retrieve_context_approx(query, store["value"])
200
+
201
  prompt = build_prompt(top_chunks, query)
202
  answer = ask_gemini(prompt, client)
203
+
204
+ logger.info(f"Generated answer: {answer[:100]}...")
205
+
206
  return {"status": "success", "answer": answer.strip(), "transcribed": transcribed}
207
 
208
  @app.get("/status")
 
212
  import sys
213
  import psutil
214
 
215
+ logger.info("Status endpoint called")
216
+
217
  # Get memory info
218
  process = psutil.Process(os.getpid())
219
  memory_info = process.memory_info()
220
 
221
+ status_info = {
222
  "status": "ok",
223
  "message": "Server is running",
224
  "google_api_key_set": bool(os.environ.get("GOOGLE_API_KEY")),
 
236
  "SYSTEM": os.environ.get("SYSTEM")
237
  }
238
  }
239
+
240
+ logger.info(f"Status response: {status_info}")
241
+
242
+ return status_info
app.py CHANGED
@@ -1,26 +1,31 @@
1
  import gradio as gr
2
- from api import app
3
  import uvicorn
4
  import threading
5
  import time
6
  import os
 
7
  from fastapi import FastAPI
8
  from fastapi.middleware.cors import CORSMiddleware
9
  from fastapi.responses import RedirectResponse
10
 
11
- # FastAPI port - used internally
12
- FASTAPI_PORT = 8000
 
 
 
 
13
 
14
- # Create a simple Gradio interface
15
  def create_interface():
16
  with gr.Blocks(title="ChatDocxAI Backend") as interface:
17
  gr.Markdown("# ChatDocxAI Backend")
18
  gr.Markdown(f"""
19
  This is the backend server for ChatDocxAI. It provides the following endpoints:
20
 
21
- - `https://codegeass321-backendserver-8000.hf.space/upload` - Upload documents
22
- - `https://codegeass321-backendserver-8000.hf.space/ask` - Ask questions about uploaded documents
23
- - `https://codegeass321-backendserver-8000.hf.space/status` - Check API status
24
 
25
  The frontend should be configured to communicate with this backend.
26
  """)
@@ -33,26 +38,26 @@ def create_interface():
33
  with gr.Row():
34
  with gr.Column():
35
  gr.Markdown("## API Documentation")
36
- doc_link = gr.HTML(f"<a href='https://codegeass321-backendserver-8000.hf.space/docs' target='_blank'>View FastAPI Docs</a>")
37
 
38
  return interface
39
 
40
- # Set up FastAPI to run on port 8000
41
- def start_fastapi():
42
- uvicorn.run(app, host="0.0.0.0", port=FASTAPI_PORT, log_level="info")
43
 
44
- # Start FastAPI in a separate thread
45
- fastapi_thread = threading.Thread(target=start_fastapi)
46
- fastapi_thread.daemon = True
47
- fastapi_thread.start()
48
 
49
- # Allow some time for FastAPI to start
50
- time.sleep(2)
51
-
52
- # Create and launch the Gradio interface
53
  interface = create_interface()
54
 
55
- # Launch the Gradio interface on the default Hugging Face Spaces port (7860)
 
 
 
 
56
  if __name__ == "__main__":
57
- # Use the port specified by Hugging Face Spaces
58
- interface.launch(server_name="0.0.0.0")
 
1
  import gradio as gr
2
+ from api import app as fastapi_app
3
  import uvicorn
4
  import threading
5
  import time
6
  import os
7
+ import logging
8
  from fastapi import FastAPI
9
  from fastapi.middleware.cors import CORSMiddleware
10
  from fastapi.responses import RedirectResponse
11
 
12
+ # Configure logging
13
+ logging.basicConfig(
14
+ level=logging.INFO,
15
+ format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
16
+ )
17
+ logger = logging.getLogger(__name__)
18
 
19
+ # Create a Gradio app that will also host the FastAPI app
20
  def create_interface():
21
  with gr.Blocks(title="ChatDocxAI Backend") as interface:
22
  gr.Markdown("# ChatDocxAI Backend")
23
  gr.Markdown(f"""
24
  This is the backend server for ChatDocxAI. It provides the following endpoints:
25
 
26
+ - `/api/upload` - Upload documents
27
+ - `/api/ask` - Ask questions about uploaded documents
28
+ - `/api/status` - Check API status
29
 
30
  The frontend should be configured to communicate with this backend.
31
  """)
 
38
  with gr.Row():
39
  with gr.Column():
40
  gr.Markdown("## API Documentation")
41
+ doc_link = gr.HTML(f"<a href='/api/docs' target='_blank'>View FastAPI Docs</a>")
42
 
43
  return interface
44
 
45
+ # Create a new FastAPI app that will mount both the original FastAPI app and the Gradio app
46
+ app = FastAPI()
 
47
 
48
+ # Mount the original FastAPI app under the /api prefix
49
+ logger.info("Mounting FastAPI app at /api")
50
+ app.mount("/api", fastapi_app)
 
51
 
52
+ # Create the Gradio interface
53
+ logger.info("Creating Gradio interface")
 
 
54
  interface = create_interface()
55
 
56
+ # Gradio blocks to FastAPI app
57
+ logger.info("Mounting Gradio app at /")
58
+ app = gr.mount_gradio_app(app, interface, path="/")
59
+
60
+ # When running directly, start the app
61
  if __name__ == "__main__":
62
+ logger.info("Starting server on port 7860")
63
+ uvicorn.run(app, host="0.0.0.0", port=7860, log_level="info")