@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
package/templates/api/src/boards/generators/implementations/fal/image/ideogram_character_edit.py
DELETED
|
@@ -1,299 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
fal.ai Ideogram V3 Character Edit generator.
|
|
3
|
-
|
|
4
|
-
Modify consistent characters while preserving their core identity.
|
|
5
|
-
Edit poses, expressions, or clothing without losing recognizable character features.
|
|
6
|
-
|
|
7
|
-
Based on Fal AI's fal-ai/ideogram/character/edit model.
|
|
8
|
-
See: https://fal.ai/models/fal-ai/ideogram/character/edit
|
|
9
|
-
"""
|
|
10
|
-
|
|
11
|
-
import os
|
|
12
|
-
from typing import Literal
|
|
13
|
-
|
|
14
|
-
from pydantic import BaseModel, Field
|
|
15
|
-
|
|
16
|
-
from ....artifacts import ImageArtifact
|
|
17
|
-
from ....base import BaseGenerator, GeneratorExecutionContext, GeneratorResult
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
class RGBColor(BaseModel):
|
|
21
|
-
"""RGB color definition."""
|
|
22
|
-
|
|
23
|
-
r: int = Field(default=0, ge=0, le=255, description="Red color value")
|
|
24
|
-
g: int = Field(default=0, ge=0, le=255, description="Green color value")
|
|
25
|
-
b: int = Field(default=0, ge=0, le=255, description="Blue color value")
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
class ColorPaletteMember(BaseModel):
|
|
29
|
-
"""Color palette member with RGB color and weight."""
|
|
30
|
-
|
|
31
|
-
rgb: RGBColor = Field(description="RGB color definition")
|
|
32
|
-
color_weight: float = Field(
|
|
33
|
-
default=0.5,
|
|
34
|
-
ge=0.05,
|
|
35
|
-
le=1.0,
|
|
36
|
-
description="The weight of the color in the color palette",
|
|
37
|
-
)
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
class ColorPalette(BaseModel):
|
|
41
|
-
"""Color palette for generation.
|
|
42
|
-
|
|
43
|
-
Can be specified via presets or explicit hexadecimal representations.
|
|
44
|
-
"""
|
|
45
|
-
|
|
46
|
-
name: (
|
|
47
|
-
Literal[
|
|
48
|
-
"EMBER",
|
|
49
|
-
"FRESH",
|
|
50
|
-
"JUNGLE",
|
|
51
|
-
"MAGIC",
|
|
52
|
-
"MELON",
|
|
53
|
-
"MOSAIC",
|
|
54
|
-
"PASTEL",
|
|
55
|
-
"ULTRAMARINE",
|
|
56
|
-
]
|
|
57
|
-
| None
|
|
58
|
-
) = Field(default=None, description="Preset color palette name")
|
|
59
|
-
members: list[ColorPaletteMember] | None = Field(
|
|
60
|
-
default=None,
|
|
61
|
-
description="Explicit color palette members with RGB values and weights",
|
|
62
|
-
)
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
class IdeogramCharacterEditInput(BaseModel):
|
|
66
|
-
"""Input schema for Ideogram Character Edit.
|
|
67
|
-
|
|
68
|
-
Artifact fields are automatically detected via type introspection
|
|
69
|
-
and resolved from generation IDs to artifact objects.
|
|
70
|
-
"""
|
|
71
|
-
|
|
72
|
-
prompt: str = Field(description="The prompt to fill the masked part of the image")
|
|
73
|
-
image_url: ImageArtifact = Field(
|
|
74
|
-
description=(
|
|
75
|
-
"The image to generate from. MUST have the exact same dimensions as the mask image"
|
|
76
|
-
)
|
|
77
|
-
)
|
|
78
|
-
mask_url: ImageArtifact = Field(
|
|
79
|
-
description=(
|
|
80
|
-
"The mask to inpaint the image. MUST have the exact same dimensions as the input image"
|
|
81
|
-
)
|
|
82
|
-
)
|
|
83
|
-
reference_image_urls: list[ImageArtifact] = Field(
|
|
84
|
-
description=(
|
|
85
|
-
"A set of images to use as character references. "
|
|
86
|
-
"Currently only 1 image is supported, rest will be ignored "
|
|
87
|
-
"(maximum total size 10MB)"
|
|
88
|
-
),
|
|
89
|
-
min_length=1,
|
|
90
|
-
)
|
|
91
|
-
style: Literal["AUTO", "REALISTIC", "FICTION"] = Field(
|
|
92
|
-
default="AUTO",
|
|
93
|
-
description="The style type to generate with. Cannot be used with style_codes",
|
|
94
|
-
)
|
|
95
|
-
expand_prompt: bool = Field(
|
|
96
|
-
default=True,
|
|
97
|
-
description="Determine if MagicPrompt should be used in generating the request or not",
|
|
98
|
-
)
|
|
99
|
-
rendering_speed: Literal["TURBO", "BALANCED", "QUALITY"] = Field(
|
|
100
|
-
default="BALANCED",
|
|
101
|
-
description="The rendering speed to use",
|
|
102
|
-
)
|
|
103
|
-
reference_mask_urls: list[ImageArtifact] | None = Field(
|
|
104
|
-
default=None,
|
|
105
|
-
description=(
|
|
106
|
-
"A set of masks to apply to character references. "
|
|
107
|
-
"Currently only 1 mask is supported (maximum total size 10MB)"
|
|
108
|
-
),
|
|
109
|
-
)
|
|
110
|
-
image_urls: list[ImageArtifact] | None = Field(
|
|
111
|
-
default=None,
|
|
112
|
-
description="A set of images to use as style references (maximum total size 10MB)",
|
|
113
|
-
)
|
|
114
|
-
num_images: int = Field(
|
|
115
|
-
default=1,
|
|
116
|
-
ge=1,
|
|
117
|
-
le=8,
|
|
118
|
-
description="Number of images to generate",
|
|
119
|
-
)
|
|
120
|
-
style_codes: list[str] | None = Field(
|
|
121
|
-
default=None,
|
|
122
|
-
description="A list of 8 character hexadecimal codes representing the style of the image",
|
|
123
|
-
)
|
|
124
|
-
color_palette: ColorPalette | None = Field(
|
|
125
|
-
default=None,
|
|
126
|
-
description="A color palette for generation",
|
|
127
|
-
)
|
|
128
|
-
sync_mode: bool = Field(
|
|
129
|
-
default=False,
|
|
130
|
-
description=(
|
|
131
|
-
"If True, the media will be returned as a data URI "
|
|
132
|
-
"and output data won't be available in request history"
|
|
133
|
-
),
|
|
134
|
-
)
|
|
135
|
-
seed: int | None = Field(
|
|
136
|
-
default=None,
|
|
137
|
-
description="Seed for the random number generator",
|
|
138
|
-
)
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
class FalIdeogramCharacterEditGenerator(BaseGenerator):
|
|
142
|
-
"""Generator for Ideogram V3 Character Edit - modify consistent characters."""
|
|
143
|
-
|
|
144
|
-
name = "fal-ideogram-character-edit"
|
|
145
|
-
artifact_type = "image"
|
|
146
|
-
description = (
|
|
147
|
-
"Fal: Ideogram V3 Character Edit - "
|
|
148
|
-
"Modify consistent characters while preserving their core identity"
|
|
149
|
-
)
|
|
150
|
-
|
|
151
|
-
def get_input_schema(self) -> type[IdeogramCharacterEditInput]:
|
|
152
|
-
"""Return the input schema for this generator."""
|
|
153
|
-
return IdeogramCharacterEditInput
|
|
154
|
-
|
|
155
|
-
async def generate(
|
|
156
|
-
self, inputs: IdeogramCharacterEditInput, context: GeneratorExecutionContext
|
|
157
|
-
) -> GeneratorResult:
|
|
158
|
-
"""Generate edited character images using fal.ai ideogram/character/edit."""
|
|
159
|
-
# Check for API key
|
|
160
|
-
if not os.getenv("FAL_KEY"):
|
|
161
|
-
raise ValueError("API configuration invalid. Missing FAL_KEY environment variable")
|
|
162
|
-
|
|
163
|
-
# Import fal_client
|
|
164
|
-
try:
|
|
165
|
-
import fal_client
|
|
166
|
-
except ImportError as e:
|
|
167
|
-
raise ImportError(
|
|
168
|
-
"fal.ai SDK is required for FalIdeogramCharacterEditGenerator. "
|
|
169
|
-
"Install with: pip install weirdfingers-boards[generators-fal]"
|
|
170
|
-
) from e
|
|
171
|
-
|
|
172
|
-
# Upload image artifacts to Fal's public storage
|
|
173
|
-
from ..utils import upload_artifacts_to_fal
|
|
174
|
-
|
|
175
|
-
# Upload required artifacts
|
|
176
|
-
image_url = await upload_artifacts_to_fal([inputs.image_url], context)
|
|
177
|
-
mask_url = await upload_artifacts_to_fal([inputs.mask_url], context)
|
|
178
|
-
reference_image_urls = await upload_artifacts_to_fal(inputs.reference_image_urls, context)
|
|
179
|
-
|
|
180
|
-
# Upload optional artifacts
|
|
181
|
-
reference_mask_urls = None
|
|
182
|
-
if inputs.reference_mask_urls:
|
|
183
|
-
reference_mask_urls = await upload_artifacts_to_fal(inputs.reference_mask_urls, context)
|
|
184
|
-
|
|
185
|
-
style_reference_urls = None
|
|
186
|
-
if inputs.image_urls:
|
|
187
|
-
style_reference_urls = await upload_artifacts_to_fal(inputs.image_urls, context)
|
|
188
|
-
|
|
189
|
-
# Prepare arguments for fal.ai API
|
|
190
|
-
arguments = {
|
|
191
|
-
"prompt": inputs.prompt,
|
|
192
|
-
"image_url": image_url[0], # Single URL
|
|
193
|
-
"mask_url": mask_url[0], # Single URL
|
|
194
|
-
"reference_image_urls": reference_image_urls, # Array
|
|
195
|
-
"style": inputs.style,
|
|
196
|
-
"expand_prompt": inputs.expand_prompt,
|
|
197
|
-
"rendering_speed": inputs.rendering_speed,
|
|
198
|
-
"num_images": inputs.num_images,
|
|
199
|
-
"sync_mode": inputs.sync_mode,
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
# Add optional parameters
|
|
203
|
-
if reference_mask_urls:
|
|
204
|
-
arguments["reference_mask_urls"] = reference_mask_urls
|
|
205
|
-
|
|
206
|
-
if style_reference_urls:
|
|
207
|
-
arguments["image_urls"] = style_reference_urls
|
|
208
|
-
|
|
209
|
-
if inputs.style_codes:
|
|
210
|
-
arguments["style_codes"] = inputs.style_codes
|
|
211
|
-
|
|
212
|
-
if inputs.color_palette:
|
|
213
|
-
# Convert Pydantic model to dict for API
|
|
214
|
-
arguments["color_palette"] = inputs.color_palette.model_dump(exclude_none=True)
|
|
215
|
-
|
|
216
|
-
if inputs.seed is not None:
|
|
217
|
-
arguments["seed"] = inputs.seed
|
|
218
|
-
|
|
219
|
-
# Submit async job
|
|
220
|
-
handler = await fal_client.submit_async(
|
|
221
|
-
"fal-ai/ideogram/character/edit",
|
|
222
|
-
arguments=arguments,
|
|
223
|
-
)
|
|
224
|
-
|
|
225
|
-
# Store external job ID
|
|
226
|
-
await context.set_external_job_id(handler.request_id)
|
|
227
|
-
|
|
228
|
-
# Stream progress updates
|
|
229
|
-
from .....progress.models import ProgressUpdate
|
|
230
|
-
|
|
231
|
-
event_count = 0
|
|
232
|
-
async for event in handler.iter_events(with_logs=True):
|
|
233
|
-
event_count += 1
|
|
234
|
-
# Sample every 3rd event to avoid spam
|
|
235
|
-
if event_count % 3 == 0:
|
|
236
|
-
# Extract logs if available
|
|
237
|
-
logs = getattr(event, "logs", None)
|
|
238
|
-
if logs:
|
|
239
|
-
# Join log entries into a single message
|
|
240
|
-
if isinstance(logs, list):
|
|
241
|
-
message = " | ".join(str(log) for log in logs if log)
|
|
242
|
-
else:
|
|
243
|
-
message = str(logs)
|
|
244
|
-
|
|
245
|
-
if message:
|
|
246
|
-
await context.publish_progress(
|
|
247
|
-
ProgressUpdate(
|
|
248
|
-
job_id=handler.request_id,
|
|
249
|
-
status="processing",
|
|
250
|
-
progress=50.0,
|
|
251
|
-
phase="processing",
|
|
252
|
-
message=message,
|
|
253
|
-
)
|
|
254
|
-
)
|
|
255
|
-
|
|
256
|
-
# Get final result
|
|
257
|
-
result = await handler.get()
|
|
258
|
-
|
|
259
|
-
# Extract images from result
|
|
260
|
-
# API returns: {"images": [{"url": "...", ...}], "seed": 123}
|
|
261
|
-
images = result.get("images", [])
|
|
262
|
-
|
|
263
|
-
if not images:
|
|
264
|
-
raise ValueError("No images returned from fal.ai API")
|
|
265
|
-
|
|
266
|
-
# Store each image using output_index
|
|
267
|
-
artifacts = []
|
|
268
|
-
for idx, image_data in enumerate(images):
|
|
269
|
-
image_url = image_data.get("url")
|
|
270
|
-
if not image_url:
|
|
271
|
-
raise ValueError(f"Image {idx} missing URL in fal.ai response")
|
|
272
|
-
|
|
273
|
-
# Extract dimensions if available (Ideogram typically generates at fixed sizes)
|
|
274
|
-
width = image_data.get("width", 1024)
|
|
275
|
-
height = image_data.get("height", 1024)
|
|
276
|
-
|
|
277
|
-
# Determine format from content_type or default to webp
|
|
278
|
-
content_type = image_data.get("content_type", "image/webp")
|
|
279
|
-
format_str = content_type.split("/")[-1] if "/" in content_type else "webp"
|
|
280
|
-
|
|
281
|
-
artifact = await context.store_image_result(
|
|
282
|
-
storage_url=image_url,
|
|
283
|
-
format=format_str,
|
|
284
|
-
width=width,
|
|
285
|
-
height=height,
|
|
286
|
-
output_index=idx,
|
|
287
|
-
)
|
|
288
|
-
artifacts.append(artifact)
|
|
289
|
-
|
|
290
|
-
return GeneratorResult(outputs=artifacts)
|
|
291
|
-
|
|
292
|
-
async def estimate_cost(self, inputs: IdeogramCharacterEditInput) -> float:
|
|
293
|
-
"""Estimate cost for this generation in USD.
|
|
294
|
-
|
|
295
|
-
Pricing information not available in documentation.
|
|
296
|
-
Using estimated cost of $0.05 per image.
|
|
297
|
-
"""
|
|
298
|
-
cost_per_image = 0.05
|
|
299
|
-
return cost_per_image * inputs.num_images
|
|
@@ -1,190 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Generate high-quality images, posters, and logos with exceptional typography handling.
|
|
3
|
-
|
|
4
|
-
Based on Fal AI's fal-ai/ideogram/v2 model.
|
|
5
|
-
See: https://fal.ai/models/fal-ai/ideogram/v2
|
|
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 IdeogramV2Input(BaseModel):
|
|
17
|
-
"""Input schema for Ideogram V2 image generation.
|
|
18
|
-
|
|
19
|
-
Ideogram V2 is optimized for generating high-quality images with exceptional
|
|
20
|
-
typography handling, making it ideal for posters, logos, and creative content.
|
|
21
|
-
"""
|
|
22
|
-
|
|
23
|
-
prompt: str = Field(description="Text description for image generation")
|
|
24
|
-
aspect_ratio: Literal[
|
|
25
|
-
"1:1",
|
|
26
|
-
"16:9",
|
|
27
|
-
"9:16",
|
|
28
|
-
"4:3",
|
|
29
|
-
"3:4",
|
|
30
|
-
"10:16",
|
|
31
|
-
"16:10",
|
|
32
|
-
"1:3",
|
|
33
|
-
"3:1",
|
|
34
|
-
"3:2",
|
|
35
|
-
"2:3",
|
|
36
|
-
] = Field(
|
|
37
|
-
default="1:1",
|
|
38
|
-
description="Aspect ratio for the generated image",
|
|
39
|
-
)
|
|
40
|
-
style: Literal["auto", "general", "realistic", "design", "render_3D", "anime"] = Field(
|
|
41
|
-
default="auto",
|
|
42
|
-
description="Visual style for the generated image",
|
|
43
|
-
)
|
|
44
|
-
expand_prompt: bool = Field(
|
|
45
|
-
default=True,
|
|
46
|
-
description="Enable MagicPrompt functionality to enhance the prompt",
|
|
47
|
-
)
|
|
48
|
-
seed: int | None = Field(
|
|
49
|
-
default=None,
|
|
50
|
-
description="Random seed for reproducibility (optional)",
|
|
51
|
-
)
|
|
52
|
-
negative_prompt: str = Field(
|
|
53
|
-
default="",
|
|
54
|
-
description="Elements to exclude from the generated image",
|
|
55
|
-
)
|
|
56
|
-
sync_mode: bool = Field(
|
|
57
|
-
default=False,
|
|
58
|
-
description="Use synchronous mode (returns data URI instead of storing in history)",
|
|
59
|
-
)
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
class FalIdeogramV2Generator(BaseGenerator):
|
|
63
|
-
"""Generator for high-quality images with exceptional typography using Ideogram V2."""
|
|
64
|
-
|
|
65
|
-
name = "fal-ideogram-v2"
|
|
66
|
-
artifact_type = "image"
|
|
67
|
-
description = (
|
|
68
|
-
"Fal: Ideogram V2 - high-quality images, posters, and logos with exceptional typography"
|
|
69
|
-
)
|
|
70
|
-
|
|
71
|
-
def get_input_schema(self) -> type[IdeogramV2Input]:
|
|
72
|
-
"""Return the input schema for this generator."""
|
|
73
|
-
return IdeogramV2Input
|
|
74
|
-
|
|
75
|
-
async def generate(
|
|
76
|
-
self, inputs: IdeogramV2Input, context: GeneratorExecutionContext
|
|
77
|
-
) -> GeneratorResult:
|
|
78
|
-
"""Generate images using fal.ai Ideogram V2 model."""
|
|
79
|
-
# Check for API key (fal-client uses FAL_KEY environment variable)
|
|
80
|
-
if not os.getenv("FAL_KEY"):
|
|
81
|
-
raise ValueError("API configuration invalid. Missing FAL_KEY environment variable")
|
|
82
|
-
|
|
83
|
-
# Import fal_client
|
|
84
|
-
try:
|
|
85
|
-
import fal_client
|
|
86
|
-
except ImportError as e:
|
|
87
|
-
raise ImportError(
|
|
88
|
-
"fal.ai SDK is required for FalIdeogramV2Generator. "
|
|
89
|
-
"Install with: pip install weirdfingers-boards[generators-fal]"
|
|
90
|
-
) from e
|
|
91
|
-
|
|
92
|
-
# Prepare arguments for fal.ai API
|
|
93
|
-
arguments = {
|
|
94
|
-
"prompt": inputs.prompt,
|
|
95
|
-
"aspect_ratio": inputs.aspect_ratio,
|
|
96
|
-
"style": inputs.style,
|
|
97
|
-
"expand_prompt": inputs.expand_prompt,
|
|
98
|
-
"negative_prompt": inputs.negative_prompt,
|
|
99
|
-
"sync_mode": inputs.sync_mode,
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
# Add seed if provided
|
|
103
|
-
if inputs.seed is not None:
|
|
104
|
-
arguments["seed"] = inputs.seed
|
|
105
|
-
|
|
106
|
-
# Submit async job and get handler
|
|
107
|
-
handler = await fal_client.submit_async(
|
|
108
|
-
"fal-ai/ideogram/v2",
|
|
109
|
-
arguments=arguments,
|
|
110
|
-
)
|
|
111
|
-
|
|
112
|
-
# Store the external job ID for tracking
|
|
113
|
-
await context.set_external_job_id(handler.request_id)
|
|
114
|
-
|
|
115
|
-
# Stream progress updates (sample every 3rd event to avoid spam)
|
|
116
|
-
from .....progress.models import ProgressUpdate
|
|
117
|
-
|
|
118
|
-
event_count = 0
|
|
119
|
-
async for event in handler.iter_events(with_logs=True):
|
|
120
|
-
event_count += 1
|
|
121
|
-
|
|
122
|
-
# Process every 3rd event to provide feedback without overwhelming
|
|
123
|
-
if event_count % 3 == 0:
|
|
124
|
-
# Extract logs if available
|
|
125
|
-
logs = getattr(event, "logs", None)
|
|
126
|
-
if logs:
|
|
127
|
-
# Join log entries into a single message
|
|
128
|
-
if isinstance(logs, list):
|
|
129
|
-
message = " | ".join(str(log) for log in logs if log)
|
|
130
|
-
else:
|
|
131
|
-
message = str(logs)
|
|
132
|
-
|
|
133
|
-
if message:
|
|
134
|
-
await context.publish_progress(
|
|
135
|
-
ProgressUpdate(
|
|
136
|
-
job_id=handler.request_id,
|
|
137
|
-
status="processing",
|
|
138
|
-
progress=50.0, # Approximate mid-point progress
|
|
139
|
-
phase="processing",
|
|
140
|
-
message=message,
|
|
141
|
-
)
|
|
142
|
-
)
|
|
143
|
-
|
|
144
|
-
# Get final result
|
|
145
|
-
result = await handler.get()
|
|
146
|
-
|
|
147
|
-
# Extract image data from result
|
|
148
|
-
# fal.ai ideogram/v2 returns:
|
|
149
|
-
# {"images": [{"url": "...", "content_type": "...", ...}], "seed": ...}
|
|
150
|
-
images = result.get("images", [])
|
|
151
|
-
if not images:
|
|
152
|
-
raise ValueError("No images returned from fal.ai API")
|
|
153
|
-
|
|
154
|
-
# Store each image using output_index
|
|
155
|
-
artifacts = []
|
|
156
|
-
for idx, image_data in enumerate(images):
|
|
157
|
-
image_url = image_data.get("url")
|
|
158
|
-
|
|
159
|
-
if not image_url:
|
|
160
|
-
raise ValueError(f"Image {idx} missing URL in fal.ai response")
|
|
161
|
-
|
|
162
|
-
# Extract dimensions if available, use defaults otherwise
|
|
163
|
-
# Ideogram V2 doesn't return explicit width/height in the response schema,
|
|
164
|
-
# so we'll use reasonable defaults based on aspect ratio
|
|
165
|
-
width = image_data.get("width", 1024)
|
|
166
|
-
height = image_data.get("height", 1024)
|
|
167
|
-
|
|
168
|
-
# Determine format from content_type (e.g., "image/png" -> "png")
|
|
169
|
-
content_type = image_data.get("content_type", "image/png")
|
|
170
|
-
format = content_type.split("/")[-1] if "/" in content_type else "png"
|
|
171
|
-
|
|
172
|
-
# Store with appropriate output_index
|
|
173
|
-
artifact = await context.store_image_result(
|
|
174
|
-
storage_url=image_url,
|
|
175
|
-
format=format,
|
|
176
|
-
width=width,
|
|
177
|
-
height=height,
|
|
178
|
-
output_index=idx,
|
|
179
|
-
)
|
|
180
|
-
artifacts.append(artifact)
|
|
181
|
-
|
|
182
|
-
return GeneratorResult(outputs=artifacts)
|
|
183
|
-
|
|
184
|
-
async def estimate_cost(self, inputs: IdeogramV2Input) -> float:
|
|
185
|
-
"""Estimate cost for Ideogram V2 generation.
|
|
186
|
-
|
|
187
|
-
Ideogram V2 pricing is approximately $0.04 per image generation.
|
|
188
|
-
Note: Actual pricing may vary. Check Fal AI documentation for current rates.
|
|
189
|
-
"""
|
|
190
|
-
return 0.04 # $0.04 per image (estimate)
|
|
@@ -1,191 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
fal.ai Imagen 4 Preview text-to-image generator.
|
|
3
|
-
|
|
4
|
-
Google's highest quality image generation model with support for multiple aspect ratios
|
|
5
|
-
and resolutions.
|
|
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 Imagen4PreviewInput(BaseModel):
|
|
17
|
-
"""Input schema for Imagen 4 Preview image generation."""
|
|
18
|
-
|
|
19
|
-
prompt: str = Field(description="Text description of desired image")
|
|
20
|
-
aspect_ratio: Literal["1:1", "16:9", "9:16", "3:4", "4:3"] = Field(
|
|
21
|
-
default="1:1",
|
|
22
|
-
description="Image aspect ratio",
|
|
23
|
-
)
|
|
24
|
-
resolution: Literal["1K", "2K"] = Field(
|
|
25
|
-
default="1K",
|
|
26
|
-
description="Image resolution (1K or 2K)",
|
|
27
|
-
)
|
|
28
|
-
num_images: int = Field(
|
|
29
|
-
default=1,
|
|
30
|
-
ge=1,
|
|
31
|
-
le=4,
|
|
32
|
-
description="Number of images to generate (max 4)",
|
|
33
|
-
)
|
|
34
|
-
seed: int | None = Field(
|
|
35
|
-
default=None,
|
|
36
|
-
description="Random seed for reproducibility (optional)",
|
|
37
|
-
)
|
|
38
|
-
negative_prompt: str = Field(
|
|
39
|
-
default="",
|
|
40
|
-
description="Content to exclude from generation",
|
|
41
|
-
)
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
class FalImagen4PreviewGenerator(BaseGenerator):
|
|
45
|
-
"""Imagen 4 Preview image generator using fal.ai."""
|
|
46
|
-
|
|
47
|
-
name = "fal-imagen4-preview"
|
|
48
|
-
artifact_type = "image"
|
|
49
|
-
description = "Fal: Imagen 4 - Google's highest quality text-to-image generation model"
|
|
50
|
-
|
|
51
|
-
def get_input_schema(self) -> type[Imagen4PreviewInput]:
|
|
52
|
-
return Imagen4PreviewInput
|
|
53
|
-
|
|
54
|
-
def _calculate_dimensions(self, aspect_ratio: str, resolution: str) -> tuple[int, int]:
|
|
55
|
-
"""Calculate image dimensions based on aspect ratio and resolution.
|
|
56
|
-
|
|
57
|
-
Args:
|
|
58
|
-
aspect_ratio: Image aspect ratio (e.g., "16:9")
|
|
59
|
-
resolution: Image resolution ("1K" or "2K")
|
|
60
|
-
|
|
61
|
-
Returns:
|
|
62
|
-
Tuple of (width, height)
|
|
63
|
-
"""
|
|
64
|
-
# Base size for each resolution
|
|
65
|
-
base_size = 1024 if resolution == "1K" else 2048
|
|
66
|
-
|
|
67
|
-
# Dimension mapping for each aspect ratio
|
|
68
|
-
dimensions = {
|
|
69
|
-
"1:1": (base_size, base_size),
|
|
70
|
-
"16:9": (base_size, int(base_size * 9 / 16)),
|
|
71
|
-
"9:16": (int(base_size * 9 / 16), base_size),
|
|
72
|
-
"4:3": (base_size, int(base_size * 3 / 4)),
|
|
73
|
-
"3:4": (int(base_size * 3 / 4), base_size),
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
return dimensions.get(aspect_ratio, (base_size, base_size))
|
|
77
|
-
|
|
78
|
-
async def generate(
|
|
79
|
-
self, inputs: Imagen4PreviewInput, context: GeneratorExecutionContext
|
|
80
|
-
) -> GeneratorResult:
|
|
81
|
-
"""Generate images using fal.ai Imagen 4 Preview model."""
|
|
82
|
-
# Check for API key (fal-client uses FAL_KEY environment variable)
|
|
83
|
-
if not os.getenv("FAL_KEY"):
|
|
84
|
-
raise ValueError("API configuration invalid. Missing FAL_KEY environment variable")
|
|
85
|
-
|
|
86
|
-
# Import fal_client
|
|
87
|
-
try:
|
|
88
|
-
import fal_client
|
|
89
|
-
except ImportError as e:
|
|
90
|
-
raise ImportError(
|
|
91
|
-
"fal.ai SDK is required for FalImagen4PreviewGenerator. "
|
|
92
|
-
"Install with: pip install weirdfingers-boards[generators-fal]"
|
|
93
|
-
) from e
|
|
94
|
-
|
|
95
|
-
# Prepare arguments for fal.ai API
|
|
96
|
-
arguments = {
|
|
97
|
-
"prompt": inputs.prompt,
|
|
98
|
-
"aspect_ratio": inputs.aspect_ratio,
|
|
99
|
-
"resolution": inputs.resolution,
|
|
100
|
-
"num_images": inputs.num_images,
|
|
101
|
-
"negative_prompt": inputs.negative_prompt,
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
# Add seed if provided
|
|
105
|
-
if inputs.seed is not None:
|
|
106
|
-
arguments["seed"] = inputs.seed
|
|
107
|
-
|
|
108
|
-
# Submit async job and get handler
|
|
109
|
-
handler = await fal_client.submit_async(
|
|
110
|
-
"fal-ai/imagen4/preview",
|
|
111
|
-
arguments=arguments,
|
|
112
|
-
)
|
|
113
|
-
|
|
114
|
-
# Store the external job ID for tracking
|
|
115
|
-
await context.set_external_job_id(handler.request_id)
|
|
116
|
-
|
|
117
|
-
# Stream progress updates (sample every 3rd event to avoid spam)
|
|
118
|
-
from .....progress.models import ProgressUpdate
|
|
119
|
-
|
|
120
|
-
event_count = 0
|
|
121
|
-
async for event in handler.iter_events(with_logs=True):
|
|
122
|
-
event_count += 1
|
|
123
|
-
|
|
124
|
-
# Process every 3rd event to provide feedback without overwhelming
|
|
125
|
-
if event_count % 3 == 0:
|
|
126
|
-
# Extract logs if available
|
|
127
|
-
logs = getattr(event, "logs", None)
|
|
128
|
-
if logs:
|
|
129
|
-
# Join log entries into a single message
|
|
130
|
-
if isinstance(logs, list):
|
|
131
|
-
message = " | ".join(str(log) for log in logs if log)
|
|
132
|
-
else:
|
|
133
|
-
message = str(logs)
|
|
134
|
-
|
|
135
|
-
if message:
|
|
136
|
-
await context.publish_progress(
|
|
137
|
-
ProgressUpdate(
|
|
138
|
-
job_id=handler.request_id,
|
|
139
|
-
status="processing",
|
|
140
|
-
progress=50.0, # Approximate mid-point progress
|
|
141
|
-
phase="processing",
|
|
142
|
-
message=message,
|
|
143
|
-
)
|
|
144
|
-
)
|
|
145
|
-
|
|
146
|
-
# Get final result
|
|
147
|
-
result = await handler.get()
|
|
148
|
-
|
|
149
|
-
# Extract image URLs from result
|
|
150
|
-
# fal.ai returns: {"images": [{"url": "...", "content_type": "...", ...}, ...]}
|
|
151
|
-
images = result.get("images", [])
|
|
152
|
-
if not images:
|
|
153
|
-
raise ValueError("No images returned from fal.ai API")
|
|
154
|
-
|
|
155
|
-
# Calculate dimensions based on inputs
|
|
156
|
-
width, height = self._calculate_dimensions(inputs.aspect_ratio, inputs.resolution)
|
|
157
|
-
|
|
158
|
-
# Store each image using output_index
|
|
159
|
-
artifacts = []
|
|
160
|
-
for idx, image_data in enumerate(images):
|
|
161
|
-
image_url = image_data.get("url")
|
|
162
|
-
|
|
163
|
-
if not image_url:
|
|
164
|
-
raise ValueError(f"Image {idx} missing URL in fal.ai response")
|
|
165
|
-
|
|
166
|
-
# Detect format from content_type or URL
|
|
167
|
-
content_type = image_data.get("content_type", "")
|
|
168
|
-
if "png" in content_type.lower():
|
|
169
|
-
format = "png"
|
|
170
|
-
else:
|
|
171
|
-
format = "jpeg"
|
|
172
|
-
|
|
173
|
-
# Store with appropriate output_index
|
|
174
|
-
artifact = await context.store_image_result(
|
|
175
|
-
storage_url=image_url,
|
|
176
|
-
format=format,
|
|
177
|
-
width=width,
|
|
178
|
-
height=height,
|
|
179
|
-
output_index=idx,
|
|
180
|
-
)
|
|
181
|
-
artifacts.append(artifact)
|
|
182
|
-
|
|
183
|
-
return GeneratorResult(outputs=artifacts)
|
|
184
|
-
|
|
185
|
-
async def estimate_cost(self, inputs: Imagen4PreviewInput) -> float:
|
|
186
|
-
"""Estimate cost for Imagen 4 Preview generation.
|
|
187
|
-
|
|
188
|
-
Imagen 4 Preview costs $0.04 per image.
|
|
189
|
-
"""
|
|
190
|
-
cost_per_image = 0.04
|
|
191
|
-
return cost_per_image * inputs.num_images
|