Files
ph_api/README.md
T

104 lines
5.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# phproxy
An asynchronous proxy for image generation through Google Gemini or OpenRouter. It supports multipart uploads, key rotation, direct long-running responses, and SQLite-backed polling jobs.
## Start locally
Requirements: Python 3.12 or newer. Uploaded images are decoded and re-encoded with Pillow; only JPEG, PNG, and WebP are accepted.
```sh
python -m venv .venv
. .venv/bin/activate
pip install -e .
cp .env.example .env
# Set ADMIN_SECRET to a unique random value before starting.
set -a && . ./.env && set +a
uvicorn app.main:app --host 127.0.0.1 --port 8000 --workers 1
```
The service refuses to start without `ADMIN_SECRET`. It creates the SQLite database and data directory at startup. Open `http://127.0.0.1:8000/ui` for basic administration. The first client and provider key can be created through the UI or API.
## Configure a client
Create a client as an administrator:
```sh
curl -X POST http://127.0.0.1:8000/api/admin/client \
-H "x-admin-secret: $ADMIN_SECRET" \
-F client_id=booth-01 -F name='Booth 01' -F client_secret='choose-a-client-secret'
```
Add an upstream key (the client secret may also add its own keys):
```sh
curl -X POST http://127.0.0.1:8000/api/keys \
-H "auth-secret: $ADMIN_SECRET" \
-F client_id=booth-01 -F provider=google -F api_key="$GOOGLE_API_KEY" -F priority=1
```
`ADMIN_SECRET` and client secrets are stored only as SHA-256 hashes for client authentication. Upstream provider keys must be retained to make requests, so they are stored in SQLite. Protect the database file and its backups as secrets.
## Generate
Direct mode holds the connection open until generation completes:
```sh
curl -X POST http://127.0.0.1:8000/v1/generate \
-H 'x-client-secret: choose-a-client-secret' \
-F client_id=booth-01 -F model=nano-banana \
-F prompt='Restyle the subject using the second image as a visual reference' \
-F params='{"aspect_ratio":"9:16","image_size":"2K"}' \
-F 'images=@capture.jpg;type=image/jpeg' -F 'images=@style.png;type=image/png'
```
Polling mode accepts the same multipart fields with `?async_mode=true`. It returns a `job_id`; poll the returned URL with `client_id` as a query parameter and the same `x-client-secret` header. Job state and compact result metadata are stored in SQLite; generated images are converted to WebP (quality 90 by default) and stored as private files under `OUTPUT_DIR` (default `./data/generated`). Downloaded async results use `image/webp`, so save them with a `.webp` extension or convert them locally to JPEG/PNG if the next step requires it. Output files are automatically deleted after 24 hours (checked at startup and every five minutes); job, timing, and cost records remain. Once a job completes or fails, its uploaded prompt/image request payload is removed from SQLite. Jobs still processing when the proxy process restarts are marked failed because the in-process worker cannot resume safely.
The built-in `nano-banana` alias defaults to Gemini 2.5 Flash Image. Set `GOOGLE_MODEL_NANO_BANANA` and `OPENROUTER_MODEL_NANO_BANANA` to provider model IDs you have confirmed are available for your account. Other model IDs are passed through (with `google/` prefix normalization for the native provider).
## Configuration
| Variable | Default | Purpose |
| --- | --- | --- |
| `ADMIN_SECRET` | required | Protects administration endpoints. Use a long random value. |
| `DB_PATH` | `./data/proxy.db` | SQLite database location. |
| `OUTPUT_DIR` | `./data/generated` | Private directory for generated output images. Back it up with the database. |
| `OUTPUT_WEBP_QUALITY` | `90` | WebP quality used for stored async outputs (1–100). |
| `OUTPUT_RETENTION_SECONDS` | `86400` | Lifetime of generated image files; cleanup runs every five minutes. |
| `MAX_UPLOAD_BYTES` | `52428800` | Aggregate raw image upload limit. |
| `MAX_IMAGES` | `8` | Maximum reference image count. |
| `MAX_IMAGE_PIXELS` | `25000000` | Maximum width × height for each uploaded image. |
| `HTTP_TIMEOUT_SECONDS` | `300` | Overall upstream request timeout. |
| `CONNECT_TIMEOUT_SECONDS` | `15` | Upstream connect timeout. |
| `GOOGLE_MODEL_NANO_BANANA` | `gemini-2.5-flash-image` | Native Google alias target. |
| `OPENROUTER_MODEL_NANO_BANANA` | `google/gemini-2.5-flash-image` | OpenRouter alias target. |
Only one Uvicorn worker is supported for this initial implementation. SQLite WAL is intended for a local persistent disk, not a shared network filesystem. Configure Nginx body and proxy timeouts to match your deployment limits; see [`deploy/nginx.conf`](deploy/nginx.conf).
## API outline
See the copyable endpoint guide in [`docs/API.md`](docs/API.md).
- `POST /v1/generate` — multipart `client_id`, `prompt`, `model`, optional `provider`, JSON `params`, repeated `images`; optional `?async_mode=true`.
- `GET /v1/jobs/{job_id}?client_id=...&include_result=false` — small, tenant-scoped job status and image download links.
- `GET /v1/jobs/{job_id}/images/{index}?token=...` — raw image download using a short-lived signed link (or use client-secret authentication).
- `GET /api/admin/state` — client and redacted key state (`x-admin-secret`).
- `POST /api/admin/client` — create/update a client (`x-admin-secret`).
- `POST /api/keys` and `DELETE /api/keys/{id}` — manage keys with `auth-secret` (admin or owning client secret).
- `GET /healthz` and `GET /ui`.
Errors use FastAPI's `{"detail": ...}` envelope. Provider failures return HTTP 502 with provider names and status codes only; upstream response bodies and credentials are not returned.
## Deploy with Docker
```sh
docker build -t phproxy .
docker run --name phproxy --restart unless-stopped \
-p 127.0.0.1:8000:8000 \
-e ADMIN_SECRET="$ADMIN_SECRET" \
-e DB_PATH=/data/proxy.db \
-v phproxy-data:/data \
phproxy
```
Terminate TLS at a trusted reverse proxy and keep the application port private. The sample Nginx configuration is a location snippet; add your certificate and domain configuration separately.