File size: 3,498 Bytes
1ba148a
 
 
 
 
 
8b7c167
 
 
 
 
 
 
1ba148a
8b7c167
 
 
 
 
 
 
 
 
86024d9
 
 
 
 
 
 
 
1ba148a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8b7c167
0bcea6b
 
 
 
 
 
86024d9
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
# Developer Guide

This documents how local development differs from running on the Hugging
Face Space, since a few things (persistent storage, secrets) work
differently in each environment.

## Provider API tokens (BYOK)

- Every provider is **bring-your-own-key**: each user pastes their own API
  key(s) into the UI. Keys are persisted in the browser's `localStorage` and
  sent to the server only transiently, as part of each generation request -
  they are never read from the server environment and never written to
  disk by this app.
- Each `ModelProvider` in [providers.py](providers.py) declares its own
  `api_token_env` (just a label identifying which key it needs, e.g.
  `FAL_KEY` or `REPLICATE_API_TOKEN` - not an actual env var read at
  runtime). `app.py` derives one key input field per distinct
  `api_token_env` automatically (`TOKEN_ENVS` in [app.py](app.py)), so
  adding a new provider with a new `api_token_env` adds its input field
  with no other UI changes needed. Providers that share an `api_token_env`
  (e.g. the two fal.ai providers both use `FAL_KEY`) share one input field.
- `.env` / `load_dotenv()` still exist for non-provider local config (e.g.
  `LOG_LEVEL`), but no longer carry provider API keys.
- Some providers need more than just a key - e.g. Azure resources are
  deployed per-user, so Sora-2-on-Azure also needs the user's own resource
  endpoint URL. Declare these via `ModelProvider.extra_config_envs` (a
  tuple of label names, analogous to `api_token_env`); `app.py` derives one
  plain (non-password) text input per distinct name automatically
  (`EXTRA_CONFIG_ENVS`), rendered in the same BYOK section and persisted
  the same way. Read a value back inside a provider's `call` function with
  `provider.extra_config("NAME")`.

## Persistent storage (`/data`)

- **On the Space**: `/data` is the mounted [persistent storage](https://huggingface.co/docs/hub/spaces-storage)
  directory (if enabled for the Space). Runs (input image + per-provider
  output videos + `runs.json` history index) are written there and survive
  restarts.
- **Locally**: `/data` is normally not writable/creatable on a developer
  machine (e.g. macOS blocks writing to `/data` at the OS level).
  `runs.ensure_data_dirs()` detects this and falls back to a `./data`
  directory next to the project files instead. This fallback dir is
  gitignored.
- You don't need to do anything to trigger the fallback - it happens
  automatically the first time `ensure_data_dirs()` fails to create `/data`.

## Running locally

```bash
pip install -r requirements.txt
python app.py
```

Then open the printed local URL (e.g. `http://127.0.0.1:7860`).

## Adding a new model provider

Add a `ModelProvider(...)` entry to `PROVIDERS` in [providers.py](providers.py).
Reuse `fal_queue_call` if the new model is also on fal.ai, `replicate_call` if
it's on Replicate, `veo_call` if it's a Veo model on the Gemini API, or write
a new `call` function matching the signature `(provider, image_path) -> bytes`
for providers with a different API contract (see `generic_sync_call` and
`polling_call` for templates of direct-response vs. job-polling contracts).
No subclassing is needed - the `call` field is what makes this duck-typed.
Give the provider an `api_token_env` label (new or reused) - the UI picks up
a BYOK input field for it automatically. If the provider needs more than a
key (e.g. a per-resource endpoint URL), also set `extra_config_envs` - see
`sora-2-azure` for an example.