@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,50 +0,0 @@
1
- """
2
- Generator resolvers for GraphQL API
3
- """
4
-
5
- import strawberry
6
-
7
- from ...generators.registry import registry
8
- from ..types.generation import ArtifactType
9
- from ..types.generator import GeneratorInfo
10
-
11
-
12
- async def resolve_generators(
13
- info: strawberry.Info,
14
- artifact_type: str | None = None,
15
- ) -> list[GeneratorInfo]:
16
- """Get all available generators, optionally filtered by artifact type.
17
-
18
- Args:
19
- info: GraphQL info context
20
- artifact_type: Optional filter by artifact type (image, video, audio, text)
21
-
22
- Returns:
23
- List of generator information
24
- """
25
- _ = info # Unused but required by GraphQL interface
26
-
27
- # Get generators from registry
28
- if artifact_type:
29
- generators = registry.list_by_artifact_type(artifact_type)
30
- else:
31
- generators = registry.list_all()
32
-
33
- # Convert to GraphQL types
34
- result = []
35
- for gen in generators:
36
- input_schema_class = gen.get_input_schema()
37
-
38
- # Convert string artifact_type to enum
39
- artifact_type_enum = ArtifactType(gen.artifact_type)
40
-
41
- result.append(
42
- GeneratorInfo(
43
- name=gen.name,
44
- description=gen.description,
45
- artifact_type=artifact_type_enum,
46
- input_schema=input_schema_class.model_json_schema(),
47
- )
48
- )
49
-
50
- return result
@@ -1,381 +0,0 @@
1
- """
2
- Lineage resolvers for ancestry and descendants tracking
3
- """
4
-
5
- from uuid import UUID
6
-
7
- import strawberry
8
- from sqlalchemy import select, text
9
-
10
- from ...database.connection import get_async_session
11
- from ...dbmodels import Boards, Generations
12
- from ...logging import get_logger
13
- from ..access_control import can_access_board, get_auth_context_from_info
14
- from ..types.generation import (
15
- AncestryNode,
16
- ArtifactLineage,
17
- ArtifactType,
18
- DescendantNode,
19
- Generation,
20
- GenerationStatus,
21
- )
22
-
23
- logger = get_logger(__name__)
24
-
25
-
26
- def convert_db_to_graphql_generation(gen: Generations) -> Generation:
27
- """Convert a database Generation model to GraphQL Generation type."""
28
- return Generation(
29
- id=gen.id,
30
- tenant_id=gen.tenant_id,
31
- board_id=gen.board_id,
32
- user_id=gen.user_id,
33
- generator_name=gen.generator_name,
34
- artifact_type=ArtifactType(gen.artifact_type),
35
- storage_url=gen.storage_url,
36
- thumbnail_url=gen.thumbnail_url,
37
- additional_files=gen.additional_files or [],
38
- input_params=gen.input_params or {},
39
- output_metadata=gen.output_metadata or {},
40
- external_job_id=gen.external_job_id,
41
- status=GenerationStatus(gen.status),
42
- progress=float(gen.progress or 0.0),
43
- error_message=gen.error_message,
44
- started_at=gen.started_at,
45
- completed_at=gen.completed_at,
46
- created_at=gen.created_at,
47
- updated_at=gen.updated_at,
48
- )
49
-
50
-
51
- async def resolve_input_artifacts(
52
- generation: Generation, info: strawberry.Info
53
- ) -> list[ArtifactLineage]:
54
- """Resolve input artifacts with role metadata."""
55
- async with get_async_session() as session:
56
- # Query the generation to get input_artifacts
57
- stmt = select(Generations).where(Generations.id == generation.id)
58
- result = await session.execute(stmt)
59
- gen = result.scalar_one_or_none()
60
-
61
- if not gen or not gen.input_artifacts:
62
- return []
63
-
64
- # Build ArtifactLineage objects
65
- lineages = []
66
- for artifact_data in gen.input_artifacts:
67
- lineages.append(
68
- ArtifactLineage(
69
- generation_id=UUID(artifact_data["generation_id"]),
70
- role=artifact_data["role"],
71
- artifact_type=ArtifactType(artifact_data["artifact_type"]),
72
- )
73
- )
74
-
75
- return lineages
76
-
77
-
78
- async def resolve_generation_by_id(info: strawberry.Info, generation_id: UUID) -> Generation | None:
79
- """Helper to resolve a generation by ID with access control."""
80
- auth_context = await get_auth_context_from_info(info)
81
- if auth_context is None:
82
- return None
83
-
84
- async with get_async_session() as session:
85
- # Query generation
86
- stmt = select(Generations).where(Generations.id == generation_id)
87
- result = await session.execute(stmt)
88
- gen = result.scalar_one_or_none()
89
-
90
- if not gen:
91
- return None
92
-
93
- # Check board access
94
- board_stmt = select(Boards).where(Boards.id == gen.board_id)
95
- board_result = await session.execute(board_stmt)
96
- board = board_result.scalar_one_or_none()
97
-
98
- if not board or not can_access_board(board, auth_context):
99
- return None
100
-
101
- return convert_db_to_graphql_generation(gen)
102
-
103
-
104
- async def resolve_ancestry(
105
- generation: Generation, info: strawberry.Info, max_depth: int = 25
106
- ) -> AncestryNode:
107
- """Build recursive ancestry tree using recursive CTE."""
108
- auth_context = await get_auth_context_from_info(info)
109
-
110
- if not auth_context:
111
- # Return empty tree if not authenticated
112
- return AncestryNode(generation=generation, depth=0, role=None, parents=[])
113
-
114
- async with get_async_session() as session:
115
- # Use recursive CTE to fetch entire ancestry tree in one query
116
- cte_query = text("""
117
- WITH RECURSIVE ancestry_tree AS (
118
- -- Base case: starting generation
119
- SELECT
120
- id,
121
- tenant_id,
122
- board_id,
123
- user_id,
124
- generator_name,
125
- artifact_type,
126
- storage_url,
127
- thumbnail_url,
128
- additional_files,
129
- input_params,
130
- output_metadata,
131
- external_job_id,
132
- status,
133
- progress,
134
- error_message,
135
- started_at,
136
- completed_at,
137
- created_at,
138
- updated_at,
139
- input_artifacts,
140
- 0 as depth,
141
- NULL::text as role,
142
- ARRAY[id] as path -- cycle detection
143
- FROM boards.generations
144
- WHERE id = :gen_id AND tenant_id = :tenant_id
145
-
146
- UNION ALL
147
-
148
- -- Recursive case: parent generations
149
- SELECT
150
- g.id,
151
- g.tenant_id,
152
- g.board_id,
153
- g.user_id,
154
- g.generator_name,
155
- g.artifact_type,
156
- g.storage_url,
157
- g.thumbnail_url,
158
- g.additional_files,
159
- g.input_params,
160
- g.output_metadata,
161
- g.external_job_id,
162
- g.status,
163
- g.progress,
164
- g.error_message,
165
- g.started_at,
166
- g.completed_at,
167
- g.created_at,
168
- g.updated_at,
169
- g.input_artifacts,
170
- at.depth + 1,
171
- artifact->>'role' as role,
172
- at.path || g.id
173
- FROM ancestry_tree at
174
- CROSS JOIN LATERAL jsonb_array_elements(at.input_artifacts) AS artifact
175
- JOIN boards.generations g ON
176
- g.id = (artifact->>'generation_id')::uuid
177
- AND NOT (g.id = ANY(at.path)) -- prevent cycles
178
- AND at.depth < :max_depth
179
- WHERE g.tenant_id = :tenant_id
180
- )
181
- SELECT * FROM ancestry_tree ORDER BY depth, id
182
- """)
183
-
184
- result = await session.execute(
185
- cte_query,
186
- {
187
- "gen_id": generation.id,
188
- "tenant_id": auth_context.tenant_id,
189
- "max_depth": max_depth,
190
- },
191
- )
192
- rows = result.fetchall()
193
-
194
- if not rows:
195
- # Return root node with no parents
196
- return AncestryNode(generation=generation, depth=0, role=None, parents=[])
197
-
198
- # Build tree from flat results
199
- nodes_by_id: dict[UUID, tuple[Generations, int, str | None]] = {}
200
- for row in rows:
201
- gen_obj = Generations()
202
- gen_obj.id = row.id
203
- gen_obj.tenant_id = row.tenant_id
204
- gen_obj.board_id = row.board_id
205
- gen_obj.user_id = row.user_id
206
- gen_obj.generator_name = row.generator_name
207
- gen_obj.artifact_type = row.artifact_type
208
- gen_obj.storage_url = row.storage_url
209
- gen_obj.thumbnail_url = row.thumbnail_url
210
- gen_obj.additional_files = row.additional_files
211
- gen_obj.input_params = row.input_params
212
- gen_obj.output_metadata = row.output_metadata
213
- gen_obj.external_job_id = row.external_job_id
214
- gen_obj.status = row.status
215
- gen_obj.progress = row.progress
216
- gen_obj.error_message = row.error_message
217
- gen_obj.started_at = row.started_at
218
- gen_obj.completed_at = row.completed_at
219
- gen_obj.created_at = row.created_at
220
- gen_obj.updated_at = row.updated_at
221
- gen_obj.input_artifacts = row.input_artifacts
222
- nodes_by_id[row.id] = (gen_obj, row.depth, row.role)
223
-
224
- # Build tree structure recursively
225
- def build_node(gen_id: UUID) -> AncestryNode:
226
- gen_obj, depth, role = nodes_by_id[gen_id]
227
- gen_graphql = convert_db_to_graphql_generation(gen_obj)
228
-
229
- # Find parent nodes
230
- parent_nodes = []
231
- if gen_obj.input_artifacts:
232
- for artifact_data in gen_obj.input_artifacts:
233
- parent_id = UUID(artifact_data["generation_id"])
234
- if parent_id in nodes_by_id:
235
- parent_nodes.append(build_node(parent_id))
236
-
237
- return AncestryNode(
238
- generation=gen_graphql, depth=depth, role=role, parents=parent_nodes
239
- )
240
-
241
- return build_node(generation.id)
242
-
243
-
244
- async def resolve_descendants(
245
- generation: Generation, info: strawberry.Info, max_depth: int = 25
246
- ) -> DescendantNode:
247
- """Build recursive descendants tree using recursive CTE."""
248
- auth_context = await get_auth_context_from_info(info)
249
-
250
- if not auth_context:
251
- # Return empty tree if not authenticated
252
- return DescendantNode(generation=generation, depth=0, role=None, children=[])
253
-
254
- async with get_async_session() as session:
255
- # Use recursive CTE to fetch entire descendants tree in one query
256
- cte_query = text("""
257
- WITH RECURSIVE descendants_tree AS (
258
- -- Base case: starting generation
259
- SELECT
260
- id,
261
- tenant_id,
262
- board_id,
263
- user_id,
264
- generator_name,
265
- artifact_type,
266
- storage_url,
267
- thumbnail_url,
268
- additional_files,
269
- input_params,
270
- output_metadata,
271
- external_job_id,
272
- status,
273
- progress,
274
- error_message,
275
- started_at,
276
- completed_at,
277
- created_at,
278
- updated_at,
279
- input_artifacts,
280
- 0 as depth,
281
- NULL::text as role,
282
- NULL::uuid as parent_id,
283
- ARRAY[id] as path -- cycle detection
284
- FROM boards.generations
285
- WHERE id = :gen_id AND tenant_id = :tenant_id
286
-
287
- UNION ALL
288
-
289
- -- Recursive case: child generations
290
- SELECT
291
- g.id,
292
- g.tenant_id,
293
- g.board_id,
294
- g.user_id,
295
- g.generator_name,
296
- g.artifact_type,
297
- g.storage_url,
298
- g.thumbnail_url,
299
- g.additional_files,
300
- g.input_params,
301
- g.output_metadata,
302
- g.external_job_id,
303
- g.status,
304
- g.progress,
305
- g.error_message,
306
- g.started_at,
307
- g.completed_at,
308
- g.created_at,
309
- g.updated_at,
310
- g.input_artifacts,
311
- dt.depth + 1,
312
- artifact->>'role' as role,
313
- dt.id as parent_id,
314
- dt.path || g.id
315
- FROM boards.generations g
316
- CROSS JOIN LATERAL jsonb_array_elements(g.input_artifacts) AS artifact
317
- JOIN descendants_tree dt ON
318
- dt.id = (artifact->>'generation_id')::uuid
319
- AND NOT (g.id = ANY(dt.path)) -- prevent cycles
320
- AND dt.depth < :max_depth
321
- WHERE g.tenant_id = :tenant_id
322
- )
323
- SELECT * FROM descendants_tree ORDER BY depth, id
324
- """)
325
-
326
- result = await session.execute(
327
- cte_query,
328
- {
329
- "gen_id": generation.id,
330
- "tenant_id": auth_context.tenant_id,
331
- "max_depth": max_depth,
332
- },
333
- )
334
- rows = result.fetchall()
335
-
336
- if not rows:
337
- # Return root node with no children
338
- return DescendantNode(generation=generation, depth=0, role=None, children=[])
339
-
340
- # Build tree from flat results
341
- nodes_by_id: dict[UUID, tuple[Generations, int, str | None, UUID | None]] = {}
342
- for row in rows:
343
- gen_obj = Generations()
344
- gen_obj.id = row.id
345
- gen_obj.tenant_id = row.tenant_id
346
- gen_obj.board_id = row.board_id
347
- gen_obj.user_id = row.user_id
348
- gen_obj.generator_name = row.generator_name
349
- gen_obj.artifact_type = row.artifact_type
350
- gen_obj.storage_url = row.storage_url
351
- gen_obj.thumbnail_url = row.thumbnail_url
352
- gen_obj.additional_files = row.additional_files
353
- gen_obj.input_params = row.input_params
354
- gen_obj.output_metadata = row.output_metadata
355
- gen_obj.external_job_id = row.external_job_id
356
- gen_obj.status = row.status
357
- gen_obj.progress = row.progress
358
- gen_obj.error_message = row.error_message
359
- gen_obj.started_at = row.started_at
360
- gen_obj.completed_at = row.completed_at
361
- gen_obj.created_at = row.created_at
362
- gen_obj.updated_at = row.updated_at
363
- gen_obj.input_artifacts = row.input_artifacts
364
- nodes_by_id[row.id] = (gen_obj, row.depth, row.role, row.parent_id)
365
-
366
- # Build tree structure recursively
367
- def build_node(gen_id: UUID) -> DescendantNode:
368
- gen_obj, depth, role, _ = nodes_by_id[gen_id]
369
- gen_graphql = convert_db_to_graphql_generation(gen_obj)
370
-
371
- # Find child nodes (nodes that have this as parent_id)
372
- child_nodes = []
373
- for child_id, (_, _, _, parent_id) in nodes_by_id.items():
374
- if parent_id == gen_id:
375
- child_nodes.append(build_node(child_id))
376
-
377
- return DescendantNode(
378
- generation=gen_graphql, depth=depth, role=role, children=child_nodes
379
- )
380
-
381
- return build_node(generation.id)