main.py (73 lines of code) (raw):
import logging
import os
from io import BytesIO
from fastapi import Response
from fastapi.responses import StreamingResponse
from chainlit.server import app as chainlit_app
from connectors import BlobClient
# Logging configuration
logging.basicConfig(level=os.environ.get('LOGLEVEL', 'INFO').upper(), force=True)
logging.getLogger("azure").setLevel(os.environ.get('AZURE_LOGLEVEL', 'WARNING').upper())
logging.getLogger("httpx").setLevel(os.environ.get('HTTPX_LOGLEVEL', 'ERROR').upper())
logging.getLogger("httpcore").setLevel(os.environ.get('HTTPCORE_LOGLEVEL', 'ERROR').upper())
logging.getLogger("urllib3").setLevel(os.environ.get('URLLIB3_LOGLEVEL', 'WARNING').upper())
logging.getLogger("urllib3.connectionpool").setLevel(os.environ.get('URLLIB3_CONNECTIONPOOL_LOGLEVEL', 'WARNING').upper())
logging.getLogger("uvicorn.error").propagate = True
logging.getLogger("uvicorn.access").propagate = True
def get_env_var(var_name: str) -> str:
"""Retrieve required environment variable or raise error."""
value = os.getenv(var_name)
if not value:
raise EnvironmentError(f"{var_name} is not set.")
return value
def download_from_blob(file_name: str) -> bytes:
logging.info("[chainlit_app] Downloading file: %s", file_name)
blob_url = f"https://{account_name}.blob.core.windows.net/{file_name}"
logging.debug(f"[chainlit_app] Constructed blob URL: {blob_url}")
try:
blob_client = BlobClient(blob_url=blob_url)
blob_data = blob_client.download_blob()
logging.debug(f"[chainlit_app] Successfully downloaded blob data: {file_name}")
return blob_data
except Exception as e:
logging.error(f"[chainlit_app] Error downloading blob {file_name}: {e}")
raise
account_name = get_env_var("STORAGE_ACCOUNT")
documents_container = get_env_var("STORAGE_CONTAINER")
images_container = get_env_var("STORAGE_CONTAINER_IMAGES")
def handle_file_download(file_path: str):
try:
file_bytes = download_from_blob(file_path)
if not file_bytes:
return Response("File not found or empty.", status_code=404, media_type="text/plain")
except Exception as e:
error_message = str(e)
status_code = 404 if "BlobNotFound" in error_message else 500
logging.exception(f"[chainlit_app] Download error: {error_message}")
return Response(
f"{'Blob not found' if status_code == 404 else 'Internal server error'}: {error_message}.",
status_code=status_code,
media_type="text/plain"
)
actual_file_name = os.path.basename(file_path)
return StreamingResponse(
BytesIO(file_bytes),
media_type="application/octet-stream",
headers={"Content-Disposition": f'attachment; filename="{actual_file_name}"'}
)
# TODO: Validate blob metadata_security_id to prevent unauthorized access.
@chainlit_app.get(f"/{documents_container}/" + "{file_path:path}")
def download_document(file_path: str):
return handle_file_download(f"{documents_container}/{file_path}")
@chainlit_app.get(f"/{images_container}/" + "{file_path:path}")
def download_image(file_path: str):
return handle_file_download(f"{images_container}/{file_path}")
# -----------------------------------
# Ensure source routes are prioritized
# -----------------------------------
try:
images_route = next(route for route in chainlit_app.router.routes if getattr(route, "path", "").startswith(f"/{documents_container}/"))
chainlit_app.router.routes.remove(images_route)
chainlit_app.router.routes.insert(0, images_route)
documents_route = next(route for route in chainlit_app.router.routes if getattr(route, "path", "").startswith(f"/{images_container}/"))
chainlit_app.router.routes.remove(documents_route)
chainlit_app.router.routes.insert(0, documents_route)
logging.info("[chainlit_app] Moved source routes to the top of the route list.")
except StopIteration:
logging.warning("[chainlit_app] source route not found; skipping reorder.")
# Import Chainlit event handlers (side-effect registration)
import app
# ASGI entry point
app = chainlit_app