Compare commits

...

13 Commits

Author SHA1 Message Date
EC2 Default User a76579785f latest fix 2025-06-18 16:44:10 +00:00
owusu 64e22d34fb Merge pull request 'fix server issues' (#7) from dev into main
Reviewed-on: #7
2025-06-18 16:30:42 +00:00
OwusuBlessing 3cae5feee7 fix server issues 2025-06-18 17:27:50 +01:00
owusu cfe4d8b619 Merge pull request 'dev' (#6) from dev into main
Reviewed-on: #6
2025-06-18 16:18:32 +00:00
OwusuBlessing 60d2368be9 added 2025-06-18 17:15:19 +01:00
OwusuBlessing 5b055f2870 added please 2025-06-18 17:09:36 +01:00
owusu 96b3a4407d Merge pull request 'dev' (#5) from dev into main
Reviewed-on: #5
2025-06-18 15:44:41 +00:00
OwusuBlessing cbfdd96412 fix chat issues 2025-06-18 16:39:29 +01:00
OwusuBlessing 95a4d0f035 aded 2025-06-18 16:00:38 +01:00
owusu 86ba5416c2 Merge pull request 'updated start sh' (#4) from dev into main
Reviewed-on: #4
2025-06-11 21:09:41 +00:00
OwusuBlessing 981901f491 updated start sh 2025-06-11 22:07:25 +01:00
owusu 14821cb487 Merge pull request 'added gunicorn' (#3) from dev into main
Reviewed-on: #3
2025-06-11 20:56:51 +00:00
OwusuBlessing 6d14e65907 added gunicorn 2025-06-11 21:53:57 +01:00
7 changed files with 129 additions and 62 deletions
+52 -3
View File
@@ -1,6 +1,6 @@
import os import os
from typing import Optional from typing import Optional
from fastapi import FastAPI, HTTPException, Security, Depends from fastapi import FastAPI, HTTPException, Security, Depends, Request
from fastapi.security import APIKeyHeader from fastapi.security import APIKeyHeader
from fastapi.middleware.cors import CORSMiddleware from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import JSONResponse from fastapi.responses import JSONResponse
@@ -27,9 +27,23 @@ import os
from PyPDF2 import PdfReader from PyPDF2 import PdfReader
from config import QUIZ_TYPES from config import QUIZ_TYPES
from config import Config from config import Config
import logging
import time
# Load environment variables # Load environment variables
load_dotenv() load_dotenv()
API_KEY = os.getenv("API_KEY_ACCESS") from config import Config
API_KEY = Config.API_KEY_ACCESS
# Configure logging
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)
# Also configure uvicorn logger
uvicorn_logger = logging.getLogger("uvicorn.access")
uvicorn_logger.setLevel(logging.INFO)
base_path = os.path.join("data", "config_files") base_path = os.path.join("data", "config_files")
QUESTIONS_PATH = os.path.join(base_path, "questions.json") QUESTIONS_PATH = os.path.join(base_path, "questions.json")
@@ -46,6 +60,40 @@ app = FastAPI(
version="1.0.0" version="1.0.0"
) )
# Add request logging middleware
@app.middleware("http")
async def log_requests(request: Request, call_next):
start_time = time.time()
# Log incoming request (using both logger and print for visibility)
log_msg = f"🔥 INCOMING REQUEST: {request.method} {request.url}"
logger.info(log_msg)
print(log_msg)
# Get request body for POST requests
if request.method == "POST":
body = await request.body()
body_msg = f"🔥 Request Body: {body.decode('utf-8') if body else 'Empty'}"
logger.info(body_msg)
print(body_msg)
# Re-create request with body for downstream processing
async def receive():
return {"type": "http.request", "body": body}
request._receive = receive
response = await call_next(request)
# Log response
process_time = time.time() - start_time
response_msg = f"🔥 RESPONSE: {response.status_code} - Time: {process_time:.4f}s"
logger.info(response_msg)
print(response_msg)
return response
# Add CORS middleware # Add CORS middleware
app.add_middleware( app.add_middleware(
CORSMiddleware, CORSMiddleware,
@@ -68,6 +116,7 @@ async def get_api_key(api_key_header: str = Security(api_key_header)) -> str:
) )
token = api_key_header.split(' ')[1] token = api_key_header.split(' ')[1]
print(f"Token : {token}")
if token != API_KEY: if token != API_KEY:
raise HTTPException( raise HTTPException(
status_code=401, status_code=401,
@@ -288,7 +337,7 @@ async def generate_pdf_endpoint(
if request.form_id: if request.form_id:
print(f"Fetching form response for form_id: {request.form_id}") # Debugging print print(f"Fetching form response for form_id: {request.form_id}") # Debugging print
try: try:
x_api_key = os.getenv("BACKEND_XAPI_KEY") x_api_key = Config.BACKEND_XAPI_KEY
url = f"{backend_base_url}/v3/api/custom/theme-document/answer/{request.form_id}?x-project={x_api_key}" url = f"{backend_base_url}/v3/api/custom/theme-document/answer/{request.form_id}?x-project={x_api_key}"
result = requests.get(url) result = requests.get(url)
result.raise_for_status() # Ensure we raise an error for bad responses result.raise_for_status() # Ensure we raise an error for bad responses
+3
View File
@@ -1,4 +1,7 @@
import os import os
from dotenv import load_dotenv
load_dotenv()
QUIZ_TYPES = { QUIZ_TYPES = {
1: { 1: {
"name": "Single Line Text Inputs", "name": "Single Line Text Inputs",
+1 -1
View File
@@ -1 +1 @@
18759 387857
+20 -52
View File
@@ -2,28 +2,25 @@ import multiprocessing
import os import os
# Server socket # Server socket
bind = "0.0.0.0:5042" # Same port as in your app.py bind = "0.0.0.0:5042"
backlog = 2048 backlog = 2048
# Worker processes # Worker processes
workers = multiprocessing.cpu_count() * 2 + 1 # Recommended formula for worker count workers = 4
worker_class = "uvicorn.workers.UvicornWorker" # Required for FastAPI worker_class = "uvicorn.workers.UvicornWorker"
worker_connections = 1000 worker_connections = 1000
timeout = 30 timeout = 120
keepalive = 2 keepalive = 2
# Process naming
proc_name = "firefighter"
pythonpath = "."
# Logging # Logging
accesslog = "logs/access.log" accesslog = "logs/access.log"
errorlog = "logs/error.log" errorlog = "logs/error.log"
loglevel = "info" loglevel = "info"
# Process naming
proc_name = "fire_fighter_api"
# SSL (uncomment and configure if using HTTPS)
# keyfile = "path/to/keyfile"
# certfile = "path/to/certfile"
# Server mechanics # Server mechanics
daemon = False daemon = False
pidfile = "gunicorn.pid" pidfile = "gunicorn.pid"
@@ -32,46 +29,17 @@ user = None
group = None group = None
tmp_upload_dir = None tmp_upload_dir = None
# Server hooks # Worker lifecycle
def on_starting(server): max_requests = 1000
""" max_requests_jitter = 50
Server startup hook graceful_timeout = 30
""" preload_app = True
# Create logs directory if it doesn't exist
os.makedirs("logs", exist_ok=True)
def post_fork(server, worker): # Debug
""" reload = False
Worker initialization hook reload_engine = "auto"
""" spew = False
server.log.info("Worker spawned (pid: %s)", worker.pid)
def pre_fork(server, worker): # Server mechanics
""" check_config = False
Pre-fork hook preload_app = True
"""
pass
def pre_exec(server):
"""
Pre-exec hook
"""
server.log.info("Forked child, re-executing.")
def when_ready(server):
"""
Server ready hook
"""
server.log.info("Server is ready. Spawning workers")
def worker_int(worker):
"""
Worker interrupt hook
"""
worker.log.info("worker received INT or QUIT signal")
def worker_abort(worker):
"""
Worker abort hook
"""
worker.log.info("worker received SIGABRT signal")
+4
View File
@@ -25,3 +25,7 @@ reportlab==4.3.1
python-docx==1.1.2 python-docx==1.1.2
unstructured==0.17.2 unstructured==0.17.2
pypdf==5.4.0 pypdf==5.4.0
gunicorn==23.0.0
python-dotenv
+46 -6
View File
@@ -62,6 +62,8 @@ def setup_prompt_template(theme: int, resume: str,full_history=None,form_respons
("system", chat_prompt(theme, resume,full_history,form_response,generate_theme)), ("system", chat_prompt(theme, resume,full_history,form_response,generate_theme)),
MessagesPlaceholder(variable_name="messages") MessagesPlaceholder(variable_name="messages")
]) ])
def fetch_conversation_history(conversation_id: str) -> List[Message]: def fetch_conversation_history(conversation_id: str) -> List[Message]:
""" """
Fetch conversation history from the API using the conversation ID. Fetch conversation history from the API using the conversation ID.
@@ -74,18 +76,54 @@ def fetch_conversation_history(conversation_id: str) -> List[Message]:
response = requests.get(url) response = requests.get(url)
response.raise_for_status() # Raise an error for bad responses response.raise_for_status() # Raise an error for bad responses
data = response.json()["data"] # First JSON parse data = response.json()["data"] # First JSON parse
data = json.loads(json.loads(data))
if isinstance(data, str):
print("Data is a string, parsing as JSON...")
data = json.loads(data)
# Parse the API response into Message objects # Parse the API response into Message objects
messages = [] messages = []
for item in data:
role = item.get("role", "unknown") # Check if data exists and is a list
content = item.get("content", "") if data and isinstance(data, list):
timestamp = datetime.now().isoformat() # Use current timestamp if not provided for item in data:
messages.append(Message(role=role, content=content))
# Check if item is a dictionary
if isinstance(item, dict):
role = item.get("role", "unknown")
content = item.get("content", "")
timestamp = datetime.now().isoformat() # Use current timestamp if not provided
messages.append(Message(role=role, content=content))
elif isinstance(item, str):
# If item is a string, it might be JSON that needs parsing
try:
parsed_item = json.loads(item)
if isinstance(parsed_item, dict):
role = parsed_item.get("role", "unknown")
content = parsed_item.get("content", "")
messages.append(Message(role=role, content=content))
else:
print(f"Parsed item is not a dict: {parsed_item}")
except json.JSONDecodeError as json_err:
print(f"Failed to parse JSON string: {item}, error: {json_err}")
else:
print(f"Unexpected item type: {type(item)} for item: {item}")
else:
print(f"No data or data is not a list. Data: {data}")
return messages return messages
except requests.RequestException as e: except requests.RequestException as e:
print(f"Error fetching conversation history: {e}") print(f"Error fetching conversation history: {e}")
return [] return []
except KeyError as e:
print(f"Expected key not found in response: {e}")
return []
except Exception as e:
print(f"Unexpected error: {e}")
return []
def convert_to_langchain_messages(messages: List[Message]) -> List[HumanMessage | AIMessage]: def convert_to_langchain_messages(messages: List[Message]) -> List[HumanMessage | AIMessage]:
@@ -130,6 +168,8 @@ def ai_chat(query: str, conversation_id: str, theme_id: int, resume: str, full_h
# Fetch conversation history from the API # Fetch conversation history from the API
history = fetch_conversation_history(conversation_id) history = fetch_conversation_history(conversation_id)
print(history)
config = {"configurable": {"thread_id": conversation_id}} config = {"configurable": {"thread_id": conversation_id}}
language = "English" language = "English"
+3
View File
@@ -3,5 +3,8 @@
# Create logs directory if it doesn't exist # Create logs directory if it doesn't exist
mkdir -p logs mkdir -p logs
# Activate virtual environment
source venv/bin/activate
# Start Gunicorn with the configuration # Start Gunicorn with the configuration
gunicorn -c gunicorn_config.py app:app gunicorn -c gunicorn_config.py app:app