@weirdfingers/baseboards 0.9.5 → 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 +561 -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
package/templates/api/src/boards/generators/implementations/fal/image/imagen4_preview_fast.py
DELETED
|
@@ -1,179 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Google Imagen 4 fast text-to-image generator.
|
|
3
|
-
|
|
4
|
-
Google's highest quality image generation model with support for multiple aspect ratios
|
|
5
|
-
and batch generation.
|
|
6
|
-
|
|
7
|
-
Based on Fal AI's fal-ai/imagen4/preview/fast model.
|
|
8
|
-
See: https://fal.ai/models/fal-ai/imagen4/preview/fast
|
|
9
|
-
"""
|
|
10
|
-
|
|
11
|
-
import os
|
|
12
|
-
from typing import Literal
|
|
13
|
-
|
|
14
|
-
from pydantic import BaseModel, Field
|
|
15
|
-
|
|
16
|
-
from ....base import BaseGenerator, GeneratorExecutionContext, GeneratorResult
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
class Imagen4PreviewFastInput(BaseModel):
|
|
20
|
-
"""Input schema for Google Imagen 4 fast image generation.
|
|
21
|
-
|
|
22
|
-
All parameters are simple types - no artifact inputs needed.
|
|
23
|
-
"""
|
|
24
|
-
|
|
25
|
-
prompt: str = Field(description="The text prompt describing what you want to see")
|
|
26
|
-
negative_prompt: str = Field(
|
|
27
|
-
default="",
|
|
28
|
-
description="Discourage specific elements in generated images",
|
|
29
|
-
)
|
|
30
|
-
aspect_ratio: Literal["1:1", "16:9", "9:16", "3:4", "4:3"] = Field(
|
|
31
|
-
default="1:1",
|
|
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 (1-4)",
|
|
39
|
-
)
|
|
40
|
-
seed: int | None = Field(
|
|
41
|
-
default=None,
|
|
42
|
-
description="Random seed for reproducible generation (optional)",
|
|
43
|
-
)
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
class FalImagen4PreviewFastGenerator(BaseGenerator):
|
|
47
|
-
"""Google Imagen 4 fast image generator using fal.ai."""
|
|
48
|
-
|
|
49
|
-
name = "fal-imagen4-preview-fast"
|
|
50
|
-
artifact_type = "image"
|
|
51
|
-
description = "Fal: Google Imagen 4 - highest quality text-to-image generation"
|
|
52
|
-
|
|
53
|
-
def get_input_schema(self) -> type[Imagen4PreviewFastInput]:
|
|
54
|
-
return Imagen4PreviewFastInput
|
|
55
|
-
|
|
56
|
-
async def generate(
|
|
57
|
-
self, inputs: Imagen4PreviewFastInput, context: GeneratorExecutionContext
|
|
58
|
-
) -> GeneratorResult:
|
|
59
|
-
"""Generate images using Google Imagen 4 via fal.ai."""
|
|
60
|
-
# Check for API key (fal-client uses FAL_KEY environment variable)
|
|
61
|
-
if not os.getenv("FAL_KEY"):
|
|
62
|
-
raise ValueError("API configuration invalid. Missing FAL_KEY environment variable")
|
|
63
|
-
|
|
64
|
-
# Import fal_client
|
|
65
|
-
try:
|
|
66
|
-
import fal_client
|
|
67
|
-
except ImportError as e:
|
|
68
|
-
raise ImportError(
|
|
69
|
-
"fal.ai SDK is required for FalImagen4PreviewFastGenerator. "
|
|
70
|
-
"Install with: pip install weirdfingers-boards[generators-fal]"
|
|
71
|
-
) from e
|
|
72
|
-
|
|
73
|
-
# Prepare arguments for fal.ai API
|
|
74
|
-
arguments = {
|
|
75
|
-
"prompt": inputs.prompt,
|
|
76
|
-
"negative_prompt": inputs.negative_prompt,
|
|
77
|
-
"aspect_ratio": inputs.aspect_ratio,
|
|
78
|
-
"num_images": inputs.num_images,
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
# Add seed if provided
|
|
82
|
-
if inputs.seed is not None:
|
|
83
|
-
arguments["seed"] = inputs.seed
|
|
84
|
-
|
|
85
|
-
# Submit async job and get handler
|
|
86
|
-
handler = await fal_client.submit_async(
|
|
87
|
-
"fal-ai/imagen4/preview/fast",
|
|
88
|
-
arguments=arguments,
|
|
89
|
-
)
|
|
90
|
-
|
|
91
|
-
# Store the external job ID for tracking
|
|
92
|
-
await context.set_external_job_id(handler.request_id)
|
|
93
|
-
|
|
94
|
-
# Stream progress updates (sample every 3rd event to avoid spam)
|
|
95
|
-
from .....progress.models import ProgressUpdate
|
|
96
|
-
|
|
97
|
-
event_count = 0
|
|
98
|
-
async for event in handler.iter_events(with_logs=True):
|
|
99
|
-
event_count += 1
|
|
100
|
-
|
|
101
|
-
# Process every 3rd event to provide feedback without overwhelming
|
|
102
|
-
if event_count % 3 == 0:
|
|
103
|
-
# Extract logs if available
|
|
104
|
-
logs = getattr(event, "logs", None)
|
|
105
|
-
if logs:
|
|
106
|
-
# Join log entries into a single message
|
|
107
|
-
if isinstance(logs, list):
|
|
108
|
-
message = " | ".join(str(log) for log in logs if log)
|
|
109
|
-
else:
|
|
110
|
-
message = str(logs)
|
|
111
|
-
|
|
112
|
-
if message:
|
|
113
|
-
await context.publish_progress(
|
|
114
|
-
ProgressUpdate(
|
|
115
|
-
job_id=handler.request_id,
|
|
116
|
-
status="processing",
|
|
117
|
-
progress=50.0, # Approximate mid-point progress
|
|
118
|
-
phase="processing",
|
|
119
|
-
message=message,
|
|
120
|
-
)
|
|
121
|
-
)
|
|
122
|
-
|
|
123
|
-
# Get final result
|
|
124
|
-
result = await handler.get()
|
|
125
|
-
|
|
126
|
-
# Extract image URLs from result
|
|
127
|
-
# fal.ai returns: {"images": [{"url": "...", "content_type": "...", ...}, ...], "seed": ...}
|
|
128
|
-
images = result.get("images", [])
|
|
129
|
-
if not images:
|
|
130
|
-
raise ValueError("No images returned from fal.ai API")
|
|
131
|
-
|
|
132
|
-
# Store each image using output_index
|
|
133
|
-
artifacts = []
|
|
134
|
-
for idx, image_data in enumerate(images):
|
|
135
|
-
image_url = image_data.get("url")
|
|
136
|
-
|
|
137
|
-
if not image_url:
|
|
138
|
-
raise ValueError(f"Image {idx} missing URL in fal.ai response")
|
|
139
|
-
|
|
140
|
-
# Imagen 4 returns PNG images
|
|
141
|
-
# We don't have width/height in the response, so use defaults based on aspect ratio
|
|
142
|
-
# This is a simplification - actual dimensions may vary
|
|
143
|
-
width, height = self._get_dimensions_for_aspect_ratio(inputs.aspect_ratio)
|
|
144
|
-
|
|
145
|
-
# Store with appropriate output_index
|
|
146
|
-
artifact = await context.store_image_result(
|
|
147
|
-
storage_url=image_url,
|
|
148
|
-
format="png",
|
|
149
|
-
width=width,
|
|
150
|
-
height=height,
|
|
151
|
-
output_index=idx,
|
|
152
|
-
)
|
|
153
|
-
artifacts.append(artifact)
|
|
154
|
-
|
|
155
|
-
return GeneratorResult(outputs=artifacts)
|
|
156
|
-
|
|
157
|
-
def _get_dimensions_for_aspect_ratio(self, aspect_ratio: str) -> tuple[int, int]:
|
|
158
|
-
"""Get approximate dimensions for a given aspect ratio.
|
|
159
|
-
|
|
160
|
-
Returns (width, height) tuple. Uses 1024 as base for 1:1 ratio.
|
|
161
|
-
"""
|
|
162
|
-
aspect_map = {
|
|
163
|
-
"1:1": (1024, 1024),
|
|
164
|
-
"16:9": (1360, 768),
|
|
165
|
-
"9:16": (768, 1360),
|
|
166
|
-
"3:4": (896, 1152),
|
|
167
|
-
"4:3": (1152, 896),
|
|
168
|
-
}
|
|
169
|
-
return aspect_map.get(aspect_ratio, (1024, 1024))
|
|
170
|
-
|
|
171
|
-
async def estimate_cost(self, inputs: Imagen4PreviewFastInput) -> float:
|
|
172
|
-
"""Estimate cost for Google Imagen 4 generation.
|
|
173
|
-
|
|
174
|
-
Pricing information was not available in the documentation.
|
|
175
|
-
Using estimated cost of $0.04 per image based on similar high-quality models.
|
|
176
|
-
"""
|
|
177
|
-
# Estimated cost per image (actual pricing may vary)
|
|
178
|
-
cost_per_image = 0.04
|
|
179
|
-
return cost_per_image * inputs.num_images
|
|
@@ -1,183 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
fal.ai nano-banana text-to-image generator.
|
|
3
|
-
|
|
4
|
-
Fast image generation using fal.ai's nano-banana model with support for batch outputs.
|
|
5
|
-
"""
|
|
6
|
-
|
|
7
|
-
import os
|
|
8
|
-
from typing import Literal
|
|
9
|
-
|
|
10
|
-
from pydantic import BaseModel, Field
|
|
11
|
-
|
|
12
|
-
from ....base import BaseGenerator, GeneratorExecutionContext, GeneratorResult
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
class NanoBananaInput(BaseModel):
|
|
16
|
-
"""Input schema for nano-banana image generation."""
|
|
17
|
-
|
|
18
|
-
prompt: str = Field(description="Text prompt for image generation")
|
|
19
|
-
image_size: Literal[
|
|
20
|
-
"square_hd",
|
|
21
|
-
"square",
|
|
22
|
-
"portrait_4_3",
|
|
23
|
-
"portrait_16_9",
|
|
24
|
-
"landscape_4_3",
|
|
25
|
-
"landscape_16_9",
|
|
26
|
-
] = Field(
|
|
27
|
-
default="landscape_4_3",
|
|
28
|
-
description="Image aspect ratio and resolution",
|
|
29
|
-
)
|
|
30
|
-
num_inference_steps: int = Field(
|
|
31
|
-
default=4,
|
|
32
|
-
ge=1,
|
|
33
|
-
le=50,
|
|
34
|
-
description="Number of inference steps (more steps = higher quality but slower)",
|
|
35
|
-
)
|
|
36
|
-
guidance_scale: float = Field(
|
|
37
|
-
default=3.5,
|
|
38
|
-
ge=1.0,
|
|
39
|
-
le=20.0,
|
|
40
|
-
description="Guidance scale for prompt adherence",
|
|
41
|
-
)
|
|
42
|
-
num_images: int = Field(
|
|
43
|
-
default=1,
|
|
44
|
-
ge=1,
|
|
45
|
-
le=10,
|
|
46
|
-
description="Number of images to generate in batch",
|
|
47
|
-
)
|
|
48
|
-
enable_safety_checker: bool = Field(
|
|
49
|
-
default=True,
|
|
50
|
-
description="Enable safety checker to filter unsafe content",
|
|
51
|
-
)
|
|
52
|
-
seed: int | None = Field(
|
|
53
|
-
default=None,
|
|
54
|
-
description="Random seed for reproducibility (optional)",
|
|
55
|
-
)
|
|
56
|
-
sync_mode: bool = Field(
|
|
57
|
-
default=True,
|
|
58
|
-
description="Use synchronous mode (wait for completion)",
|
|
59
|
-
)
|
|
60
|
-
output_format: Literal["jpeg", "png"] = Field(
|
|
61
|
-
default="jpeg",
|
|
62
|
-
description="Output image format",
|
|
63
|
-
)
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
class FalNanoBananaGenerator(BaseGenerator):
|
|
67
|
-
"""nano-banana image generator using fal.ai."""
|
|
68
|
-
|
|
69
|
-
name = "fal-nano-banana"
|
|
70
|
-
artifact_type = "image"
|
|
71
|
-
description = "Fal: nano-banana - fast text-to-image generation with batch support"
|
|
72
|
-
|
|
73
|
-
def get_input_schema(self) -> type[NanoBananaInput]:
|
|
74
|
-
return NanoBananaInput
|
|
75
|
-
|
|
76
|
-
async def generate(
|
|
77
|
-
self, inputs: NanoBananaInput, context: GeneratorExecutionContext
|
|
78
|
-
) -> GeneratorResult:
|
|
79
|
-
"""Generate images using fal.ai nano-banana model."""
|
|
80
|
-
# Check for API key (fal-client uses FAL_KEY environment variable)
|
|
81
|
-
if not os.getenv("FAL_KEY"):
|
|
82
|
-
raise ValueError("API configuration invalid. Missing FAL_KEY environment variable")
|
|
83
|
-
|
|
84
|
-
# Import fal_client
|
|
85
|
-
try:
|
|
86
|
-
import fal_client
|
|
87
|
-
except ImportError as e:
|
|
88
|
-
raise ImportError(
|
|
89
|
-
"fal.ai SDK is required for NanoBananaGenerator. "
|
|
90
|
-
"Install with: pip install weirdfingers-boards[generators-fal]"
|
|
91
|
-
) from e
|
|
92
|
-
|
|
93
|
-
# Prepare arguments for fal.ai API
|
|
94
|
-
arguments = {
|
|
95
|
-
"prompt": inputs.prompt,
|
|
96
|
-
"image_size": inputs.image_size,
|
|
97
|
-
"num_inference_steps": inputs.num_inference_steps,
|
|
98
|
-
"guidance_scale": inputs.guidance_scale,
|
|
99
|
-
"num_images": inputs.num_images,
|
|
100
|
-
"enable_safety_checker": inputs.enable_safety_checker,
|
|
101
|
-
"sync_mode": inputs.sync_mode,
|
|
102
|
-
"output_format": inputs.output_format,
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
# Add seed if provided
|
|
106
|
-
if inputs.seed is not None:
|
|
107
|
-
arguments["seed"] = inputs.seed
|
|
108
|
-
|
|
109
|
-
# Submit async job and get handler
|
|
110
|
-
handler = await fal_client.submit_async(
|
|
111
|
-
"fal-ai/nano-banana",
|
|
112
|
-
arguments=arguments,
|
|
113
|
-
)
|
|
114
|
-
|
|
115
|
-
# Store the external job ID for tracking
|
|
116
|
-
await context.set_external_job_id(handler.request_id)
|
|
117
|
-
|
|
118
|
-
# Stream progress updates (sample every 3rd event to avoid spam)
|
|
119
|
-
from .....progress.models import ProgressUpdate
|
|
120
|
-
|
|
121
|
-
event_count = 0
|
|
122
|
-
async for event in handler.iter_events(with_logs=True):
|
|
123
|
-
event_count += 1
|
|
124
|
-
|
|
125
|
-
# Process every 3rd event to provide feedback without overwhelming
|
|
126
|
-
if event_count % 3 == 0:
|
|
127
|
-
# Extract logs if available
|
|
128
|
-
logs = getattr(event, "logs", None)
|
|
129
|
-
if logs:
|
|
130
|
-
# Join log entries into a single message
|
|
131
|
-
if isinstance(logs, list):
|
|
132
|
-
message = " | ".join(str(log) for log in logs if log)
|
|
133
|
-
else:
|
|
134
|
-
message = str(logs)
|
|
135
|
-
|
|
136
|
-
if message:
|
|
137
|
-
await context.publish_progress(
|
|
138
|
-
ProgressUpdate(
|
|
139
|
-
job_id=handler.request_id,
|
|
140
|
-
status="processing",
|
|
141
|
-
progress=50.0, # Approximate mid-point progress
|
|
142
|
-
phase="processing",
|
|
143
|
-
message=message,
|
|
144
|
-
)
|
|
145
|
-
)
|
|
146
|
-
|
|
147
|
-
# Get final result
|
|
148
|
-
result = await handler.get()
|
|
149
|
-
|
|
150
|
-
# Extract image URLs from result
|
|
151
|
-
# fal.ai returns: {"images": [{"url": "...", "width": ..., "height": ...}, ...]}
|
|
152
|
-
images = result.get("images", [])
|
|
153
|
-
if not images:
|
|
154
|
-
raise ValueError("No images returned from fal.ai API")
|
|
155
|
-
|
|
156
|
-
# Store each image using output_index
|
|
157
|
-
artifacts = []
|
|
158
|
-
for idx, image_data in enumerate(images):
|
|
159
|
-
image_url = image_data.get("url")
|
|
160
|
-
width = image_data.get("width")
|
|
161
|
-
height = image_data.get("height")
|
|
162
|
-
|
|
163
|
-
if not image_url:
|
|
164
|
-
raise ValueError(f"Image {idx} missing URL in fal.ai response")
|
|
165
|
-
|
|
166
|
-
# Store with appropriate output_index
|
|
167
|
-
artifact = await context.store_image_result(
|
|
168
|
-
storage_url=image_url,
|
|
169
|
-
format=inputs.output_format,
|
|
170
|
-
width=width,
|
|
171
|
-
height=height,
|
|
172
|
-
output_index=idx,
|
|
173
|
-
)
|
|
174
|
-
artifacts.append(artifact)
|
|
175
|
-
|
|
176
|
-
return GeneratorResult(outputs=artifacts)
|
|
177
|
-
|
|
178
|
-
async def estimate_cost(self, inputs: NanoBananaInput) -> float:
|
|
179
|
-
"""Estimate cost for nano-banana generation.
|
|
180
|
-
|
|
181
|
-
nano-banana typically costs around $0.003 per image.
|
|
182
|
-
"""
|
|
183
|
-
return 0.003 * inputs.num_images # $0.003 per image, scaled by batch size
|
|
@@ -1,212 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
fal.ai nano-banana image-to-image editing generator.
|
|
3
|
-
|
|
4
|
-
Edit images using fal.ai's nano-banana/edit model (powered by Gemini).
|
|
5
|
-
Supports editing multiple input images with a text prompt.
|
|
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 NanoBananaEditInput(BaseModel):
|
|
18
|
-
"""Input schema for nano-banana image editing.
|
|
19
|
-
|
|
20
|
-
Artifact fields (like image_sources) are automatically detected via type
|
|
21
|
-
introspection and resolved from generation IDs to ImageArtifact objects.
|
|
22
|
-
"""
|
|
23
|
-
|
|
24
|
-
prompt: str = Field(description="The prompt for image editing")
|
|
25
|
-
image_sources: list[ImageArtifact] = Field(
|
|
26
|
-
description="List of input images for editing (from previous generations)",
|
|
27
|
-
min_length=1,
|
|
28
|
-
)
|
|
29
|
-
num_images: int = Field(
|
|
30
|
-
default=1,
|
|
31
|
-
ge=1,
|
|
32
|
-
le=10,
|
|
33
|
-
description="Number of images to generate",
|
|
34
|
-
)
|
|
35
|
-
output_format: Literal["jpeg", "png", "webp"] = Field(
|
|
36
|
-
default="jpeg",
|
|
37
|
-
description="Output image format",
|
|
38
|
-
)
|
|
39
|
-
sync_mode: bool = Field(
|
|
40
|
-
default=False,
|
|
41
|
-
description=(
|
|
42
|
-
"If True, the media will be returned as a data URI and the output "
|
|
43
|
-
"data won't be available in the request history"
|
|
44
|
-
),
|
|
45
|
-
)
|
|
46
|
-
limit_generations: bool = Field(
|
|
47
|
-
default=False,
|
|
48
|
-
description=(
|
|
49
|
-
"Experimental parameter to limit the number of generations from each "
|
|
50
|
-
"round of prompting to 1. Set to True to disregard any instructions in "
|
|
51
|
-
"the prompt regarding the number of images to generate"
|
|
52
|
-
),
|
|
53
|
-
)
|
|
54
|
-
aspect_ratio: (
|
|
55
|
-
Literal[
|
|
56
|
-
"21:9",
|
|
57
|
-
"1:1",
|
|
58
|
-
"4:3",
|
|
59
|
-
"3:2",
|
|
60
|
-
"2:3",
|
|
61
|
-
"5:4",
|
|
62
|
-
"4:5",
|
|
63
|
-
"3:4",
|
|
64
|
-
"16:9",
|
|
65
|
-
"9:16",
|
|
66
|
-
]
|
|
67
|
-
| None
|
|
68
|
-
) = Field(
|
|
69
|
-
default=None,
|
|
70
|
-
description=(
|
|
71
|
-
"Aspect ratio for generated images. Default is None, which takes one "
|
|
72
|
-
"of the input images' aspect ratio"
|
|
73
|
-
),
|
|
74
|
-
)
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
class FalNanoBananaEditGenerator(BaseGenerator):
|
|
78
|
-
"""nano-banana image editing generator using fal.ai."""
|
|
79
|
-
|
|
80
|
-
name = "fal-nano-banana-edit"
|
|
81
|
-
artifact_type = "image"
|
|
82
|
-
description = "Fal: nano-banana edit - AI-powered image editing with Gemini"
|
|
83
|
-
|
|
84
|
-
def get_input_schema(self) -> type[NanoBananaEditInput]:
|
|
85
|
-
return NanoBananaEditInput
|
|
86
|
-
|
|
87
|
-
async def generate(
|
|
88
|
-
self, inputs: NanoBananaEditInput, context: GeneratorExecutionContext
|
|
89
|
-
) -> GeneratorResult:
|
|
90
|
-
"""Edit images using fal.ai nano-banana/edit model."""
|
|
91
|
-
# Check for API key (fal-client uses FAL_KEY environment variable)
|
|
92
|
-
if not os.getenv("FAL_KEY"):
|
|
93
|
-
raise ValueError("API configuration invalid. Missing FAL_KEY environment variable")
|
|
94
|
-
|
|
95
|
-
# Import fal_client
|
|
96
|
-
try:
|
|
97
|
-
import fal_client
|
|
98
|
-
except ImportError as e:
|
|
99
|
-
raise ImportError(
|
|
100
|
-
"fal.ai SDK is required for FalNanoBananaEditGenerator. "
|
|
101
|
-
"Install with: pip install weirdfingers-boards[generators-fal]"
|
|
102
|
-
) from e
|
|
103
|
-
|
|
104
|
-
# Upload image artifacts to Fal's public storage
|
|
105
|
-
# Fal API requires publicly accessible URLs, but our storage_url might be:
|
|
106
|
-
# - Localhost URLs (not publicly accessible)
|
|
107
|
-
# - Private S3 buckets (not publicly accessible)
|
|
108
|
-
# So we upload to Fal's temporary storage first
|
|
109
|
-
from ..utils import upload_artifacts_to_fal
|
|
110
|
-
|
|
111
|
-
image_urls = await upload_artifacts_to_fal(inputs.image_sources, context)
|
|
112
|
-
|
|
113
|
-
# Prepare arguments for fal.ai API
|
|
114
|
-
arguments = {
|
|
115
|
-
"prompt": inputs.prompt,
|
|
116
|
-
"image_urls": image_urls,
|
|
117
|
-
"num_images": inputs.num_images,
|
|
118
|
-
"output_format": inputs.output_format,
|
|
119
|
-
"sync_mode": inputs.sync_mode,
|
|
120
|
-
"limit_generations": inputs.limit_generations,
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
# Add aspect_ratio if provided
|
|
124
|
-
if inputs.aspect_ratio is not None:
|
|
125
|
-
arguments["aspect_ratio"] = inputs.aspect_ratio
|
|
126
|
-
|
|
127
|
-
# Submit async job and get handler
|
|
128
|
-
handler = await fal_client.submit_async(
|
|
129
|
-
"fal-ai/nano-banana/edit",
|
|
130
|
-
arguments=arguments,
|
|
131
|
-
)
|
|
132
|
-
|
|
133
|
-
# Store the external job ID for tracking
|
|
134
|
-
await context.set_external_job_id(handler.request_id)
|
|
135
|
-
|
|
136
|
-
# Stream progress updates (sample every 3rd event to avoid spam)
|
|
137
|
-
from .....progress.models import ProgressUpdate
|
|
138
|
-
|
|
139
|
-
event_count = 0
|
|
140
|
-
async for event in handler.iter_events(with_logs=True):
|
|
141
|
-
event_count += 1
|
|
142
|
-
|
|
143
|
-
# Process every 3rd event to provide feedback without overwhelming
|
|
144
|
-
if event_count % 3 == 0:
|
|
145
|
-
# Extract logs if available
|
|
146
|
-
logs = getattr(event, "logs", None)
|
|
147
|
-
if logs:
|
|
148
|
-
# Join log entries into a single message
|
|
149
|
-
if isinstance(logs, list):
|
|
150
|
-
message = " | ".join(str(log) for log in logs if log)
|
|
151
|
-
else:
|
|
152
|
-
message = str(logs)
|
|
153
|
-
|
|
154
|
-
if message:
|
|
155
|
-
await context.publish_progress(
|
|
156
|
-
ProgressUpdate(
|
|
157
|
-
job_id=handler.request_id,
|
|
158
|
-
status="processing",
|
|
159
|
-
progress=50.0, # Approximate mid-point progress
|
|
160
|
-
phase="processing",
|
|
161
|
-
message=message,
|
|
162
|
-
)
|
|
163
|
-
)
|
|
164
|
-
|
|
165
|
-
# Get final result
|
|
166
|
-
result = await handler.get()
|
|
167
|
-
|
|
168
|
-
# Extract image URLs and description from result
|
|
169
|
-
# fal.ai returns: {
|
|
170
|
-
# "images": [{"url": "...", ...}, ...],
|
|
171
|
-
# "description": "Text description from Gemini"
|
|
172
|
-
# }
|
|
173
|
-
images = result.get("images", [])
|
|
174
|
-
|
|
175
|
-
if not images:
|
|
176
|
-
raise ValueError("No images returned from fal.ai API")
|
|
177
|
-
|
|
178
|
-
# Store each image using output_index
|
|
179
|
-
artifacts = []
|
|
180
|
-
for idx, image_data in enumerate(images):
|
|
181
|
-
image_url = image_data.get("url")
|
|
182
|
-
# Extract dimensions if available, otherwise use sensible defaults
|
|
183
|
-
width = image_data.get("width", 1024)
|
|
184
|
-
height = image_data.get("height", 1024)
|
|
185
|
-
|
|
186
|
-
if not image_url:
|
|
187
|
-
raise ValueError(f"Image {idx} missing URL in fal.ai response")
|
|
188
|
-
|
|
189
|
-
# Store with appropriate output_index
|
|
190
|
-
# Note: The Gemini description from the API response (result.get("description"))
|
|
191
|
-
# is not currently stored with the artifact. Consider extending ImageArtifact
|
|
192
|
-
# to support metadata in the future.
|
|
193
|
-
artifact = await context.store_image_result(
|
|
194
|
-
storage_url=image_url,
|
|
195
|
-
format=inputs.output_format,
|
|
196
|
-
width=width,
|
|
197
|
-
height=height,
|
|
198
|
-
output_index=idx,
|
|
199
|
-
)
|
|
200
|
-
artifacts.append(artifact)
|
|
201
|
-
|
|
202
|
-
return GeneratorResult(outputs=artifacts)
|
|
203
|
-
|
|
204
|
-
async def estimate_cost(self, inputs: NanoBananaEditInput) -> float:
|
|
205
|
-
"""Estimate cost for nano-banana edit generation.
|
|
206
|
-
|
|
207
|
-
nano-banana/edit uses Gemini for image editing, which has variable costs
|
|
208
|
-
depending on input complexity. Using a conservative estimate.
|
|
209
|
-
"""
|
|
210
|
-
# Base cost per edit operation + per-image multiplier
|
|
211
|
-
per_image_cost = 0.039
|
|
212
|
-
return per_image_cost * inputs.num_images
|