Route recipe responses through Gemini proxy

This commit is contained in:
“Naeel”
2026-08-29 02:45:44 +03:00
parent 169662566d
commit 9f0974aaa9
5 changed files with 61 additions and 24 deletions
+1 -2
View File
@@ -1,4 +1,3 @@
GEMINI_API_KEY=replace-with-secret
GEMINI_MODEL=gemini-3.6-flash
GEMINI_PROXY_URL=http://95.179.252.111:8768/gemini
GEMINI_GENERATION_CONFIG={"temperature":0,"maxOutputTokens":300,"thinkingConfig":{"thinkingLevel":"minimal"}}
RECIPE_API_TOKEN=replace-with-random-token
+5 -3
View File
@@ -6,9 +6,11 @@
`prompt`. Требуется заголовок `Authorization: Bearer <RECIPE_API_TOKEN>`.
Изображение: JPEG/PNG/WEBP, не более 10 MB.
Ключ Gemini и JSON-строка `GEMINI_GENERATION_CONFIG` находятся только в
серверном EnvironmentFile. Сервис передаёт изображение, prompt и настройки в
Gemini и возвращает `text` и `usage`; разбор рецепта выполняется позднее.
URL немецкого Gemini proxy и JSON-строка `GEMINI_GENERATION_CONFIG` находятся
только в серверном EnvironmentFile. Сервис передаёт изображение, prompt и
настройки в proxy и возвращает `text` и `usage`; разбор рецепта выполняется
позднее. API-ключ Gemini остаётся только на немецкой ВМ и в recipe service не
передаётся.
Токен `RECIPE_API_TOKEN` также хранится только в EnvironmentFile и не
передаётся в Gemini.
+9 -19
View File
@@ -1,4 +1,3 @@
import base64
import json
import os
@@ -9,20 +8,17 @@ 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"
PROXY_URL = "http://95.179.252.111:8768/gemini"
def settings() -> tuple[str, dict]:
key = os.environ.get("GEMINI_API_KEY")
if not key:
raise RuntimeError("GEMINI_API_KEY is not configured")
def settings() -> dict:
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
return config
def authorized() -> bool:
@@ -50,17 +46,11 @@ def recipe():
if len(image_data) > MAX_IMAGE_BYTES:
return jsonify(error="image is too large"), 413
try:
key, config = settings()
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,
},
os.environ.get("GEMINI_PROXY_URL", PROXY_URL),
files={"image": (image.filename or "image", image_data, image.mimetype)},
data={"prompt": prompt, "generation_config": json.dumps(config)},
timeout=180,
)
except (requests.RequestException, RuntimeError) as exc:
@@ -73,6 +63,6 @@ def recipe():
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", {}),
text=data.get("text"),
usage=data.get("usage", {}),
)
+13
View File
@@ -1,3 +1,16 @@
location = /recipe {
proxy_pass http://127.0.0.1:8770/recipe;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
client_max_body_size 10m;
proxy_connect_timeout 10s;
proxy_send_timeout 200s;
proxy_read_timeout 200s;
}
location /recipe/ {
proxy_pass http://127.0.0.1:8770/;
proxy_http_version 1.1;