Files
ph_api/README.md
T

99 lines
4.9 KiB
Markdown
Raw Normal View History

# 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. Jobs and their results are stored in SQLite so they survive a client disconnect. 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. |
| `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=...` — job status/result, tenant-scoped.
- `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.