@weirdfingers/baseboards 0.9.6 → 0.9.7
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.
- package/dist/index.js +560 -469
- package/dist/index.js.map +1 -1
- package/package.json +2 -5
- package/templates/README.md +0 -122
- package/templates/api/.env.example +0 -65
- package/templates/api/ARTIFACT_RESOLUTION_GUIDE.md +0 -148
- package/templates/api/Dockerfile +0 -32
- package/templates/api/README.md +0 -264
- package/templates/api/alembic/env.py +0 -114
- package/templates/api/alembic/script.py.mako +0 -28
- package/templates/api/alembic/versions/20250101_000000_initial_schema.py +0 -506
- package/templates/api/alembic/versions/20251022_174729_remove_provider_name_from_generations.py +0 -75
- package/templates/api/alembic/versions/20251023_165852_switch_to_declarative_base_and_mapping.py +0 -467
- package/templates/api/alembic/versions/20251202_000000_add_artifact_lineage.py +0 -134
- package/templates/api/alembic/versions/2025925_62735_add_seed_data_for_default_tenant.py +0 -88
- package/templates/api/alembic.ini +0 -36
- package/templates/api/config/generators.yaml +0 -237
- package/templates/api/config/storage_config.yaml +0 -26
- package/templates/api/docs/ADDING_GENERATORS.md +0 -409
- package/templates/api/docs/GENERATORS_API.md +0 -502
- package/templates/api/docs/MIGRATIONS.md +0 -472
- package/templates/api/docs/TESTING_LIVE_APIS.md +0 -417
- package/templates/api/docs/storage_providers.md +0 -337
- package/templates/api/pyproject.toml +0 -205
- package/templates/api/src/boards/__init__.py +0 -10
- package/templates/api/src/boards/api/app.py +0 -172
- package/templates/api/src/boards/api/auth.py +0 -75
- package/templates/api/src/boards/api/endpoints/__init__.py +0 -3
- package/templates/api/src/boards/api/endpoints/jobs.py +0 -76
- package/templates/api/src/boards/api/endpoints/setup.py +0 -505
- package/templates/api/src/boards/api/endpoints/sse.py +0 -129
- package/templates/api/src/boards/api/endpoints/storage.py +0 -155
- package/templates/api/src/boards/api/endpoints/tenant_registration.py +0 -296
- package/templates/api/src/boards/api/endpoints/uploads.py +0 -149
- package/templates/api/src/boards/api/endpoints/webhooks.py +0 -13
- package/templates/api/src/boards/auth/__init__.py +0 -15
- package/templates/api/src/boards/auth/adapters/__init__.py +0 -27
- package/templates/api/src/boards/auth/adapters/auth0.py +0 -220
- package/templates/api/src/boards/auth/adapters/base.py +0 -73
- package/templates/api/src/boards/auth/adapters/clerk.py +0 -172
- package/templates/api/src/boards/auth/adapters/jwt.py +0 -122
- package/templates/api/src/boards/auth/adapters/none.py +0 -102
- package/templates/api/src/boards/auth/adapters/oidc.py +0 -284
- package/templates/api/src/boards/auth/adapters/supabase.py +0 -110
- package/templates/api/src/boards/auth/context.py +0 -35
- package/templates/api/src/boards/auth/factory.py +0 -129
- package/templates/api/src/boards/auth/middleware.py +0 -221
- package/templates/api/src/boards/auth/provisioning.py +0 -129
- package/templates/api/src/boards/auth/tenant_extraction.py +0 -278
- package/templates/api/src/boards/cli.py +0 -354
- package/templates/api/src/boards/config.py +0 -131
- package/templates/api/src/boards/database/__init__.py +0 -7
- package/templates/api/src/boards/database/cli.py +0 -110
- package/templates/api/src/boards/database/connection.py +0 -292
- package/templates/api/src/boards/database/models.py +0 -19
- package/templates/api/src/boards/database/seed_data.py +0 -182
- package/templates/api/src/boards/dbmodels/__init__.py +0 -441
- package/templates/api/src/boards/generators/__init__.py +0 -57
- package/templates/api/src/boards/generators/artifact_resolution.py +0 -405
- package/templates/api/src/boards/generators/artifacts.py +0 -53
- package/templates/api/src/boards/generators/base.py +0 -144
- package/templates/api/src/boards/generators/implementations/__init__.py +0 -14
- package/templates/api/src/boards/generators/implementations/fal/__init__.py +0 -25
- package/templates/api/src/boards/generators/implementations/fal/audio/__init__.py +0 -23
- package/templates/api/src/boards/generators/implementations/fal/audio/beatoven_music_generation.py +0 -171
- package/templates/api/src/boards/generators/implementations/fal/audio/beatoven_sound_effect_generation.py +0 -167
- package/templates/api/src/boards/generators/implementations/fal/audio/chatterbox_text_to_speech.py +0 -176
- package/templates/api/src/boards/generators/implementations/fal/audio/chatterbox_tts_turbo.py +0 -195
- package/templates/api/src/boards/generators/implementations/fal/audio/elevenlabs_sound_effects_v2.py +0 -194
- package/templates/api/src/boards/generators/implementations/fal/audio/elevenlabs_tts_eleven_v3.py +0 -209
- package/templates/api/src/boards/generators/implementations/fal/audio/fal_elevenlabs_tts_turbo_v2_5.py +0 -206
- package/templates/api/src/boards/generators/implementations/fal/audio/fal_minimax_speech_26_hd.py +0 -237
- package/templates/api/src/boards/generators/implementations/fal/audio/minimax_music_v2.py +0 -173
- package/templates/api/src/boards/generators/implementations/fal/audio/minimax_speech_2_6_turbo.py +0 -221
- package/templates/api/src/boards/generators/implementations/fal/image/__init__.py +0 -63
- package/templates/api/src/boards/generators/implementations/fal/image/bytedance_seedream_v45_edit.py +0 -219
- package/templates/api/src/boards/generators/implementations/fal/image/clarity_upscaler.py +0 -220
- package/templates/api/src/boards/generators/implementations/fal/image/crystal_upscaler.py +0 -173
- package/templates/api/src/boards/generators/implementations/fal/image/fal_ideogram_character.py +0 -227
- package/templates/api/src/boards/generators/implementations/fal/image/flux_2.py +0 -203
- package/templates/api/src/boards/generators/implementations/fal/image/flux_2_edit.py +0 -230
- package/templates/api/src/boards/generators/implementations/fal/image/flux_2_pro.py +0 -204
- package/templates/api/src/boards/generators/implementations/fal/image/flux_2_pro_edit.py +0 -221
- package/templates/api/src/boards/generators/implementations/fal/image/flux_pro_kontext.py +0 -216
- package/templates/api/src/boards/generators/implementations/fal/image/flux_pro_ultra.py +0 -197
- package/templates/api/src/boards/generators/implementations/fal/image/gemini_25_flash_image.py +0 -177
- package/templates/api/src/boards/generators/implementations/fal/image/gemini_25_flash_image_edit.py +0 -208
- package/templates/api/src/boards/generators/implementations/fal/image/gpt_image_15_edit.py +0 -216
- package/templates/api/src/boards/generators/implementations/fal/image/gpt_image_1_5.py +0 -177
- package/templates/api/src/boards/generators/implementations/fal/image/gpt_image_1_edit_image.py +0 -182
- package/templates/api/src/boards/generators/implementations/fal/image/gpt_image_1_mini.py +0 -167
- package/templates/api/src/boards/generators/implementations/fal/image/ideogram_character_edit.py +0 -299
- package/templates/api/src/boards/generators/implementations/fal/image/ideogram_v2.py +0 -190
- package/templates/api/src/boards/generators/implementations/fal/image/imagen4_preview.py +0 -191
- package/templates/api/src/boards/generators/implementations/fal/image/imagen4_preview_fast.py +0 -179
- package/templates/api/src/boards/generators/implementations/fal/image/nano_banana.py +0 -183
- package/templates/api/src/boards/generators/implementations/fal/image/nano_banana_edit.py +0 -212
- package/templates/api/src/boards/generators/implementations/fal/image/nano_banana_pro.py +0 -179
- package/templates/api/src/boards/generators/implementations/fal/image/nano_banana_pro_edit.py +0 -226
- package/templates/api/src/boards/generators/implementations/fal/image/qwen_image.py +0 -249
- package/templates/api/src/boards/generators/implementations/fal/image/qwen_image_edit.py +0 -244
- package/templates/api/src/boards/generators/implementations/fal/image/reve_edit.py +0 -178
- package/templates/api/src/boards/generators/implementations/fal/image/reve_text_to_image.py +0 -155
- package/templates/api/src/boards/generators/implementations/fal/image/seedream_v45_text_to_image.py +0 -180
- package/templates/api/src/boards/generators/implementations/fal/utils.py +0 -61
- package/templates/api/src/boards/generators/implementations/fal/video/__init__.py +0 -77
- package/templates/api/src/boards/generators/implementations/fal/video/bytedance_seedance_v1_pro_text_to_video.py +0 -209
- package/templates/api/src/boards/generators/implementations/fal/video/creatify_lipsync.py +0 -161
- package/templates/api/src/boards/generators/implementations/fal/video/fal_bytedance_seedance_v1_pro_image_to_video.py +0 -222
- package/templates/api/src/boards/generators/implementations/fal/video/fal_minimax_hailuo_02_standard_text_to_video.py +0 -152
- package/templates/api/src/boards/generators/implementations/fal/video/fal_pixverse_lipsync.py +0 -197
- package/templates/api/src/boards/generators/implementations/fal/video/fal_sora_2_text_to_video.py +0 -173
- package/templates/api/src/boards/generators/implementations/fal/video/infinitalk.py +0 -221
- package/templates/api/src/boards/generators/implementations/fal/video/kling_video_ai_avatar_v2_pro.py +0 -168
- package/templates/api/src/boards/generators/implementations/fal/video/kling_video_ai_avatar_v2_standard.py +0 -159
- package/templates/api/src/boards/generators/implementations/fal/video/kling_video_v2_5_turbo_pro_image_to_video.py +0 -175
- package/templates/api/src/boards/generators/implementations/fal/video/kling_video_v2_5_turbo_pro_text_to_video.py +0 -168
- package/templates/api/src/boards/generators/implementations/fal/video/minimax_hailuo_2_3_pro_image_to_video.py +0 -153
- package/templates/api/src/boards/generators/implementations/fal/video/sora2_image_to_video.py +0 -172
- package/templates/api/src/boards/generators/implementations/fal/video/sora_2_image_to_video_pro.py +0 -175
- package/templates/api/src/boards/generators/implementations/fal/video/sora_2_text_to_video_pro.py +0 -163
- package/templates/api/src/boards/generators/implementations/fal/video/sync_lipsync_v2.py +0 -167
- package/templates/api/src/boards/generators/implementations/fal/video/sync_lipsync_v2_pro.py +0 -155
- package/templates/api/src/boards/generators/implementations/fal/video/veed_fabric_1_0.py +0 -180
- package/templates/api/src/boards/generators/implementations/fal/video/veed_lipsync.py +0 -174
- package/templates/api/src/boards/generators/implementations/fal/video/veo3.py +0 -194
- package/templates/api/src/boards/generators/implementations/fal/video/veo31.py +0 -190
- package/templates/api/src/boards/generators/implementations/fal/video/veo31_fast.py +0 -190
- package/templates/api/src/boards/generators/implementations/fal/video/veo31_fast_image_to_video.py +0 -191
- package/templates/api/src/boards/generators/implementations/fal/video/veo31_first_last_frame_to_video.py +0 -187
- package/templates/api/src/boards/generators/implementations/fal/video/veo31_image_to_video.py +0 -183
- package/templates/api/src/boards/generators/implementations/fal/video/veo31_reference_to_video.py +0 -172
- package/templates/api/src/boards/generators/implementations/fal/video/wan_25_preview_image_to_video.py +0 -212
- package/templates/api/src/boards/generators/implementations/fal/video/wan_25_preview_text_to_video.py +0 -208
- package/templates/api/src/boards/generators/implementations/fal/video/wan_pro_image_to_video.py +0 -158
- package/templates/api/src/boards/generators/implementations/kie/__init__.py +0 -11
- package/templates/api/src/boards/generators/implementations/kie/base.py +0 -316
- package/templates/api/src/boards/generators/implementations/kie/image/__init__.py +0 -3
- package/templates/api/src/boards/generators/implementations/kie/image/nano_banana_edit.py +0 -190
- package/templates/api/src/boards/generators/implementations/kie/utils.py +0 -98
- package/templates/api/src/boards/generators/implementations/kie/video/__init__.py +0 -8
- package/templates/api/src/boards/generators/implementations/kie/video/veo3.py +0 -161
- package/templates/api/src/boards/generators/implementations/openai/__init__.py +0 -1
- package/templates/api/src/boards/generators/implementations/openai/audio/__init__.py +0 -1
- package/templates/api/src/boards/generators/implementations/openai/audio/whisper.py +0 -69
- package/templates/api/src/boards/generators/implementations/openai/image/__init__.py +0 -1
- package/templates/api/src/boards/generators/implementations/openai/image/dalle3.py +0 -96
- package/templates/api/src/boards/generators/implementations/replicate/__init__.py +0 -1
- package/templates/api/src/boards/generators/implementations/replicate/image/__init__.py +0 -1
- package/templates/api/src/boards/generators/implementations/replicate/image/flux_pro.py +0 -88
- package/templates/api/src/boards/generators/implementations/replicate/video/__init__.py +0 -1
- package/templates/api/src/boards/generators/implementations/replicate/video/lipsync.py +0 -73
- package/templates/api/src/boards/generators/loader.py +0 -253
- package/templates/api/src/boards/generators/registry.py +0 -114
- package/templates/api/src/boards/generators/resolution.py +0 -632
- package/templates/api/src/boards/generators/testmods/class_gen.py +0 -34
- package/templates/api/src/boards/generators/testmods/import_side_effect.py +0 -35
- package/templates/api/src/boards/graphql/__init__.py +0 -7
- package/templates/api/src/boards/graphql/access_control.py +0 -136
- package/templates/api/src/boards/graphql/mutations/root.py +0 -148
- package/templates/api/src/boards/graphql/queries/root.py +0 -116
- package/templates/api/src/boards/graphql/resolvers/__init__.py +0 -8
- package/templates/api/src/boards/graphql/resolvers/auth.py +0 -12
- package/templates/api/src/boards/graphql/resolvers/board.py +0 -1053
- package/templates/api/src/boards/graphql/resolvers/generation.py +0 -666
- package/templates/api/src/boards/graphql/resolvers/generator.py +0 -50
- package/templates/api/src/boards/graphql/resolvers/lineage.py +0 -381
- package/templates/api/src/boards/graphql/resolvers/upload.py +0 -463
- package/templates/api/src/boards/graphql/resolvers/user.py +0 -25
- package/templates/api/src/boards/graphql/schema.py +0 -81
- package/templates/api/src/boards/graphql/types/board.py +0 -102
- package/templates/api/src/boards/graphql/types/generation.py +0 -166
- package/templates/api/src/boards/graphql/types/generator.py +0 -17
- package/templates/api/src/boards/graphql/types/user.py +0 -47
- package/templates/api/src/boards/jobs/repository.py +0 -153
- package/templates/api/src/boards/logging.py +0 -195
- package/templates/api/src/boards/middleware.py +0 -339
- package/templates/api/src/boards/progress/__init__.py +0 -4
- package/templates/api/src/boards/progress/models.py +0 -25
- package/templates/api/src/boards/progress/publisher.py +0 -64
- package/templates/api/src/boards/py.typed +0 -0
- package/templates/api/src/boards/redis_pool.py +0 -118
- package/templates/api/src/boards/storage/__init__.py +0 -52
- package/templates/api/src/boards/storage/base.py +0 -363
- package/templates/api/src/boards/storage/config.py +0 -187
- package/templates/api/src/boards/storage/factory.py +0 -288
- package/templates/api/src/boards/storage/implementations/__init__.py +0 -27
- package/templates/api/src/boards/storage/implementations/gcs.py +0 -340
- package/templates/api/src/boards/storage/implementations/local.py +0 -201
- package/templates/api/src/boards/storage/implementations/s3.py +0 -294
- package/templates/api/src/boards/storage/implementations/supabase.py +0 -218
- package/templates/api/src/boards/tenant_isolation.py +0 -446
- package/templates/api/src/boards/validation.py +0 -262
- package/templates/api/src/boards/workers/__init__.py +0 -1
- package/templates/api/src/boards/workers/actors.py +0 -274
- package/templates/api/src/boards/workers/cli.py +0 -125
- package/templates/api/src/boards/workers/context.py +0 -348
- package/templates/api/src/boards/workers/middleware.py +0 -58
- package/templates/api/src/py.typed +0 -0
- package/templates/compose.web.yaml +0 -35
- package/templates/compose.yaml +0 -116
- package/templates/docker/env.example +0 -23
- package/templates/web/.env.example +0 -28
- package/templates/web/Dockerfile +0 -51
- package/templates/web/components.json +0 -22
- package/templates/web/imageLoader.js +0 -18
- package/templates/web/next-env.d.ts +0 -5
- package/templates/web/next.config.js +0 -36
- package/templates/web/package.json +0 -41
- package/templates/web/postcss.config.mjs +0 -7
- package/templates/web/public/favicon.ico +0 -0
- package/templates/web/src/app/boards/[boardId]/page.tsx +0 -353
- package/templates/web/src/app/globals.css +0 -123
- package/templates/web/src/app/layout.tsx +0 -31
- package/templates/web/src/app/lineage/[generationId]/page.tsx +0 -235
- package/templates/web/src/app/page.tsx +0 -35
- package/templates/web/src/app/providers.tsx +0 -18
- package/templates/web/src/components/boards/ArtifactInputSlots.tsx +0 -206
- package/templates/web/src/components/boards/ArtifactPreview.tsx +0 -466
- package/templates/web/src/components/boards/GenerationGrid.tsx +0 -282
- package/templates/web/src/components/boards/GenerationInput.tsx +0 -370
- package/templates/web/src/components/boards/GeneratorSelector.tsx +0 -272
- package/templates/web/src/components/boards/UploadArtifact.tsx +0 -563
- package/templates/web/src/components/header.tsx +0 -32
- package/templates/web/src/components/theme-provider.tsx +0 -10
- package/templates/web/src/components/theme-toggle.tsx +0 -75
- package/templates/web/src/components/ui/alert-dialog.tsx +0 -157
- package/templates/web/src/components/ui/button.tsx +0 -58
- package/templates/web/src/components/ui/card.tsx +0 -92
- package/templates/web/src/components/ui/dropdown-menu.tsx +0 -200
- package/templates/web/src/components/ui/navigation-menu.tsx +0 -168
- package/templates/web/src/components/ui/toast.tsx +0 -128
- package/templates/web/src/components/ui/toaster.tsx +0 -35
- package/templates/web/src/components/ui/use-toast.ts +0 -187
- package/templates/web/src/hooks/useGeneratorMRU.ts +0 -57
- package/templates/web/src/lib/utils.ts +0 -6
- package/templates/web/tsconfig.json +0 -41
|
@@ -1,221 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
fal.ai FLUX 2 Pro Edit generator.
|
|
3
|
-
|
|
4
|
-
Production-grade multi-reference image editing that combines up to 9 reference
|
|
5
|
-
images through a streamlined pipeline for professional image manipulation.
|
|
6
|
-
Supports natural language precision, explicit image indexing with @ symbol,
|
|
7
|
-
and zero-configuration workflow.
|
|
8
|
-
|
|
9
|
-
Based on Fal AI's fal-ai/flux-2-pro/edit model.
|
|
10
|
-
See: https://fal.ai/models/fal-ai/flux-2-pro/edit
|
|
11
|
-
"""
|
|
12
|
-
|
|
13
|
-
import os
|
|
14
|
-
from typing import Literal
|
|
15
|
-
|
|
16
|
-
from pydantic import BaseModel, Field
|
|
17
|
-
|
|
18
|
-
from ....artifacts import ImageArtifact
|
|
19
|
-
from ....base import BaseGenerator, GeneratorExecutionContext, GeneratorResult
|
|
20
|
-
|
|
21
|
-
# Image size presets supported by the API
|
|
22
|
-
ImageSizePreset = Literal[
|
|
23
|
-
"auto",
|
|
24
|
-
"square_hd",
|
|
25
|
-
"square",
|
|
26
|
-
"portrait_4_3",
|
|
27
|
-
"portrait_16_9",
|
|
28
|
-
"landscape_4_3",
|
|
29
|
-
"landscape_16_9",
|
|
30
|
-
]
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
class Flux2ProEditInput(BaseModel):
|
|
34
|
-
"""Input schema for FLUX 2 Pro Edit.
|
|
35
|
-
|
|
36
|
-
Artifact fields (like image_sources) are automatically detected via type
|
|
37
|
-
introspection and resolved from generation IDs to ImageArtifact objects.
|
|
38
|
-
"""
|
|
39
|
-
|
|
40
|
-
prompt: str = Field(
|
|
41
|
-
description="The prompt to edit the images. Use @ symbol to reference "
|
|
42
|
-
"specific input images by index (e.g., '@1' for first image)."
|
|
43
|
-
)
|
|
44
|
-
image_sources: list[ImageArtifact] = Field(
|
|
45
|
-
description="List of input images for editing (up to 9 images, 9 MP total)",
|
|
46
|
-
min_length=1,
|
|
47
|
-
max_length=9,
|
|
48
|
-
)
|
|
49
|
-
image_size: ImageSizePreset | None = Field(
|
|
50
|
-
default="auto",
|
|
51
|
-
description="The size of the generated image. If 'auto', the size will be "
|
|
52
|
-
"determined by the model based on input images.",
|
|
53
|
-
)
|
|
54
|
-
output_format: Literal["jpeg", "png"] = Field(
|
|
55
|
-
default="jpeg",
|
|
56
|
-
description="The format of the generated image.",
|
|
57
|
-
)
|
|
58
|
-
sync_mode: bool = Field(
|
|
59
|
-
default=False,
|
|
60
|
-
description=(
|
|
61
|
-
"If True, the media will be returned as a data URI and the output "
|
|
62
|
-
"data won't be available in the request history."
|
|
63
|
-
),
|
|
64
|
-
)
|
|
65
|
-
safety_tolerance: Literal["1", "2", "3", "4", "5"] = Field(
|
|
66
|
-
default="2",
|
|
67
|
-
description=(
|
|
68
|
-
"The safety tolerance level for the generated image. "
|
|
69
|
-
"1 is most strict, 5 is most permissive."
|
|
70
|
-
),
|
|
71
|
-
)
|
|
72
|
-
enable_safety_checker: bool = Field(
|
|
73
|
-
default=True,
|
|
74
|
-
description="Whether to enable the safety checker.",
|
|
75
|
-
)
|
|
76
|
-
seed: int | None = Field(
|
|
77
|
-
default=None,
|
|
78
|
-
description="The seed to use for the generation. Leave empty for random.",
|
|
79
|
-
)
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
class FalFlux2ProEditGenerator(BaseGenerator):
|
|
83
|
-
"""FLUX 2 Pro Edit image generator using fal.ai.
|
|
84
|
-
|
|
85
|
-
Production-grade multi-reference editing that combines up to 9 reference
|
|
86
|
-
images through a streamlined pipeline. Supports natural language precision
|
|
87
|
-
for describing complex edits without masks.
|
|
88
|
-
"""
|
|
89
|
-
|
|
90
|
-
name = "fal-flux-2-pro-edit"
|
|
91
|
-
artifact_type = "image"
|
|
92
|
-
description = "Fal: FLUX 2 Pro Edit - Production-grade multi-reference image editing"
|
|
93
|
-
|
|
94
|
-
def get_input_schema(self) -> type[Flux2ProEditInput]:
|
|
95
|
-
return Flux2ProEditInput
|
|
96
|
-
|
|
97
|
-
async def generate(
|
|
98
|
-
self, inputs: Flux2ProEditInput, context: GeneratorExecutionContext
|
|
99
|
-
) -> GeneratorResult:
|
|
100
|
-
"""Edit images using fal.ai FLUX 2 Pro Edit model."""
|
|
101
|
-
# Check for API key (fal-client uses FAL_KEY environment variable)
|
|
102
|
-
if not os.getenv("FAL_KEY"):
|
|
103
|
-
raise ValueError("API configuration invalid. Missing FAL_KEY environment variable")
|
|
104
|
-
|
|
105
|
-
# Import fal_client
|
|
106
|
-
try:
|
|
107
|
-
import fal_client
|
|
108
|
-
except ImportError as e:
|
|
109
|
-
raise ImportError(
|
|
110
|
-
"fal.ai SDK is required for FalFlux2ProEditGenerator. "
|
|
111
|
-
"Install with: pip install weirdfingers-boards[generators-fal]"
|
|
112
|
-
) from e
|
|
113
|
-
|
|
114
|
-
# Upload image artifacts to Fal's public storage
|
|
115
|
-
# Fal API requires publicly accessible URLs, but our storage_url might be:
|
|
116
|
-
# - Localhost URLs (not publicly accessible)
|
|
117
|
-
# - Private S3 buckets (not publicly accessible)
|
|
118
|
-
# So we upload to Fal's temporary storage first
|
|
119
|
-
from ..utils import upload_artifacts_to_fal
|
|
120
|
-
|
|
121
|
-
image_urls = await upload_artifacts_to_fal(inputs.image_sources, context)
|
|
122
|
-
|
|
123
|
-
# Prepare arguments for fal.ai API
|
|
124
|
-
arguments: dict = {
|
|
125
|
-
"prompt": inputs.prompt,
|
|
126
|
-
"image_urls": image_urls,
|
|
127
|
-
"output_format": inputs.output_format,
|
|
128
|
-
"sync_mode": inputs.sync_mode,
|
|
129
|
-
"safety_tolerance": inputs.safety_tolerance,
|
|
130
|
-
"enable_safety_checker": inputs.enable_safety_checker,
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
# Add optional parameters
|
|
134
|
-
if inputs.image_size is not None:
|
|
135
|
-
arguments["image_size"] = inputs.image_size
|
|
136
|
-
if inputs.seed is not None:
|
|
137
|
-
arguments["seed"] = inputs.seed
|
|
138
|
-
|
|
139
|
-
# Submit async job and get handler
|
|
140
|
-
handler = await fal_client.submit_async(
|
|
141
|
-
"fal-ai/flux-2-pro/edit",
|
|
142
|
-
arguments=arguments,
|
|
143
|
-
)
|
|
144
|
-
|
|
145
|
-
# Store the external job ID for tracking
|
|
146
|
-
await context.set_external_job_id(handler.request_id)
|
|
147
|
-
|
|
148
|
-
# Stream progress updates (sample every 3rd event to avoid spam)
|
|
149
|
-
from .....progress.models import ProgressUpdate
|
|
150
|
-
|
|
151
|
-
event_count = 0
|
|
152
|
-
async for event in handler.iter_events(with_logs=True):
|
|
153
|
-
event_count += 1
|
|
154
|
-
|
|
155
|
-
# Process every 3rd event to provide feedback without overwhelming
|
|
156
|
-
if event_count % 3 == 0:
|
|
157
|
-
# Extract logs if available
|
|
158
|
-
logs = getattr(event, "logs", None)
|
|
159
|
-
if logs:
|
|
160
|
-
# Join log entries into a single message
|
|
161
|
-
if isinstance(logs, list):
|
|
162
|
-
message = " | ".join(str(log) for log in logs if log)
|
|
163
|
-
else:
|
|
164
|
-
message = str(logs)
|
|
165
|
-
|
|
166
|
-
if message:
|
|
167
|
-
await context.publish_progress(
|
|
168
|
-
ProgressUpdate(
|
|
169
|
-
job_id=handler.request_id,
|
|
170
|
-
status="processing",
|
|
171
|
-
progress=50.0, # Approximate mid-point progress
|
|
172
|
-
phase="processing",
|
|
173
|
-
message=message,
|
|
174
|
-
)
|
|
175
|
-
)
|
|
176
|
-
|
|
177
|
-
# Get final result
|
|
178
|
-
result = await handler.get()
|
|
179
|
-
|
|
180
|
-
# Extract image URLs from result
|
|
181
|
-
# fal.ai returns: {
|
|
182
|
-
# "images": [{"url": "...", "width": ..., "height": ..., ...}, ...],
|
|
183
|
-
# "seed": ...
|
|
184
|
-
# }
|
|
185
|
-
images = result.get("images", [])
|
|
186
|
-
|
|
187
|
-
if not images:
|
|
188
|
-
raise ValueError("No images returned from fal.ai API")
|
|
189
|
-
|
|
190
|
-
# Store each image using output_index
|
|
191
|
-
artifacts = []
|
|
192
|
-
for idx, image_data in enumerate(images):
|
|
193
|
-
image_url = image_data.get("url")
|
|
194
|
-
# Extract dimensions if available, otherwise use sensible defaults
|
|
195
|
-
width = image_data.get("width", 1024)
|
|
196
|
-
height = image_data.get("height", 1024)
|
|
197
|
-
|
|
198
|
-
if not image_url:
|
|
199
|
-
raise ValueError(f"Image {idx} missing URL in fal.ai response")
|
|
200
|
-
|
|
201
|
-
# Store with appropriate output_index
|
|
202
|
-
artifact = await context.store_image_result(
|
|
203
|
-
storage_url=image_url,
|
|
204
|
-
format=inputs.output_format,
|
|
205
|
-
width=width,
|
|
206
|
-
height=height,
|
|
207
|
-
output_index=idx,
|
|
208
|
-
)
|
|
209
|
-
artifacts.append(artifact)
|
|
210
|
-
|
|
211
|
-
return GeneratorResult(outputs=artifacts)
|
|
212
|
-
|
|
213
|
-
async def estimate_cost(self, inputs: Flux2ProEditInput) -> float:
|
|
214
|
-
"""Estimate cost for FLUX 2 Pro Edit generation.
|
|
215
|
-
|
|
216
|
-
Pricing: $0.03 for first megapixel, $0.015 for additional megapixels.
|
|
217
|
-
Using base cost of $0.03 as default (1 MP output).
|
|
218
|
-
"""
|
|
219
|
-
# Base cost per generation (1 megapixel default)
|
|
220
|
-
base_cost = 0.03
|
|
221
|
-
return base_cost
|
|
@@ -1,216 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
fal.ai FLUX.1 [pro] Kontext image-to-image generator.
|
|
3
|
-
|
|
4
|
-
Handles both text and reference images as inputs, enabling targeted local edits
|
|
5
|
-
and complex transformations of entire scenes using fal.ai's flux-pro/kontext model.
|
|
6
|
-
"""
|
|
7
|
-
|
|
8
|
-
import os
|
|
9
|
-
from typing import Literal
|
|
10
|
-
|
|
11
|
-
from pydantic import BaseModel, Field
|
|
12
|
-
|
|
13
|
-
from ....artifacts import ImageArtifact
|
|
14
|
-
from ....base import BaseGenerator, GeneratorExecutionContext, GeneratorResult
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
class FluxProKontextInput(BaseModel):
|
|
18
|
-
"""Input schema for FLUX.1 [pro] Kontext image generation.
|
|
19
|
-
|
|
20
|
-
Artifact fields (like image_url) are automatically detected via type
|
|
21
|
-
introspection and resolved from generation IDs to ImageArtifact objects.
|
|
22
|
-
"""
|
|
23
|
-
|
|
24
|
-
prompt: str = Field(
|
|
25
|
-
description="Text prompt for image editing (e.g., 'Put a donut next to the flour')"
|
|
26
|
-
)
|
|
27
|
-
image_url: ImageArtifact = Field(
|
|
28
|
-
description="Reference image for transformation (from previous generation)",
|
|
29
|
-
)
|
|
30
|
-
aspect_ratio: (
|
|
31
|
-
Literal[
|
|
32
|
-
"21:9",
|
|
33
|
-
"16:9",
|
|
34
|
-
"4:3",
|
|
35
|
-
"3:2",
|
|
36
|
-
"1:1",
|
|
37
|
-
"2:3",
|
|
38
|
-
"3:4",
|
|
39
|
-
"9:16",
|
|
40
|
-
"9:21",
|
|
41
|
-
]
|
|
42
|
-
| None
|
|
43
|
-
) = Field(
|
|
44
|
-
default=None,
|
|
45
|
-
description="Image aspect ratio (optional)",
|
|
46
|
-
)
|
|
47
|
-
num_images: int = Field(
|
|
48
|
-
default=1,
|
|
49
|
-
ge=1,
|
|
50
|
-
le=4,
|
|
51
|
-
description="Number of images to generate (1-4)",
|
|
52
|
-
)
|
|
53
|
-
output_format: Literal["jpeg", "png"] = Field(
|
|
54
|
-
default="jpeg",
|
|
55
|
-
description="Output image format",
|
|
56
|
-
)
|
|
57
|
-
sync_mode: bool = Field(
|
|
58
|
-
default=False,
|
|
59
|
-
description=(
|
|
60
|
-
"If True, the media will be returned as a data URI and the output "
|
|
61
|
-
"data won't be available in the request history"
|
|
62
|
-
),
|
|
63
|
-
)
|
|
64
|
-
safety_tolerance: str = Field(
|
|
65
|
-
default="2",
|
|
66
|
-
description="Safety tolerance level (1-6 scale, higher is more permissive)",
|
|
67
|
-
)
|
|
68
|
-
guidance_scale: float = Field(
|
|
69
|
-
default=3.5,
|
|
70
|
-
ge=1.0,
|
|
71
|
-
le=20.0,
|
|
72
|
-
description="Guidance scale for image generation (1-20)",
|
|
73
|
-
)
|
|
74
|
-
seed: int | None = Field(
|
|
75
|
-
default=None,
|
|
76
|
-
description="Random seed for reproducible outputs (optional)",
|
|
77
|
-
)
|
|
78
|
-
enhance_prompt: bool = Field(
|
|
79
|
-
default=False,
|
|
80
|
-
description="Automatically enhance the prompt for better quality",
|
|
81
|
-
)
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
class FalFluxProKontextGenerator(BaseGenerator):
|
|
85
|
-
"""FLUX.1 [pro] Kontext image-to-image generator using fal.ai."""
|
|
86
|
-
|
|
87
|
-
name = "fal-flux-pro-kontext"
|
|
88
|
-
artifact_type = "image"
|
|
89
|
-
description = (
|
|
90
|
-
"Fal: FLUX.1 [pro] Kontext - Image-to-image editing with text and reference images"
|
|
91
|
-
)
|
|
92
|
-
|
|
93
|
-
def get_input_schema(self) -> type[FluxProKontextInput]:
|
|
94
|
-
return FluxProKontextInput
|
|
95
|
-
|
|
96
|
-
async def generate(
|
|
97
|
-
self, inputs: FluxProKontextInput, context: GeneratorExecutionContext
|
|
98
|
-
) -> GeneratorResult:
|
|
99
|
-
"""Generate images using fal.ai flux-pro/kontext model."""
|
|
100
|
-
# Check for API key (fal-client uses FAL_KEY environment variable)
|
|
101
|
-
if not os.getenv("FAL_KEY"):
|
|
102
|
-
raise ValueError("API configuration invalid. Missing FAL_KEY environment variable")
|
|
103
|
-
|
|
104
|
-
# Import fal_client
|
|
105
|
-
try:
|
|
106
|
-
import fal_client
|
|
107
|
-
except ImportError as e:
|
|
108
|
-
raise ImportError(
|
|
109
|
-
"fal.ai SDK is required for FalFluxProKontextGenerator. "
|
|
110
|
-
"Install with: pip install weirdfingers-boards[generators-fal]"
|
|
111
|
-
) from e
|
|
112
|
-
|
|
113
|
-
# Upload image artifact to Fal's public storage
|
|
114
|
-
# Fal API requires publicly accessible URLs
|
|
115
|
-
from ..utils import upload_artifacts_to_fal
|
|
116
|
-
|
|
117
|
-
image_urls = await upload_artifacts_to_fal([inputs.image_url], context)
|
|
118
|
-
image_url = image_urls[0] # Extract single URL from list
|
|
119
|
-
|
|
120
|
-
# Prepare arguments for fal.ai API
|
|
121
|
-
arguments = {
|
|
122
|
-
"prompt": inputs.prompt,
|
|
123
|
-
"image_url": image_url,
|
|
124
|
-
"num_images": inputs.num_images,
|
|
125
|
-
"output_format": inputs.output_format,
|
|
126
|
-
"sync_mode": inputs.sync_mode,
|
|
127
|
-
"safety_tolerance": inputs.safety_tolerance,
|
|
128
|
-
"guidance_scale": inputs.guidance_scale,
|
|
129
|
-
"enhance_prompt": inputs.enhance_prompt,
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
# Add optional fields if provided
|
|
133
|
-
if inputs.aspect_ratio is not None:
|
|
134
|
-
arguments["aspect_ratio"] = inputs.aspect_ratio
|
|
135
|
-
|
|
136
|
-
if inputs.seed is not None:
|
|
137
|
-
arguments["seed"] = inputs.seed
|
|
138
|
-
|
|
139
|
-
# Submit async job and get handler
|
|
140
|
-
handler = await fal_client.submit_async(
|
|
141
|
-
"fal-ai/flux-pro/kontext",
|
|
142
|
-
arguments=arguments,
|
|
143
|
-
)
|
|
144
|
-
|
|
145
|
-
# Store the external job ID for tracking
|
|
146
|
-
await context.set_external_job_id(handler.request_id)
|
|
147
|
-
|
|
148
|
-
# Stream progress updates (sample every 3rd event to avoid spam)
|
|
149
|
-
from .....progress.models import ProgressUpdate
|
|
150
|
-
|
|
151
|
-
event_count = 0
|
|
152
|
-
async for event in handler.iter_events(with_logs=True):
|
|
153
|
-
event_count += 1
|
|
154
|
-
|
|
155
|
-
# Process every 3rd event to provide feedback without overwhelming
|
|
156
|
-
if event_count % 3 == 0:
|
|
157
|
-
# Extract logs if available
|
|
158
|
-
logs = getattr(event, "logs", None)
|
|
159
|
-
if logs:
|
|
160
|
-
# Join log entries into a single message
|
|
161
|
-
if isinstance(logs, list):
|
|
162
|
-
message = " | ".join(str(log) for log in logs if log)
|
|
163
|
-
else:
|
|
164
|
-
message = str(logs)
|
|
165
|
-
|
|
166
|
-
if message:
|
|
167
|
-
await context.publish_progress(
|
|
168
|
-
ProgressUpdate(
|
|
169
|
-
job_id=handler.request_id,
|
|
170
|
-
status="processing",
|
|
171
|
-
progress=50.0, # Approximate mid-point progress
|
|
172
|
-
phase="processing",
|
|
173
|
-
message=message,
|
|
174
|
-
)
|
|
175
|
-
)
|
|
176
|
-
|
|
177
|
-
# Get final result
|
|
178
|
-
result = await handler.get()
|
|
179
|
-
|
|
180
|
-
# Extract image URLs from result
|
|
181
|
-
# fal.ai returns: {"images": [{"url": "...", "width": ..., "height": ...}, ...]}
|
|
182
|
-
images = result.get("images", [])
|
|
183
|
-
if not images:
|
|
184
|
-
raise ValueError("No images returned from fal.ai API")
|
|
185
|
-
|
|
186
|
-
# Store each image using output_index
|
|
187
|
-
artifacts = []
|
|
188
|
-
for idx, image_data in enumerate(images):
|
|
189
|
-
image_url_result = image_data.get("url")
|
|
190
|
-
width = image_data.get("width", 1024)
|
|
191
|
-
height = image_data.get("height", 1024)
|
|
192
|
-
|
|
193
|
-
if not image_url_result:
|
|
194
|
-
raise ValueError(f"Image {idx} missing URL in fal.ai response")
|
|
195
|
-
|
|
196
|
-
# Store with appropriate output_index
|
|
197
|
-
artifact = await context.store_image_result(
|
|
198
|
-
storage_url=image_url_result,
|
|
199
|
-
format=inputs.output_format,
|
|
200
|
-
width=width,
|
|
201
|
-
height=height,
|
|
202
|
-
output_index=idx,
|
|
203
|
-
)
|
|
204
|
-
artifacts.append(artifact)
|
|
205
|
-
|
|
206
|
-
return GeneratorResult(outputs=artifacts)
|
|
207
|
-
|
|
208
|
-
async def estimate_cost(self, inputs: FluxProKontextInput) -> float:
|
|
209
|
-
"""Estimate cost for FLUX.1 [pro] Kontext generation.
|
|
210
|
-
|
|
211
|
-
FLUX.1 [pro] Kontext is a premium image-to-image model. Estimated cost
|
|
212
|
-
is approximately $0.055 per image based on similar Flux Pro models.
|
|
213
|
-
"""
|
|
214
|
-
# Cost per image * number of images
|
|
215
|
-
cost_per_image = 0.055
|
|
216
|
-
return cost_per_image * inputs.num_images
|
|
@@ -1,197 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
fal.ai FLUX1.1 [pro] ultra text-to-image generator.
|
|
3
|
-
|
|
4
|
-
High-quality image generation using fal.ai's FLUX1.1 [pro] ultra model with support for
|
|
5
|
-
batch outputs and advanced controls.
|
|
6
|
-
"""
|
|
7
|
-
|
|
8
|
-
import os
|
|
9
|
-
from typing import Literal
|
|
10
|
-
|
|
11
|
-
from pydantic import BaseModel, Field
|
|
12
|
-
|
|
13
|
-
from ....base import BaseGenerator, GeneratorExecutionContext, GeneratorResult
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
class FluxProUltraInput(BaseModel):
|
|
17
|
-
"""Input schema for FLUX1.1 [pro] ultra image generation."""
|
|
18
|
-
|
|
19
|
-
prompt: str = Field(description="Text prompt for image generation")
|
|
20
|
-
aspect_ratio: Literal[
|
|
21
|
-
"21:9",
|
|
22
|
-
"16:9",
|
|
23
|
-
"4:3",
|
|
24
|
-
"3:2",
|
|
25
|
-
"1:1",
|
|
26
|
-
"2:3",
|
|
27
|
-
"3:4",
|
|
28
|
-
"9:16",
|
|
29
|
-
"9:21",
|
|
30
|
-
] = Field(
|
|
31
|
-
default="16:9",
|
|
32
|
-
description="Image aspect ratio",
|
|
33
|
-
)
|
|
34
|
-
num_images: int = Field(
|
|
35
|
-
default=1,
|
|
36
|
-
ge=1,
|
|
37
|
-
le=4,
|
|
38
|
-
description="Number of images to generate in batch (max 4)",
|
|
39
|
-
)
|
|
40
|
-
enable_safety_checker: bool = Field(
|
|
41
|
-
default=True,
|
|
42
|
-
description="Enable safety checker to filter unsafe content",
|
|
43
|
-
)
|
|
44
|
-
safety_tolerance: int = Field(
|
|
45
|
-
default=2,
|
|
46
|
-
ge=1,
|
|
47
|
-
le=6,
|
|
48
|
-
description="Safety tolerance level (1 = most strict, 6 = most permissive)",
|
|
49
|
-
)
|
|
50
|
-
seed: int | None = Field(
|
|
51
|
-
default=None,
|
|
52
|
-
description="Random seed for reproducibility (optional)",
|
|
53
|
-
)
|
|
54
|
-
output_format: Literal["jpeg", "png"] = Field(
|
|
55
|
-
default="jpeg",
|
|
56
|
-
description="Output image format",
|
|
57
|
-
)
|
|
58
|
-
enhance_prompt: bool = Field(
|
|
59
|
-
default=False,
|
|
60
|
-
description="Whether to enhance the prompt for better results",
|
|
61
|
-
)
|
|
62
|
-
raw: bool = Field(
|
|
63
|
-
default=False,
|
|
64
|
-
description="Generate less processed, more natural-looking images",
|
|
65
|
-
)
|
|
66
|
-
sync_mode: bool = Field(
|
|
67
|
-
default=True,
|
|
68
|
-
description="Use synchronous mode (wait for completion)",
|
|
69
|
-
)
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
class FalFluxProUltraGenerator(BaseGenerator):
|
|
73
|
-
"""FLUX1.1 [pro] ultra image generator using fal.ai."""
|
|
74
|
-
|
|
75
|
-
name = "fal-flux-pro-ultra"
|
|
76
|
-
artifact_type = "image"
|
|
77
|
-
description = (
|
|
78
|
-
"Fal: FLUX1.1 [pro] ultra - high-quality text-to-image generation with advanced controls"
|
|
79
|
-
)
|
|
80
|
-
|
|
81
|
-
def get_input_schema(self) -> type[FluxProUltraInput]:
|
|
82
|
-
return FluxProUltraInput
|
|
83
|
-
|
|
84
|
-
async def generate(
|
|
85
|
-
self, inputs: FluxProUltraInput, context: GeneratorExecutionContext
|
|
86
|
-
) -> GeneratorResult:
|
|
87
|
-
"""Generate images using fal.ai FLUX1.1 [pro] ultra model."""
|
|
88
|
-
# Check for API key (fal-client uses FAL_KEY environment variable)
|
|
89
|
-
if not os.getenv("FAL_KEY"):
|
|
90
|
-
raise ValueError("API configuration invalid. Missing FAL_KEY environment variable")
|
|
91
|
-
|
|
92
|
-
# Import fal_client
|
|
93
|
-
try:
|
|
94
|
-
import fal_client
|
|
95
|
-
except ImportError as e:
|
|
96
|
-
raise ImportError(
|
|
97
|
-
"fal.ai SDK is required for FalFluxProUltraGenerator. "
|
|
98
|
-
"Install with: pip install weirdfingers-boards[generators-fal]"
|
|
99
|
-
) from e
|
|
100
|
-
|
|
101
|
-
# Prepare arguments for fal.ai API
|
|
102
|
-
arguments = {
|
|
103
|
-
"prompt": inputs.prompt,
|
|
104
|
-
"aspect_ratio": inputs.aspect_ratio,
|
|
105
|
-
"num_images": inputs.num_images,
|
|
106
|
-
"enable_safety_checker": inputs.enable_safety_checker,
|
|
107
|
-
"safety_tolerance": inputs.safety_tolerance,
|
|
108
|
-
"output_format": inputs.output_format,
|
|
109
|
-
"enhance_prompt": inputs.enhance_prompt,
|
|
110
|
-
"raw": inputs.raw,
|
|
111
|
-
"sync_mode": inputs.sync_mode,
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
# Add seed if provided
|
|
115
|
-
if inputs.seed is not None:
|
|
116
|
-
arguments["seed"] = inputs.seed
|
|
117
|
-
|
|
118
|
-
# Submit async job and get handler
|
|
119
|
-
handler = await fal_client.submit_async(
|
|
120
|
-
"fal-ai/flux-pro/v1.1-ultra",
|
|
121
|
-
arguments=arguments,
|
|
122
|
-
)
|
|
123
|
-
|
|
124
|
-
# Store the external job ID for tracking
|
|
125
|
-
await context.set_external_job_id(handler.request_id)
|
|
126
|
-
|
|
127
|
-
# Stream progress updates (sample every 3rd event to avoid spam)
|
|
128
|
-
from .....progress.models import ProgressUpdate
|
|
129
|
-
|
|
130
|
-
event_count = 0
|
|
131
|
-
async for event in handler.iter_events(with_logs=True):
|
|
132
|
-
event_count += 1
|
|
133
|
-
|
|
134
|
-
# Process every 3rd event to provide feedback without overwhelming
|
|
135
|
-
if event_count % 3 == 0:
|
|
136
|
-
# Extract logs if available
|
|
137
|
-
logs = getattr(event, "logs", None)
|
|
138
|
-
if logs:
|
|
139
|
-
# Join log entries into a single message
|
|
140
|
-
if isinstance(logs, list):
|
|
141
|
-
message = " | ".join(str(log) for log in logs if log)
|
|
142
|
-
else:
|
|
143
|
-
message = str(logs)
|
|
144
|
-
|
|
145
|
-
if message:
|
|
146
|
-
await context.publish_progress(
|
|
147
|
-
ProgressUpdate(
|
|
148
|
-
job_id=handler.request_id,
|
|
149
|
-
status="processing",
|
|
150
|
-
progress=50.0, # Approximate mid-point progress
|
|
151
|
-
phase="processing",
|
|
152
|
-
message=message,
|
|
153
|
-
)
|
|
154
|
-
)
|
|
155
|
-
|
|
156
|
-
# Get final result
|
|
157
|
-
result = await handler.get()
|
|
158
|
-
|
|
159
|
-
# Extract image URLs from result
|
|
160
|
-
# fal.ai returns: {"images": [{"url": "...", "width": ..., "height": ...}, ...]}
|
|
161
|
-
images = result.get("images", [])
|
|
162
|
-
if not images:
|
|
163
|
-
raise ValueError("No images returned from fal.ai API")
|
|
164
|
-
|
|
165
|
-
# Store each image using output_index
|
|
166
|
-
artifacts = []
|
|
167
|
-
for idx, image_data in enumerate(images):
|
|
168
|
-
image_url = image_data.get("url")
|
|
169
|
-
width = image_data.get("width", 1024)
|
|
170
|
-
height = image_data.get("height", 1024)
|
|
171
|
-
|
|
172
|
-
if not image_url:
|
|
173
|
-
raise ValueError(f"Image {idx} missing URL in fal.ai response")
|
|
174
|
-
|
|
175
|
-
# Store with appropriate output_index
|
|
176
|
-
artifact = await context.store_image_result(
|
|
177
|
-
storage_url=image_url,
|
|
178
|
-
format=inputs.output_format,
|
|
179
|
-
width=width,
|
|
180
|
-
height=height,
|
|
181
|
-
output_index=idx,
|
|
182
|
-
)
|
|
183
|
-
artifacts.append(artifact)
|
|
184
|
-
|
|
185
|
-
return GeneratorResult(outputs=artifacts)
|
|
186
|
-
|
|
187
|
-
async def estimate_cost(self, inputs: FluxProUltraInput) -> float:
|
|
188
|
-
"""Estimate cost for FLUX1.1 [pro] ultra generation.
|
|
189
|
-
|
|
190
|
-
FLUX1.1 [pro] ultra billing is based on megapixels (rounded up).
|
|
191
|
-
The aspect ratios map to different resolutions, all roughly in the 2-4MP range.
|
|
192
|
-
Estimated at approximately $0.04 per image based on typical resolutions.
|
|
193
|
-
"""
|
|
194
|
-
# Approximate cost per image (varies slightly by resolution/megapixels)
|
|
195
|
-
# Most aspect ratios result in 2-4 megapixels
|
|
196
|
-
cost_per_image = 0.04
|
|
197
|
-
return cost_per_image * inputs.num_images
|