@weirdfingers/baseboards 0.2.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 (139) hide show
  1. package/README.md +191 -0
  2. package/dist/index.d.ts +1 -0
  3. package/dist/index.js +887 -0
  4. package/dist/index.js.map +1 -0
  5. package/package.json +64 -0
  6. package/templates/README.md +120 -0
  7. package/templates/api/.env.example +62 -0
  8. package/templates/api/Dockerfile +32 -0
  9. package/templates/api/README.md +132 -0
  10. package/templates/api/alembic/env.py +106 -0
  11. package/templates/api/alembic/script.py.mako +28 -0
  12. package/templates/api/alembic/versions/20250101_000000_initial_schema.py +448 -0
  13. package/templates/api/alembic/versions/20251022_174729_remove_provider_name_from_generations.py +71 -0
  14. package/templates/api/alembic/versions/20251023_165852_switch_to_declarative_base_and_mapping.py +411 -0
  15. package/templates/api/alembic/versions/2025925_62735_add_seed_data_for_default_tenant.py +85 -0
  16. package/templates/api/alembic.ini +36 -0
  17. package/templates/api/config/generators.yaml +25 -0
  18. package/templates/api/config/storage_config.yaml +26 -0
  19. package/templates/api/docs/ADDING_GENERATORS.md +409 -0
  20. package/templates/api/docs/GENERATORS_API.md +502 -0
  21. package/templates/api/docs/MIGRATIONS.md +472 -0
  22. package/templates/api/docs/storage_providers.md +337 -0
  23. package/templates/api/pyproject.toml +165 -0
  24. package/templates/api/src/boards/__init__.py +10 -0
  25. package/templates/api/src/boards/api/app.py +171 -0
  26. package/templates/api/src/boards/api/auth.py +75 -0
  27. package/templates/api/src/boards/api/endpoints/__init__.py +3 -0
  28. package/templates/api/src/boards/api/endpoints/jobs.py +76 -0
  29. package/templates/api/src/boards/api/endpoints/setup.py +505 -0
  30. package/templates/api/src/boards/api/endpoints/sse.py +129 -0
  31. package/templates/api/src/boards/api/endpoints/storage.py +74 -0
  32. package/templates/api/src/boards/api/endpoints/tenant_registration.py +296 -0
  33. package/templates/api/src/boards/api/endpoints/webhooks.py +13 -0
  34. package/templates/api/src/boards/auth/__init__.py +15 -0
  35. package/templates/api/src/boards/auth/adapters/__init__.py +20 -0
  36. package/templates/api/src/boards/auth/adapters/auth0.py +220 -0
  37. package/templates/api/src/boards/auth/adapters/base.py +73 -0
  38. package/templates/api/src/boards/auth/adapters/clerk.py +172 -0
  39. package/templates/api/src/boards/auth/adapters/jwt.py +122 -0
  40. package/templates/api/src/boards/auth/adapters/none.py +102 -0
  41. package/templates/api/src/boards/auth/adapters/oidc.py +284 -0
  42. package/templates/api/src/boards/auth/adapters/supabase.py +110 -0
  43. package/templates/api/src/boards/auth/context.py +35 -0
  44. package/templates/api/src/boards/auth/factory.py +115 -0
  45. package/templates/api/src/boards/auth/middleware.py +221 -0
  46. package/templates/api/src/boards/auth/provisioning.py +129 -0
  47. package/templates/api/src/boards/auth/tenant_extraction.py +278 -0
  48. package/templates/api/src/boards/cli.py +354 -0
  49. package/templates/api/src/boards/config.py +116 -0
  50. package/templates/api/src/boards/database/__init__.py +7 -0
  51. package/templates/api/src/boards/database/cli.py +110 -0
  52. package/templates/api/src/boards/database/connection.py +252 -0
  53. package/templates/api/src/boards/database/models.py +19 -0
  54. package/templates/api/src/boards/database/seed_data.py +182 -0
  55. package/templates/api/src/boards/dbmodels/__init__.py +455 -0
  56. package/templates/api/src/boards/generators/__init__.py +57 -0
  57. package/templates/api/src/boards/generators/artifacts.py +53 -0
  58. package/templates/api/src/boards/generators/base.py +140 -0
  59. package/templates/api/src/boards/generators/implementations/__init__.py +12 -0
  60. package/templates/api/src/boards/generators/implementations/audio/__init__.py +3 -0
  61. package/templates/api/src/boards/generators/implementations/audio/whisper.py +66 -0
  62. package/templates/api/src/boards/generators/implementations/image/__init__.py +3 -0
  63. package/templates/api/src/boards/generators/implementations/image/dalle3.py +93 -0
  64. package/templates/api/src/boards/generators/implementations/image/flux_pro.py +85 -0
  65. package/templates/api/src/boards/generators/implementations/video/__init__.py +3 -0
  66. package/templates/api/src/boards/generators/implementations/video/lipsync.py +70 -0
  67. package/templates/api/src/boards/generators/loader.py +253 -0
  68. package/templates/api/src/boards/generators/registry.py +114 -0
  69. package/templates/api/src/boards/generators/resolution.py +515 -0
  70. package/templates/api/src/boards/generators/testmods/class_gen.py +34 -0
  71. package/templates/api/src/boards/generators/testmods/import_side_effect.py +35 -0
  72. package/templates/api/src/boards/graphql/__init__.py +7 -0
  73. package/templates/api/src/boards/graphql/access_control.py +136 -0
  74. package/templates/api/src/boards/graphql/mutations/root.py +136 -0
  75. package/templates/api/src/boards/graphql/queries/root.py +116 -0
  76. package/templates/api/src/boards/graphql/resolvers/__init__.py +8 -0
  77. package/templates/api/src/boards/graphql/resolvers/auth.py +12 -0
  78. package/templates/api/src/boards/graphql/resolvers/board.py +1055 -0
  79. package/templates/api/src/boards/graphql/resolvers/generation.py +889 -0
  80. package/templates/api/src/boards/graphql/resolvers/generator.py +50 -0
  81. package/templates/api/src/boards/graphql/resolvers/user.py +25 -0
  82. package/templates/api/src/boards/graphql/schema.py +81 -0
  83. package/templates/api/src/boards/graphql/types/board.py +102 -0
  84. package/templates/api/src/boards/graphql/types/generation.py +130 -0
  85. package/templates/api/src/boards/graphql/types/generator.py +17 -0
  86. package/templates/api/src/boards/graphql/types/user.py +47 -0
  87. package/templates/api/src/boards/jobs/repository.py +104 -0
  88. package/templates/api/src/boards/logging.py +195 -0
  89. package/templates/api/src/boards/middleware.py +339 -0
  90. package/templates/api/src/boards/progress/__init__.py +4 -0
  91. package/templates/api/src/boards/progress/models.py +25 -0
  92. package/templates/api/src/boards/progress/publisher.py +64 -0
  93. package/templates/api/src/boards/py.typed +0 -0
  94. package/templates/api/src/boards/redis_pool.py +118 -0
  95. package/templates/api/src/boards/storage/__init__.py +52 -0
  96. package/templates/api/src/boards/storage/base.py +363 -0
  97. package/templates/api/src/boards/storage/config.py +187 -0
  98. package/templates/api/src/boards/storage/factory.py +278 -0
  99. package/templates/api/src/boards/storage/implementations/__init__.py +27 -0
  100. package/templates/api/src/boards/storage/implementations/gcs.py +340 -0
  101. package/templates/api/src/boards/storage/implementations/local.py +201 -0
  102. package/templates/api/src/boards/storage/implementations/s3.py +294 -0
  103. package/templates/api/src/boards/storage/implementations/supabase.py +218 -0
  104. package/templates/api/src/boards/tenant_isolation.py +446 -0
  105. package/templates/api/src/boards/validation.py +262 -0
  106. package/templates/api/src/boards/workers/__init__.py +1 -0
  107. package/templates/api/src/boards/workers/actors.py +201 -0
  108. package/templates/api/src/boards/workers/cli.py +125 -0
  109. package/templates/api/src/boards/workers/context.py +188 -0
  110. package/templates/api/src/boards/workers/middleware.py +58 -0
  111. package/templates/api/src/py.typed +0 -0
  112. package/templates/compose.dev.yaml +39 -0
  113. package/templates/compose.yaml +109 -0
  114. package/templates/docker/env.example +23 -0
  115. package/templates/web/.env.example +28 -0
  116. package/templates/web/Dockerfile +51 -0
  117. package/templates/web/components.json +22 -0
  118. package/templates/web/imageLoader.js +18 -0
  119. package/templates/web/next-env.d.ts +5 -0
  120. package/templates/web/next.config.js +36 -0
  121. package/templates/web/package.json +37 -0
  122. package/templates/web/postcss.config.mjs +7 -0
  123. package/templates/web/public/favicon.ico +0 -0
  124. package/templates/web/src/app/boards/[boardId]/page.tsx +232 -0
  125. package/templates/web/src/app/globals.css +120 -0
  126. package/templates/web/src/app/layout.tsx +21 -0
  127. package/templates/web/src/app/page.tsx +35 -0
  128. package/templates/web/src/app/providers.tsx +18 -0
  129. package/templates/web/src/components/boards/ArtifactInputSlots.tsx +142 -0
  130. package/templates/web/src/components/boards/ArtifactPreview.tsx +125 -0
  131. package/templates/web/src/components/boards/GenerationGrid.tsx +45 -0
  132. package/templates/web/src/components/boards/GenerationInput.tsx +251 -0
  133. package/templates/web/src/components/boards/GeneratorSelector.tsx +89 -0
  134. package/templates/web/src/components/header.tsx +30 -0
  135. package/templates/web/src/components/ui/button.tsx +58 -0
  136. package/templates/web/src/components/ui/card.tsx +92 -0
  137. package/templates/web/src/components/ui/navigation-menu.tsx +168 -0
  138. package/templates/web/src/lib/utils.ts +6 -0
  139. package/templates/web/tsconfig.json +47 -0
@@ -0,0 +1,455 @@
1
+ """
2
+ Database models for Boards (authoritative ORM definitions).
3
+
4
+ This module defines the SQLAlchemy Base with a naming convention for stable
5
+ Alembic autogenerate diffs, and exposes `target_metadata` for Alembic.
6
+ """
7
+
8
+ from datetime import datetime
9
+ from decimal import Decimal
10
+ from typing import Any
11
+ from uuid import UUID
12
+
13
+ from sqlalchemy import (
14
+ ARRAY,
15
+ Boolean,
16
+ CheckConstraint,
17
+ Column,
18
+ DateTime,
19
+ ForeignKeyConstraint,
20
+ Index,
21
+ MetaData,
22
+ Numeric,
23
+ PrimaryKeyConstraint,
24
+ String,
25
+ Text,
26
+ UniqueConstraint,
27
+ Uuid,
28
+ text,
29
+ )
30
+ from sqlalchemy.dialects.postgresql import JSONB
31
+ from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, relationship
32
+
33
+ # Naming convention for deterministic constraint/index names in Alembic diffs
34
+ naming_convention = {
35
+ "ix": "ix_%(column_0_label)s",
36
+ "uq": "uq_%(table_name)s_%(column_0_name)s",
37
+ "ck": "ck_%(table_name)s_%(constraint_name)s",
38
+ "fk": "fk_%(table_name)s_%(column_0_name)s_%(referred_table_name)s",
39
+ "pk": "pk_%(table_name)s",
40
+ }
41
+
42
+
43
+ class Base(DeclarativeBase):
44
+ """Base class for all database models with type checking support."""
45
+
46
+ metadata = MetaData(naming_convention=naming_convention)
47
+
48
+
49
+ class Tenants(Base):
50
+ __tablename__ = "tenants"
51
+ __table_args__ = (
52
+ PrimaryKeyConstraint("id", name="tenants_pkey"),
53
+ UniqueConstraint("slug", name="tenants_slug_key"),
54
+ )
55
+
56
+ id: Mapped[UUID] = mapped_column(Uuid, server_default=text("uuid_generate_v4()"))
57
+ name: Mapped[str] = mapped_column(String(255), nullable=False)
58
+ slug: Mapped[str] = mapped_column(String(255), nullable=False)
59
+ settings: Mapped[dict[str, Any]] = mapped_column(JSONB, server_default=text("'{}'::jsonb"))
60
+ created_at: Mapped[datetime] = mapped_column(
61
+ DateTime(True), server_default=text("CURRENT_TIMESTAMP")
62
+ )
63
+ updated_at: Mapped[datetime] = mapped_column(
64
+ DateTime(True), server_default=text("CURRENT_TIMESTAMP")
65
+ )
66
+
67
+ provider_configs: Mapped[list["ProviderConfigs"]] = relationship(
68
+ "ProviderConfigs", uselist=True, back_populates="tenant"
69
+ )
70
+ users: Mapped[list["Users"]] = relationship("Users", uselist=True, back_populates="tenant")
71
+ boards: Mapped[list["Boards"]] = relationship("Boards", uselist=True, back_populates="tenant")
72
+ lora_models: Mapped[list["LoraModels"]] = relationship(
73
+ "LoraModels", uselist=True, back_populates="tenant"
74
+ )
75
+ generations: Mapped[list["Generations"]] = relationship(
76
+ "Generations", uselist=True, back_populates="tenant"
77
+ )
78
+ credit_transactions: Mapped[list["CreditTransactions"]] = relationship(
79
+ "CreditTransactions", uselist=True, back_populates="tenant"
80
+ )
81
+
82
+
83
+ class ProviderConfigs(Base):
84
+ __tablename__ = "provider_configs"
85
+ __table_args__ = (
86
+ ForeignKeyConstraint(
87
+ ["tenant_id"],
88
+ ["tenants.id"],
89
+ ondelete="CASCADE",
90
+ name="provider_configs_tenant_id_fkey",
91
+ ),
92
+ PrimaryKeyConstraint("id", name="provider_configs_pkey"),
93
+ UniqueConstraint(
94
+ "tenant_id",
95
+ "provider_name",
96
+ name="provider_configs_tenant_id_provider_name_key",
97
+ ),
98
+ )
99
+
100
+ id: Mapped[UUID] = mapped_column(Uuid, server_default=text("uuid_generate_v4()"))
101
+ tenant_id: Mapped[UUID] = mapped_column(Uuid, nullable=False)
102
+ provider_name: Mapped[str] = mapped_column(String(100), nullable=False)
103
+ config: Mapped[dict[str, Any]] = mapped_column(JSONB, nullable=False)
104
+ is_enabled: Mapped[bool] = mapped_column(Boolean, server_default=text("true"))
105
+ created_at: Mapped[datetime] = mapped_column(
106
+ DateTime(True), server_default=text("CURRENT_TIMESTAMP")
107
+ )
108
+ updated_at: Mapped[datetime] = mapped_column(
109
+ DateTime(True), server_default=text("CURRENT_TIMESTAMP")
110
+ )
111
+
112
+ tenant: Mapped["Tenants"] = relationship("Tenants", back_populates="provider_configs")
113
+
114
+
115
+ class Users(Base):
116
+ __tablename__ = "users"
117
+ __table_args__ = (
118
+ ForeignKeyConstraint(
119
+ ["tenant_id"],
120
+ ["tenants.id"],
121
+ ondelete="CASCADE",
122
+ name="users_tenant_id_fkey",
123
+ ),
124
+ PrimaryKeyConstraint("id", name="users_pkey"),
125
+ UniqueConstraint(
126
+ "tenant_id",
127
+ "auth_provider",
128
+ "auth_subject",
129
+ name="users_tenant_id_auth_provider_auth_subject_key",
130
+ ),
131
+ Index("idx_users_auth", "auth_provider", "auth_subject"),
132
+ Index("idx_users_tenant", "tenant_id"),
133
+ )
134
+
135
+ id: Mapped[UUID] = mapped_column(Uuid, server_default=text("uuid_generate_v4()"))
136
+ tenant_id: Mapped[UUID] = mapped_column(Uuid, nullable=False)
137
+ auth_provider: Mapped[str] = mapped_column(String(50), nullable=False)
138
+ auth_subject: Mapped[str] = mapped_column(String(255), nullable=False)
139
+ email: Mapped[str | None] = mapped_column(String(255))
140
+ display_name: Mapped[str | None] = mapped_column(String(255))
141
+ avatar_url: Mapped[str | None] = mapped_column(Text)
142
+ metadata_: Mapped[dict[str, Any]] = mapped_column(
143
+ "metadata", JSONB, server_default=text("'{}'::jsonb")
144
+ )
145
+ created_at: Mapped[datetime] = mapped_column(
146
+ DateTime(True), server_default=text("CURRENT_TIMESTAMP")
147
+ )
148
+ updated_at: Mapped[datetime] = mapped_column(
149
+ DateTime(True), server_default=text("CURRENT_TIMESTAMP")
150
+ )
151
+
152
+ tenant: Mapped["Tenants"] = relationship("Tenants", back_populates="users")
153
+ boards: Mapped[list["Boards"]] = relationship("Boards", uselist=True, back_populates="owner")
154
+ lora_models: Mapped[list["LoraModels"]] = relationship(
155
+ "LoraModels", uselist=True, back_populates="user"
156
+ )
157
+ board_members: Mapped[list["BoardMembers"]] = relationship(
158
+ "BoardMembers",
159
+ uselist=True,
160
+ foreign_keys="[BoardMembers.invited_by]",
161
+ back_populates="users",
162
+ )
163
+ board_members_: Mapped[list["BoardMembers"]] = relationship(
164
+ "BoardMembers",
165
+ uselist=True,
166
+ foreign_keys="[BoardMembers.user_id]",
167
+ back_populates="user",
168
+ )
169
+ generations: Mapped[list["Generations"]] = relationship(
170
+ "Generations", uselist=True, back_populates="user"
171
+ )
172
+ credit_transactions: Mapped[list["CreditTransactions"]] = relationship(
173
+ "CreditTransactions", uselist=True, back_populates="user"
174
+ )
175
+
176
+
177
+ class Boards(Base):
178
+ __tablename__ = "boards"
179
+ __table_args__ = (
180
+ ForeignKeyConstraint(
181
+ ["owner_id"], ["users.id"], ondelete="CASCADE", name="boards_owner_id_fkey"
182
+ ),
183
+ ForeignKeyConstraint(
184
+ ["tenant_id"],
185
+ ["tenants.id"],
186
+ ondelete="CASCADE",
187
+ name="boards_tenant_id_fkey",
188
+ ),
189
+ PrimaryKeyConstraint("id", name="boards_pkey"),
190
+ Index("idx_boards_owner", "owner_id"),
191
+ Index("idx_boards_tenant", "tenant_id"),
192
+ )
193
+
194
+ id: Mapped[UUID] = mapped_column(Uuid, server_default=text("uuid_generate_v4()"))
195
+ tenant_id: Mapped[UUID] = mapped_column(Uuid, nullable=False)
196
+ owner_id: Mapped[UUID] = mapped_column(Uuid, nullable=False)
197
+ title: Mapped[str] = mapped_column(String(255), nullable=False)
198
+ description: Mapped[str | None] = mapped_column(Text)
199
+ is_public: Mapped[bool] = mapped_column(Boolean, server_default=text("false"))
200
+ settings: Mapped[dict[str, Any]] = mapped_column(JSONB, server_default=text("'{}'::jsonb"))
201
+ metadata_: Mapped[dict[str, Any]] = mapped_column(
202
+ "metadata", JSONB, server_default=text("'{}'::jsonb")
203
+ )
204
+ created_at: Mapped[datetime] = mapped_column(
205
+ DateTime(True), server_default=text("CURRENT_TIMESTAMP")
206
+ )
207
+ updated_at: Mapped[datetime] = mapped_column(
208
+ DateTime(True), server_default=text("CURRENT_TIMESTAMP")
209
+ )
210
+
211
+ owner: Mapped["Users"] = relationship("Users", back_populates="boards")
212
+ tenant: Mapped["Tenants"] = relationship("Tenants", back_populates="boards")
213
+ board_members: Mapped[list["BoardMembers"]] = relationship(
214
+ "BoardMembers", uselist=True, back_populates="board"
215
+ )
216
+ generations: Mapped[list["Generations"]] = relationship(
217
+ "Generations", uselist=True, back_populates="board"
218
+ )
219
+
220
+
221
+ class LoraModels(Base):
222
+ __tablename__ = "lora_models"
223
+ __table_args__ = (
224
+ ForeignKeyConstraint(
225
+ ["tenant_id"],
226
+ ["tenants.id"],
227
+ ondelete="CASCADE",
228
+ name="lora_models_tenant_id_fkey",
229
+ ),
230
+ ForeignKeyConstraint(
231
+ ["user_id"],
232
+ ["users.id"],
233
+ ondelete="CASCADE",
234
+ name="lora_models_user_id_fkey",
235
+ ),
236
+ PrimaryKeyConstraint("id", name="lora_models_pkey"),
237
+ )
238
+
239
+ id: Mapped[UUID] = mapped_column(Uuid, server_default=text("uuid_generate_v4()"))
240
+ tenant_id: Mapped[UUID] = mapped_column(Uuid, nullable=False)
241
+ user_id: Mapped[UUID] = mapped_column(Uuid, nullable=False)
242
+ name: Mapped[str] = mapped_column(String(255), nullable=False)
243
+ base_model: Mapped[str] = mapped_column(String(100), nullable=False)
244
+ storage_url: Mapped[str] = mapped_column(Text, nullable=False)
245
+ config: Mapped[dict[str, Any]] = mapped_column(JSONB, nullable=False)
246
+ trigger_word: Mapped[str | None] = mapped_column(String(100))
247
+ metadata_: Mapped[dict[str, Any]] = mapped_column(
248
+ "metadata", JSONB, server_default=text("'{}'::jsonb")
249
+ )
250
+ is_public: Mapped[bool] = mapped_column(Boolean, server_default=text("false"))
251
+ created_at: Mapped[datetime] = mapped_column(
252
+ DateTime(True), server_default=text("CURRENT_TIMESTAMP")
253
+ )
254
+ updated_at: Mapped[datetime] = mapped_column(
255
+ DateTime(True), server_default=text("CURRENT_TIMESTAMP")
256
+ )
257
+
258
+ tenant: Mapped["Tenants"] = relationship("Tenants", back_populates="lora_models")
259
+ user: Mapped["Users"] = relationship("Users", back_populates="lora_models")
260
+
261
+
262
+ class BoardMembers(Base):
263
+ __tablename__ = "board_members"
264
+ __table_args__ = (
265
+ CheckConstraint(
266
+ "role::text = ANY (ARRAY["
267
+ "'viewer'::character varying, 'editor'::character varying, "
268
+ "'admin'::character varying]::text[])",
269
+ name="board_members_role_check",
270
+ ),
271
+ ForeignKeyConstraint(
272
+ ["board_id"],
273
+ ["boards.id"],
274
+ ondelete="CASCADE",
275
+ name="board_members_board_id_fkey",
276
+ ),
277
+ ForeignKeyConstraint(["invited_by"], ["users.id"], name="board_members_invited_by_fkey"),
278
+ ForeignKeyConstraint(
279
+ ["user_id"],
280
+ ["users.id"],
281
+ ondelete="CASCADE",
282
+ name="board_members_user_id_fkey",
283
+ ),
284
+ PrimaryKeyConstraint("id", name="board_members_pkey"),
285
+ UniqueConstraint("board_id", "user_id", name="board_members_board_id_user_id_key"),
286
+ Index("idx_board_members_board", "board_id"),
287
+ Index("idx_board_members_user", "user_id"),
288
+ )
289
+
290
+ id: Mapped[UUID] = mapped_column(Uuid, server_default=text("uuid_generate_v4()"))
291
+ board_id: Mapped[UUID] = mapped_column(Uuid, nullable=False)
292
+ user_id: Mapped[UUID] = mapped_column(Uuid, nullable=False)
293
+ role: Mapped[str] = mapped_column(String(20), nullable=False)
294
+ invited_by: Mapped[UUID | None] = mapped_column(Uuid)
295
+ joined_at: Mapped[datetime] = mapped_column(
296
+ DateTime(True), server_default=text("CURRENT_TIMESTAMP")
297
+ )
298
+
299
+ board: Mapped["Boards"] = relationship("Boards", back_populates="board_members")
300
+ users: Mapped["Users | None"] = relationship(
301
+ "Users", foreign_keys=[invited_by], back_populates="board_members"
302
+ )
303
+ user: Mapped["Users"] = relationship(
304
+ "Users", foreign_keys=[user_id], back_populates="board_members_"
305
+ )
306
+
307
+
308
+ class Generations(Base):
309
+ __tablename__ = "generations"
310
+ __table_args__ = (
311
+ CheckConstraint(
312
+ "artifact_type::text = ANY (ARRAY["
313
+ "'image'::character varying, 'video'::character varying, "
314
+ "'audio'::character varying, 'text'::character varying, "
315
+ "'lora'::character varying, 'model'::character varying"
316
+ "]::text[])",
317
+ name="generations_artifact_type_check",
318
+ ),
319
+ ForeignKeyConstraint(
320
+ ["board_id"],
321
+ ["boards.id"],
322
+ ondelete="CASCADE",
323
+ name="generations_board_id_fkey",
324
+ ),
325
+ ForeignKeyConstraint(
326
+ ["parent_generation_id"],
327
+ ["generations.id"],
328
+ name="generations_parent_generation_id_fkey",
329
+ ),
330
+ ForeignKeyConstraint(
331
+ ["tenant_id"],
332
+ ["tenants.id"],
333
+ ondelete="CASCADE",
334
+ name="generations_tenant_id_fkey",
335
+ ),
336
+ ForeignKeyConstraint(
337
+ ["user_id"],
338
+ ["users.id"],
339
+ ondelete="CASCADE",
340
+ name="generations_user_id_fkey",
341
+ ),
342
+ PrimaryKeyConstraint("id", name="generations_pkey"),
343
+ Index("idx_generations_board", "board_id"),
344
+ Index("idx_generations_lineage", "parent_generation_id"),
345
+ Index("idx_generations_status", "status"),
346
+ Index("idx_generations_tenant", "tenant_id"),
347
+ Index("idx_generations_user", "user_id"),
348
+ )
349
+
350
+ id: Mapped[UUID] = mapped_column(Uuid, server_default=text("uuid_generate_v4()"))
351
+ tenant_id: Mapped[UUID] = mapped_column(Uuid, nullable=False)
352
+ board_id: Mapped[UUID] = mapped_column(Uuid, nullable=False)
353
+ user_id: Mapped[UUID] = mapped_column(Uuid, nullable=False)
354
+ generator_name: Mapped[str] = mapped_column(String(100), nullable=False)
355
+ artifact_type: Mapped[str] = mapped_column(String(50), nullable=False)
356
+ input_params: Mapped[dict[str, Any]] = mapped_column(JSONB, nullable=False)
357
+ status: Mapped[str] = mapped_column(
358
+ String(50), nullable=False, server_default=text("'pending'::character varying")
359
+ )
360
+ storage_url: Mapped[str | None] = mapped_column(Text)
361
+ thumbnail_url: Mapped[str | None] = mapped_column(Text)
362
+ additional_files: Mapped[list[Any]] = mapped_column(JSONB, server_default=text("'[]'::jsonb"))
363
+ output_metadata: Mapped[dict[str, Any]] = mapped_column(
364
+ JSONB, server_default=text("'{}'::jsonb")
365
+ )
366
+ parent_generation_id: Mapped[UUID | None] = mapped_column(Uuid)
367
+ input_generation_ids: Mapped[list[UUID]] = mapped_column(
368
+ ARRAY(Uuid()), server_default=text("'{}'::uuid[]")
369
+ )
370
+ external_job_id: Mapped[str | None] = mapped_column(String(255))
371
+ progress: Mapped[Decimal] = mapped_column(Numeric(5, 2), server_default=text("0.0"))
372
+ error_message: Mapped[str | None] = mapped_column(Text)
373
+ started_at: Mapped[datetime | None] = mapped_column(DateTime(True))
374
+ completed_at: Mapped[datetime | None] = mapped_column(DateTime(True))
375
+ created_at: Mapped[datetime] = mapped_column(
376
+ DateTime(True), server_default=text("CURRENT_TIMESTAMP")
377
+ )
378
+ updated_at: Mapped[datetime] = mapped_column(
379
+ DateTime(True), server_default=text("CURRENT_TIMESTAMP")
380
+ )
381
+
382
+ board: Mapped["Boards"] = relationship("Boards", back_populates="generations")
383
+ parent_generation: Mapped["Generations | None"] = relationship(
384
+ "Generations",
385
+ remote_side="Generations.id",
386
+ back_populates="parent_generation_reverse",
387
+ )
388
+ parent_generation_reverse: Mapped[list["Generations"]] = relationship(
389
+ "Generations",
390
+ uselist=True,
391
+ remote_side="Generations.parent_generation_id",
392
+ back_populates="parent_generation",
393
+ )
394
+ tenant: Mapped["Tenants"] = relationship("Tenants", back_populates="generations")
395
+ user: Mapped["Users"] = relationship("Users", back_populates="generations")
396
+ credit_transactions: Mapped[list["CreditTransactions"]] = relationship(
397
+ "CreditTransactions", uselist=True, back_populates="generation"
398
+ )
399
+
400
+
401
+ class CreditTransactions(Base):
402
+ __tablename__ = "credit_transactions"
403
+ __table_args__ = (
404
+ CheckConstraint(
405
+ "transaction_type::text = ANY (ARRAY["
406
+ "'reserve'::character varying, 'finalize'::character varying, "
407
+ "'refund'::character varying, 'purchase'::character varying, "
408
+ "'grant'::character varying]::text[])",
409
+ name="credit_transactions_transaction_type_check",
410
+ ),
411
+ ForeignKeyConstraint(
412
+ ["generation_id"],
413
+ ["generations.id"],
414
+ name="credit_transactions_generation_id_fkey",
415
+ ),
416
+ ForeignKeyConstraint(
417
+ ["tenant_id"],
418
+ ["tenants.id"],
419
+ ondelete="CASCADE",
420
+ name="credit_transactions_tenant_id_fkey",
421
+ ),
422
+ ForeignKeyConstraint(
423
+ ["user_id"],
424
+ ["users.id"],
425
+ ondelete="CASCADE",
426
+ name="credit_transactions_user_id_fkey",
427
+ ),
428
+ PrimaryKeyConstraint("id", name="credit_transactions_pkey"),
429
+ Index("idx_credit_transactions_user", "user_id"),
430
+ )
431
+
432
+ id: Mapped[UUID] = mapped_column(Uuid, server_default=text("uuid_generate_v4()"))
433
+ tenant_id: Mapped[UUID] = mapped_column(Uuid, nullable=False)
434
+ user_id: Mapped[UUID] = mapped_column(Uuid, nullable=False)
435
+ transaction_type: Mapped[str] = mapped_column(String(20), nullable=False)
436
+ amount: Mapped[Decimal] = mapped_column(Numeric(10, 4), nullable=False)
437
+ balance_after: Mapped[Decimal] = mapped_column(Numeric(10, 4), nullable=False)
438
+ generation_id: Mapped[UUID | None] = mapped_column(Uuid)
439
+ description: Mapped[str | None] = mapped_column(Text)
440
+ metadata_: Mapped[dict[str, Any]] = mapped_column(
441
+ "metadata", JSONB, server_default=text("'{}'::jsonb")
442
+ )
443
+ created_at: Mapped[datetime] = mapped_column(
444
+ DateTime(True), server_default=text("CURRENT_TIMESTAMP")
445
+ )
446
+
447
+ generation: Mapped["Generations | None"] = relationship(
448
+ "Generations", back_populates="credit_transactions"
449
+ )
450
+ tenant: Mapped["Tenants"] = relationship("Tenants", back_populates="credit_transactions")
451
+ user: Mapped["Users"] = relationship("Users", back_populates="credit_transactions")
452
+
453
+
454
+ # Expose for Alembic
455
+ target_metadata = Base.metadata
@@ -0,0 +1,57 @@
1
+ """
2
+ Boards generators system for integrating AI generation services.
3
+
4
+ This package provides a simple, type-safe way to define and use AI generators
5
+ that produce various types of artifacts (images, videos, audio, text, etc.).
6
+
7
+ Key components:
8
+ - BaseGenerator: Abstract base class for all generators
9
+ - Artifact types: Pydantic models for different content types
10
+ - Registry: System for discovering and managing generators
11
+ - Resolution utilities: For converting artifacts to files for provider SDKs
12
+
13
+ Example usage:
14
+ from boards.generators import registry
15
+ from boards.generators.implementations.image.flux_pro import FluxProGenerator
16
+
17
+ # Get available generators
18
+ image_generators = registry.list_by_artifact_type("image")
19
+
20
+ # Use a specific generator
21
+ flux = registry.get("flux-pro")
22
+ result = await flux.generate(inputs)
23
+ """
24
+
25
+ from .artifacts import (
26
+ AudioArtifact,
27
+ ImageArtifact,
28
+ LoRArtifact,
29
+ TextArtifact,
30
+ VideoArtifact,
31
+ )
32
+ from .base import BaseGenerator
33
+ from .registry import GeneratorRegistry, registry
34
+ from .resolution import (
35
+ resolve_artifact,
36
+ store_audio_result,
37
+ store_image_result,
38
+ store_video_result,
39
+ )
40
+
41
+ __all__ = [
42
+ # Core classes
43
+ "BaseGenerator",
44
+ "GeneratorRegistry",
45
+ "registry",
46
+ # Artifact types
47
+ "AudioArtifact",
48
+ "VideoArtifact",
49
+ "ImageArtifact",
50
+ "TextArtifact",
51
+ "LoRArtifact",
52
+ # Utilities
53
+ "resolve_artifact",
54
+ "store_image_result",
55
+ "store_video_result",
56
+ "store_audio_result",
57
+ ]
@@ -0,0 +1,53 @@
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 = Field(description="Video width in pixels")
32
+ height: int = Field(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 = Field(description="Image width in pixels")
40
+ height: int = Field(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")