@weirdfingers/baseboards 0.2.1 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/README.md +14 -4
  2. package/dist/index.js +13 -4
  3. package/dist/index.js.map +1 -1
  4. package/package.json +1 -1
  5. package/templates/api/ARTIFACT_RESOLUTION_GUIDE.md +148 -0
  6. package/templates/api/Dockerfile +2 -2
  7. package/templates/api/README.md +138 -6
  8. package/templates/api/config/generators.yaml +41 -7
  9. package/templates/api/docs/TESTING_LIVE_APIS.md +417 -0
  10. package/templates/api/pyproject.toml +49 -9
  11. package/templates/api/src/boards/__init__.py +1 -1
  12. package/templates/api/src/boards/auth/adapters/__init__.py +9 -2
  13. package/templates/api/src/boards/auth/factory.py +16 -2
  14. package/templates/api/src/boards/generators/__init__.py +2 -2
  15. package/templates/api/src/boards/generators/artifact_resolution.py +372 -0
  16. package/templates/api/src/boards/generators/artifacts.py +4 -4
  17. package/templates/api/src/boards/generators/base.py +8 -4
  18. package/templates/api/src/boards/generators/implementations/__init__.py +4 -2
  19. package/templates/api/src/boards/generators/implementations/fal/__init__.py +25 -0
  20. package/templates/api/src/boards/generators/implementations/fal/audio/__init__.py +4 -0
  21. package/templates/api/src/boards/generators/implementations/fal/audio/minimax_music_v2.py +173 -0
  22. package/templates/api/src/boards/generators/implementations/fal/audio/minimax_speech_2_6_turbo.py +221 -0
  23. package/templates/api/src/boards/generators/implementations/fal/image/__init__.py +17 -0
  24. package/templates/api/src/boards/generators/implementations/fal/image/flux_pro_kontext.py +216 -0
  25. package/templates/api/src/boards/generators/implementations/fal/image/flux_pro_ultra.py +197 -0
  26. package/templates/api/src/boards/generators/implementations/fal/image/imagen4_preview.py +191 -0
  27. package/templates/api/src/boards/generators/implementations/fal/image/imagen4_preview_fast.py +179 -0
  28. package/templates/api/src/boards/generators/implementations/fal/image/nano_banana.py +183 -0
  29. package/templates/api/src/boards/generators/implementations/fal/image/nano_banana_edit.py +212 -0
  30. package/templates/api/src/boards/generators/implementations/fal/utils.py +61 -0
  31. package/templates/api/src/boards/generators/implementations/fal/video/__init__.py +13 -0
  32. package/templates/api/src/boards/generators/implementations/fal/video/kling_video_v2_5_turbo_pro_text_to_video.py +168 -0
  33. package/templates/api/src/boards/generators/implementations/fal/video/sync_lipsync_v2.py +167 -0
  34. package/templates/api/src/boards/generators/implementations/fal/video/veo31_first_last_frame_to_video.py +180 -0
  35. package/templates/api/src/boards/generators/implementations/openai/__init__.py +1 -0
  36. package/templates/api/src/boards/generators/implementations/openai/audio/__init__.py +1 -0
  37. package/templates/api/src/boards/generators/implementations/{audio → openai/audio}/whisper.py +9 -6
  38. package/templates/api/src/boards/generators/implementations/openai/image/__init__.py +1 -0
  39. package/templates/api/src/boards/generators/implementations/{image → openai/image}/dalle3.py +8 -5
  40. package/templates/api/src/boards/generators/implementations/replicate/__init__.py +1 -0
  41. package/templates/api/src/boards/generators/implementations/replicate/image/__init__.py +1 -0
  42. package/templates/api/src/boards/generators/implementations/{image → replicate/image}/flux_pro.py +8 -5
  43. package/templates/api/src/boards/generators/implementations/replicate/video/__init__.py +1 -0
  44. package/templates/api/src/boards/generators/implementations/{video → replicate/video}/lipsync.py +9 -6
  45. package/templates/api/src/boards/generators/resolution.py +80 -20
  46. package/templates/api/src/boards/jobs/repository.py +49 -0
  47. package/templates/api/src/boards/storage/factory.py +16 -6
  48. package/templates/api/src/boards/workers/actors.py +69 -5
  49. package/templates/api/src/boards/workers/context.py +177 -21
  50. package/templates/web/package.json +2 -1
  51. package/templates/web/src/components/boards/GenerationInput.tsx +154 -52
  52. package/templates/web/src/components/boards/GeneratorSelector.tsx +57 -59
  53. package/templates/web/src/components/ui/dropdown-menu.tsx +200 -0
  54. package/templates/api/src/boards/generators/implementations/audio/__init__.py +0 -3
  55. package/templates/api/src/boards/generators/implementations/image/__init__.py +0 -3
  56. package/templates/api/src/boards/generators/implementations/video/__init__.py +0 -3
@@ -15,21 +15,72 @@ Backend for the Boards open-source creative toolkit for AI-generated content.
15
15
 
16
16
  ## Installation
17
17
 
18
+ ### Minimal Install
19
+
20
+ The minimal installation includes core functionality with local filesystem storage only:
21
+
22
+ ```bash
23
+ pip install weirdfingers-boards
24
+ ```
25
+
26
+ ### Install with Specific Generators
27
+
28
+ Install only the generator providers you need:
29
+
30
+ ```bash
31
+ # OpenAI generators (DALL-E 3, Whisper)
32
+ pip install weirdfingers-boards[generators-openai]
33
+
34
+ # Replicate generators (Flux Pro, Lipsync)
35
+ pip install weirdfingers-boards[generators-replicate]
36
+
37
+ # fal.ai generators (nano-banana)
38
+ pip install weirdfingers-boards[generators-fal]
39
+
40
+ # Multiple generators
41
+ pip install weirdfingers-boards[generators-openai,generators-replicate]
42
+
43
+ # All generators
44
+ pip install weirdfingers-boards[generators-all]
45
+ ```
46
+
47
+ ### Install with Storage Backends
48
+
49
+ Add cloud storage providers as needed:
50
+
18
51
  ```bash
19
- # Install from PyPI (includes core dependencies: Redis, PyJWT)
20
- pip install boards-backend
52
+ # S3 storage (AWS, MinIO, DigitalOcean Spaces)
53
+ pip install weirdfingers-boards[storage-s3]
54
+
55
+ # Supabase storage
56
+ pip install weirdfingers-boards[storage-supabase]
21
57
 
22
- # Or with optional provider/storage dependencies
23
- pip install boards-backend[providers,storage-s3,storage-gcs]
58
+ # Google Cloud Storage
59
+ pip install weirdfingers-boards[storage-gcs]
60
+
61
+ # All storage backends
62
+ pip install weirdfingers-boards[storage-all]
63
+ ```
64
+
65
+ ### Full Installation
66
+
67
+ Install everything (all generators and storage backends):
68
+
69
+ ```bash
70
+ pip install weirdfingers-boards[all]
24
71
  ```
25
72
 
26
73
  ### Development Installation
27
74
 
75
+ For local development with all providers for type checking and testing:
76
+
28
77
  ```bash
29
- # Clone the repository and install (includes all extras for typecheck)
78
+ # Clone the repository
30
79
  git clone https://github.com/weirdfingers/boards.git
31
80
  cd boards/packages/backend
32
- uv sync # Automatically installs dev dependencies including all providers/storage
81
+
82
+ # Install with dev dependencies (includes all providers)
83
+ uv sync
33
84
  ```
34
85
 
35
86
  ## Configuration
@@ -127,6 +178,87 @@ packages/backend/
127
178
  └── tests/
128
179
  ```
129
180
 
181
+ ## Community & Social
182
+
183
+ Join the Weirdfingers community:
184
+
185
+ - **TikTok**: [https://www.tiktok.com/@weirdfingers](https://www.tiktok.com/@weirdfingers)
186
+ - **X (Twitter)**: [https://x.com/_Weirdfingers_](https://x.com/_Weirdfingers_)
187
+ - **YouTube**: [https://www.youtube.com/@Weirdfingers](https://www.youtube.com/@Weirdfingers)
188
+ - **Discord**: [https://discord.gg/rvVuHyuPEx](https://discord.gg/rvVuHyuPEx)
189
+ - **Instagram**: [https://www.instagram.com/_weirdfingers_/](https://www.instagram.com/_weirdfingers_/)
190
+ ## Testing
191
+
192
+ Boards uses pytest for testing with both unit tests (mocked) and optional live API tests.
193
+
194
+ ### Running Tests
195
+
196
+ ```bash
197
+ # Run all unit tests (excludes live API tests)
198
+ make test-backend
199
+
200
+ # Or using pytest directly
201
+ cd packages/backend
202
+ uv run pytest tests/
203
+
204
+ # Run with coverage
205
+ uv run pytest tests/ --cov=src/boards --cov-report=html
206
+
207
+ # Run specific test file
208
+ uv run pytest tests/generators/implementations/test_flux_pro.py -v
209
+ ```
210
+
211
+ ### Unit Tests vs Live API Tests
212
+
213
+ **Unit tests** (default):
214
+ - Use mocked provider SDKs
215
+ - Fast and free
216
+ - Run automatically in CI/CD
217
+ - Located: `tests/**/test_*.py`
218
+
219
+ **Live API tests** (opt-in only):
220
+ - Make real API calls to providers
221
+ - Consume API credits
222
+ - **Never run by default**
223
+ - Located: `tests/**/test_*_live.py`
224
+
225
+ ### Running Live API Tests
226
+
227
+ Live tests verify real connectivity with provider APIs but cost money. They are **excluded from default test runs**.
228
+
229
+ ```bash
230
+ # Set up API key
231
+ export BOARDS_GENERATOR_API_KEYS='{"REPLICATE_API_TOKEN": "r8_..."}'
232
+
233
+ # Run a specific generator's live test
234
+ uv run pytest tests/generators/implementations/test_flux_pro_live.py -v -m live_api
235
+
236
+ # Run all live tests for one provider
237
+ uv run pytest -m live_replicate -v
238
+
239
+ # Run all live tests (not recommended - expensive!)
240
+ uv run pytest -m live_api -v
241
+ ```
242
+
243
+ For detailed information on live API testing, see:
244
+ - [Live API Testing Guide](docs/TESTING_LIVE_APIS.md)
245
+ - [Generator Testing Documentation](../../apps/docs/docs/generators/testing.md)
246
+
247
+ ### Test Organization
248
+
249
+ ```
250
+ tests/
251
+ ├── conftest.py # Shared fixtures (database, etc.)
252
+ ├── generators/
253
+ │ └── implementations/
254
+ │ ├── conftest.py # Generator-specific fixtures
255
+ │ ├── test_flux_pro.py # Unit tests (mocked)
256
+ │ ├── test_flux_pro_live.py # Live API tests (opt-in)
257
+ │ └── ...
258
+ ├── graphql/ # GraphQL API tests
259
+ └── storage/ # Storage backend tests
260
+ ```
261
+
130
262
  ## License
131
263
 
132
264
  MIT
@@ -8,18 +8,52 @@
8
8
  strict_mode: true
9
9
  allow_unlisted: false
10
10
 
11
+ # NOTE: Please keep generators in alphabetical order by class name for easier maintenance
11
12
  generators:
12
- # Image generators
13
- - class: "boards.generators.implementations.image.flux_pro.FluxProGenerator"
13
+ # Fal.ai generators
14
+ - class: "boards.generators.implementations.fal.image.flux_pro_kontext.FalFluxProKontextGenerator"
14
15
  enabled: true
15
16
 
16
- # Audio generators
17
- - class: "boards.generators.implementations.audio.whisper.WhisperGenerator"
17
+ - class: "boards.generators.implementations.fal.image.flux_pro_ultra.FalFluxProUltraGenerator"
18
18
  enabled: true
19
19
 
20
- - class: "boards.generators.implementations.image.dalle3.DallE3Generator"
20
+ - class: "boards.generators.implementations.fal.image.imagen4_preview_fast.FalImagen4PreviewFastGenerator"
21
21
  enabled: true
22
22
 
23
- # Video generators
24
- - class: "boards.generators.implementations.video.lipsync.LipsyncGenerator"
23
+ - class: "boards.generators.implementations.fal.image.imagen4_preview.FalImagen4PreviewGenerator"
24
+ enabled: true
25
+
26
+ - class: "boards.generators.implementations.fal.video.kling_video_v2_5_turbo_pro_text_to_video.FalKlingVideoV25TurboProTextToVideoGenerator"
27
+ enabled: true
28
+
29
+ - class: "boards.generators.implementations.fal.audio.minimax_music_v2.FalMinimaxMusicV2Generator"
30
+ enabled: true
31
+
32
+ - class: "boards.generators.implementations.fal.audio.minimax_speech_2_6_turbo.FalMinimaxSpeech26TurboGenerator"
33
+ enabled: true
34
+
35
+ - class: "boards.generators.implementations.fal.image.nano_banana_edit.FalNanoBananaEditGenerator"
36
+ enabled: true
37
+
38
+ - class: "boards.generators.implementations.fal.image.nano_banana.FalNanoBananaGenerator"
39
+ enabled: true
40
+
41
+ - class: "boards.generators.implementations.fal.video.sync_lipsync_v2.FalSyncLipsyncV2Generator"
42
+ enabled: true
43
+
44
+ - class: "boards.generators.implementations.fal.video.veo31_first_last_frame_to_video.FalVeo31FirstLastFrameToVideoGenerator"
45
+ enabled: true
46
+
47
+ # OpenAI generators
48
+ - class: "boards.generators.implementations.openai.image.dalle3.OpenAIDallE3Generator"
49
+ enabled: true
50
+
51
+ - class: "boards.generators.implementations.openai.audio.whisper.OpenAIWhisperGenerator"
52
+ enabled: true
53
+
54
+ # Replicate generators
55
+ - class: "boards.generators.implementations.replicate.image.flux_pro.ReplicateFluxProGenerator"
56
+ enabled: true
57
+
58
+ - class: "boards.generators.implementations.replicate.video.lipsync.ReplicateLipsyncGenerator"
25
59
  enabled: true
@@ -0,0 +1,417 @@
1
+ # Live API Testing Guide for Generators
2
+
3
+ This guide explains how to test generators with real provider APIs (Replicate, Fal.ai, OpenAI, etc.) rather than mocked responses.
4
+
5
+ ## Overview
6
+
7
+ Boards includes two types of generator tests:
8
+
9
+ 1. **Unit tests** (`test_<generator>.py`) - Use mocks, run by default, fast and free
10
+ 2. **Live API tests** (`test_<generator>_live.py`) - Call real APIs, opt-in only, consume credits
11
+
12
+ Live API tests are **never run by default**. They must be explicitly invoked and require valid API keys.
13
+
14
+ ## When to Run Live API Tests
15
+
16
+ Run live API tests when:
17
+
18
+ - ✅ You've modified a generator implementation
19
+ - ✅ You've updated a provider SDK dependency
20
+ - ✅ You want to verify real API connectivity
21
+ - ✅ You're debugging unexpected behavior in production
22
+ - ✅ Before releasing a new generator to production
23
+
24
+ **Don't run live tests:**
25
+
26
+ - ❌ During regular development (use unit tests instead)
27
+ - ❌ In pre-commit hooks
28
+ - ❌ As part of `make test` or `make test-backend`
29
+ - ❌ In CI/CD pipelines (unless explicitly configured)
30
+
31
+ ## Setting Up API Keys
32
+
33
+ Live tests require provider API keys. There are two ways to configure them:
34
+
35
+ ### Option 1: Via Environment Variables (Direct)
36
+
37
+ ```bash
38
+ # Replicate
39
+ export REPLICATE_API_TOKEN="r8_..."
40
+
41
+ # Fal.ai
42
+ export FAL_KEY="..."
43
+
44
+ # OpenAI
45
+ export OPENAI_API_KEY="sk-..."
46
+ ```
47
+
48
+ ### Option 2: Via Boards Configuration (Recommended)
49
+
50
+ ```bash
51
+ # Single key
52
+ export BOARDS_GENERATOR_API_KEYS='{"REPLICATE_API_TOKEN": "r8_..."}'
53
+
54
+ # Multiple keys (JSON format)
55
+ export BOARDS_GENERATOR_API_KEYS='{
56
+ "REPLICATE_API_TOKEN": "r8_...",
57
+ "FAL_KEY": "...",
58
+ "OPENAI_API_KEY": "sk-..."
59
+ }'
60
+ ```
61
+
62
+ Or add to your `.env` file:
63
+
64
+ ```bash
65
+ BOARDS_GENERATOR_API_KEYS={"REPLICATE_API_TOKEN": "r8_..."}
66
+ ```
67
+
68
+ The Boards config system will automatically sync these keys to `os.environ` for third-party SDKs.
69
+
70
+ ## Running Live API Tests
71
+
72
+ ### Run a Single Generator's Live Test
73
+
74
+ This is the **most common workflow** when developing generators:
75
+
76
+ ```bash
77
+ # After modifying flux_pro.py
78
+ export REPLICATE_API_TOKEN="r8_..."
79
+ pytest tests/generators/implementations/test_flux_pro_live.py -v -m live_api
80
+ ```
81
+
82
+ Example output:
83
+ ```
84
+ tests/generators/implementations/test_flux_pro_live.py::TestFluxProGeneratorLive::test_generate_basic PASSED
85
+ tests/generators/implementations/test_flux_pro_live.py::TestFluxProGeneratorLive::test_generate_with_aspect_ratio PASSED
86
+ ```
87
+
88
+ If the API key is missing, tests will be skipped:
89
+ ```
90
+ tests/generators/implementations/test_flux_pro_live.py::TestFluxProGeneratorLive::test_generate_basic SKIPPED (REPLICATE_API_TOKEN not set)
91
+ ```
92
+
93
+ ### Run All Live Tests for One Provider
94
+
95
+ ```bash
96
+ # All Replicate generators
97
+ export REPLICATE_API_TOKEN="r8_..."
98
+ pytest -m live_replicate -v
99
+
100
+ # All Fal generators
101
+ export FAL_KEY="..."
102
+ pytest -m live_fal -v
103
+
104
+ # All OpenAI generators
105
+ export OPENAI_API_KEY="sk-..."
106
+ pytest -m live_openai -v
107
+ ```
108
+
109
+ ### Run All Live API Tests (Rarely Used)
110
+
111
+ ```bash
112
+ # Requires ALL provider API keys to be set
113
+ export REPLICATE_API_TOKEN="r8_..."
114
+ export FAL_KEY="..."
115
+ export OPENAI_API_KEY="sk-..."
116
+
117
+ pytest -m live_api -v
118
+ ```
119
+
120
+ ⚠️ **Warning:** This will consume credits across all providers!
121
+
122
+ ### Run With Extra Verbosity
123
+
124
+ ```bash
125
+ # Show detailed output including print statements and logs
126
+ pytest tests/generators/implementations/test_flux_pro_live.py -v -s -m live_api
127
+
128
+ # Show full error tracebacks
129
+ pytest tests/generators/implementations/test_flux_pro_live.py -v --tb=long -m live_api
130
+ ```
131
+
132
+ ## Cost Management
133
+
134
+ Live API tests consume real provider credits. Follow these practices:
135
+
136
+ ### 1. Cost Logging
137
+
138
+ All live tests log estimated costs before running:
139
+
140
+ ```python
141
+ @pytest.mark.asyncio
142
+ async def test_generate_basic(self, cost_logger):
143
+ cost_logger("replicate-flux-pro", 0.055)
144
+ # Test implementation...
145
+ ```
146
+
147
+ Look for log output like:
148
+ ```
149
+ WARNING live_api_test_cost generator=replicate-flux-pro estimated_cost_usd=0.055
150
+ ```
151
+
152
+ ### 2. Minimal Inputs
153
+
154
+ Tests use minimal/cheap parameters:
155
+
156
+ ```python
157
+ # ✅ Good - Simple prompt, small size
158
+ inputs = FluxProInput(
159
+ prompt="A simple red circle",
160
+ aspect_ratio="1:1",
161
+ )
162
+
163
+ # ❌ Bad - Complex prompt, large size, multiple images
164
+ inputs = FluxProInput(
165
+ prompt="Ultra detailed 8K photorealistic...",
166
+ aspect_ratio="21:9",
167
+ num_images=10,
168
+ )
169
+ ```
170
+
171
+ ### 3. Approximate Costs (as of 2024)
172
+
173
+ | Provider | Generator | Estimated Cost |
174
+ |----------|-----------|----------------|
175
+ | Replicate | flux-pro | $0.055/image |
176
+ | Fal.ai | nano-banana | $0.05/image |
177
+ | Fal.ai | nano-banana-edit | $0.05/image |
178
+ | OpenAI | dalle3 | $0.04-$0.08/image |
179
+ | OpenAI | whisper | $0.006/minute |
180
+
181
+ Running a full test suite for one generator typically costs **$0.10 - $0.30**.
182
+
183
+ ## Verifying Tests Are Excluded
184
+
185
+ Live tests are **automatically excluded by default** thanks to `pytest.ini` configuration:
186
+
187
+ ```bash
188
+ # All of these commands exclude live tests by default:
189
+ make test-backend
190
+ cd packages/backend && uv run pytest
191
+ cd packages/backend && uv run pytest tests/
192
+ ```
193
+
194
+ You should see output like:
195
+ ```
196
+ 337/350 tests collected (13 deselected)
197
+ ```
198
+
199
+ The 13 deselected tests are the live API tests.
200
+
201
+ Check pytest markers to see which tests would run:
202
+ ```bash
203
+ # List all live API tests (without running them)
204
+ pytest --collect-only -m live_api
205
+
206
+ # Verify default behavior excludes live tests
207
+ pytest --collect-only -q | tail -1
208
+ # Should show: "337/350 tests collected (13 deselected)"
209
+ ```
210
+
211
+ ## Troubleshooting
212
+
213
+ ### Tests Are Skipped
214
+
215
+ **Problem:** All tests show `SKIPPED` status
216
+
217
+ **Cause:** API key not found
218
+
219
+ **Solution:**
220
+ ```bash
221
+ # Verify API key is set
222
+ echo $REPLICATE_API_TOKEN
223
+
224
+ # If empty, export it
225
+ export REPLICATE_API_TOKEN="r8_..."
226
+
227
+ # Or check Boards config
228
+ python -c "from boards.config import settings; print(settings.generator_api_keys)"
229
+ ```
230
+
231
+ ### API Errors
232
+
233
+ **Problem:** Test fails with authentication error
234
+
235
+ **Solutions:**
236
+ 1. Verify API key is valid (not expired/revoked)
237
+ 2. Check API key has correct permissions
238
+ 3. Verify provider account has sufficient credits
239
+
240
+ **Example error:**
241
+ ```
242
+ ValueError: API configuration invalid. Missing REPLICATE_API_TOKEN
243
+ ```
244
+
245
+ ### Unexpected Costs
246
+
247
+ **Problem:** Tests consumed more credits than expected
248
+
249
+ **Prevention:**
250
+ 1. Always read the test file first to understand what it does
251
+ 2. Check cost logger output before tests run
252
+ 3. Use single generator tests, not `pytest -m live_api`
253
+ 4. Start with one test function: `pytest test_flux_pro_live.py::TestFluxProGeneratorLive::test_estimate_cost`
254
+
255
+ ## Adding Live Tests for New Generators
256
+
257
+ When creating a new generator, follow these steps:
258
+
259
+ ### 1. Create the Live Test File
260
+
261
+ ```bash
262
+ # Template: test_<generator>_live.py
263
+ touch tests/generators/implementations/test_my_generator_live.py
264
+ ```
265
+
266
+ ### 2. Use This Template
267
+
268
+ ```python
269
+ """
270
+ Live API tests for MyGenerator.
271
+
272
+ To run: pytest tests/generators/implementations/test_my_generator_live.py -v -m live_api
273
+ """
274
+ import pytest
275
+ from boards.config import initialize_generator_api_keys
276
+ from boards.generators.implementations.provider.type.my_generator import (
277
+ MyGeneratorInput,
278
+ MyGenerator,
279
+ )
280
+
281
+ pytestmark = [pytest.mark.live_api, pytest.mark.live_<provider>]
282
+
283
+
284
+ class TestMyGeneratorLive:
285
+ """Live API tests for MyGenerator."""
286
+
287
+ def setup_method(self):
288
+ """Set up generator and sync API keys."""
289
+ self.generator = MyGenerator()
290
+ initialize_generator_api_keys()
291
+
292
+ @pytest.mark.asyncio
293
+ async def test_generate_basic(
294
+ self, skip_if_no_<provider>_key, dummy_context, cost_logger
295
+ ):
296
+ """Test basic generation with minimal parameters."""
297
+ # Log cost
298
+ estimated_cost = await self.generator.estimate_cost(
299
+ MyGeneratorInput(prompt="test")
300
+ )
301
+ cost_logger(self.generator.name, estimated_cost)
302
+
303
+ # Minimal input to reduce cost
304
+ inputs = MyGeneratorInput(prompt="Simple test")
305
+
306
+ # Execute
307
+ result = await self.generator.generate(inputs, dummy_context)
308
+
309
+ # Verify
310
+ assert result.outputs is not None
311
+ assert len(result.outputs) > 0
312
+ assert result.outputs[0].storage_url is not None
313
+ ```
314
+
315
+ ### 3. Add Provider-Specific Skip Fixture (If New Provider)
316
+
317
+ If adding a new provider (not Replicate/Fal/OpenAI), add to `conftest.py`:
318
+
319
+ ```python
320
+ @pytest.fixture
321
+ def skip_if_no_my_provider_key():
322
+ """Skip test if MY_PROVIDER_API_KEY is not available."""
323
+ if not check_api_key("MY_PROVIDER_API_KEY"):
324
+ pytest.skip("MY_PROVIDER_API_KEY not set, skipping live API test")
325
+ ```
326
+
327
+ ### 4. Add Pytest Marker (If New Provider)
328
+
329
+ Update `pytest.ini`:
330
+
331
+ ```ini
332
+ markers =
333
+ ...
334
+ live_my_provider: marks tests that call MyProvider API (subset of live_api)
335
+ ```
336
+
337
+ ### 5. Test Your Live Test
338
+
339
+ ```bash
340
+ # Without API key - should skip
341
+ pytest tests/generators/implementations/test_my_generator_live.py -v -m live_api
342
+
343
+ # With API key - should run
344
+ export MY_PROVIDER_API_KEY="..."
345
+ pytest tests/generators/implementations/test_my_generator_live.py -v -m live_api
346
+ ```
347
+
348
+ ## Best Practices
349
+
350
+ ### DO:
351
+ - ✅ Run live tests individually after modifying a generator
352
+ - ✅ Use minimal inputs to reduce costs
353
+ - ✅ Log estimated costs before running tests
354
+ - ✅ Keep live tests simple and focused
355
+ - ✅ Verify tests are skipped when API keys are missing
356
+ - ✅ Document expected costs in test docstrings
357
+
358
+ ### DON'T:
359
+ - ❌ Run `pytest -m live_api` unless you mean it
360
+ - ❌ Add live tests to CI without explicit approval
361
+ - ❌ Use live tests for load/performance testing
362
+ - ❌ Test with expensive parameters (high quality, large sizes, etc.)
363
+ - ❌ Create hundreds of test cases for one generator
364
+ - ❌ Commit API keys to the repository
365
+
366
+ ## Continuous Integration (Optional)
367
+
368
+ If you want to run live tests in CI (not recommended for most projects):
369
+
370
+ ### GitHub Actions Example
371
+
372
+ ```yaml
373
+ name: Live API Tests (Manual)
374
+
375
+ on:
376
+ workflow_dispatch: # Manual trigger only
377
+
378
+ jobs:
379
+ live-tests:
380
+ runs-on: ubuntu-latest
381
+ steps:
382
+ - uses: actions/checkout@v3
383
+ - name: Set up Python
384
+ uses: actions/setup-python@v4
385
+ with:
386
+ python-version: '3.12'
387
+ - name: Install dependencies
388
+ run: |
389
+ cd packages/backend
390
+ pip install uv
391
+ uv sync
392
+ - name: Run Replicate live tests
393
+ env:
394
+ REPLICATE_API_TOKEN: ${{ secrets.REPLICATE_API_TOKEN }}
395
+ run: |
396
+ cd packages/backend
397
+ uv run pytest -m live_replicate --tb=short
398
+ ```
399
+
400
+ **Important:** Only run on:
401
+ - Manual workflow dispatch
402
+ - Scheduled (e.g., nightly) runs
403
+ - Release branches
404
+
405
+ **Never** run live tests on every PR or commit.
406
+
407
+ ## Summary
408
+
409
+ - Live API tests verify real connectivity to provider APIs
410
+ - They are **opt-in only** and never run by default
411
+ - Set up API keys via environment variables or Boards config
412
+ - Run individual tests after modifying generators: `pytest test_flux_pro_live.py -v -m live_api`
413
+ - Monitor costs using cost logger output
414
+ - Use minimal inputs to reduce expenses
415
+ - Follow the template when adding tests for new generators
416
+
417
+ For questions or issues, see the main [generator testing documentation](../../../apps/docs/docs/generators/testing.md).