Compare commits
15 Commits
bf2379e39d
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| a76579785f | |||
| 64e22d34fb | |||
| 3cae5feee7 | |||
| cfe4d8b619 | |||
| 60d2368be9 | |||
| 5b055f2870 | |||
| 96b3a4407d | |||
| cbfdd96412 | |||
| 95a4d0f035 | |||
| 86ba5416c2 | |||
| 981901f491 | |||
| 14821cb487 | |||
| 6d14e65907 | |||
| 34ddbd50dd | |||
| 95d99f7ce9 |
@@ -1,6 +1,6 @@
|
||||
import os
|
||||
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.middleware.cors import CORSMiddleware
|
||||
from fastapi.responses import JSONResponse
|
||||
@@ -27,9 +27,23 @@ import os
|
||||
from PyPDF2 import PdfReader
|
||||
from config import QUIZ_TYPES
|
||||
from config import Config
|
||||
import logging
|
||||
import time
|
||||
# Load environment variables
|
||||
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")
|
||||
QUESTIONS_PATH = os.path.join(base_path, "questions.json")
|
||||
@@ -46,6 +60,40 @@ app = FastAPI(
|
||||
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
|
||||
app.add_middleware(
|
||||
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]
|
||||
print(f"Token : {token}")
|
||||
if token != API_KEY:
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
@@ -288,7 +337,7 @@ async def generate_pdf_endpoint(
|
||||
if request.form_id:
|
||||
print(f"Fetching form response for form_id: {request.form_id}") # Debugging print
|
||||
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}"
|
||||
result = requests.get(url)
|
||||
result.raise_for_status() # Ensure we raise an error for bad responses
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
import os
|
||||
from dotenv import load_dotenv
|
||||
|
||||
load_dotenv()
|
||||
QUIZ_TYPES = {
|
||||
1: {
|
||||
"name": "Single Line Text Inputs",
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
18759
|
||||
387857
|
||||
|
||||
+20
-52
@@ -2,28 +2,25 @@ import multiprocessing
|
||||
import os
|
||||
|
||||
# Server socket
|
||||
bind = "0.0.0.0:5042" # Same port as in your app.py
|
||||
bind = "0.0.0.0:5042"
|
||||
backlog = 2048
|
||||
|
||||
# Worker processes
|
||||
workers = multiprocessing.cpu_count() * 2 + 1 # Recommended formula for worker count
|
||||
worker_class = "uvicorn.workers.UvicornWorker" # Required for FastAPI
|
||||
workers = 4
|
||||
worker_class = "uvicorn.workers.UvicornWorker"
|
||||
worker_connections = 1000
|
||||
timeout = 30
|
||||
timeout = 120
|
||||
keepalive = 2
|
||||
|
||||
# Process naming
|
||||
proc_name = "firefighter"
|
||||
pythonpath = "."
|
||||
|
||||
# Logging
|
||||
accesslog = "logs/access.log"
|
||||
errorlog = "logs/error.log"
|
||||
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
|
||||
daemon = False
|
||||
pidfile = "gunicorn.pid"
|
||||
@@ -32,46 +29,17 @@ user = None
|
||||
group = None
|
||||
tmp_upload_dir = None
|
||||
|
||||
# Server hooks
|
||||
def on_starting(server):
|
||||
"""
|
||||
Server startup hook
|
||||
"""
|
||||
# Create logs directory if it doesn't exist
|
||||
os.makedirs("logs", exist_ok=True)
|
||||
# Worker lifecycle
|
||||
max_requests = 1000
|
||||
max_requests_jitter = 50
|
||||
graceful_timeout = 30
|
||||
preload_app = True
|
||||
|
||||
def post_fork(server, worker):
|
||||
"""
|
||||
Worker initialization hook
|
||||
"""
|
||||
server.log.info("Worker spawned (pid: %s)", worker.pid)
|
||||
# Debug
|
||||
reload = False
|
||||
reload_engine = "auto"
|
||||
spew = False
|
||||
|
||||
def pre_fork(server, worker):
|
||||
"""
|
||||
Pre-fork hook
|
||||
"""
|
||||
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")
|
||||
# Server mechanics
|
||||
check_config = False
|
||||
preload_app = True
|
||||
|
||||
@@ -25,3 +25,7 @@ reportlab==4.3.1
|
||||
python-docx==1.1.2
|
||||
unstructured==0.17.2
|
||||
pypdf==5.4.0
|
||||
gunicorn==23.0.0
|
||||
python-dotenv
|
||||
|
||||
|
||||
|
||||
Regular → Executable
+20
-9
@@ -2,7 +2,7 @@
|
||||
|
||||
# Configuration
|
||||
REPO_URL="http://owusu:890eccfcea010beb94a0adba246aaf9258330b70@23.29.118.76:3000/owusu/ds-fire-fighter.git"
|
||||
APP_DIR="/home/owusu/ds-fire-fighter"
|
||||
APP_DIR="/home/ec2-user/ds-fire-fighter"
|
||||
BRANCH="main"
|
||||
PYTHON_VERSION="3.11"
|
||||
WORKERS=4
|
||||
@@ -61,11 +61,22 @@ command_exists() {
|
||||
# Function to install Python 3.11
|
||||
install_python() {
|
||||
log "INFO" "Installing Python ${PYTHON_VERSION}..."
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y software-properties-common
|
||||
sudo add-apt-repository -y ppa:deadsnakes/ppa
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y python${PYTHON_VERSION} python${PYTHON_VERSION}-venv python${PYTHON_VERSION}-dev
|
||||
sudo yum update -y
|
||||
sudo yum groupinstall -y "Development Tools"
|
||||
sudo yum install -y openssl-devel bzip2-devel libffi-devel xz-devel
|
||||
|
||||
# Install Python 3.11 from source
|
||||
cd /tmp
|
||||
wget https://www.python.org/ftp/python/3.11.0/Python-3.11.0.tgz
|
||||
tar xzf Python-3.11.0.tgz
|
||||
cd Python-3.11.0
|
||||
./configure --enable-optimizations
|
||||
make -j $(nproc)
|
||||
sudo make altinstall
|
||||
|
||||
# Create symlink for python3.11
|
||||
sudo ln -sf /usr/local/bin/python3.11 /usr/bin/python3.11
|
||||
sudo ln -sf /usr/local/bin/pip3.11 /usr/bin/pip3.11
|
||||
}
|
||||
|
||||
# Function to setup git repository
|
||||
@@ -86,7 +97,7 @@ setup_repo() {
|
||||
setup_venv() {
|
||||
log "INFO" "Setting up virtual environment..."
|
||||
if [ ! -d "$APP_DIR/venv" ]; then
|
||||
python${PYTHON_VERSION} -m venv $APP_DIR/venv
|
||||
python3.11 -m venv $APP_DIR/venv
|
||||
fi
|
||||
source $APP_DIR/venv/bin/activate
|
||||
pip install --upgrade pip
|
||||
@@ -154,7 +165,7 @@ Description=Fire Fighter Interview API
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
User=$USER
|
||||
User=ec2-user
|
||||
WorkingDirectory=$APP_DIR
|
||||
Environment="PATH=$APP_DIR/venv/bin"
|
||||
Environment="PYTHONPATH=$APP_DIR"
|
||||
@@ -176,7 +187,7 @@ main() {
|
||||
log "INFO" "Starting deployment process..."
|
||||
|
||||
# Check and install Python if needed
|
||||
if ! command_exists python${PYTHON_VERSION}; then
|
||||
if ! command_exists python3.11; then
|
||||
install_python
|
||||
fi
|
||||
|
||||
|
||||
+46
-6
@@ -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)),
|
||||
MessagesPlaceholder(variable_name="messages")
|
||||
])
|
||||
|
||||
|
||||
def fetch_conversation_history(conversation_id: str) -> List[Message]:
|
||||
"""
|
||||
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.raise_for_status() # Raise an error for bad responses
|
||||
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
|
||||
messages = []
|
||||
for item in data:
|
||||
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))
|
||||
|
||||
# Check if data exists and is a list
|
||||
if data and isinstance(data, list):
|
||||
for item in data:
|
||||
|
||||
|
||||
# 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
|
||||
|
||||
except requests.RequestException as e:
|
||||
print(f"Error fetching conversation history: {e}")
|
||||
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]:
|
||||
@@ -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
|
||||
history = fetch_conversation_history(conversation_id)
|
||||
|
||||
print(history)
|
||||
|
||||
config = {"configurable": {"thread_id": conversation_id}}
|
||||
language = "English"
|
||||
|
||||
Reference in New Issue
Block a user