@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/video/veo31_reference_to_video.py
DELETED
|
@@ -1,172 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Google Veo 3.1 reference-to-video generator.
|
|
3
|
-
|
|
4
|
-
Generates videos from multiple reference images to maintain consistent subject
|
|
5
|
-
appearance while creating dynamic video content based on text prompts.
|
|
6
|
-
|
|
7
|
-
Based on Fal AI's fal-ai/veo3.1/reference-to-video model.
|
|
8
|
-
See: https://fal.ai/models/fal-ai/veo3.1/reference-to-video
|
|
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 Veo31ReferenceToVideoInput(BaseModel):
|
|
21
|
-
"""Input schema for Veo 3.1 reference-to-video generation.
|
|
22
|
-
|
|
23
|
-
Artifact fields (image_urls) are automatically detected via type
|
|
24
|
-
introspection and resolved from generation IDs to ImageArtifact objects.
|
|
25
|
-
"""
|
|
26
|
-
|
|
27
|
-
image_urls: list[ImageArtifact] = Field(
|
|
28
|
-
description="URLs of reference images for consistent subject appearance"
|
|
29
|
-
)
|
|
30
|
-
prompt: str = Field(description="Text description of desired video content")
|
|
31
|
-
duration: Literal["8s"] = Field(
|
|
32
|
-
default="8s",
|
|
33
|
-
description="Duration of the generated video in seconds (currently only 8s is supported)",
|
|
34
|
-
)
|
|
35
|
-
resolution: Literal["720p", "1080p"] = Field(
|
|
36
|
-
default="720p",
|
|
37
|
-
description="Resolution of the generated video",
|
|
38
|
-
)
|
|
39
|
-
generate_audio: bool = Field(
|
|
40
|
-
default=True,
|
|
41
|
-
description="Whether to generate audio for the video. Disabling uses 50% fewer credits",
|
|
42
|
-
)
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
class FalVeo31ReferenceToVideoGenerator(BaseGenerator):
|
|
46
|
-
"""Generator for creating videos from reference images using Google Veo 3.1."""
|
|
47
|
-
|
|
48
|
-
name = "fal-veo31-reference-to-video"
|
|
49
|
-
description = "Fal: Veo 3.1 - Generate videos from reference images with consistent subjects"
|
|
50
|
-
artifact_type = "video"
|
|
51
|
-
|
|
52
|
-
def get_input_schema(self) -> type[Veo31ReferenceToVideoInput]:
|
|
53
|
-
"""Return the input schema for this generator."""
|
|
54
|
-
return Veo31ReferenceToVideoInput
|
|
55
|
-
|
|
56
|
-
async def generate(
|
|
57
|
-
self, inputs: Veo31ReferenceToVideoInput, context: GeneratorExecutionContext
|
|
58
|
-
) -> GeneratorResult:
|
|
59
|
-
"""Generate video using fal.ai veo3.1/reference-to-video."""
|
|
60
|
-
# Check for API key
|
|
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 FalVeo31ReferenceToVideoGenerator. "
|
|
70
|
-
"Install with: pip install weirdfingers-boards[generators-fal]"
|
|
71
|
-
) from e
|
|
72
|
-
|
|
73
|
-
# Upload image artifacts to Fal's public storage
|
|
74
|
-
# Fal API requires publicly accessible URLs, but our storage_url might be:
|
|
75
|
-
# - Localhost URLs (not publicly accessible)
|
|
76
|
-
# - Private S3 buckets (not publicly accessible)
|
|
77
|
-
# So we upload to Fal's temporary storage first
|
|
78
|
-
from ..utils import upload_artifacts_to_fal
|
|
79
|
-
|
|
80
|
-
reference_image_urls = await upload_artifacts_to_fal(inputs.image_urls, context)
|
|
81
|
-
|
|
82
|
-
# Prepare arguments for fal.ai API
|
|
83
|
-
arguments = {
|
|
84
|
-
"image_urls": reference_image_urls,
|
|
85
|
-
"prompt": inputs.prompt,
|
|
86
|
-
"duration": inputs.duration,
|
|
87
|
-
"resolution": inputs.resolution,
|
|
88
|
-
"generate_audio": inputs.generate_audio,
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
# Submit async job
|
|
92
|
-
handler = await fal_client.submit_async(
|
|
93
|
-
"fal-ai/veo3.1/reference-to-video",
|
|
94
|
-
arguments=arguments,
|
|
95
|
-
)
|
|
96
|
-
|
|
97
|
-
# Store external job ID
|
|
98
|
-
await context.set_external_job_id(handler.request_id)
|
|
99
|
-
|
|
100
|
-
# Stream progress updates
|
|
101
|
-
from .....progress.models import ProgressUpdate
|
|
102
|
-
|
|
103
|
-
event_count = 0
|
|
104
|
-
async for event in handler.iter_events(with_logs=True):
|
|
105
|
-
event_count += 1
|
|
106
|
-
# Sample every 3rd event to avoid spam
|
|
107
|
-
if event_count % 3 == 0:
|
|
108
|
-
# Extract logs if available
|
|
109
|
-
logs = getattr(event, "logs", None)
|
|
110
|
-
if logs:
|
|
111
|
-
# Join log entries into a single message
|
|
112
|
-
if isinstance(logs, list):
|
|
113
|
-
message = " | ".join(str(log) for log in logs if log)
|
|
114
|
-
else:
|
|
115
|
-
message = str(logs)
|
|
116
|
-
|
|
117
|
-
if message:
|
|
118
|
-
await context.publish_progress(
|
|
119
|
-
ProgressUpdate(
|
|
120
|
-
job_id=handler.request_id,
|
|
121
|
-
status="processing",
|
|
122
|
-
progress=50.0,
|
|
123
|
-
phase="processing",
|
|
124
|
-
message=message,
|
|
125
|
-
)
|
|
126
|
-
)
|
|
127
|
-
|
|
128
|
-
# Get final result
|
|
129
|
-
result = await handler.get()
|
|
130
|
-
|
|
131
|
-
# Extract video from result
|
|
132
|
-
# Expected structure: {"video": {"url": "...", "content_type": "...", ...}}
|
|
133
|
-
video_data = result.get("video")
|
|
134
|
-
if not video_data:
|
|
135
|
-
raise ValueError("No video returned from fal.ai API")
|
|
136
|
-
|
|
137
|
-
video_url = video_data.get("url")
|
|
138
|
-
if not video_url:
|
|
139
|
-
raise ValueError("Video missing URL in fal.ai response")
|
|
140
|
-
|
|
141
|
-
# Store video result
|
|
142
|
-
# Note: Fal API doesn't provide video dimensions/duration in the response,
|
|
143
|
-
# so we'll use defaults based on input parameters
|
|
144
|
-
width = 1280 if inputs.resolution == "720p" else 1920
|
|
145
|
-
height = 720 if inputs.resolution == "720p" else 1080
|
|
146
|
-
|
|
147
|
-
# Parse duration from "8s" format
|
|
148
|
-
duration_seconds = int(inputs.duration.rstrip("s"))
|
|
149
|
-
|
|
150
|
-
artifact = await context.store_video_result(
|
|
151
|
-
storage_url=video_url,
|
|
152
|
-
format="mp4",
|
|
153
|
-
width=width,
|
|
154
|
-
height=height,
|
|
155
|
-
duration=duration_seconds,
|
|
156
|
-
output_index=0,
|
|
157
|
-
)
|
|
158
|
-
|
|
159
|
-
return GeneratorResult(outputs=[artifact])
|
|
160
|
-
|
|
161
|
-
async def estimate_cost(self, inputs: Veo31ReferenceToVideoInput) -> float:
|
|
162
|
-
"""Estimate cost for this generation in USD.
|
|
163
|
-
|
|
164
|
-
Note: Pricing information not available in Fal documentation.
|
|
165
|
-
Using placeholder value that should be updated with actual pricing.
|
|
166
|
-
"""
|
|
167
|
-
# TODO: Update with actual pricing from Fal when available
|
|
168
|
-
# Base cost, with 50% reduction if audio is disabled
|
|
169
|
-
base_cost = 0.15 # Placeholder estimate
|
|
170
|
-
if not inputs.generate_audio:
|
|
171
|
-
return base_cost * 0.5
|
|
172
|
-
return base_cost
|
|
@@ -1,212 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
WAN 2.5 Preview image-to-video generator.
|
|
3
|
-
|
|
4
|
-
An image-to-video generation model that creates dynamic video content from static
|
|
5
|
-
images using text prompts to guide motion and camera movement. Supports durations
|
|
6
|
-
of 5 or 10 seconds at 480p, 720p, or 1080p resolution.
|
|
7
|
-
|
|
8
|
-
Based on Fal AI's fal-ai/wan-25-preview/image-to-video model.
|
|
9
|
-
See: https://fal.ai/models/fal-ai/wan-25-preview/image-to-video
|
|
10
|
-
"""
|
|
11
|
-
|
|
12
|
-
import os
|
|
13
|
-
from typing import Literal
|
|
14
|
-
|
|
15
|
-
from pydantic import BaseModel, Field
|
|
16
|
-
|
|
17
|
-
from ....artifacts import ImageArtifact
|
|
18
|
-
from ....base import BaseGenerator, GeneratorExecutionContext, GeneratorResult
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
class Wan25PreviewImageToVideoInput(BaseModel):
|
|
22
|
-
"""Input schema for WAN 2.5 Preview image-to-video generation.
|
|
23
|
-
|
|
24
|
-
Artifact fields (image) are automatically detected via type introspection
|
|
25
|
-
and resolved from generation IDs to ImageArtifact objects.
|
|
26
|
-
"""
|
|
27
|
-
|
|
28
|
-
image: ImageArtifact = Field(
|
|
29
|
-
description="The image to use as the first frame for video generation"
|
|
30
|
-
)
|
|
31
|
-
prompt: str = Field(
|
|
32
|
-
description="The text prompt describing the desired video motion. Max 800 characters.",
|
|
33
|
-
max_length=800,
|
|
34
|
-
)
|
|
35
|
-
duration: Literal["5", "10"] = Field(
|
|
36
|
-
default="5",
|
|
37
|
-
description="Duration of the generated video in seconds",
|
|
38
|
-
)
|
|
39
|
-
resolution: Literal["480p", "720p", "1080p"] = Field(
|
|
40
|
-
default="1080p",
|
|
41
|
-
description="Resolution of the generated video",
|
|
42
|
-
)
|
|
43
|
-
audio_url: str | None = Field(
|
|
44
|
-
default=None,
|
|
45
|
-
description=(
|
|
46
|
-
"URL of a WAV or MP3 audio file (3-30 seconds, max 15MB) for background music. "
|
|
47
|
-
"Audio is truncated or padded to match video duration."
|
|
48
|
-
),
|
|
49
|
-
)
|
|
50
|
-
seed: int | None = Field(
|
|
51
|
-
default=None,
|
|
52
|
-
description=(
|
|
53
|
-
"Random seed for reproducibility. If not specified, a random seed will be used."
|
|
54
|
-
),
|
|
55
|
-
)
|
|
56
|
-
negative_prompt: str | None = Field(
|
|
57
|
-
default=None,
|
|
58
|
-
description="Content to avoid in the generated video. Max 500 characters.",
|
|
59
|
-
max_length=500,
|
|
60
|
-
)
|
|
61
|
-
enable_prompt_expansion: bool = Field(
|
|
62
|
-
default=True,
|
|
63
|
-
description="Enable LLM-based prompt rewriting to improve results",
|
|
64
|
-
)
|
|
65
|
-
enable_safety_checker: bool = Field(
|
|
66
|
-
default=True,
|
|
67
|
-
description="Enable content safety filtering",
|
|
68
|
-
)
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
class FalWan25PreviewImageToVideoGenerator(BaseGenerator):
|
|
72
|
-
"""Generator for creating videos from static images using WAN 2.5 Preview."""
|
|
73
|
-
|
|
74
|
-
name = "fal-wan-25-preview-image-to-video"
|
|
75
|
-
description = "Fal: WAN 2.5 Preview - Generate videos from images with motion guidance"
|
|
76
|
-
artifact_type = "video"
|
|
77
|
-
|
|
78
|
-
def get_input_schema(self) -> type[Wan25PreviewImageToVideoInput]:
|
|
79
|
-
"""Return the input schema for this generator."""
|
|
80
|
-
return Wan25PreviewImageToVideoInput
|
|
81
|
-
|
|
82
|
-
async def generate(
|
|
83
|
-
self, inputs: Wan25PreviewImageToVideoInput, context: GeneratorExecutionContext
|
|
84
|
-
) -> GeneratorResult:
|
|
85
|
-
"""Generate video using fal.ai wan-25-preview/image-to-video."""
|
|
86
|
-
# Check for API key
|
|
87
|
-
if not os.getenv("FAL_KEY"):
|
|
88
|
-
raise ValueError("API configuration invalid. Missing FAL_KEY environment variable")
|
|
89
|
-
|
|
90
|
-
# Import fal_client
|
|
91
|
-
try:
|
|
92
|
-
import fal_client
|
|
93
|
-
except ImportError as e:
|
|
94
|
-
raise ImportError(
|
|
95
|
-
"fal.ai SDK is required for FalWan25PreviewImageToVideoGenerator. "
|
|
96
|
-
"Install with: pip install weirdfingers-boards[generators-fal]"
|
|
97
|
-
) from e
|
|
98
|
-
|
|
99
|
-
# Upload image artifact to Fal's public storage
|
|
100
|
-
from ..utils import upload_artifacts_to_fal
|
|
101
|
-
|
|
102
|
-
image_urls = await upload_artifacts_to_fal([inputs.image], context)
|
|
103
|
-
|
|
104
|
-
# Prepare arguments for fal.ai API
|
|
105
|
-
arguments: dict = {
|
|
106
|
-
"image_url": image_urls[0],
|
|
107
|
-
"prompt": inputs.prompt,
|
|
108
|
-
"duration": inputs.duration,
|
|
109
|
-
"resolution": inputs.resolution,
|
|
110
|
-
"enable_prompt_expansion": inputs.enable_prompt_expansion,
|
|
111
|
-
"enable_safety_checker": inputs.enable_safety_checker,
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
# Only add optional parameters if provided
|
|
115
|
-
if inputs.seed is not None:
|
|
116
|
-
arguments["seed"] = inputs.seed
|
|
117
|
-
|
|
118
|
-
if inputs.negative_prompt is not None:
|
|
119
|
-
arguments["negative_prompt"] = inputs.negative_prompt
|
|
120
|
-
|
|
121
|
-
if inputs.audio_url is not None:
|
|
122
|
-
arguments["audio_url"] = inputs.audio_url
|
|
123
|
-
|
|
124
|
-
# Submit async job
|
|
125
|
-
handler = await fal_client.submit_async(
|
|
126
|
-
"fal-ai/wan-25-preview/image-to-video",
|
|
127
|
-
arguments=arguments,
|
|
128
|
-
)
|
|
129
|
-
|
|
130
|
-
# Store external job ID
|
|
131
|
-
await context.set_external_job_id(handler.request_id)
|
|
132
|
-
|
|
133
|
-
# Stream progress updates
|
|
134
|
-
from .....progress.models import ProgressUpdate
|
|
135
|
-
|
|
136
|
-
event_count = 0
|
|
137
|
-
async for event in handler.iter_events(with_logs=True):
|
|
138
|
-
event_count += 1
|
|
139
|
-
# Sample every 3rd event to avoid spam
|
|
140
|
-
if event_count % 3 == 0:
|
|
141
|
-
# Extract logs if available
|
|
142
|
-
logs = getattr(event, "logs", None)
|
|
143
|
-
if logs:
|
|
144
|
-
# Join log entries into a single message
|
|
145
|
-
if isinstance(logs, list):
|
|
146
|
-
message = " | ".join(str(log) for log in logs if log)
|
|
147
|
-
else:
|
|
148
|
-
message = str(logs)
|
|
149
|
-
|
|
150
|
-
if message:
|
|
151
|
-
await context.publish_progress(
|
|
152
|
-
ProgressUpdate(
|
|
153
|
-
job_id=handler.request_id,
|
|
154
|
-
status="processing",
|
|
155
|
-
progress=50.0,
|
|
156
|
-
phase="processing",
|
|
157
|
-
message=message,
|
|
158
|
-
)
|
|
159
|
-
)
|
|
160
|
-
|
|
161
|
-
# Get final result
|
|
162
|
-
result = await handler.get()
|
|
163
|
-
|
|
164
|
-
# Extract video from result
|
|
165
|
-
# Expected structure: {"video": {"url": "...", "width": ..., "height": ..., ...}}
|
|
166
|
-
video_data = result.get("video")
|
|
167
|
-
if not video_data:
|
|
168
|
-
raise ValueError("No video returned from fal.ai API")
|
|
169
|
-
|
|
170
|
-
video_url = video_data.get("url")
|
|
171
|
-
if not video_url:
|
|
172
|
-
raise ValueError("Video missing URL in fal.ai response")
|
|
173
|
-
|
|
174
|
-
# Get video dimensions based on resolution setting
|
|
175
|
-
resolution_map = {
|
|
176
|
-
"480p": (854, 480),
|
|
177
|
-
"720p": (1280, 720),
|
|
178
|
-
"1080p": (1920, 1080),
|
|
179
|
-
}
|
|
180
|
-
default_width, default_height = resolution_map.get(inputs.resolution, (1920, 1080))
|
|
181
|
-
|
|
182
|
-
# Use actual dimensions from response if available, otherwise use defaults
|
|
183
|
-
width = video_data.get("width", default_width)
|
|
184
|
-
height = video_data.get("height", default_height)
|
|
185
|
-
fps = video_data.get("fps", 30)
|
|
186
|
-
duration = video_data.get("duration", int(inputs.duration))
|
|
187
|
-
|
|
188
|
-
# Store video result
|
|
189
|
-
artifact = await context.store_video_result(
|
|
190
|
-
storage_url=video_url,
|
|
191
|
-
format="mp4",
|
|
192
|
-
width=width,
|
|
193
|
-
height=height,
|
|
194
|
-
duration=duration,
|
|
195
|
-
fps=fps,
|
|
196
|
-
output_index=0,
|
|
197
|
-
)
|
|
198
|
-
|
|
199
|
-
return GeneratorResult(outputs=[artifact])
|
|
200
|
-
|
|
201
|
-
async def estimate_cost(self, inputs: Wan25PreviewImageToVideoInput) -> float:
|
|
202
|
-
"""Estimate cost for this generation in USD.
|
|
203
|
-
|
|
204
|
-
Note: Pricing information not available in Fal documentation.
|
|
205
|
-
Using placeholder value that should be updated with actual pricing.
|
|
206
|
-
"""
|
|
207
|
-
# TODO: Update with actual pricing from Fal when available
|
|
208
|
-
# Estimate based on duration - longer videos cost more
|
|
209
|
-
base_cost = 0.10
|
|
210
|
-
if inputs.duration == "10":
|
|
211
|
-
return base_cost * 2.0 # 10 second videos cost more
|
|
212
|
-
return base_cost
|
|
@@ -1,208 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Wan 2.5 Preview text-to-video generator.
|
|
3
|
-
|
|
4
|
-
A text-to-video generation model that converts text prompts into video content,
|
|
5
|
-
supporting both Chinese and English inputs up to 800 characters.
|
|
6
|
-
|
|
7
|
-
Based on Fal AI's fal-ai/wan-25-preview/text-to-video model.
|
|
8
|
-
See: https://fal.ai/models/fal-ai/wan-25-preview/text-to-video
|
|
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 Wan25PreviewTextToVideoInput(BaseModel):
|
|
20
|
-
"""Input schema for Wan 2.5 Preview text-to-video generation.
|
|
21
|
-
|
|
22
|
-
Artifact fields are automatically detected via type introspection
|
|
23
|
-
and resolved from generation IDs to artifact objects.
|
|
24
|
-
"""
|
|
25
|
-
|
|
26
|
-
prompt: str = Field(
|
|
27
|
-
description="Text prompt for video generation. Supports Chinese and English.",
|
|
28
|
-
min_length=1,
|
|
29
|
-
max_length=800,
|
|
30
|
-
)
|
|
31
|
-
aspect_ratio: Literal["16:9", "9:16", "1:1"] = Field(
|
|
32
|
-
default="16:9",
|
|
33
|
-
description="Aspect ratio of the generated video",
|
|
34
|
-
)
|
|
35
|
-
resolution: Literal["480p", "720p", "1080p"] = Field(
|
|
36
|
-
default="1080p",
|
|
37
|
-
description="Resolution of the generated video",
|
|
38
|
-
)
|
|
39
|
-
duration: Literal[5, 10] = Field(
|
|
40
|
-
default=5,
|
|
41
|
-
description="Duration of the generated video in seconds",
|
|
42
|
-
)
|
|
43
|
-
audio_url: str | None = Field(
|
|
44
|
-
default=None,
|
|
45
|
-
description="URL of background audio (WAV/MP3). Must be 3-30 seconds and max 15MB. "
|
|
46
|
-
"Audio longer than video is truncated; shorter audio results in silent sections.",
|
|
47
|
-
)
|
|
48
|
-
seed: int | None = Field(
|
|
49
|
-
default=None,
|
|
50
|
-
description="Random seed for reproducibility",
|
|
51
|
-
)
|
|
52
|
-
enable_safety_checker: bool = Field(
|
|
53
|
-
default=True,
|
|
54
|
-
description="Whether to enable safety filtering",
|
|
55
|
-
)
|
|
56
|
-
negative_prompt: str | None = Field(
|
|
57
|
-
default=None,
|
|
58
|
-
description="Content to avoid in generation",
|
|
59
|
-
max_length=500,
|
|
60
|
-
)
|
|
61
|
-
enable_prompt_expansion: bool = Field(
|
|
62
|
-
default=True,
|
|
63
|
-
description="Whether to expand short prompts for improved results. "
|
|
64
|
-
"Increases processing time but improves quality for short prompts.",
|
|
65
|
-
)
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
class FalWan25PreviewTextToVideoGenerator(BaseGenerator):
|
|
69
|
-
"""Generator for text-to-video using Wan 2.5 Preview."""
|
|
70
|
-
|
|
71
|
-
name = "fal-wan-25-preview-text-to-video"
|
|
72
|
-
description = (
|
|
73
|
-
"Fal: Wan 2.5 Preview - Text-to-video generation supporting "
|
|
74
|
-
"Chinese/English prompts up to 800 characters"
|
|
75
|
-
)
|
|
76
|
-
artifact_type = "video"
|
|
77
|
-
|
|
78
|
-
def get_input_schema(self) -> type[Wan25PreviewTextToVideoInput]:
|
|
79
|
-
"""Return the input schema for this generator."""
|
|
80
|
-
return Wan25PreviewTextToVideoInput
|
|
81
|
-
|
|
82
|
-
async def generate(
|
|
83
|
-
self, inputs: Wan25PreviewTextToVideoInput, context: GeneratorExecutionContext
|
|
84
|
-
) -> GeneratorResult:
|
|
85
|
-
"""Generate video using fal.ai Wan 2.5 Preview model."""
|
|
86
|
-
# Check for API key
|
|
87
|
-
if not os.getenv("FAL_KEY"):
|
|
88
|
-
raise ValueError("API configuration invalid. Missing FAL_KEY environment variable")
|
|
89
|
-
|
|
90
|
-
# Import fal_client
|
|
91
|
-
try:
|
|
92
|
-
import fal_client
|
|
93
|
-
except ImportError as e:
|
|
94
|
-
raise ImportError(
|
|
95
|
-
"fal.ai SDK is required for FalWan25PreviewTextToVideoGenerator. "
|
|
96
|
-
"Install with: pip install weirdfingers-boards[generators-fal]"
|
|
97
|
-
) from e
|
|
98
|
-
|
|
99
|
-
# Prepare arguments for fal.ai API
|
|
100
|
-
arguments: dict = {
|
|
101
|
-
"prompt": inputs.prompt,
|
|
102
|
-
"aspect_ratio": inputs.aspect_ratio,
|
|
103
|
-
"resolution": inputs.resolution,
|
|
104
|
-
"duration": inputs.duration,
|
|
105
|
-
"enable_safety_checker": inputs.enable_safety_checker,
|
|
106
|
-
"enable_prompt_expansion": inputs.enable_prompt_expansion,
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
# Add optional parameters
|
|
110
|
-
if inputs.audio_url is not None:
|
|
111
|
-
arguments["audio_url"] = inputs.audio_url
|
|
112
|
-
if inputs.seed is not None:
|
|
113
|
-
arguments["seed"] = inputs.seed
|
|
114
|
-
if inputs.negative_prompt is not None:
|
|
115
|
-
arguments["negative_prompt"] = inputs.negative_prompt
|
|
116
|
-
|
|
117
|
-
# Submit async job
|
|
118
|
-
handler = await fal_client.submit_async(
|
|
119
|
-
"fal-ai/wan-25-preview/text-to-video",
|
|
120
|
-
arguments=arguments,
|
|
121
|
-
)
|
|
122
|
-
|
|
123
|
-
# Store external job ID
|
|
124
|
-
await context.set_external_job_id(handler.request_id)
|
|
125
|
-
|
|
126
|
-
# Stream progress updates
|
|
127
|
-
from .....progress.models import ProgressUpdate
|
|
128
|
-
|
|
129
|
-
event_count = 0
|
|
130
|
-
async for event in handler.iter_events(with_logs=True):
|
|
131
|
-
event_count += 1
|
|
132
|
-
# Sample every 3rd event to avoid spam
|
|
133
|
-
if event_count % 3 == 0:
|
|
134
|
-
# Extract logs if available
|
|
135
|
-
logs = getattr(event, "logs", None)
|
|
136
|
-
if logs:
|
|
137
|
-
# Join log entries into a single message
|
|
138
|
-
if isinstance(logs, list):
|
|
139
|
-
message = " | ".join(str(log) for log in logs if log)
|
|
140
|
-
else:
|
|
141
|
-
message = str(logs)
|
|
142
|
-
|
|
143
|
-
if message:
|
|
144
|
-
await context.publish_progress(
|
|
145
|
-
ProgressUpdate(
|
|
146
|
-
job_id=handler.request_id,
|
|
147
|
-
status="processing",
|
|
148
|
-
progress=50.0, # Approximate mid-point progress
|
|
149
|
-
phase="processing",
|
|
150
|
-
message=message,
|
|
151
|
-
)
|
|
152
|
-
)
|
|
153
|
-
|
|
154
|
-
# Get final result
|
|
155
|
-
result = await handler.get()
|
|
156
|
-
|
|
157
|
-
# Extract video from result
|
|
158
|
-
video_data = result.get("video")
|
|
159
|
-
if not video_data:
|
|
160
|
-
raise ValueError("No video returned from fal.ai API")
|
|
161
|
-
|
|
162
|
-
video_url = video_data.get("url")
|
|
163
|
-
if not video_url:
|
|
164
|
-
raise ValueError("Video missing URL in fal.ai response")
|
|
165
|
-
|
|
166
|
-
# Extract video metadata from response or use defaults
|
|
167
|
-
width = video_data.get("width")
|
|
168
|
-
height = video_data.get("height")
|
|
169
|
-
duration = video_data.get("duration")
|
|
170
|
-
fps = video_data.get("fps")
|
|
171
|
-
|
|
172
|
-
# If dimensions not provided, determine based on aspect ratio and resolution
|
|
173
|
-
if width is None or height is None:
|
|
174
|
-
resolution_dimensions = {
|
|
175
|
-
"480p": {"16:9": (854, 480), "9:16": (480, 854), "1:1": (480, 480)},
|
|
176
|
-
"720p": {"16:9": (1280, 720), "9:16": (720, 1280), "1:1": (720, 720)},
|
|
177
|
-
"1080p": {"16:9": (1920, 1080), "9:16": (1080, 1920), "1:1": (1080, 1080)},
|
|
178
|
-
}
|
|
179
|
-
dims = resolution_dimensions.get(inputs.resolution, {}).get(
|
|
180
|
-
inputs.aspect_ratio, (1920, 1080)
|
|
181
|
-
)
|
|
182
|
-
width, height = dims
|
|
183
|
-
|
|
184
|
-
# Store video result
|
|
185
|
-
artifact = await context.store_video_result(
|
|
186
|
-
storage_url=video_url,
|
|
187
|
-
format="mp4",
|
|
188
|
-
width=width,
|
|
189
|
-
height=height,
|
|
190
|
-
duration=float(duration) if duration else float(inputs.duration),
|
|
191
|
-
fps=fps,
|
|
192
|
-
output_index=0,
|
|
193
|
-
)
|
|
194
|
-
|
|
195
|
-
return GeneratorResult(outputs=[artifact])
|
|
196
|
-
|
|
197
|
-
async def estimate_cost(self, inputs: Wan25PreviewTextToVideoInput) -> float:
|
|
198
|
-
"""Estimate cost for Wan 2.5 Preview generation.
|
|
199
|
-
|
|
200
|
-
Pricing information not provided in official documentation.
|
|
201
|
-
Estimated at $0.10 per 5-second video based on typical video generation costs.
|
|
202
|
-
Cost scales with duration.
|
|
203
|
-
"""
|
|
204
|
-
# Base cost per 5-second video
|
|
205
|
-
base_cost = 0.10
|
|
206
|
-
# Scale by duration: 5s = 1x, 10s = 2x
|
|
207
|
-
duration_multiplier = inputs.duration / 5
|
|
208
|
-
return base_cost * duration_multiplier
|