70 lines
2.4 KiB
Python
70 lines
2.4 KiB
Python
import base64
|
|
import json
|
|
import os
|
|
|
|
import requests
|
|
from flask import Flask, jsonify, request
|
|
|
|
|
|
app = Flask(__name__)
|
|
MAX_IMAGE_BYTES = 10 * 1024 * 1024
|
|
ALLOWED_TYPES = {"image/jpeg", "image/png", "image/webp"}
|
|
GEMINI_URL = "https://generativelanguage.googleapis.com/v1beta/models"
|
|
|
|
|
|
def settings() -> tuple[str, dict]:
|
|
key = os.environ.get("GEMINI_API_KEY")
|
|
if not key:
|
|
raise RuntimeError("GEMINI_API_KEY is not configured")
|
|
try:
|
|
config = json.loads(os.environ.get("GEMINI_GENERATION_CONFIG", "{}"))
|
|
except json.JSONDecodeError as exc:
|
|
raise RuntimeError("GEMINI_GENERATION_CONFIG is invalid") from exc
|
|
if not isinstance(config, dict):
|
|
raise RuntimeError("GEMINI_GENERATION_CONFIG must be an object")
|
|
return key, config
|
|
|
|
|
|
@app.get("/health")
|
|
def health():
|
|
return jsonify(status="ok")
|
|
|
|
|
|
@app.post("/recipe")
|
|
def recipe():
|
|
image = request.files.get("image")
|
|
prompt = request.form.get("prompt")
|
|
if image is None or not prompt:
|
|
return jsonify(error="image and prompt are required"), 400
|
|
if image.mimetype not in ALLOWED_TYPES:
|
|
return jsonify(error="unsupported image type"), 415
|
|
image_data = image.read(MAX_IMAGE_BYTES + 1)
|
|
if len(image_data) > MAX_IMAGE_BYTES:
|
|
return jsonify(error="image is too large"), 413
|
|
try:
|
|
key, config = settings()
|
|
response = requests.post(
|
|
f"{GEMINI_URL}/{os.environ.get('GEMINI_MODEL', 'gemini-3.6-flash')}:generateContent",
|
|
params={"key": key},
|
|
json={
|
|
"contents": [{"parts": [{"text": prompt}, {"inline_data": {
|
|
"mime_type": image.mimetype,
|
|
"data": base64.b64encode(image_data).decode("ascii"),
|
|
}}]}],
|
|
"generationConfig": config,
|
|
},
|
|
timeout=180,
|
|
)
|
|
except (requests.RequestException, RuntimeError) as exc:
|
|
return jsonify(error=str(exc) if isinstance(exc, RuntimeError) else "Gemini unavailable"), 503
|
|
if response.status_code != 200:
|
|
try:
|
|
detail = response.json().get("error", {}).get("message", "Gemini request failed")
|
|
except ValueError:
|
|
detail = "Gemini request failed"
|
|
return jsonify(error=detail), 502
|
|
data = response.json()
|
|
return jsonify(
|
|
text=data.get("candidates", [{}])[0].get("content", {}).get("parts", [{}])[0].get("text"),
|
|
usage=data.get("usageMetadata", {}),
|
|
) |