@weirdfingers/baseboards 0.5.3 → 0.6.0

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 (74) hide show
  1. package/README.md +1 -1
  2. package/package.json +1 -1
  3. package/templates/api/alembic/env.py +9 -1
  4. package/templates/api/alembic/versions/20250101_000000_initial_schema.py +107 -49
  5. package/templates/api/alembic/versions/20251022_174729_remove_provider_name_from_generations.py +7 -3
  6. package/templates/api/alembic/versions/20251023_165852_switch_to_declarative_base_and_mapping.py +57 -1
  7. package/templates/api/alembic/versions/20251202_000000_add_artifact_lineage.py +134 -0
  8. package/templates/api/alembic/versions/2025925_62735_add_seed_data_for_default_tenant.py +8 -5
  9. package/templates/api/config/generators.yaml +111 -0
  10. package/templates/api/src/boards/__init__.py +1 -1
  11. package/templates/api/src/boards/api/app.py +2 -1
  12. package/templates/api/src/boards/api/endpoints/tenant_registration.py +1 -1
  13. package/templates/api/src/boards/api/endpoints/uploads.py +150 -0
  14. package/templates/api/src/boards/auth/factory.py +1 -1
  15. package/templates/api/src/boards/dbmodels/__init__.py +8 -22
  16. package/templates/api/src/boards/generators/artifact_resolution.py +45 -12
  17. package/templates/api/src/boards/generators/implementations/fal/audio/__init__.py +16 -1
  18. package/templates/api/src/boards/generators/implementations/fal/audio/beatoven_music_generation.py +171 -0
  19. package/templates/api/src/boards/generators/implementations/fal/audio/beatoven_sound_effect_generation.py +167 -0
  20. package/templates/api/src/boards/generators/implementations/fal/audio/elevenlabs_sound_effects_v2.py +194 -0
  21. package/templates/api/src/boards/generators/implementations/fal/audio/elevenlabs_tts_eleven_v3.py +209 -0
  22. package/templates/api/src/boards/generators/implementations/fal/audio/fal_elevenlabs_tts_turbo_v2_5.py +206 -0
  23. package/templates/api/src/boards/generators/implementations/fal/audio/fal_minimax_speech_26_hd.py +237 -0
  24. package/templates/api/src/boards/generators/implementations/fal/audio/minimax_speech_2_6_turbo.py +1 -1
  25. package/templates/api/src/boards/generators/implementations/fal/image/__init__.py +30 -0
  26. package/templates/api/src/boards/generators/implementations/fal/image/clarity_upscaler.py +220 -0
  27. package/templates/api/src/boards/generators/implementations/fal/image/crystal_upscaler.py +173 -0
  28. package/templates/api/src/boards/generators/implementations/fal/image/fal_ideogram_character.py +227 -0
  29. package/templates/api/src/boards/generators/implementations/fal/image/flux_2.py +203 -0
  30. package/templates/api/src/boards/generators/implementations/fal/image/flux_2_edit.py +230 -0
  31. package/templates/api/src/boards/generators/implementations/fal/image/flux_2_pro.py +204 -0
  32. package/templates/api/src/boards/generators/implementations/fal/image/flux_2_pro_edit.py +221 -0
  33. package/templates/api/src/boards/generators/implementations/fal/image/gemini_25_flash_image.py +177 -0
  34. package/templates/api/src/boards/generators/implementations/fal/image/gpt_image_1_edit_image.py +182 -0
  35. package/templates/api/src/boards/generators/implementations/fal/image/gpt_image_1_mini.py +167 -0
  36. package/templates/api/src/boards/generators/implementations/fal/image/ideogram_character_edit.py +299 -0
  37. package/templates/api/src/boards/generators/implementations/fal/image/ideogram_v2.py +190 -0
  38. package/templates/api/src/boards/generators/implementations/fal/image/nano_banana_pro_edit.py +226 -0
  39. package/templates/api/src/boards/generators/implementations/fal/image/qwen_image.py +249 -0
  40. package/templates/api/src/boards/generators/implementations/fal/image/qwen_image_edit.py +244 -0
  41. package/templates/api/src/boards/generators/implementations/fal/video/__init__.py +42 -0
  42. package/templates/api/src/boards/generators/implementations/fal/video/bytedance_seedance_v1_pro_text_to_video.py +209 -0
  43. package/templates/api/src/boards/generators/implementations/fal/video/creatify_lipsync.py +161 -0
  44. package/templates/api/src/boards/generators/implementations/fal/video/fal_bytedance_seedance_v1_pro_image_to_video.py +222 -0
  45. package/templates/api/src/boards/generators/implementations/fal/video/fal_minimax_hailuo_02_standard_text_to_video.py +152 -0
  46. package/templates/api/src/boards/generators/implementations/fal/video/fal_pixverse_lipsync.py +197 -0
  47. package/templates/api/src/boards/generators/implementations/fal/video/fal_sora_2_text_to_video.py +173 -0
  48. package/templates/api/src/boards/generators/implementations/fal/video/infinitalk.py +221 -0
  49. package/templates/api/src/boards/generators/implementations/fal/video/kling_video_v2_5_turbo_pro_image_to_video.py +175 -0
  50. package/templates/api/src/boards/generators/implementations/fal/video/minimax_hailuo_2_3_pro_image_to_video.py +153 -0
  51. package/templates/api/src/boards/generators/implementations/fal/video/sora2_image_to_video.py +172 -0
  52. package/templates/api/src/boards/generators/implementations/fal/video/sora_2_image_to_video_pro.py +175 -0
  53. package/templates/api/src/boards/generators/implementations/fal/video/sora_2_text_to_video_pro.py +163 -0
  54. package/templates/api/src/boards/generators/implementations/fal/video/sync_lipsync_v2_pro.py +155 -0
  55. package/templates/api/src/boards/generators/implementations/fal/video/veed_lipsync.py +174 -0
  56. package/templates/api/src/boards/generators/implementations/fal/video/veo3.py +194 -0
  57. package/templates/api/src/boards/generators/implementations/fal/video/veo31_first_last_frame_to_video.py +1 -1
  58. package/templates/api/src/boards/generators/implementations/fal/video/wan_pro_image_to_video.py +158 -0
  59. package/templates/api/src/boards/graphql/access_control.py +1 -1
  60. package/templates/api/src/boards/graphql/mutations/root.py +16 -4
  61. package/templates/api/src/boards/graphql/resolvers/board.py +0 -2
  62. package/templates/api/src/boards/graphql/resolvers/generation.py +10 -233
  63. package/templates/api/src/boards/graphql/resolvers/lineage.py +381 -0
  64. package/templates/api/src/boards/graphql/resolvers/upload.py +463 -0
  65. package/templates/api/src/boards/graphql/types/generation.py +62 -26
  66. package/templates/api/src/boards/middleware.py +1 -1
  67. package/templates/api/src/boards/storage/factory.py +2 -2
  68. package/templates/api/src/boards/tenant_isolation.py +9 -9
  69. package/templates/api/src/boards/workers/actors.py +10 -1
  70. package/templates/web/package.json +1 -1
  71. package/templates/web/src/app/boards/[boardId]/page.tsx +14 -5
  72. package/templates/web/src/app/lineage/[generationId]/page.tsx +233 -0
  73. package/templates/web/src/components/boards/ArtifactPreview.tsx +20 -1
  74. package/templates/web/src/components/boards/UploadArtifact.tsx +253 -0
@@ -7,7 +7,6 @@ Create Date: 2025-10-23 16:58:52.004927
7
7
  """
8
8
 
9
9
  from collections.abc import Sequence
10
- from typing import Union
11
10
 
12
11
  import sqlalchemy as sa
13
12
  from sqlalchemy.dialects import postgresql
@@ -20,6 +19,9 @@ down_revision: str | Sequence[str] | None = "d7c16af77c9e"
20
19
  branch_labels: str | Sequence[str] | None = None
21
20
  depends_on: str | Sequence[str] | None = None
22
21
 
22
+ # Schema name for all Boards tables
23
+ SCHEMA = "boards"
24
+
23
25
 
24
26
  def upgrade() -> None:
25
27
  """Upgrade schema."""
@@ -30,6 +32,7 @@ def upgrade() -> None:
30
32
  existing_type=postgresql.TIMESTAMP(timezone=True),
31
33
  nullable=False,
32
34
  existing_server_default=sa.text("CURRENT_TIMESTAMP"),
35
+ schema=SCHEMA,
33
36
  )
34
37
  op.alter_column(
35
38
  "boards",
@@ -37,6 +40,7 @@ def upgrade() -> None:
37
40
  existing_type=sa.BOOLEAN(),
38
41
  nullable=False,
39
42
  existing_server_default=sa.text("false"),
43
+ schema=SCHEMA,
40
44
  )
41
45
  op.alter_column(
42
46
  "boards",
@@ -44,6 +48,7 @@ def upgrade() -> None:
44
48
  existing_type=postgresql.JSONB(astext_type=sa.Text()),
45
49
  nullable=False,
46
50
  existing_server_default=sa.text("'{}'::jsonb"),
51
+ schema=SCHEMA,
47
52
  )
48
53
  op.alter_column(
49
54
  "boards",
@@ -51,6 +56,7 @@ def upgrade() -> None:
51
56
  existing_type=postgresql.JSONB(astext_type=sa.Text()),
52
57
  nullable=False,
53
58
  existing_server_default=sa.text("'{}'::jsonb"),
59
+ schema=SCHEMA,
54
60
  )
55
61
  op.alter_column(
56
62
  "boards",
@@ -58,6 +64,7 @@ def upgrade() -> None:
58
64
  existing_type=postgresql.TIMESTAMP(timezone=True),
59
65
  nullable=False,
60
66
  existing_server_default=sa.text("CURRENT_TIMESTAMP"),
67
+ schema=SCHEMA,
61
68
  )
62
69
  op.alter_column(
63
70
  "boards",
@@ -65,6 +72,7 @@ def upgrade() -> None:
65
72
  existing_type=postgresql.TIMESTAMP(timezone=True),
66
73
  nullable=False,
67
74
  existing_server_default=sa.text("CURRENT_TIMESTAMP"),
75
+ schema=SCHEMA,
68
76
  )
69
77
  op.alter_column(
70
78
  "credit_transactions",
@@ -72,6 +80,7 @@ def upgrade() -> None:
72
80
  existing_type=postgresql.JSONB(astext_type=sa.Text()),
73
81
  nullable=False,
74
82
  existing_server_default=sa.text("'{}'::jsonb"),
83
+ schema=SCHEMA,
75
84
  )
76
85
  op.alter_column(
77
86
  "credit_transactions",
@@ -79,6 +88,7 @@ def upgrade() -> None:
79
88
  existing_type=postgresql.TIMESTAMP(timezone=True),
80
89
  nullable=False,
81
90
  existing_server_default=sa.text("CURRENT_TIMESTAMP"),
91
+ schema=SCHEMA,
82
92
  )
83
93
  op.alter_column(
84
94
  "generations",
@@ -86,6 +96,7 @@ def upgrade() -> None:
86
96
  existing_type=postgresql.JSONB(astext_type=sa.Text()),
87
97
  nullable=False,
88
98
  existing_server_default=sa.text("'[]'::jsonb"),
99
+ schema=SCHEMA,
89
100
  )
90
101
  op.alter_column(
91
102
  "generations",
@@ -93,6 +104,7 @@ def upgrade() -> None:
93
104
  existing_type=postgresql.JSONB(astext_type=sa.Text()),
94
105
  nullable=False,
95
106
  existing_server_default=sa.text("'{}'::jsonb"),
107
+ schema=SCHEMA,
96
108
  )
97
109
  op.alter_column(
98
110
  "generations",
@@ -100,6 +112,7 @@ def upgrade() -> None:
100
112
  existing_type=postgresql.ARRAY(sa.UUID()),
101
113
  nullable=False,
102
114
  existing_server_default=sa.text("'{}'::uuid[]"),
115
+ schema=SCHEMA,
103
116
  )
104
117
  op.alter_column(
105
118
  "generations",
@@ -107,6 +120,7 @@ def upgrade() -> None:
107
120
  existing_type=sa.NUMERIC(precision=5, scale=2),
108
121
  nullable=False,
109
122
  existing_server_default=sa.text("0.0"),
123
+ schema=SCHEMA,
110
124
  )
111
125
  op.alter_column(
112
126
  "generations",
@@ -114,6 +128,7 @@ def upgrade() -> None:
114
128
  existing_type=postgresql.TIMESTAMP(timezone=True),
115
129
  nullable=False,
116
130
  existing_server_default=sa.text("CURRENT_TIMESTAMP"),
131
+ schema=SCHEMA,
117
132
  )
118
133
  op.alter_column(
119
134
  "generations",
@@ -121,6 +136,7 @@ def upgrade() -> None:
121
136
  existing_type=postgresql.TIMESTAMP(timezone=True),
122
137
  nullable=False,
123
138
  existing_server_default=sa.text("CURRENT_TIMESTAMP"),
139
+ schema=SCHEMA,
124
140
  )
125
141
  op.alter_column(
126
142
  "lora_models",
@@ -128,6 +144,7 @@ def upgrade() -> None:
128
144
  existing_type=postgresql.JSONB(astext_type=sa.Text()),
129
145
  nullable=False,
130
146
  existing_server_default=sa.text("'{}'::jsonb"),
147
+ schema=SCHEMA,
131
148
  )
132
149
  op.alter_column(
133
150
  "lora_models",
@@ -135,6 +152,7 @@ def upgrade() -> None:
135
152
  existing_type=sa.BOOLEAN(),
136
153
  nullable=False,
137
154
  existing_server_default=sa.text("false"),
155
+ schema=SCHEMA,
138
156
  )
139
157
  op.alter_column(
140
158
  "lora_models",
@@ -142,6 +160,7 @@ def upgrade() -> None:
142
160
  existing_type=postgresql.TIMESTAMP(timezone=True),
143
161
  nullable=False,
144
162
  existing_server_default=sa.text("CURRENT_TIMESTAMP"),
163
+ schema=SCHEMA,
145
164
  )
146
165
  op.alter_column(
147
166
  "lora_models",
@@ -149,6 +168,7 @@ def upgrade() -> None:
149
168
  existing_type=postgresql.TIMESTAMP(timezone=True),
150
169
  nullable=False,
151
170
  existing_server_default=sa.text("CURRENT_TIMESTAMP"),
171
+ schema=SCHEMA,
152
172
  )
153
173
  op.alter_column(
154
174
  "provider_configs",
@@ -156,6 +176,7 @@ def upgrade() -> None:
156
176
  existing_type=sa.BOOLEAN(),
157
177
  nullable=False,
158
178
  existing_server_default=sa.text("true"),
179
+ schema=SCHEMA,
159
180
  )
160
181
  op.alter_column(
161
182
  "provider_configs",
@@ -163,6 +184,7 @@ def upgrade() -> None:
163
184
  existing_type=postgresql.TIMESTAMP(timezone=True),
164
185
  nullable=False,
165
186
  existing_server_default=sa.text("CURRENT_TIMESTAMP"),
187
+ schema=SCHEMA,
166
188
  )
167
189
  op.alter_column(
168
190
  "provider_configs",
@@ -170,6 +192,7 @@ def upgrade() -> None:
170
192
  existing_type=postgresql.TIMESTAMP(timezone=True),
171
193
  nullable=False,
172
194
  existing_server_default=sa.text("CURRENT_TIMESTAMP"),
195
+ schema=SCHEMA,
173
196
  )
174
197
  op.alter_column(
175
198
  "tenants",
@@ -177,6 +200,7 @@ def upgrade() -> None:
177
200
  existing_type=postgresql.JSONB(astext_type=sa.Text()),
178
201
  nullable=False,
179
202
  existing_server_default=sa.text("'{}'::jsonb"),
203
+ schema=SCHEMA,
180
204
  )
181
205
  op.alter_column(
182
206
  "tenants",
@@ -184,6 +208,7 @@ def upgrade() -> None:
184
208
  existing_type=postgresql.TIMESTAMP(timezone=True),
185
209
  nullable=False,
186
210
  existing_server_default=sa.text("CURRENT_TIMESTAMP"),
211
+ schema=SCHEMA,
187
212
  )
188
213
  op.alter_column(
189
214
  "tenants",
@@ -191,6 +216,7 @@ def upgrade() -> None:
191
216
  existing_type=postgresql.TIMESTAMP(timezone=True),
192
217
  nullable=False,
193
218
  existing_server_default=sa.text("CURRENT_TIMESTAMP"),
219
+ schema=SCHEMA,
194
220
  )
195
221
  op.alter_column(
196
222
  "users",
@@ -198,6 +224,7 @@ def upgrade() -> None:
198
224
  existing_type=postgresql.JSONB(astext_type=sa.Text()),
199
225
  nullable=False,
200
226
  existing_server_default=sa.text("'{}'::jsonb"),
227
+ schema=SCHEMA,
201
228
  )
202
229
  op.alter_column(
203
230
  "users",
@@ -205,6 +232,7 @@ def upgrade() -> None:
205
232
  existing_type=postgresql.TIMESTAMP(timezone=True),
206
233
  nullable=False,
207
234
  existing_server_default=sa.text("CURRENT_TIMESTAMP"),
235
+ schema=SCHEMA,
208
236
  )
209
237
  op.alter_column(
210
238
  "users",
@@ -212,6 +240,7 @@ def upgrade() -> None:
212
240
  existing_type=postgresql.TIMESTAMP(timezone=True),
213
241
  nullable=False,
214
242
  existing_server_default=sa.text("CURRENT_TIMESTAMP"),
243
+ schema=SCHEMA,
215
244
  )
216
245
  # ### end Alembic commands ###
217
246
 
@@ -225,6 +254,7 @@ def downgrade() -> None:
225
254
  existing_type=postgresql.TIMESTAMP(timezone=True),
226
255
  nullable=True,
227
256
  existing_server_default=sa.text("CURRENT_TIMESTAMP"),
257
+ schema=SCHEMA,
228
258
  )
229
259
  op.alter_column(
230
260
  "users",
@@ -232,6 +262,7 @@ def downgrade() -> None:
232
262
  existing_type=postgresql.TIMESTAMP(timezone=True),
233
263
  nullable=True,
234
264
  existing_server_default=sa.text("CURRENT_TIMESTAMP"),
265
+ schema=SCHEMA,
235
266
  )
236
267
  op.alter_column(
237
268
  "users",
@@ -239,6 +270,7 @@ def downgrade() -> None:
239
270
  existing_type=postgresql.JSONB(astext_type=sa.Text()),
240
271
  nullable=True,
241
272
  existing_server_default=sa.text("'{}'::jsonb"),
273
+ schema=SCHEMA,
242
274
  )
243
275
  op.alter_column(
244
276
  "tenants",
@@ -246,6 +278,7 @@ def downgrade() -> None:
246
278
  existing_type=postgresql.TIMESTAMP(timezone=True),
247
279
  nullable=True,
248
280
  existing_server_default=sa.text("CURRENT_TIMESTAMP"),
281
+ schema=SCHEMA,
249
282
  )
250
283
  op.alter_column(
251
284
  "tenants",
@@ -253,6 +286,7 @@ def downgrade() -> None:
253
286
  existing_type=postgresql.TIMESTAMP(timezone=True),
254
287
  nullable=True,
255
288
  existing_server_default=sa.text("CURRENT_TIMESTAMP"),
289
+ schema=SCHEMA,
256
290
  )
257
291
  op.alter_column(
258
292
  "tenants",
@@ -260,6 +294,7 @@ def downgrade() -> None:
260
294
  existing_type=postgresql.JSONB(astext_type=sa.Text()),
261
295
  nullable=True,
262
296
  existing_server_default=sa.text("'{}'::jsonb"),
297
+ schema=SCHEMA,
263
298
  )
264
299
  op.alter_column(
265
300
  "provider_configs",
@@ -267,6 +302,7 @@ def downgrade() -> None:
267
302
  existing_type=postgresql.TIMESTAMP(timezone=True),
268
303
  nullable=True,
269
304
  existing_server_default=sa.text("CURRENT_TIMESTAMP"),
305
+ schema=SCHEMA,
270
306
  )
271
307
  op.alter_column(
272
308
  "provider_configs",
@@ -274,6 +310,7 @@ def downgrade() -> None:
274
310
  existing_type=postgresql.TIMESTAMP(timezone=True),
275
311
  nullable=True,
276
312
  existing_server_default=sa.text("CURRENT_TIMESTAMP"),
313
+ schema=SCHEMA,
277
314
  )
278
315
  op.alter_column(
279
316
  "provider_configs",
@@ -281,6 +318,7 @@ def downgrade() -> None:
281
318
  existing_type=sa.BOOLEAN(),
282
319
  nullable=True,
283
320
  existing_server_default=sa.text("true"),
321
+ schema=SCHEMA,
284
322
  )
285
323
  op.alter_column(
286
324
  "lora_models",
@@ -288,6 +326,7 @@ def downgrade() -> None:
288
326
  existing_type=postgresql.TIMESTAMP(timezone=True),
289
327
  nullable=True,
290
328
  existing_server_default=sa.text("CURRENT_TIMESTAMP"),
329
+ schema=SCHEMA,
291
330
  )
292
331
  op.alter_column(
293
332
  "lora_models",
@@ -295,6 +334,7 @@ def downgrade() -> None:
295
334
  existing_type=postgresql.TIMESTAMP(timezone=True),
296
335
  nullable=True,
297
336
  existing_server_default=sa.text("CURRENT_TIMESTAMP"),
337
+ schema=SCHEMA,
298
338
  )
299
339
  op.alter_column(
300
340
  "lora_models",
@@ -302,6 +342,7 @@ def downgrade() -> None:
302
342
  existing_type=sa.BOOLEAN(),
303
343
  nullable=True,
304
344
  existing_server_default=sa.text("false"),
345
+ schema=SCHEMA,
305
346
  )
306
347
  op.alter_column(
307
348
  "lora_models",
@@ -309,6 +350,7 @@ def downgrade() -> None:
309
350
  existing_type=postgresql.JSONB(astext_type=sa.Text()),
310
351
  nullable=True,
311
352
  existing_server_default=sa.text("'{}'::jsonb"),
353
+ schema=SCHEMA,
312
354
  )
313
355
  op.alter_column(
314
356
  "generations",
@@ -316,6 +358,7 @@ def downgrade() -> None:
316
358
  existing_type=postgresql.TIMESTAMP(timezone=True),
317
359
  nullable=True,
318
360
  existing_server_default=sa.text("CURRENT_TIMESTAMP"),
361
+ schema=SCHEMA,
319
362
  )
320
363
  op.alter_column(
321
364
  "generations",
@@ -323,6 +366,7 @@ def downgrade() -> None:
323
366
  existing_type=postgresql.TIMESTAMP(timezone=True),
324
367
  nullable=True,
325
368
  existing_server_default=sa.text("CURRENT_TIMESTAMP"),
369
+ schema=SCHEMA,
326
370
  )
327
371
  op.alter_column(
328
372
  "generations",
@@ -330,6 +374,7 @@ def downgrade() -> None:
330
374
  existing_type=sa.NUMERIC(precision=5, scale=2),
331
375
  nullable=True,
332
376
  existing_server_default=sa.text("0.0"),
377
+ schema=SCHEMA,
333
378
  )
334
379
  op.alter_column(
335
380
  "generations",
@@ -337,6 +382,7 @@ def downgrade() -> None:
337
382
  existing_type=postgresql.ARRAY(sa.UUID()),
338
383
  nullable=True,
339
384
  existing_server_default=sa.text("'{}'::uuid[]"),
385
+ schema=SCHEMA,
340
386
  )
341
387
  op.alter_column(
342
388
  "generations",
@@ -344,6 +390,7 @@ def downgrade() -> None:
344
390
  existing_type=postgresql.JSONB(astext_type=sa.Text()),
345
391
  nullable=True,
346
392
  existing_server_default=sa.text("'{}'::jsonb"),
393
+ schema=SCHEMA,
347
394
  )
348
395
  op.alter_column(
349
396
  "generations",
@@ -351,6 +398,7 @@ def downgrade() -> None:
351
398
  existing_type=postgresql.JSONB(astext_type=sa.Text()),
352
399
  nullable=True,
353
400
  existing_server_default=sa.text("'[]'::jsonb"),
401
+ schema=SCHEMA,
354
402
  )
355
403
  op.alter_column(
356
404
  "credit_transactions",
@@ -358,6 +406,7 @@ def downgrade() -> None:
358
406
  existing_type=postgresql.TIMESTAMP(timezone=True),
359
407
  nullable=True,
360
408
  existing_server_default=sa.text("CURRENT_TIMESTAMP"),
409
+ schema=SCHEMA,
361
410
  )
362
411
  op.alter_column(
363
412
  "credit_transactions",
@@ -365,6 +414,7 @@ def downgrade() -> None:
365
414
  existing_type=postgresql.JSONB(astext_type=sa.Text()),
366
415
  nullable=True,
367
416
  existing_server_default=sa.text("'{}'::jsonb"),
417
+ schema=SCHEMA,
368
418
  )
369
419
  op.alter_column(
370
420
  "boards",
@@ -372,6 +422,7 @@ def downgrade() -> None:
372
422
  existing_type=postgresql.TIMESTAMP(timezone=True),
373
423
  nullable=True,
374
424
  existing_server_default=sa.text("CURRENT_TIMESTAMP"),
425
+ schema=SCHEMA,
375
426
  )
376
427
  op.alter_column(
377
428
  "boards",
@@ -379,6 +430,7 @@ def downgrade() -> None:
379
430
  existing_type=postgresql.TIMESTAMP(timezone=True),
380
431
  nullable=True,
381
432
  existing_server_default=sa.text("CURRENT_TIMESTAMP"),
433
+ schema=SCHEMA,
382
434
  )
383
435
  op.alter_column(
384
436
  "boards",
@@ -386,6 +438,7 @@ def downgrade() -> None:
386
438
  existing_type=postgresql.JSONB(astext_type=sa.Text()),
387
439
  nullable=True,
388
440
  existing_server_default=sa.text("'{}'::jsonb"),
441
+ schema=SCHEMA,
389
442
  )
390
443
  op.alter_column(
391
444
  "boards",
@@ -393,6 +446,7 @@ def downgrade() -> None:
393
446
  existing_type=postgresql.JSONB(astext_type=sa.Text()),
394
447
  nullable=True,
395
448
  existing_server_default=sa.text("'{}'::jsonb"),
449
+ schema=SCHEMA,
396
450
  )
397
451
  op.alter_column(
398
452
  "boards",
@@ -400,6 +454,7 @@ def downgrade() -> None:
400
454
  existing_type=sa.BOOLEAN(),
401
455
  nullable=True,
402
456
  existing_server_default=sa.text("false"),
457
+ schema=SCHEMA,
403
458
  )
404
459
  op.alter_column(
405
460
  "board_members",
@@ -407,5 +462,6 @@ def downgrade() -> None:
407
462
  existing_type=postgresql.TIMESTAMP(timezone=True),
408
463
  nullable=True,
409
464
  existing_server_default=sa.text("CURRENT_TIMESTAMP"),
465
+ schema=SCHEMA,
410
466
  )
411
467
  # ### end Alembic commands ###
@@ -0,0 +1,134 @@
1
+ """add artifact lineage with input_artifacts
2
+
3
+ Revision ID: add_artifact_lineage
4
+ Revises: cdad231052d5
5
+ Create Date: 2025-12-02 00:00:00.000000
6
+
7
+ """
8
+
9
+ from typing import Sequence, Union
10
+
11
+ from alembic import op
12
+ import sqlalchemy as sa
13
+ from sqlalchemy.dialects import postgresql
14
+
15
+ # revision identifiers, used by Alembic.
16
+ revision: str = "add_artifact_lineage"
17
+ down_revision: Union[str, Sequence[str], None] = "cdad231052d5"
18
+ branch_labels: Union[str, Sequence[str], None] = None
19
+ depends_on: Union[str, Sequence[str], None] = None
20
+
21
+ # Schema name for all Boards tables
22
+ SCHEMA = "boards"
23
+
24
+
25
+ def upgrade() -> None:
26
+ """Upgrade schema to use input_artifacts for lineage tracking."""
27
+ # Add input_artifacts JSONB column
28
+ op.add_column(
29
+ "generations",
30
+ sa.Column(
31
+ "input_artifacts",
32
+ postgresql.JSONB(astext_type=sa.Text()),
33
+ server_default=sa.text("'[]'::jsonb"),
34
+ nullable=False,
35
+ ),
36
+ schema=SCHEMA,
37
+ )
38
+
39
+ # Migrate existing data from input_generation_ids to input_artifacts
40
+ # For backwards compatibility, use role="input" for legacy data
41
+ op.execute(f"""
42
+ UPDATE {SCHEMA}.generations
43
+ SET input_artifacts = (
44
+ SELECT COALESCE(jsonb_agg(
45
+ jsonb_build_object(
46
+ 'generation_id', gen_id::text,
47
+ 'role', 'input',
48
+ 'artifact_type', COALESCE(g.artifact_type, 'unknown')
49
+ )
50
+ ), '[]'::jsonb)
51
+ FROM unnest(input_generation_ids) AS gen_id
52
+ LEFT JOIN {SCHEMA}.generations g ON g.id = gen_id
53
+ )
54
+ WHERE input_generation_ids IS NOT NULL
55
+ AND array_length(input_generation_ids, 1) > 0
56
+ """)
57
+
58
+ # Add GIN index for JSONB queries
59
+ op.create_index(
60
+ "idx_generations_input_artifacts_gin",
61
+ "generations",
62
+ ["input_artifacts"],
63
+ unique=False,
64
+ postgresql_using="gin",
65
+ schema=SCHEMA,
66
+ )
67
+
68
+ # Drop old lineage index
69
+ op.drop_index("idx_generations_lineage", table_name="generations", schema=SCHEMA)
70
+
71
+ # Drop old foreign key constraint on parent_generation_id
72
+ op.drop_constraint(
73
+ "generations_parent_generation_id_fkey",
74
+ "generations",
75
+ type_="foreignkey",
76
+ schema=SCHEMA,
77
+ )
78
+
79
+ # Drop old columns
80
+ op.drop_column("generations", "parent_generation_id", schema=SCHEMA)
81
+ op.drop_column("generations", "input_generation_ids", schema=SCHEMA)
82
+
83
+
84
+ def downgrade() -> None:
85
+ """Downgrade schema back to parent_generation_id and input_generation_ids."""
86
+ # Add back old columns
87
+ op.add_column(
88
+ "generations",
89
+ sa.Column(
90
+ "input_generation_ids",
91
+ postgresql.ARRAY(postgresql.UUID()),
92
+ server_default=sa.text("'{}'::uuid[]"),
93
+ autoincrement=False,
94
+ nullable=False,
95
+ ),
96
+ schema=SCHEMA,
97
+ )
98
+ op.add_column(
99
+ "generations",
100
+ sa.Column("parent_generation_id", postgresql.UUID(), autoincrement=False, nullable=True),
101
+ schema=SCHEMA,
102
+ )
103
+
104
+ # Migrate data back from input_artifacts to input_generation_ids
105
+ op.execute(f"""
106
+ UPDATE {SCHEMA}.generations
107
+ SET input_generation_ids = (
108
+ SELECT COALESCE(
109
+ array_agg((elem->>'generation_id')::uuid),
110
+ '{{}}'::uuid[]
111
+ )
112
+ FROM jsonb_array_elements(input_artifacts) elem
113
+ )
114
+ WHERE input_artifacts IS NOT NULL
115
+ AND jsonb_array_length(input_artifacts) > 0
116
+ """)
117
+
118
+ # Recreate foreign key constraint
119
+ op.create_foreign_key(
120
+ "generations_parent_generation_id_fkey",
121
+ "generations",
122
+ "generations",
123
+ ["parent_generation_id"],
124
+ ["id"],
125
+ source_schema=SCHEMA,
126
+ referent_schema=SCHEMA,
127
+ )
128
+
129
+ # Recreate old index
130
+ op.create_index("idx_generations_lineage", "generations", ["parent_generation_id"], unique=False, schema=SCHEMA)
131
+
132
+ # Drop new index and column
133
+ op.drop_index("idx_generations_input_artifacts_gin", table_name="generations", schema=SCHEMA)
134
+ op.drop_column("generations", "input_artifacts", schema=SCHEMA)
@@ -24,6 +24,9 @@ down_revision: str | Sequence[str] | None = '20250101_000000_initial_schema'
24
24
  branch_labels: str | Sequence[str] | None = None
25
25
  depends_on: str | Sequence[str] | None = None
26
26
 
27
+ # Schema name for all Boards tables
28
+ SCHEMA = "boards"
29
+
27
30
 
28
31
  def upgrade() -> None:
29
32
  """Upgrade schema and seed default tenant."""
@@ -37,16 +40,16 @@ def upgrade() -> None:
37
40
 
38
41
  # Check if tenant already exists
39
42
  existing_tenant = connection.execute(
40
- sa.text("SELECT id FROM tenants WHERE slug = :slug"),
43
+ sa.text(f"SELECT id FROM {SCHEMA}.tenants WHERE slug = :slug"),
41
44
  {"slug": tenant_slug}
42
45
  ).fetchone()
43
46
 
44
47
  if not existing_tenant:
45
48
  # Insert the default tenant
46
49
  connection.execute(
47
- sa.text("""
48
- INSERT INTO tenants (name, slug, settings, created_at, updated_at)
49
- VALUES (:name, :slug, '{}', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
50
+ sa.text(f"""
51
+ INSERT INTO {SCHEMA}.tenants (name, slug, settings, created_at, updated_at)
52
+ VALUES (:name, :slug, '{{}}'::jsonb, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
50
53
  """),
51
54
  {
52
55
  "name": tenant_name,
@@ -68,7 +71,7 @@ def downgrade() -> None:
68
71
  connection = op.get_bind()
69
72
 
70
73
  result = connection.execute(
71
- sa.text("DELETE FROM tenants WHERE slug = :slug"),
74
+ sa.text(f"DELETE FROM {SCHEMA}.tenants WHERE slug = :slug"),
72
75
  {"slug": tenant_slug}
73
76
  )
74
77