@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
|
@@ -1,244 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
fal.ai Qwen image editing generator.
|
|
3
|
-
|
|
4
|
-
An image editing model specializing in text editing within images.
|
|
5
|
-
Based on Fal AI's qwen-image-edit model.
|
|
6
|
-
See: https://fal.ai/models/fal-ai/qwen-image-edit
|
|
7
|
-
"""
|
|
8
|
-
|
|
9
|
-
import os
|
|
10
|
-
from typing import Literal
|
|
11
|
-
|
|
12
|
-
from pydantic import BaseModel, Field
|
|
13
|
-
|
|
14
|
-
from ....artifacts import ImageArtifact
|
|
15
|
-
from ....base import BaseGenerator, GeneratorExecutionContext, GeneratorResult
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
class ImageSize(BaseModel):
|
|
19
|
-
"""Custom image size with explicit width and height."""
|
|
20
|
-
|
|
21
|
-
width: int = Field(default=512, ge=1, le=14142, description="Image width in pixels")
|
|
22
|
-
height: int = Field(default=512, ge=1, le=14142, description="Image height in pixels")
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
class QwenImageEditInput(BaseModel):
|
|
26
|
-
"""Input schema for Qwen image editing.
|
|
27
|
-
|
|
28
|
-
Artifact fields (like image_url) are automatically detected via type
|
|
29
|
-
introspection and resolved from generation IDs to ImageArtifact objects.
|
|
30
|
-
"""
|
|
31
|
-
|
|
32
|
-
prompt: str = Field(description="Text guidance for image editing")
|
|
33
|
-
image_url: ImageArtifact = Field(description="Source image to be edited")
|
|
34
|
-
num_images: int = Field(
|
|
35
|
-
default=1,
|
|
36
|
-
ge=1,
|
|
37
|
-
le=4,
|
|
38
|
-
description="Number of edited images to generate",
|
|
39
|
-
)
|
|
40
|
-
image_size: (
|
|
41
|
-
Literal[
|
|
42
|
-
"square_hd",
|
|
43
|
-
"square",
|
|
44
|
-
"portrait_4_3",
|
|
45
|
-
"portrait_16_9",
|
|
46
|
-
"landscape_4_3",
|
|
47
|
-
"landscape_16_9",
|
|
48
|
-
]
|
|
49
|
-
| ImageSize
|
|
50
|
-
| None
|
|
51
|
-
) = Field(
|
|
52
|
-
default=None,
|
|
53
|
-
description=(
|
|
54
|
-
"Output image dimensions. Can be a preset (e.g., 'square_hd') or "
|
|
55
|
-
"custom dimensions with width/height"
|
|
56
|
-
),
|
|
57
|
-
)
|
|
58
|
-
acceleration: Literal["none", "regular", "high"] = Field(
|
|
59
|
-
default="regular",
|
|
60
|
-
description="Speed optimization level",
|
|
61
|
-
)
|
|
62
|
-
output_format: Literal["jpeg", "png"] = Field(
|
|
63
|
-
default="png",
|
|
64
|
-
description="Output image format",
|
|
65
|
-
)
|
|
66
|
-
guidance_scale: float = Field(
|
|
67
|
-
default=4.0,
|
|
68
|
-
ge=0.0,
|
|
69
|
-
le=20.0,
|
|
70
|
-
description="CFG intensity controlling prompt adherence (0-20)",
|
|
71
|
-
)
|
|
72
|
-
num_inference_steps: int = Field(
|
|
73
|
-
default=30,
|
|
74
|
-
ge=2,
|
|
75
|
-
le=50,
|
|
76
|
-
description="Number of processing iterations for quality",
|
|
77
|
-
)
|
|
78
|
-
seed: int | None = Field(
|
|
79
|
-
default=None,
|
|
80
|
-
description="Random seed for reproducibility (optional)",
|
|
81
|
-
)
|
|
82
|
-
negative_prompt: str = Field(
|
|
83
|
-
default=" ",
|
|
84
|
-
description="Undesired characteristics to avoid in the edited image",
|
|
85
|
-
)
|
|
86
|
-
sync_mode: bool = Field(
|
|
87
|
-
default=False,
|
|
88
|
-
description=(
|
|
89
|
-
"If True, returns data URI instead of stored media "
|
|
90
|
-
"(output won't be available in request history)"
|
|
91
|
-
),
|
|
92
|
-
)
|
|
93
|
-
enable_safety_checker: bool = Field(
|
|
94
|
-
default=True,
|
|
95
|
-
description="Enable NSFW content filtering",
|
|
96
|
-
)
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
class FalQwenImageEditGenerator(BaseGenerator):
|
|
100
|
-
"""Qwen image editing generator using fal.ai."""
|
|
101
|
-
|
|
102
|
-
name = "fal-qwen-image-edit"
|
|
103
|
-
artifact_type = "image"
|
|
104
|
-
description = "Fal: Qwen Image Edit - AI-powered image editing with text editing capabilities"
|
|
105
|
-
|
|
106
|
-
def get_input_schema(self) -> type[QwenImageEditInput]:
|
|
107
|
-
return QwenImageEditInput
|
|
108
|
-
|
|
109
|
-
async def generate(
|
|
110
|
-
self, inputs: QwenImageEditInput, context: GeneratorExecutionContext
|
|
111
|
-
) -> GeneratorResult:
|
|
112
|
-
"""Edit images using fal.ai qwen-image-edit model."""
|
|
113
|
-
# Check for API key (fal-client uses FAL_KEY environment variable)
|
|
114
|
-
if not os.getenv("FAL_KEY"):
|
|
115
|
-
raise ValueError("API configuration invalid. Missing FAL_KEY environment variable")
|
|
116
|
-
|
|
117
|
-
# Import fal_client
|
|
118
|
-
try:
|
|
119
|
-
import fal_client
|
|
120
|
-
except ImportError as e:
|
|
121
|
-
raise ImportError(
|
|
122
|
-
"fal.ai SDK is required for FalQwenImageEditGenerator. "
|
|
123
|
-
"Install with: pip install weirdfingers-boards[generators-fal]"
|
|
124
|
-
) from e
|
|
125
|
-
|
|
126
|
-
# Upload image artifact to Fal's public storage
|
|
127
|
-
# Fal API requires publicly accessible URLs
|
|
128
|
-
from ..utils import upload_artifacts_to_fal
|
|
129
|
-
|
|
130
|
-
image_urls = await upload_artifacts_to_fal([inputs.image_url], context)
|
|
131
|
-
image_url = image_urls[0]
|
|
132
|
-
|
|
133
|
-
# Prepare arguments for fal.ai API
|
|
134
|
-
arguments = {
|
|
135
|
-
"prompt": inputs.prompt,
|
|
136
|
-
"image_url": image_url,
|
|
137
|
-
"num_images": inputs.num_images,
|
|
138
|
-
"acceleration": inputs.acceleration,
|
|
139
|
-
"output_format": inputs.output_format,
|
|
140
|
-
"guidance_scale": inputs.guidance_scale,
|
|
141
|
-
"num_inference_steps": inputs.num_inference_steps,
|
|
142
|
-
"negative_prompt": inputs.negative_prompt,
|
|
143
|
-
"sync_mode": inputs.sync_mode,
|
|
144
|
-
"enable_safety_checker": inputs.enable_safety_checker,
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
# Add optional fields if provided
|
|
148
|
-
if inputs.image_size is not None:
|
|
149
|
-
# If ImageSize object, convert to dict; otherwise use string directly
|
|
150
|
-
if isinstance(inputs.image_size, ImageSize):
|
|
151
|
-
arguments["image_size"] = {
|
|
152
|
-
"width": inputs.image_size.width,
|
|
153
|
-
"height": inputs.image_size.height,
|
|
154
|
-
}
|
|
155
|
-
else:
|
|
156
|
-
arguments["image_size"] = inputs.image_size
|
|
157
|
-
|
|
158
|
-
if inputs.seed is not None:
|
|
159
|
-
arguments["seed"] = inputs.seed
|
|
160
|
-
|
|
161
|
-
# Submit async job and get handler
|
|
162
|
-
handler = await fal_client.submit_async(
|
|
163
|
-
"fal-ai/qwen-image-edit",
|
|
164
|
-
arguments=arguments,
|
|
165
|
-
)
|
|
166
|
-
|
|
167
|
-
# Store the external job ID for tracking
|
|
168
|
-
await context.set_external_job_id(handler.request_id)
|
|
169
|
-
|
|
170
|
-
# Stream progress updates (sample every 3rd event to avoid spam)
|
|
171
|
-
from .....progress.models import ProgressUpdate
|
|
172
|
-
|
|
173
|
-
event_count = 0
|
|
174
|
-
async for event in handler.iter_events(with_logs=True):
|
|
175
|
-
event_count += 1
|
|
176
|
-
|
|
177
|
-
# Process every 3rd event to provide feedback without overwhelming
|
|
178
|
-
if event_count % 3 == 0:
|
|
179
|
-
# Extract logs if available
|
|
180
|
-
logs = getattr(event, "logs", None)
|
|
181
|
-
if logs:
|
|
182
|
-
# Join log entries into a single message
|
|
183
|
-
if isinstance(logs, list):
|
|
184
|
-
message = " | ".join(str(log) for log in logs if log)
|
|
185
|
-
else:
|
|
186
|
-
message = str(logs)
|
|
187
|
-
|
|
188
|
-
if message:
|
|
189
|
-
await context.publish_progress(
|
|
190
|
-
ProgressUpdate(
|
|
191
|
-
job_id=handler.request_id,
|
|
192
|
-
status="processing",
|
|
193
|
-
progress=50.0, # Approximate mid-point progress
|
|
194
|
-
phase="processing",
|
|
195
|
-
message=message,
|
|
196
|
-
)
|
|
197
|
-
)
|
|
198
|
-
|
|
199
|
-
# Get final result
|
|
200
|
-
result = await handler.get()
|
|
201
|
-
|
|
202
|
-
# Extract image URLs from result
|
|
203
|
-
# fal.ai returns: {
|
|
204
|
-
# "images": [{"url": "...", "width": ..., "height": ..., "content_type": "..."}, ...],
|
|
205
|
-
# "prompt": "...",
|
|
206
|
-
# "seed": ...,
|
|
207
|
-
# "has_nsfw_concepts": [...]
|
|
208
|
-
# }
|
|
209
|
-
images = result.get("images", [])
|
|
210
|
-
|
|
211
|
-
if not images:
|
|
212
|
-
raise ValueError("No images returned from fal.ai API")
|
|
213
|
-
|
|
214
|
-
# Store each image using output_index
|
|
215
|
-
artifacts = []
|
|
216
|
-
for idx, image_data in enumerate(images):
|
|
217
|
-
image_url_result = image_data.get("url")
|
|
218
|
-
# Extract dimensions from the response
|
|
219
|
-
width = image_data.get("width", 1024)
|
|
220
|
-
height = image_data.get("height", 1024)
|
|
221
|
-
|
|
222
|
-
if not image_url_result:
|
|
223
|
-
raise ValueError(f"Image {idx} missing URL in fal.ai response")
|
|
224
|
-
|
|
225
|
-
# Store with appropriate output_index
|
|
226
|
-
artifact = await context.store_image_result(
|
|
227
|
-
storage_url=image_url_result,
|
|
228
|
-
format=inputs.output_format,
|
|
229
|
-
width=width,
|
|
230
|
-
height=height,
|
|
231
|
-
output_index=idx,
|
|
232
|
-
)
|
|
233
|
-
artifacts.append(artifact)
|
|
234
|
-
|
|
235
|
-
return GeneratorResult(outputs=artifacts)
|
|
236
|
-
|
|
237
|
-
async def estimate_cost(self, inputs: QwenImageEditInput) -> float:
|
|
238
|
-
"""Estimate cost for Qwen image edit generation.
|
|
239
|
-
|
|
240
|
-
Based on typical Fal image editing model pricing.
|
|
241
|
-
Using $0.05 per image as a reasonable estimate.
|
|
242
|
-
"""
|
|
243
|
-
per_image_cost = 0.05
|
|
244
|
-
return per_image_cost * inputs.num_images
|
|
@@ -1,178 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
fal.ai Reve image editing generator.
|
|
3
|
-
|
|
4
|
-
Edit images using fal.ai's Reve edit model.
|
|
5
|
-
Allows uploading an existing image and transforming it through text prompts.
|
|
6
|
-
|
|
7
|
-
See: https://fal.ai/models/fal-ai/reve/edit
|
|
8
|
-
"""
|
|
9
|
-
|
|
10
|
-
import os
|
|
11
|
-
from typing import Literal
|
|
12
|
-
|
|
13
|
-
from pydantic import BaseModel, Field
|
|
14
|
-
|
|
15
|
-
from ....artifacts import ImageArtifact
|
|
16
|
-
from ....base import BaseGenerator, GeneratorExecutionContext, GeneratorResult
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
class ReveEditInput(BaseModel):
|
|
20
|
-
"""Input schema for Reve image editing.
|
|
21
|
-
|
|
22
|
-
Artifact fields (like image_url) are automatically detected via type
|
|
23
|
-
introspection and resolved from generation IDs to ImageArtifact objects.
|
|
24
|
-
"""
|
|
25
|
-
|
|
26
|
-
prompt: str = Field(
|
|
27
|
-
description="Text describing how to edit the image",
|
|
28
|
-
min_length=1,
|
|
29
|
-
max_length=2560,
|
|
30
|
-
)
|
|
31
|
-
image_url: ImageArtifact = Field(
|
|
32
|
-
description="Reference image to edit (from a previous generation)",
|
|
33
|
-
)
|
|
34
|
-
num_images: int = Field(
|
|
35
|
-
default=1,
|
|
36
|
-
ge=1,
|
|
37
|
-
le=4,
|
|
38
|
-
description="Number of images to generate",
|
|
39
|
-
)
|
|
40
|
-
output_format: Literal["png", "jpeg", "webp"] = Field(
|
|
41
|
-
default="png",
|
|
42
|
-
description="Output image format",
|
|
43
|
-
)
|
|
44
|
-
sync_mode: bool = Field(
|
|
45
|
-
default=False,
|
|
46
|
-
description=(
|
|
47
|
-
"If True, the media will be returned as a data URI and the output "
|
|
48
|
-
"data won't be available in the request history"
|
|
49
|
-
),
|
|
50
|
-
)
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
class FalReveEditGenerator(BaseGenerator):
|
|
54
|
-
"""Reve image editing generator using fal.ai."""
|
|
55
|
-
|
|
56
|
-
name = "fal-reve-edit"
|
|
57
|
-
artifact_type = "image"
|
|
58
|
-
description = "Fal: Reve edit - AI-powered image editing and transformation"
|
|
59
|
-
|
|
60
|
-
def get_input_schema(self) -> type[ReveEditInput]:
|
|
61
|
-
return ReveEditInput
|
|
62
|
-
|
|
63
|
-
async def generate(
|
|
64
|
-
self, inputs: ReveEditInput, context: GeneratorExecutionContext
|
|
65
|
-
) -> GeneratorResult:
|
|
66
|
-
"""Edit images using fal.ai Reve edit model."""
|
|
67
|
-
# Check for API key (fal-client uses FAL_KEY environment variable)
|
|
68
|
-
if not os.getenv("FAL_KEY"):
|
|
69
|
-
raise ValueError("API configuration invalid. Missing FAL_KEY environment variable")
|
|
70
|
-
|
|
71
|
-
# Import fal_client
|
|
72
|
-
try:
|
|
73
|
-
import fal_client
|
|
74
|
-
except ImportError as e:
|
|
75
|
-
raise ImportError(
|
|
76
|
-
"fal.ai SDK is required for FalReveEditGenerator. "
|
|
77
|
-
"Install with: pip install weirdfingers-boards[generators-fal]"
|
|
78
|
-
) from e
|
|
79
|
-
|
|
80
|
-
# Upload image artifact to Fal's public storage
|
|
81
|
-
# Fal API requires publicly accessible URLs, but our storage_url might be:
|
|
82
|
-
# - Localhost URLs (not publicly accessible)
|
|
83
|
-
# - Private S3 buckets (not publicly accessible)
|
|
84
|
-
# So we upload to Fal's temporary storage first
|
|
85
|
-
from ..utils import upload_artifacts_to_fal
|
|
86
|
-
|
|
87
|
-
image_urls = await upload_artifacts_to_fal([inputs.image_url], context)
|
|
88
|
-
uploaded_image_url = image_urls[0]
|
|
89
|
-
|
|
90
|
-
# Prepare arguments for fal.ai API
|
|
91
|
-
arguments = {
|
|
92
|
-
"prompt": inputs.prompt,
|
|
93
|
-
"image_url": uploaded_image_url,
|
|
94
|
-
"num_images": inputs.num_images,
|
|
95
|
-
"output_format": inputs.output_format,
|
|
96
|
-
"sync_mode": inputs.sync_mode,
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
# Submit async job and get handler
|
|
100
|
-
handler = await fal_client.submit_async(
|
|
101
|
-
"fal-ai/reve/edit",
|
|
102
|
-
arguments=arguments,
|
|
103
|
-
)
|
|
104
|
-
|
|
105
|
-
# Store the external job ID for tracking
|
|
106
|
-
await context.set_external_job_id(handler.request_id)
|
|
107
|
-
|
|
108
|
-
# Stream progress updates (sample every 3rd event to avoid spam)
|
|
109
|
-
from .....progress.models import ProgressUpdate
|
|
110
|
-
|
|
111
|
-
event_count = 0
|
|
112
|
-
async for event in handler.iter_events(with_logs=True):
|
|
113
|
-
event_count += 1
|
|
114
|
-
|
|
115
|
-
# Process every 3rd event to provide feedback without overwhelming
|
|
116
|
-
if event_count % 3 == 0:
|
|
117
|
-
# Extract logs if available
|
|
118
|
-
logs = getattr(event, "logs", None)
|
|
119
|
-
if logs:
|
|
120
|
-
# Join log entries into a single message
|
|
121
|
-
if isinstance(logs, list):
|
|
122
|
-
message = " | ".join(str(log) for log in logs if log)
|
|
123
|
-
else:
|
|
124
|
-
message = str(logs)
|
|
125
|
-
|
|
126
|
-
if message:
|
|
127
|
-
await context.publish_progress(
|
|
128
|
-
ProgressUpdate(
|
|
129
|
-
job_id=handler.request_id,
|
|
130
|
-
status="processing",
|
|
131
|
-
progress=50.0, # Approximate mid-point progress
|
|
132
|
-
phase="processing",
|
|
133
|
-
message=message,
|
|
134
|
-
)
|
|
135
|
-
)
|
|
136
|
-
|
|
137
|
-
# Get final result
|
|
138
|
-
result = await handler.get()
|
|
139
|
-
|
|
140
|
-
# Extract image URLs from result
|
|
141
|
-
# fal.ai returns: {
|
|
142
|
-
# "images": [{"url": "...", "width": ..., "height": ..., ...}, ...]
|
|
143
|
-
# }
|
|
144
|
-
images = result.get("images", [])
|
|
145
|
-
|
|
146
|
-
if not images:
|
|
147
|
-
raise ValueError("No images returned from fal.ai API")
|
|
148
|
-
|
|
149
|
-
# Store each image using output_index
|
|
150
|
-
artifacts = []
|
|
151
|
-
for idx, image_data in enumerate(images):
|
|
152
|
-
image_url = image_data.get("url")
|
|
153
|
-
# Extract dimensions if available, otherwise use sensible defaults
|
|
154
|
-
width = image_data.get("width", 1024)
|
|
155
|
-
height = image_data.get("height", 1024)
|
|
156
|
-
|
|
157
|
-
if not image_url:
|
|
158
|
-
raise ValueError(f"Image {idx} missing URL in fal.ai response")
|
|
159
|
-
|
|
160
|
-
# Store with appropriate output_index
|
|
161
|
-
artifact = await context.store_image_result(
|
|
162
|
-
storage_url=image_url,
|
|
163
|
-
format=inputs.output_format,
|
|
164
|
-
width=width,
|
|
165
|
-
height=height,
|
|
166
|
-
output_index=idx,
|
|
167
|
-
)
|
|
168
|
-
artifacts.append(artifact)
|
|
169
|
-
|
|
170
|
-
return GeneratorResult(outputs=artifacts)
|
|
171
|
-
|
|
172
|
-
async def estimate_cost(self, inputs: ReveEditInput) -> float:
|
|
173
|
-
"""Estimate cost for Reve edit generation.
|
|
174
|
-
|
|
175
|
-
Reve edit pricing is $0.04 per image generated.
|
|
176
|
-
"""
|
|
177
|
-
per_image_cost = 0.04
|
|
178
|
-
return per_image_cost * inputs.num_images
|
|
@@ -1,155 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
fal.ai Reve text-to-image generator.
|
|
3
|
-
|
|
4
|
-
Reve's text-to-image model generates detailed visual output that closely follows
|
|
5
|
-
your instructions, with strong aesthetic quality and accurate text rendering.
|
|
6
|
-
|
|
7
|
-
Based on Fal AI's fal-ai/reve/text-to-image model.
|
|
8
|
-
See: https://fal.ai/models/fal-ai/reve/text-to-image
|
|
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 ReveTextToImageInput(BaseModel):
|
|
20
|
-
"""Input schema for Reve text-to-image generation."""
|
|
21
|
-
|
|
22
|
-
prompt: str = Field(
|
|
23
|
-
description="Text description of desired image",
|
|
24
|
-
min_length=1,
|
|
25
|
-
max_length=2560,
|
|
26
|
-
)
|
|
27
|
-
num_images: int = Field(
|
|
28
|
-
default=1,
|
|
29
|
-
ge=1,
|
|
30
|
-
le=4,
|
|
31
|
-
description="Number of images to generate",
|
|
32
|
-
)
|
|
33
|
-
aspect_ratio: Literal["16:9", "9:16", "3:2", "2:3", "4:3", "3:4", "1:1"] = Field(
|
|
34
|
-
default="3:2",
|
|
35
|
-
description="Desired image aspect ratio",
|
|
36
|
-
)
|
|
37
|
-
output_format: Literal["png", "jpeg", "webp"] = Field(
|
|
38
|
-
default="png",
|
|
39
|
-
description="Output image format",
|
|
40
|
-
)
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
class FalReveTextToImageGenerator(BaseGenerator):
|
|
44
|
-
"""Reve text-to-image generator using fal.ai."""
|
|
45
|
-
|
|
46
|
-
name = "fal-reve-text-to-image"
|
|
47
|
-
artifact_type = "image"
|
|
48
|
-
description = (
|
|
49
|
-
"Fal: Reve - detailed text-to-image with strong aesthetic quality "
|
|
50
|
-
"and accurate text rendering"
|
|
51
|
-
)
|
|
52
|
-
|
|
53
|
-
def get_input_schema(self) -> type[ReveTextToImageInput]:
|
|
54
|
-
return ReveTextToImageInput
|
|
55
|
-
|
|
56
|
-
async def generate(
|
|
57
|
-
self, inputs: ReveTextToImageInput, context: GeneratorExecutionContext
|
|
58
|
-
) -> GeneratorResult:
|
|
59
|
-
"""Generate images using fal.ai Reve text-to-image model."""
|
|
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 FalReveTextToImageGenerator. "
|
|
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
|
-
"num_images": inputs.num_images,
|
|
77
|
-
"aspect_ratio": inputs.aspect_ratio,
|
|
78
|
-
"output_format": inputs.output_format,
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
# Submit async job and get handler
|
|
82
|
-
handler = await fal_client.submit_async(
|
|
83
|
-
"fal-ai/reve/text-to-image",
|
|
84
|
-
arguments=arguments,
|
|
85
|
-
)
|
|
86
|
-
|
|
87
|
-
# Store the external job ID for tracking
|
|
88
|
-
await context.set_external_job_id(handler.request_id)
|
|
89
|
-
|
|
90
|
-
# Stream progress updates (sample every 3rd event to avoid spam)
|
|
91
|
-
from .....progress.models import ProgressUpdate
|
|
92
|
-
|
|
93
|
-
event_count = 0
|
|
94
|
-
async for event in handler.iter_events(with_logs=True):
|
|
95
|
-
event_count += 1
|
|
96
|
-
|
|
97
|
-
# Process every 3rd event to provide feedback without overwhelming
|
|
98
|
-
if event_count % 3 == 0:
|
|
99
|
-
# Extract logs if available
|
|
100
|
-
logs = getattr(event, "logs", None)
|
|
101
|
-
if logs:
|
|
102
|
-
# Join log entries into a single message
|
|
103
|
-
if isinstance(logs, list):
|
|
104
|
-
message = " | ".join(str(log) for log in logs if log)
|
|
105
|
-
else:
|
|
106
|
-
message = str(logs)
|
|
107
|
-
|
|
108
|
-
if message:
|
|
109
|
-
await context.publish_progress(
|
|
110
|
-
ProgressUpdate(
|
|
111
|
-
job_id=handler.request_id,
|
|
112
|
-
status="processing",
|
|
113
|
-
progress=50.0, # Approximate mid-point progress
|
|
114
|
-
phase="processing",
|
|
115
|
-
message=message,
|
|
116
|
-
)
|
|
117
|
-
)
|
|
118
|
-
|
|
119
|
-
# Get final result
|
|
120
|
-
result = await handler.get()
|
|
121
|
-
|
|
122
|
-
# Extract image URLs from result
|
|
123
|
-
# fal.ai returns: {"images": [{"url": "...", "width": ..., "height": ...}, ...]}
|
|
124
|
-
images = result.get("images", [])
|
|
125
|
-
if not images:
|
|
126
|
-
raise ValueError("No images returned from fal.ai API")
|
|
127
|
-
|
|
128
|
-
# Store each image using output_index
|
|
129
|
-
artifacts = []
|
|
130
|
-
for idx, image_data in enumerate(images):
|
|
131
|
-
image_url = image_data.get("url")
|
|
132
|
-
width = image_data.get("width")
|
|
133
|
-
height = image_data.get("height")
|
|
134
|
-
|
|
135
|
-
if not image_url:
|
|
136
|
-
raise ValueError(f"Image {idx} missing URL in fal.ai response")
|
|
137
|
-
|
|
138
|
-
# Store with appropriate output_index
|
|
139
|
-
artifact = await context.store_image_result(
|
|
140
|
-
storage_url=image_url,
|
|
141
|
-
format=inputs.output_format,
|
|
142
|
-
width=width,
|
|
143
|
-
height=height,
|
|
144
|
-
output_index=idx,
|
|
145
|
-
)
|
|
146
|
-
artifacts.append(artifact)
|
|
147
|
-
|
|
148
|
-
return GeneratorResult(outputs=artifacts)
|
|
149
|
-
|
|
150
|
-
async def estimate_cost(self, inputs: ReveTextToImageInput) -> float:
|
|
151
|
-
"""Estimate cost for Reve text-to-image generation.
|
|
152
|
-
|
|
153
|
-
Reve typically costs around $0.03 per image.
|
|
154
|
-
"""
|
|
155
|
-
return 0.03 * inputs.num_images
|