Add image upload and vision analysis to Ask Simba chat

Users can now attach images in the web chat for Simba to analyze using
Ollama's gemma3 vision model. Images are stored in Garage (S3-compatible)
and displayed in chat history.

Also fixes aerich migration config by extracting TORTOISE_CONFIG into a
standalone config/db.py module, removing the stale aerich_config.py, and
adding missing MODELS_STATE to migration 3.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-04 08:03:19 -04:00
parent ac9c821ec7
commit 0415610d64
17 changed files with 501 additions and 58 deletions

53
utils/s3_client.py Normal file
View File

@@ -0,0 +1,53 @@
import os
import logging
import aioboto3
from dotenv import load_dotenv
load_dotenv()
logging.basicConfig(level=logging.INFO)
S3_ENDPOINT_URL = os.getenv("S3_ENDPOINT_URL")
S3_ACCESS_KEY_ID = os.getenv("S3_ACCESS_KEY_ID")
S3_SECRET_ACCESS_KEY = os.getenv("S3_SECRET_ACCESS_KEY")
S3_BUCKET_NAME = os.getenv("S3_BUCKET_NAME", "asksimba-images")
S3_REGION = os.getenv("S3_REGION", "garage")
session = aioboto3.Session()
def _get_client():
return session.client(
"s3",
endpoint_url=S3_ENDPOINT_URL,
aws_access_key_id=S3_ACCESS_KEY_ID,
aws_secret_access_key=S3_SECRET_ACCESS_KEY,
region_name=S3_REGION,
)
async def upload_image(file_bytes: bytes, key: str, content_type: str) -> str:
async with _get_client() as client:
await client.put_object(
Bucket=S3_BUCKET_NAME,
Key=key,
Body=file_bytes,
ContentType=content_type,
)
logging.info(f"Uploaded image to S3: {key}")
return key
async def get_image(key: str) -> tuple[bytes, str]:
async with _get_client() as client:
response = await client.get_object(Bucket=S3_BUCKET_NAME, Key=key)
body = await response["Body"].read()
content_type = response.get("ContentType", "image/jpeg")
return body, content_type
async def delete_image(key: str) -> None:
async with _get_client() as client:
await client.delete_object(Bucket=S3_BUCKET_NAME, Key=key)
logging.info(f"Deleted image from S3: {key}")