@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.
Files changed (237) hide show
  1. package/dist/index.js +561 -469
  2. package/dist/index.js.map +1 -1
  3. package/package.json +2 -5
  4. package/templates/README.md +0 -122
  5. package/templates/api/.env.example +0 -65
  6. package/templates/api/ARTIFACT_RESOLUTION_GUIDE.md +0 -148
  7. package/templates/api/Dockerfile +0 -32
  8. package/templates/api/README.md +0 -264
  9. package/templates/api/alembic/env.py +0 -114
  10. package/templates/api/alembic/script.py.mako +0 -28
  11. package/templates/api/alembic/versions/20250101_000000_initial_schema.py +0 -506
  12. package/templates/api/alembic/versions/20251022_174729_remove_provider_name_from_generations.py +0 -75
  13. package/templates/api/alembic/versions/20251023_165852_switch_to_declarative_base_and_mapping.py +0 -467
  14. package/templates/api/alembic/versions/20251202_000000_add_artifact_lineage.py +0 -134
  15. package/templates/api/alembic/versions/2025925_62735_add_seed_data_for_default_tenant.py +0 -88
  16. package/templates/api/alembic.ini +0 -36
  17. package/templates/api/config/generators.yaml +0 -237
  18. package/templates/api/config/storage_config.yaml +0 -26
  19. package/templates/api/docs/ADDING_GENERATORS.md +0 -409
  20. package/templates/api/docs/GENERATORS_API.md +0 -502
  21. package/templates/api/docs/MIGRATIONS.md +0 -472
  22. package/templates/api/docs/TESTING_LIVE_APIS.md +0 -417
  23. package/templates/api/docs/storage_providers.md +0 -337
  24. package/templates/api/pyproject.toml +0 -205
  25. package/templates/api/src/boards/__init__.py +0 -10
  26. package/templates/api/src/boards/api/app.py +0 -172
  27. package/templates/api/src/boards/api/auth.py +0 -75
  28. package/templates/api/src/boards/api/endpoints/__init__.py +0 -3
  29. package/templates/api/src/boards/api/endpoints/jobs.py +0 -76
  30. package/templates/api/src/boards/api/endpoints/setup.py +0 -505
  31. package/templates/api/src/boards/api/endpoints/sse.py +0 -129
  32. package/templates/api/src/boards/api/endpoints/storage.py +0 -155
  33. package/templates/api/src/boards/api/endpoints/tenant_registration.py +0 -296
  34. package/templates/api/src/boards/api/endpoints/uploads.py +0 -149
  35. package/templates/api/src/boards/api/endpoints/webhooks.py +0 -13
  36. package/templates/api/src/boards/auth/__init__.py +0 -15
  37. package/templates/api/src/boards/auth/adapters/__init__.py +0 -27
  38. package/templates/api/src/boards/auth/adapters/auth0.py +0 -220
  39. package/templates/api/src/boards/auth/adapters/base.py +0 -73
  40. package/templates/api/src/boards/auth/adapters/clerk.py +0 -172
  41. package/templates/api/src/boards/auth/adapters/jwt.py +0 -122
  42. package/templates/api/src/boards/auth/adapters/none.py +0 -102
  43. package/templates/api/src/boards/auth/adapters/oidc.py +0 -284
  44. package/templates/api/src/boards/auth/adapters/supabase.py +0 -110
  45. package/templates/api/src/boards/auth/context.py +0 -35
  46. package/templates/api/src/boards/auth/factory.py +0 -129
  47. package/templates/api/src/boards/auth/middleware.py +0 -221
  48. package/templates/api/src/boards/auth/provisioning.py +0 -129
  49. package/templates/api/src/boards/auth/tenant_extraction.py +0 -278
  50. package/templates/api/src/boards/cli.py +0 -354
  51. package/templates/api/src/boards/config.py +0 -131
  52. package/templates/api/src/boards/database/__init__.py +0 -7
  53. package/templates/api/src/boards/database/cli.py +0 -110
  54. package/templates/api/src/boards/database/connection.py +0 -292
  55. package/templates/api/src/boards/database/models.py +0 -19
  56. package/templates/api/src/boards/database/seed_data.py +0 -182
  57. package/templates/api/src/boards/dbmodels/__init__.py +0 -441
  58. package/templates/api/src/boards/generators/__init__.py +0 -57
  59. package/templates/api/src/boards/generators/artifact_resolution.py +0 -405
  60. package/templates/api/src/boards/generators/artifacts.py +0 -53
  61. package/templates/api/src/boards/generators/base.py +0 -144
  62. package/templates/api/src/boards/generators/implementations/__init__.py +0 -14
  63. package/templates/api/src/boards/generators/implementations/fal/__init__.py +0 -25
  64. package/templates/api/src/boards/generators/implementations/fal/audio/__init__.py +0 -23
  65. package/templates/api/src/boards/generators/implementations/fal/audio/beatoven_music_generation.py +0 -171
  66. package/templates/api/src/boards/generators/implementations/fal/audio/beatoven_sound_effect_generation.py +0 -167
  67. package/templates/api/src/boards/generators/implementations/fal/audio/chatterbox_text_to_speech.py +0 -176
  68. package/templates/api/src/boards/generators/implementations/fal/audio/chatterbox_tts_turbo.py +0 -195
  69. package/templates/api/src/boards/generators/implementations/fal/audio/elevenlabs_sound_effects_v2.py +0 -194
  70. package/templates/api/src/boards/generators/implementations/fal/audio/elevenlabs_tts_eleven_v3.py +0 -209
  71. package/templates/api/src/boards/generators/implementations/fal/audio/fal_elevenlabs_tts_turbo_v2_5.py +0 -206
  72. package/templates/api/src/boards/generators/implementations/fal/audio/fal_minimax_speech_26_hd.py +0 -237
  73. package/templates/api/src/boards/generators/implementations/fal/audio/minimax_music_v2.py +0 -173
  74. package/templates/api/src/boards/generators/implementations/fal/audio/minimax_speech_2_6_turbo.py +0 -221
  75. package/templates/api/src/boards/generators/implementations/fal/image/__init__.py +0 -63
  76. package/templates/api/src/boards/generators/implementations/fal/image/bytedance_seedream_v45_edit.py +0 -219
  77. package/templates/api/src/boards/generators/implementations/fal/image/clarity_upscaler.py +0 -220
  78. package/templates/api/src/boards/generators/implementations/fal/image/crystal_upscaler.py +0 -173
  79. package/templates/api/src/boards/generators/implementations/fal/image/fal_ideogram_character.py +0 -227
  80. package/templates/api/src/boards/generators/implementations/fal/image/flux_2.py +0 -203
  81. package/templates/api/src/boards/generators/implementations/fal/image/flux_2_edit.py +0 -230
  82. package/templates/api/src/boards/generators/implementations/fal/image/flux_2_pro.py +0 -204
  83. package/templates/api/src/boards/generators/implementations/fal/image/flux_2_pro_edit.py +0 -221
  84. package/templates/api/src/boards/generators/implementations/fal/image/flux_pro_kontext.py +0 -216
  85. package/templates/api/src/boards/generators/implementations/fal/image/flux_pro_ultra.py +0 -197
  86. package/templates/api/src/boards/generators/implementations/fal/image/gemini_25_flash_image.py +0 -177
  87. package/templates/api/src/boards/generators/implementations/fal/image/gemini_25_flash_image_edit.py +0 -208
  88. package/templates/api/src/boards/generators/implementations/fal/image/gpt_image_15_edit.py +0 -216
  89. package/templates/api/src/boards/generators/implementations/fal/image/gpt_image_1_5.py +0 -177
  90. package/templates/api/src/boards/generators/implementations/fal/image/gpt_image_1_edit_image.py +0 -182
  91. package/templates/api/src/boards/generators/implementations/fal/image/gpt_image_1_mini.py +0 -167
  92. package/templates/api/src/boards/generators/implementations/fal/image/ideogram_character_edit.py +0 -299
  93. package/templates/api/src/boards/generators/implementations/fal/image/ideogram_v2.py +0 -190
  94. package/templates/api/src/boards/generators/implementations/fal/image/imagen4_preview.py +0 -191
  95. package/templates/api/src/boards/generators/implementations/fal/image/imagen4_preview_fast.py +0 -179
  96. package/templates/api/src/boards/generators/implementations/fal/image/nano_banana.py +0 -183
  97. package/templates/api/src/boards/generators/implementations/fal/image/nano_banana_edit.py +0 -212
  98. package/templates/api/src/boards/generators/implementations/fal/image/nano_banana_pro.py +0 -179
  99. package/templates/api/src/boards/generators/implementations/fal/image/nano_banana_pro_edit.py +0 -226
  100. package/templates/api/src/boards/generators/implementations/fal/image/qwen_image.py +0 -249
  101. package/templates/api/src/boards/generators/implementations/fal/image/qwen_image_edit.py +0 -244
  102. package/templates/api/src/boards/generators/implementations/fal/image/reve_edit.py +0 -178
  103. package/templates/api/src/boards/generators/implementations/fal/image/reve_text_to_image.py +0 -155
  104. package/templates/api/src/boards/generators/implementations/fal/image/seedream_v45_text_to_image.py +0 -180
  105. package/templates/api/src/boards/generators/implementations/fal/utils.py +0 -61
  106. package/templates/api/src/boards/generators/implementations/fal/video/__init__.py +0 -77
  107. package/templates/api/src/boards/generators/implementations/fal/video/bytedance_seedance_v1_pro_text_to_video.py +0 -209
  108. package/templates/api/src/boards/generators/implementations/fal/video/creatify_lipsync.py +0 -161
  109. package/templates/api/src/boards/generators/implementations/fal/video/fal_bytedance_seedance_v1_pro_image_to_video.py +0 -222
  110. package/templates/api/src/boards/generators/implementations/fal/video/fal_minimax_hailuo_02_standard_text_to_video.py +0 -152
  111. package/templates/api/src/boards/generators/implementations/fal/video/fal_pixverse_lipsync.py +0 -197
  112. package/templates/api/src/boards/generators/implementations/fal/video/fal_sora_2_text_to_video.py +0 -173
  113. package/templates/api/src/boards/generators/implementations/fal/video/infinitalk.py +0 -221
  114. package/templates/api/src/boards/generators/implementations/fal/video/kling_video_ai_avatar_v2_pro.py +0 -168
  115. package/templates/api/src/boards/generators/implementations/fal/video/kling_video_ai_avatar_v2_standard.py +0 -159
  116. package/templates/api/src/boards/generators/implementations/fal/video/kling_video_v2_5_turbo_pro_image_to_video.py +0 -175
  117. package/templates/api/src/boards/generators/implementations/fal/video/kling_video_v2_5_turbo_pro_text_to_video.py +0 -168
  118. package/templates/api/src/boards/generators/implementations/fal/video/minimax_hailuo_2_3_pro_image_to_video.py +0 -153
  119. package/templates/api/src/boards/generators/implementations/fal/video/sora2_image_to_video.py +0 -172
  120. package/templates/api/src/boards/generators/implementations/fal/video/sora_2_image_to_video_pro.py +0 -175
  121. package/templates/api/src/boards/generators/implementations/fal/video/sora_2_text_to_video_pro.py +0 -163
  122. package/templates/api/src/boards/generators/implementations/fal/video/sync_lipsync_v2.py +0 -167
  123. package/templates/api/src/boards/generators/implementations/fal/video/sync_lipsync_v2_pro.py +0 -155
  124. package/templates/api/src/boards/generators/implementations/fal/video/veed_fabric_1_0.py +0 -180
  125. package/templates/api/src/boards/generators/implementations/fal/video/veed_lipsync.py +0 -174
  126. package/templates/api/src/boards/generators/implementations/fal/video/veo3.py +0 -194
  127. package/templates/api/src/boards/generators/implementations/fal/video/veo31.py +0 -190
  128. package/templates/api/src/boards/generators/implementations/fal/video/veo31_fast.py +0 -190
  129. package/templates/api/src/boards/generators/implementations/fal/video/veo31_fast_image_to_video.py +0 -191
  130. package/templates/api/src/boards/generators/implementations/fal/video/veo31_first_last_frame_to_video.py +0 -187
  131. package/templates/api/src/boards/generators/implementations/fal/video/veo31_image_to_video.py +0 -183
  132. package/templates/api/src/boards/generators/implementations/fal/video/veo31_reference_to_video.py +0 -172
  133. package/templates/api/src/boards/generators/implementations/fal/video/wan_25_preview_image_to_video.py +0 -212
  134. package/templates/api/src/boards/generators/implementations/fal/video/wan_25_preview_text_to_video.py +0 -208
  135. package/templates/api/src/boards/generators/implementations/fal/video/wan_pro_image_to_video.py +0 -158
  136. package/templates/api/src/boards/generators/implementations/kie/__init__.py +0 -11
  137. package/templates/api/src/boards/generators/implementations/kie/base.py +0 -316
  138. package/templates/api/src/boards/generators/implementations/kie/image/__init__.py +0 -3
  139. package/templates/api/src/boards/generators/implementations/kie/image/nano_banana_edit.py +0 -190
  140. package/templates/api/src/boards/generators/implementations/kie/utils.py +0 -98
  141. package/templates/api/src/boards/generators/implementations/kie/video/__init__.py +0 -8
  142. package/templates/api/src/boards/generators/implementations/kie/video/veo3.py +0 -161
  143. package/templates/api/src/boards/generators/implementations/openai/__init__.py +0 -1
  144. package/templates/api/src/boards/generators/implementations/openai/audio/__init__.py +0 -1
  145. package/templates/api/src/boards/generators/implementations/openai/audio/whisper.py +0 -69
  146. package/templates/api/src/boards/generators/implementations/openai/image/__init__.py +0 -1
  147. package/templates/api/src/boards/generators/implementations/openai/image/dalle3.py +0 -96
  148. package/templates/api/src/boards/generators/implementations/replicate/__init__.py +0 -1
  149. package/templates/api/src/boards/generators/implementations/replicate/image/__init__.py +0 -1
  150. package/templates/api/src/boards/generators/implementations/replicate/image/flux_pro.py +0 -88
  151. package/templates/api/src/boards/generators/implementations/replicate/video/__init__.py +0 -1
  152. package/templates/api/src/boards/generators/implementations/replicate/video/lipsync.py +0 -73
  153. package/templates/api/src/boards/generators/loader.py +0 -253
  154. package/templates/api/src/boards/generators/registry.py +0 -114
  155. package/templates/api/src/boards/generators/resolution.py +0 -632
  156. package/templates/api/src/boards/generators/testmods/class_gen.py +0 -34
  157. package/templates/api/src/boards/generators/testmods/import_side_effect.py +0 -35
  158. package/templates/api/src/boards/graphql/__init__.py +0 -7
  159. package/templates/api/src/boards/graphql/access_control.py +0 -136
  160. package/templates/api/src/boards/graphql/mutations/root.py +0 -148
  161. package/templates/api/src/boards/graphql/queries/root.py +0 -116
  162. package/templates/api/src/boards/graphql/resolvers/__init__.py +0 -8
  163. package/templates/api/src/boards/graphql/resolvers/auth.py +0 -12
  164. package/templates/api/src/boards/graphql/resolvers/board.py +0 -1053
  165. package/templates/api/src/boards/graphql/resolvers/generation.py +0 -666
  166. package/templates/api/src/boards/graphql/resolvers/generator.py +0 -50
  167. package/templates/api/src/boards/graphql/resolvers/lineage.py +0 -381
  168. package/templates/api/src/boards/graphql/resolvers/upload.py +0 -463
  169. package/templates/api/src/boards/graphql/resolvers/user.py +0 -25
  170. package/templates/api/src/boards/graphql/schema.py +0 -81
  171. package/templates/api/src/boards/graphql/types/board.py +0 -102
  172. package/templates/api/src/boards/graphql/types/generation.py +0 -166
  173. package/templates/api/src/boards/graphql/types/generator.py +0 -17
  174. package/templates/api/src/boards/graphql/types/user.py +0 -47
  175. package/templates/api/src/boards/jobs/repository.py +0 -153
  176. package/templates/api/src/boards/logging.py +0 -195
  177. package/templates/api/src/boards/middleware.py +0 -339
  178. package/templates/api/src/boards/progress/__init__.py +0 -4
  179. package/templates/api/src/boards/progress/models.py +0 -25
  180. package/templates/api/src/boards/progress/publisher.py +0 -64
  181. package/templates/api/src/boards/py.typed +0 -0
  182. package/templates/api/src/boards/redis_pool.py +0 -118
  183. package/templates/api/src/boards/storage/__init__.py +0 -52
  184. package/templates/api/src/boards/storage/base.py +0 -363
  185. package/templates/api/src/boards/storage/config.py +0 -187
  186. package/templates/api/src/boards/storage/factory.py +0 -288
  187. package/templates/api/src/boards/storage/implementations/__init__.py +0 -27
  188. package/templates/api/src/boards/storage/implementations/gcs.py +0 -340
  189. package/templates/api/src/boards/storage/implementations/local.py +0 -201
  190. package/templates/api/src/boards/storage/implementations/s3.py +0 -294
  191. package/templates/api/src/boards/storage/implementations/supabase.py +0 -218
  192. package/templates/api/src/boards/tenant_isolation.py +0 -446
  193. package/templates/api/src/boards/validation.py +0 -262
  194. package/templates/api/src/boards/workers/__init__.py +0 -1
  195. package/templates/api/src/boards/workers/actors.py +0 -274
  196. package/templates/api/src/boards/workers/cli.py +0 -125
  197. package/templates/api/src/boards/workers/context.py +0 -348
  198. package/templates/api/src/boards/workers/middleware.py +0 -58
  199. package/templates/api/src/py.typed +0 -0
  200. package/templates/compose.web.yaml +0 -35
  201. package/templates/compose.yaml +0 -116
  202. package/templates/docker/env.example +0 -23
  203. package/templates/web/.env.example +0 -28
  204. package/templates/web/Dockerfile +0 -51
  205. package/templates/web/components.json +0 -22
  206. package/templates/web/imageLoader.js +0 -18
  207. package/templates/web/next-env.d.ts +0 -5
  208. package/templates/web/next.config.js +0 -36
  209. package/templates/web/package.json +0 -41
  210. package/templates/web/postcss.config.mjs +0 -7
  211. package/templates/web/public/favicon.ico +0 -0
  212. package/templates/web/src/app/boards/[boardId]/page.tsx +0 -353
  213. package/templates/web/src/app/globals.css +0 -123
  214. package/templates/web/src/app/layout.tsx +0 -31
  215. package/templates/web/src/app/lineage/[generationId]/page.tsx +0 -235
  216. package/templates/web/src/app/page.tsx +0 -35
  217. package/templates/web/src/app/providers.tsx +0 -18
  218. package/templates/web/src/components/boards/ArtifactInputSlots.tsx +0 -206
  219. package/templates/web/src/components/boards/ArtifactPreview.tsx +0 -466
  220. package/templates/web/src/components/boards/GenerationGrid.tsx +0 -282
  221. package/templates/web/src/components/boards/GenerationInput.tsx +0 -370
  222. package/templates/web/src/components/boards/GeneratorSelector.tsx +0 -272
  223. package/templates/web/src/components/boards/UploadArtifact.tsx +0 -563
  224. package/templates/web/src/components/header.tsx +0 -32
  225. package/templates/web/src/components/theme-provider.tsx +0 -10
  226. package/templates/web/src/components/theme-toggle.tsx +0 -75
  227. package/templates/web/src/components/ui/alert-dialog.tsx +0 -157
  228. package/templates/web/src/components/ui/button.tsx +0 -58
  229. package/templates/web/src/components/ui/card.tsx +0 -92
  230. package/templates/web/src/components/ui/dropdown-menu.tsx +0 -200
  231. package/templates/web/src/components/ui/navigation-menu.tsx +0 -168
  232. package/templates/web/src/components/ui/toast.tsx +0 -128
  233. package/templates/web/src/components/ui/toaster.tsx +0 -35
  234. package/templates/web/src/components/ui/use-toast.ts +0 -187
  235. package/templates/web/src/hooks/useGeneratorMRU.ts +0 -57
  236. package/templates/web/src/lib/utils.ts +0 -6
  237. package/templates/web/tsconfig.json +0 -41
@@ -1,405 +0,0 @@
1
- """
2
- Utilities for resolving generation IDs to artifact objects.
3
-
4
- This module provides utilities for converting generation ID strings (UUIDs)
5
- into typed artifact objects (ImageArtifact, AudioArtifact, etc.) with proper
6
- validation of ownership and completion status.
7
-
8
- The main approach is to automatically detect artifact fields via type introspection
9
- and resolve them BEFORE Pydantic validation.
10
- """
11
-
12
- from typing import Any, TypeVar, get_args, get_origin
13
- from uuid import UUID
14
-
15
- from pydantic import BaseModel
16
- from sqlalchemy.ext.asyncio import AsyncSession
17
-
18
- from ..dbmodels import Generations
19
- from ..jobs import repository as jobs_repo
20
- from ..logging import get_logger
21
- from .artifacts import AudioArtifact, ImageArtifact, TextArtifact, VideoArtifact
22
-
23
- logger = get_logger(__name__)
24
-
25
- # Type variable for artifact types
26
- TArtifact = TypeVar("TArtifact", ImageArtifact, VideoArtifact, AudioArtifact, TextArtifact)
27
-
28
- # Set of all artifact types for quick checking
29
- ARTIFACT_TYPES = {ImageArtifact, VideoArtifact, AudioArtifact, TextArtifact}
30
-
31
-
32
- def _extract_artifact_type(annotation: Any) -> type[TArtifact] | None:
33
- """Extract artifact type from a field annotation.
34
-
35
- Handles both single artifacts (ImageArtifact) and lists (list[ImageArtifact]).
36
-
37
- Args:
38
- annotation: The type annotation from a Pydantic field
39
-
40
- Returns:
41
- The artifact class if this is an artifact field, None otherwise
42
-
43
- Examples:
44
- _extract_artifact_type(ImageArtifact) -> ImageArtifact
45
- _extract_artifact_type(list[ImageArtifact]) -> ImageArtifact
46
- _extract_artifact_type(str) -> None
47
- """
48
- # Direct artifact type (e.g., ImageArtifact)
49
- if annotation in ARTIFACT_TYPES:
50
- return annotation # type: ignore[return-value]
51
-
52
- # List of artifacts (e.g., list[ImageArtifact])
53
- origin = get_origin(annotation)
54
- if origin is list:
55
- args = get_args(annotation)
56
- if args and args[0] in ARTIFACT_TYPES:
57
- return args[0] # type: ignore[return-value]
58
-
59
- return None
60
-
61
-
62
- def extract_artifact_fields(
63
- schema: type[BaseModel],
64
- ) -> dict[str, tuple[type[TArtifact], bool]]:
65
- """Automatically extract artifact fields from a Pydantic schema.
66
-
67
- Inspects the schema's field annotations and returns a mapping of field names
68
- to their artifact types and whether they expect a list.
69
-
70
- Args:
71
- schema: Pydantic model class to inspect
72
-
73
- Returns:
74
- Dictionary mapping field names to (artifact_type, is_list) tuples
75
-
76
- Example:
77
- class MyInput(BaseModel):
78
- prompt: str
79
- image_source: ImageArtifact
80
- video_sources: list[VideoArtifact]
81
-
82
- extract_artifact_fields(MyInput)
83
- # Returns: {"image_source": (ImageArtifact, False), "video_sources": (VideoArtifact, True)}
84
- """
85
- artifact_fields: dict[str, tuple[type[TArtifact], bool]] = {}
86
-
87
- for field_name, field_info in schema.model_fields.items():
88
- artifact_type = _extract_artifact_type(field_info.annotation)
89
- if artifact_type is not None:
90
- # Check if the field is a list type
91
- origin = get_origin(field_info.annotation)
92
- is_list = origin is list
93
- artifact_fields[field_name] = (artifact_type, is_list)
94
-
95
- return artifact_fields
96
-
97
-
98
- def _get_artifact_type_name[
99
- T: (
100
- ImageArtifact,
101
- VideoArtifact,
102
- AudioArtifact,
103
- TextArtifact,
104
- )
105
- ](
106
- artifact_class: type[T],
107
- ) -> str:
108
- """Get the database artifact_type string for an artifact class."""
109
- type_map = {
110
- ImageArtifact: "image",
111
- VideoArtifact: "video",
112
- AudioArtifact: "audio",
113
- TextArtifact: "text",
114
- }
115
- artifact_type = type_map.get(artifact_class)
116
- if artifact_type is None:
117
- raise ValueError(f"Unsupported artifact class: {artifact_class}")
118
- return artifact_type
119
-
120
-
121
- def _generation_to_artifact[
122
- T: (
123
- ImageArtifact,
124
- VideoArtifact,
125
- AudioArtifact,
126
- TextArtifact,
127
- )
128
- ](generation: Generations, artifact_class: type[T]) -> T:
129
- """Convert a Generations database record to an artifact object.
130
-
131
- Args:
132
- generation: Database generation record
133
- artifact_class: Target artifact class (ImageArtifact, VideoArtifact, etc.)
134
-
135
- Returns:
136
- Artifact object populated from the generation record
137
-
138
- Raises:
139
- ValueError: If generation is missing required fields or data
140
- """
141
- if not generation.storage_url:
142
- raise ValueError(
143
- f"Generation {generation.id} has no storage_url - generation may not be completed"
144
- )
145
-
146
- # Get output metadata
147
- metadata = generation.output_metadata or {}
148
-
149
- # Build artifact based on type
150
- if artifact_class == ImageArtifact:
151
- width = metadata.get("width")
152
- height = metadata.get("height")
153
- return ImageArtifact(
154
- generation_id=str(generation.id),
155
- storage_url=generation.storage_url,
156
- format=metadata.get("format", "png"),
157
- width=width,
158
- height=height,
159
- ) # type: ignore[return-value]
160
-
161
- elif artifact_class == VideoArtifact:
162
- width = metadata.get("width")
163
- height = metadata.get("height")
164
- return VideoArtifact(
165
- generation_id=str(generation.id),
166
- storage_url=generation.storage_url,
167
- format=metadata.get("format", "mp4"),
168
- width=width,
169
- height=height,
170
- duration=metadata.get("duration"),
171
- fps=metadata.get("fps"),
172
- ) # type: ignore[return-value]
173
-
174
- elif artifact_class == AudioArtifact:
175
- return AudioArtifact(
176
- generation_id=str(generation.id),
177
- storage_url=generation.storage_url,
178
- format=metadata.get("format", "mp3"),
179
- duration=metadata.get("duration"),
180
- sample_rate=metadata.get("sample_rate"),
181
- channels=metadata.get("channels"),
182
- ) # type: ignore[return-value]
183
-
184
- elif artifact_class == TextArtifact:
185
- content = metadata.get("content")
186
- if content is None:
187
- raise ValueError(f"Generation {generation.id} missing text content in output_metadata")
188
- return TextArtifact(
189
- generation_id=str(generation.id),
190
- storage_url=generation.storage_url,
191
- format=metadata.get("format", "plain"),
192
- content=content,
193
- ) # type: ignore[return-value]
194
-
195
- else:
196
- raise ValueError(f"Unsupported artifact class: {artifact_class}")
197
-
198
-
199
- async def resolve_generation_ids_to_artifacts[
200
- T: (
201
- ImageArtifact,
202
- VideoArtifact,
203
- AudioArtifact,
204
- TextArtifact,
205
- )
206
- ](
207
- generation_ids: list[str | UUID],
208
- artifact_class: type[T],
209
- session: AsyncSession,
210
- tenant_id: UUID,
211
- ) -> list[T]:
212
- """Convert a list of generation IDs to typed artifact objects.
213
-
214
- This function:
215
- 1. Queries the database for each generation ID
216
- 2. Validates the generation is completed
217
- 3. Validates the artifact type matches
218
- 4. Validates the user has access (tenant_id matches)
219
- 5. Converts to the appropriate artifact object
220
-
221
- Args:
222
- generation_ids: List of generation IDs (as strings or UUIDs)
223
- artifact_class: Target artifact class (ImageArtifact, VideoArtifact, etc.)
224
- session: Database session for queries
225
- tenant_id: Tenant ID for access validation
226
-
227
- Returns:
228
- List of artifact objects
229
-
230
- Raises:
231
- ValueError: If any generation is not found, not completed, wrong type, or access denied
232
- """
233
- expected_artifact_type = _get_artifact_type_name(artifact_class)
234
- artifacts: list[T] = []
235
-
236
- for gen_id in generation_ids:
237
- # Query generation from database
238
- try:
239
- generation = await jobs_repo.get_generation(session, gen_id)
240
- except Exception as e:
241
- raise ValueError(f"Generation {gen_id} not found") from e
242
-
243
- # Validate tenant access
244
- if generation.tenant_id != tenant_id:
245
- raise ValueError(f"Access denied to generation {gen_id} - tenant mismatch")
246
-
247
- # Validate completion status
248
- if generation.status != "completed":
249
- raise ValueError(f"Generation {gen_id} is not completed (status: {generation.status})")
250
-
251
- # Validate artifact type
252
- if generation.artifact_type != expected_artifact_type:
253
- raise ValueError(
254
- f"Generation {gen_id} has wrong artifact type: "
255
- f"expected {expected_artifact_type}, got {generation.artifact_type}"
256
- )
257
-
258
- # Convert to artifact object
259
- try:
260
- artifact = _generation_to_artifact(generation, artifact_class)
261
- artifacts.append(artifact)
262
- except ValueError as e:
263
- raise ValueError(f"Failed to convert generation {gen_id} to artifact: {e}") from e
264
-
265
- return artifacts
266
-
267
-
268
- async def resolve_input_artifacts(
269
- input_params: dict[str, Any],
270
- schema: type[BaseModel],
271
- session: AsyncSession,
272
- tenant_id: UUID,
273
- ) -> tuple[dict[str, Any], list[dict[str, Any]]]:
274
- """Resolve generation IDs to artifact objects in input parameters.
275
-
276
- This function automatically detects artifact fields from the Pydantic schema
277
- via type introspection, then resolves generation ID strings to typed artifact
278
- objects before Pydantic validation.
279
-
280
- The function respects the field's type annotation:
281
- - If field is `ImageArtifact`, input can be a single ID string → returns single artifact
282
- - If field is `list[ImageArtifact]`, input can be a list of IDs → returns list of artifacts
283
- (always a list, even if only one ID is provided)
284
-
285
- Usage in generator input schema (no special declarations needed!):
286
- class MyGeneratorInput(BaseModel):
287
- prompt: str = Field(...)
288
- image_source: ImageArtifact = Field(...) # Automatically detected
289
- video_sources: list[VideoArtifact] = Field(...) # Also detected
290
-
291
- Usage in actors.py:
292
- # Artifacts are automatically detected and resolved
293
- resolved_params, lineage_metadata = await resolve_input_artifacts(
294
- input_params,
295
- MyGeneratorInput, # Just pass the schema class
296
- session,
297
- tenant_id,
298
- )
299
- # Now validate with resolved artifacts
300
- typed_inputs = MyGeneratorInput.model_validate(resolved_params)
301
-
302
- Args:
303
- input_params: Raw input parameters dictionary (may contain generation IDs)
304
- schema: Pydantic model class to inspect for artifact fields
305
- session: Database session for queries
306
- tenant_id: Tenant ID for access validation
307
-
308
- Returns:
309
- Tuple of (resolved_params, lineage_metadata):
310
- - resolved_params: Updated input_params dictionary with generation IDs resolved to artifacts
311
- - lineage_metadata: List of dicts with generation_id, role, and artifact_type for lineage
312
-
313
- Raises:
314
- ValueError: If any generation ID cannot be resolved or validated
315
- """
316
- # Automatically extract artifact fields from schema
317
- artifact_field_map = extract_artifact_fields(schema)
318
-
319
- # If no artifact fields, just return original params with empty lineage
320
- if not artifact_field_map:
321
- return input_params, []
322
-
323
- # Create a new dict with resolved artifacts
324
- # Use dict constructor to avoid shallow copy issues
325
- resolved_params = dict(input_params)
326
- lineage_metadata: list[dict[str, Any]] = []
327
-
328
- for field_name, (artifact_class, expects_list) in artifact_field_map.items():
329
- field_value = resolved_params.get(field_name)
330
-
331
- # Skip if field is not present
332
- if field_value is None:
333
- continue
334
-
335
- # Skip if already artifacts (already resolved)
336
- if isinstance(field_value, list) and all(
337
- isinstance(item, artifact_class) for item in field_value
338
- ):
339
- continue
340
-
341
- # Also check for single artifact (only if field expects a single artifact)
342
- if not expects_list and isinstance(field_value, artifact_class):
343
- continue
344
-
345
- # Convert field value to list of UUIDs
346
- generation_ids: list[str | UUID]
347
- if isinstance(field_value, str):
348
- # Single generation ID - convert to list for processing
349
- generation_ids = [field_value]
350
- elif isinstance(field_value, list):
351
- # List of generation IDs - ensure all are strings
352
- # Convert each item to str to ensure type consistency
353
- generation_ids = [str(item) for item in field_value]
354
- else:
355
- raise ValueError(
356
- f"Field '{field_name}' must be a generation ID (UUID string) "
357
- f"or list of generation IDs, got: {type(field_value)}"
358
- )
359
-
360
- # Resolve to artifacts
361
- try:
362
- artifacts = await resolve_generation_ids_to_artifacts(
363
- generation_ids, artifact_class, session, tenant_id
364
- )
365
- except ValueError as e:
366
- raise ValueError(f"Failed to resolve field '{field_name}': {e}") from e
367
-
368
- # Capture lineage metadata for each artifact
369
- artifact_type_name = _get_artifact_type_name(artifact_class)
370
- for gen_id in generation_ids:
371
- lineage_metadata.append(
372
- {
373
- "generation_id": str(gen_id),
374
- "role": field_name, # Field name IS the role!
375
- "artifact_type": artifact_type_name,
376
- }
377
- )
378
-
379
- # Update params with resolved artifacts
380
- # If field expects a single artifact (not a list), unwrap the first artifact
381
- # Otherwise, keep as a list (even if there's only one artifact)
382
- if expects_list:
383
- resolved_value = artifacts
384
- else:
385
- # Field expects a single artifact, so unwrap
386
- if len(artifacts) != 1:
387
- raise ValueError(
388
- f"Field '{field_name}' expects a single artifact, "
389
- f"but got {len(artifacts)} generation IDs"
390
- )
391
- resolved_value = artifacts[0]
392
-
393
- # Debug logging
394
- logger.debug(
395
- "Resolved artifact field",
396
- field_name=field_name,
397
- expects_list=expects_list,
398
- artifacts_type=type(artifacts),
399
- artifacts_len=len(artifacts),
400
- resolved_value_type=type(resolved_value),
401
- )
402
-
403
- resolved_params[field_name] = resolved_value
404
-
405
- return resolved_params, lineage_metadata
@@ -1,53 +0,0 @@
1
- """
2
- Artifact type definitions for the Boards generators system.
3
-
4
- These Pydantic models represent different types of generated content
5
- that can be used as inputs and outputs for generators.
6
- """
7
-
8
- from pydantic import BaseModel, Field
9
-
10
-
11
- class DigitalArtifact(BaseModel):
12
- """Represents a digital artifact from a generation."""
13
-
14
- generation_id: str = Field(description="ID of the generation that created this artifact")
15
- storage_url: str = Field(description="URL where the digital file is stored")
16
- format: str = Field(description="Digital format (png, jpg, webp, etc.)")
17
-
18
-
19
- class AudioArtifact(DigitalArtifact):
20
- """Represents an audio file artifact from a generation."""
21
-
22
- duration: float | None = Field(None, description="Duration in seconds")
23
- sample_rate: int | None = Field(None, description="Sample rate in Hz")
24
- channels: int | None = Field(None, description="Number of audio channels")
25
-
26
-
27
- class VideoArtifact(DigitalArtifact):
28
- """Represents a video file artifact from a generation."""
29
-
30
- duration: float | None = Field(None, description="Duration in seconds")
31
- width: int | None = Field(None, description="Video width in pixels")
32
- height: int | None = Field(None, description="Video height in pixels")
33
- fps: float | None = Field(None, description="Frames per second")
34
-
35
-
36
- class ImageArtifact(DigitalArtifact):
37
- """Represents an image file artifact from a generation."""
38
-
39
- width: int | None = Field(None, description="Image width in pixels")
40
- height: int | None = Field(None, description="Image height in pixels")
41
-
42
-
43
- class TextArtifact(DigitalArtifact):
44
- """Represents a text artifact from a generation."""
45
-
46
- content: str = Field(description="The generated text content")
47
-
48
-
49
- class LoRArtifact(DigitalArtifact):
50
- """Represents a LoRA (Low-Rank Adaptation) model artifact."""
51
-
52
- base_model: str = Field(description="Base model this LoRA was trained on")
53
- trigger_words: list[str] | None = Field(None, description="Trigger words for this LoRA")
@@ -1,144 +0,0 @@
1
- """
2
- Base generator classes and interfaces for the Boards generators system.
3
- """
4
-
5
- from abc import ABC, abstractmethod
6
- from typing import Protocol, runtime_checkable
7
-
8
- from pydantic import BaseModel
9
-
10
- from ..progress.models import ProgressUpdate
11
- from .artifacts import (
12
- AudioArtifact,
13
- DigitalArtifact,
14
- ImageArtifact,
15
- TextArtifact,
16
- VideoArtifact,
17
- )
18
-
19
-
20
- class GeneratorResult(BaseModel):
21
- """All generators return a list of urls to the artifacts they produce."""
22
-
23
- outputs: list[DigitalArtifact]
24
-
25
-
26
- class BaseGenerator(ABC):
27
- """
28
- Abstract base class for all generators in the Boards system.
29
-
30
- Generators define input/output schemas using Pydantic models and implement
31
- the generation logic using provider SDKs directly.
32
- """
33
-
34
- # Class attributes that subclasses must define
35
- name: str
36
- artifact_type: str # 'image', 'video', 'audio', 'text', 'lora'
37
- description: str
38
-
39
- @abstractmethod
40
- def get_input_schema(self) -> type[BaseModel]:
41
- """
42
- Return the Pydantic model class that defines the input schema for this generator.
43
-
44
- Returns:
45
- Type[BaseModel]: Pydantic model class for input validation
46
- """
47
- pass
48
-
49
- @abstractmethod
50
- async def generate(
51
- self, inputs: BaseModel, context: "GeneratorExecutionContext"
52
- ) -> GeneratorResult:
53
- """
54
- Execute the generation process using the provided inputs.
55
-
56
- Args:
57
- inputs: Validated input data matching the input schema
58
-
59
- Returns:
60
- BaseModel: Generated output matching the output schema
61
- """
62
- pass
63
-
64
- @abstractmethod
65
- async def estimate_cost(self, inputs: BaseModel) -> float:
66
- """
67
- Estimate the cost of running this generation in USD.
68
-
69
- Args:
70
- inputs: Input data for cost estimation
71
-
72
- Returns:
73
- float: Estimated cost in USD
74
- """
75
- pass
76
-
77
- def __repr__(self) -> str:
78
- return f"<{self.__class__.__name__}(name='{self.name}', type='{self.artifact_type}')>"
79
-
80
-
81
- @runtime_checkable
82
- class GeneratorExecutionContext(Protocol):
83
- """Typed protocol for the execution context passed to generators.
84
-
85
- This protocol defines the interface that generators can use to interact
86
- with storage, database, and progress tracking systems.
87
- """
88
-
89
- async def resolve_artifact(self, artifact: DigitalArtifact) -> str:
90
- """Resolve an artifact to a local file path for use with provider SDKs."""
91
- ...
92
-
93
- async def store_image_result(
94
- self,
95
- storage_url: str,
96
- format: str,
97
- width: int | None = None,
98
- height: int | None = None,
99
- output_index: int = 0,
100
- ) -> ImageArtifact:
101
- """Store an image result to permanent storage."""
102
- ...
103
-
104
- async def store_video_result(
105
- self,
106
- storage_url: str,
107
- format: str,
108
- width: int | None = None,
109
- height: int | None = None,
110
- duration: float | None = None,
111
- fps: float | None = None,
112
- output_index: int = 0,
113
- ) -> VideoArtifact:
114
- """Store a video result to permanent storage."""
115
- ...
116
-
117
- async def store_audio_result(
118
- self,
119
- storage_url: str,
120
- format: str,
121
- duration: float | None = None,
122
- sample_rate: int | None = None,
123
- channels: int | None = None,
124
- output_index: int = 0,
125
- ) -> AudioArtifact:
126
- """Store an audio result to permanent storage."""
127
- ...
128
-
129
- async def store_text_result(
130
- self,
131
- content: str,
132
- format: str,
133
- output_index: int = 0,
134
- ) -> TextArtifact:
135
- """Store a text result to permanent storage."""
136
- ...
137
-
138
- async def publish_progress(self, update: ProgressUpdate) -> None:
139
- """Publish a progress update for this generation."""
140
- ...
141
-
142
- async def set_external_job_id(self, external_id: str) -> None:
143
- """Set the external job ID from the provider (e.g., Replicate prediction ID)."""
144
- ...
@@ -1,14 +0,0 @@
1
- """
2
- Built-in generator implementations for the Boards system.
3
-
4
- This package contains example generators that demonstrate how to integrate
5
- various AI services using their native SDKs.
6
-
7
- Generators are organized by provider (Replicate, Fal, OpenAI, etc.).
8
-
9
- Import this module to automatically register all built-in generators:
10
- import boards.generators.implementations
11
- """
12
-
13
- # Import all provider modules to make them available
14
- from . import fal, openai, replicate
@@ -1,25 +0,0 @@
1
- """Fal.ai provider generators."""
2
-
3
- from . import audio, image, video
4
- from .image import (
5
- FalFluxProUltraGenerator,
6
- FalNanoBananaEditGenerator,
7
- FalNanoBananaGenerator,
8
- )
9
- from .video import (
10
- FalKlingVideoV25TurboProTextToVideoGenerator,
11
- FalSyncLipsyncV2Generator,
12
- FalVeo31FirstLastFrameToVideoGenerator,
13
- )
14
-
15
- # Maintain alphabetical order
16
- __all__ = [
17
- # Image generators
18
- "FalFluxProUltraGenerator",
19
- "FalNanoBananaEditGenerator",
20
- "FalNanoBananaGenerator",
21
- # Video generators
22
- "FalKlingVideoV25TurboProTextToVideoGenerator",
23
- "FalSyncLipsyncV2Generator",
24
- "FalVeo31FirstLastFrameToVideoGenerator",
25
- ]
@@ -1,23 +0,0 @@
1
- from .beatoven_music_generation import FalBeatovenMusicGenerationGenerator
2
- from .beatoven_sound_effect_generation import FalBeatovenSoundEffectGenerationGenerator
3
- from .chatterbox_text_to_speech import FalChatterboxTextToSpeechGenerator
4
- from .chatterbox_tts_turbo import FalChatterboxTtsTurboGenerator
5
- from .elevenlabs_sound_effects_v2 import FalElevenlabsSoundEffectsV2Generator
6
- from .elevenlabs_tts_eleven_v3 import FalElevenlabsTtsElevenV3Generator
7
- from .fal_elevenlabs_tts_turbo_v2_5 import FalElevenlabsTtsTurboV25Generator
8
- from .fal_minimax_speech_26_hd import FalMinimaxSpeech26HdGenerator
9
- from .minimax_music_v2 import FalMinimaxMusicV2Generator
10
- from .minimax_speech_2_6_turbo import FalMinimaxSpeech26TurboGenerator
11
-
12
- __all__ = [
13
- "FalBeatovenMusicGenerationGenerator",
14
- "FalBeatovenSoundEffectGenerationGenerator",
15
- "FalChatterboxTextToSpeechGenerator",
16
- "FalChatterboxTtsTurboGenerator",
17
- "FalElevenlabsSoundEffectsV2Generator",
18
- "FalElevenlabsTtsElevenV3Generator",
19
- "FalElevenlabsTtsTurboV25Generator",
20
- "FalMinimaxMusicV2Generator",
21
- "FalMinimaxSpeech26HdGenerator",
22
- "FalMinimaxSpeech26TurboGenerator",
23
- ]