fix caprover log problem

parent 498fa39d
# gunicorn.conf.py
bind = "0.0.0.0:8000"
worker_class = "uvicorn.workers.UvicornWorker"
workers = 4 # Or read from an environment variable
accesslog = "-"
errorlog = "-"
loglevel = "info"
\ No newline at end of file
...@@ -4,6 +4,7 @@ sounddevice ...@@ -4,6 +4,7 @@ sounddevice
soundfile soundfile
fastapi fastapi
uvicorn[standard] uvicorn[standard]
gunicorn
python-multipart python-multipart
openai openai
psycopg2-binary psycopg2-binary
......
...@@ -2,8 +2,6 @@ ...@@ -2,8 +2,6 @@
import json import json
import base64 import base64
from typing import Optional, Dict from typing import Optional, Dict
# Import the Redis client that all workers will share
from .redis_client import redis_client from .redis_client import redis_client
class ResponseManager: class ResponseManager:
...@@ -14,70 +12,53 @@ class ResponseManager: ...@@ -14,70 +12,53 @@ class ResponseManager:
""" """
def __init__(self): def __init__(self):
"""Initializes by connecting to the shared Redis client."""
if redis_client is None: if redis_client is None:
raise ConnectionError("ResponseManager requires a valid Redis connection.") raise ConnectionError("ResponseManager requires a valid Redis connection.")
self.redis = redis_client self.redis = redis_client
self.ttl_seconds = 600 # A key will expire 10 mins after the LAST item is added self.ttl_seconds = 600 # A student's queue will expire 10 mins after the last item is added
def _get_key(self, student_id: str) -> str: def _get_key(self, student_id: str) -> str:
"""Creates a consistent key for the student's queue.""" """Creates a consistent key for the student's queue."""
return f"student_queue:{student_id}" return f"student_queue:{student_id}"
def store_response(self, student_id: str, text: str, audio_filepath: Optional[str] = None, audio_bytes: Optional[bytes] = None) -> None: def store_response(self, student_id: str, text: str, audio_filepath: Optional[str] = None, audio_bytes: Optional[bytes] = None) -> None:
""" """Adds a new response to the END of the queue for a specific student."""
Adds a new response to the END of the queue for a specific student.
"""
key = self._get_key(student_id) key = self._get_key(student_id)
encoded_audio = base64.b64encode(audio_bytes).decode('utf-8') if audio_bytes else None encoded_audio = base64.b64encode(audio_bytes).decode('utf-8') if audio_bytes else None
payload = { payload = {
"text": text, "text": text,
"audio_filepath": audio_filepath, "audio_filepath": audio_filepath,
"audio_bytes_b64": encoded_audio "audio_bytes_b64": encoded_audio
} }
# RPUSH adds the new item to the right (end) of the list. # RPUSH adds the new item to the right (end) of the list.
self.redis.rpush(key, json.dumps(payload)) self.redis.rpush(key, json.dumps(payload))
# Reset the expiration time for the whole queue each time a new item is added. # Reset the expiration time for the whole queue each time a new item is added.
self.redis.expire(key, self.ttl_seconds) self.redis.expire(key, self.ttl_seconds)
def get_response(self, student_id: str) -> Dict: def get_response(self, student_id: str) -> Dict:
""" """Atomically retrieves and removes the OLDEST response from the front of the queue."""
Atomically retrieves and removes the OLDEST response from the front of the queue.
"""
key = self._get_key(student_id) key = self._get_key(student_id)
# LPOP atomically retrieves and removes the item from the left (start) of the list. # LPOP atomically retrieves and removes the item from the left (start) of the list.
json_value = self.redis.lpop(key) json_value = self.redis.lpop(key)
if not json_value: if not json_value:
return {"text": None, "audio_filepath": None, "audio_bytes": None} return {"text": None, "audio_filepath": None, "audio_bytes": None}
# Decode the payload.
payload = json.loads(json_value) payload = json.loads(json_value)
if payload.get("audio_bytes_b64"): if payload.get("audio_bytes_b64"):
payload["audio_bytes"] = base64.b64decode(payload["audio_bytes_b64"]) payload["audio_bytes"] = base64.b64decode(payload["audio_bytes_b64"])
else: else:
payload["audio_bytes"] = None payload["audio_bytes"] = None
del payload["audio_bytes_b64"] del payload["audio_bytes_b64"]
return payload return payload
def clear_response(self, student_id: str) -> None: def clear_response(self, student_id: str) -> None:
""" """Completely deletes the entire queue for a student."""
Completely deletes the entire queue for a student. Useful for '/clear' endpoints.
"""
key = self._get_key(student_id) key = self._get_key(student_id)
self.redis.delete(key) self.redis.delete(key)
def is_response_fresh(self, student_id: str) -> bool: def is_response_fresh(self, student_id: str) -> bool:
""" """Checks if there are any items in the student's queue."""
Checks if there are any items in the student's queue.
"""
key = self._get_key(student_id) key = self._get_key(student_id)
# LLEN gets the length of the list. If it's > 0, there's a response ready. # LLEN gets the length of the list. If it's > 0, a response is ready.
return self.redis.llen(key) > 0 return self.redis.llen(key) > 0
\ No newline at end of file
...@@ -13,4 +13,4 @@ echo "Curriculum structure setup complete." ...@@ -13,4 +13,4 @@ echo "Curriculum structure setup complete."
sleep 5 sleep 5
# Start the web server and keep it as the main process # Start the web server and keep it as the main process
exec uvicorn main:app --host 0.0.0.0 --port 8000 exec gunicorn -c gunicorn.conf.py main:app
\ No newline at end of file \ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment