planar 0.5.0__py3-none-any.whl

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 (289) hide show
  1. planar/.__init__.py.un~ +0 -0
  2. planar/._version.py.un~ +0 -0
  3. planar/.app.py.un~ +0 -0
  4. planar/.cli.py.un~ +0 -0
  5. planar/.config.py.un~ +0 -0
  6. planar/.context.py.un~ +0 -0
  7. planar/.db.py.un~ +0 -0
  8. planar/.di.py.un~ +0 -0
  9. planar/.engine.py.un~ +0 -0
  10. planar/.files.py.un~ +0 -0
  11. planar/.log_context.py.un~ +0 -0
  12. planar/.log_metadata.py.un~ +0 -0
  13. planar/.logging.py.un~ +0 -0
  14. planar/.object_registry.py.un~ +0 -0
  15. planar/.otel.py.un~ +0 -0
  16. planar/.server.py.un~ +0 -0
  17. planar/.session.py.un~ +0 -0
  18. planar/.sqlalchemy.py.un~ +0 -0
  19. planar/.task_local.py.un~ +0 -0
  20. planar/.test_app.py.un~ +0 -0
  21. planar/.test_config.py.un~ +0 -0
  22. planar/.test_object_config.py.un~ +0 -0
  23. planar/.test_sqlalchemy.py.un~ +0 -0
  24. planar/.test_utils.py.un~ +0 -0
  25. planar/.util.py.un~ +0 -0
  26. planar/.utils.py.un~ +0 -0
  27. planar/__init__.py +26 -0
  28. planar/_version.py +1 -0
  29. planar/ai/.__init__.py.un~ +0 -0
  30. planar/ai/._models.py.un~ +0 -0
  31. planar/ai/.agent.py.un~ +0 -0
  32. planar/ai/.agent_utils.py.un~ +0 -0
  33. planar/ai/.events.py.un~ +0 -0
  34. planar/ai/.files.py.un~ +0 -0
  35. planar/ai/.models.py.un~ +0 -0
  36. planar/ai/.providers.py.un~ +0 -0
  37. planar/ai/.pydantic_ai.py.un~ +0 -0
  38. planar/ai/.pydantic_ai_agent.py.un~ +0 -0
  39. planar/ai/.pydantic_ai_provider.py.un~ +0 -0
  40. planar/ai/.step.py.un~ +0 -0
  41. planar/ai/.test_agent.py.un~ +0 -0
  42. planar/ai/.test_agent_serialization.py.un~ +0 -0
  43. planar/ai/.test_providers.py.un~ +0 -0
  44. planar/ai/.utils.py.un~ +0 -0
  45. planar/ai/__init__.py +15 -0
  46. planar/ai/agent.py +457 -0
  47. planar/ai/agent_utils.py +205 -0
  48. planar/ai/models.py +140 -0
  49. planar/ai/providers.py +1088 -0
  50. planar/ai/test_agent.py +1298 -0
  51. planar/ai/test_agent_serialization.py +229 -0
  52. planar/ai/test_providers.py +463 -0
  53. planar/ai/utils.py +102 -0
  54. planar/app.py +494 -0
  55. planar/cli.py +282 -0
  56. planar/config.py +544 -0
  57. planar/db/.db.py.un~ +0 -0
  58. planar/db/__init__.py +17 -0
  59. planar/db/alembic/env.py +136 -0
  60. planar/db/alembic/script.py.mako +28 -0
  61. planar/db/alembic/versions/3476068c153c_initial_system_tables_migration.py +339 -0
  62. planar/db/alembic.ini +128 -0
  63. planar/db/db.py +318 -0
  64. planar/files/.config.py.un~ +0 -0
  65. planar/files/.local.py.un~ +0 -0
  66. planar/files/.local_filesystem.py.un~ +0 -0
  67. planar/files/.model.py.un~ +0 -0
  68. planar/files/.models.py.un~ +0 -0
  69. planar/files/.s3.py.un~ +0 -0
  70. planar/files/.storage.py.un~ +0 -0
  71. planar/files/.test_files.py.un~ +0 -0
  72. planar/files/__init__.py +2 -0
  73. planar/files/models.py +162 -0
  74. planar/files/storage/.__init__.py.un~ +0 -0
  75. planar/files/storage/.base.py.un~ +0 -0
  76. planar/files/storage/.config.py.un~ +0 -0
  77. planar/files/storage/.context.py.un~ +0 -0
  78. planar/files/storage/.local_directory.py.un~ +0 -0
  79. planar/files/storage/.test_local_directory.py.un~ +0 -0
  80. planar/files/storage/.test_s3.py.un~ +0 -0
  81. planar/files/storage/base.py +61 -0
  82. planar/files/storage/config.py +44 -0
  83. planar/files/storage/context.py +15 -0
  84. planar/files/storage/local_directory.py +188 -0
  85. planar/files/storage/s3.py +220 -0
  86. planar/files/storage/test_local_directory.py +162 -0
  87. planar/files/storage/test_s3.py +299 -0
  88. planar/files/test_files.py +283 -0
  89. planar/human/.human.py.un~ +0 -0
  90. planar/human/.test_human.py.un~ +0 -0
  91. planar/human/__init__.py +2 -0
  92. planar/human/human.py +458 -0
  93. planar/human/models.py +80 -0
  94. planar/human/test_human.py +385 -0
  95. planar/logging/.__init__.py.un~ +0 -0
  96. planar/logging/.attributes.py.un~ +0 -0
  97. planar/logging/.formatter.py.un~ +0 -0
  98. planar/logging/.logger.py.un~ +0 -0
  99. planar/logging/.otel.py.un~ +0 -0
  100. planar/logging/.tracer.py.un~ +0 -0
  101. planar/logging/__init__.py +10 -0
  102. planar/logging/attributes.py +54 -0
  103. planar/logging/context.py +14 -0
  104. planar/logging/formatter.py +113 -0
  105. planar/logging/logger.py +114 -0
  106. planar/logging/otel.py +51 -0
  107. planar/modeling/.mixin.py.un~ +0 -0
  108. planar/modeling/.storage.py.un~ +0 -0
  109. planar/modeling/__init__.py +0 -0
  110. planar/modeling/field_helpers.py +59 -0
  111. planar/modeling/json_schema_generator.py +94 -0
  112. planar/modeling/mixins/__init__.py +10 -0
  113. planar/modeling/mixins/auditable.py +52 -0
  114. planar/modeling/mixins/test_auditable.py +97 -0
  115. planar/modeling/mixins/test_timestamp.py +134 -0
  116. planar/modeling/mixins/test_uuid_primary_key.py +52 -0
  117. planar/modeling/mixins/timestamp.py +53 -0
  118. planar/modeling/mixins/uuid_primary_key.py +19 -0
  119. planar/modeling/orm/.planar_base_model.py.un~ +0 -0
  120. planar/modeling/orm/__init__.py +18 -0
  121. planar/modeling/orm/planar_base_entity.py +29 -0
  122. planar/modeling/orm/query_filter_builder.py +122 -0
  123. planar/modeling/orm/reexports.py +15 -0
  124. planar/object_config/.object_config.py.un~ +0 -0
  125. planar/object_config/__init__.py +11 -0
  126. planar/object_config/models.py +114 -0
  127. planar/object_config/object_config.py +378 -0
  128. planar/object_registry.py +100 -0
  129. planar/registry_items.py +65 -0
  130. planar/routers/.__init__.py.un~ +0 -0
  131. planar/routers/.agents_router.py.un~ +0 -0
  132. planar/routers/.crud.py.un~ +0 -0
  133. planar/routers/.decision.py.un~ +0 -0
  134. planar/routers/.event.py.un~ +0 -0
  135. planar/routers/.file_attachment.py.un~ +0 -0
  136. planar/routers/.files.py.un~ +0 -0
  137. planar/routers/.files_router.py.un~ +0 -0
  138. planar/routers/.human.py.un~ +0 -0
  139. planar/routers/.info.py.un~ +0 -0
  140. planar/routers/.models.py.un~ +0 -0
  141. planar/routers/.object_config_router.py.un~ +0 -0
  142. planar/routers/.rule.py.un~ +0 -0
  143. planar/routers/.test_object_config_router.py.un~ +0 -0
  144. planar/routers/.test_workflow_router.py.un~ +0 -0
  145. planar/routers/.workflow.py.un~ +0 -0
  146. planar/routers/__init__.py +13 -0
  147. planar/routers/agents_router.py +197 -0
  148. planar/routers/entity_router.py +143 -0
  149. planar/routers/event.py +91 -0
  150. planar/routers/files.py +142 -0
  151. planar/routers/human.py +151 -0
  152. planar/routers/info.py +131 -0
  153. planar/routers/models.py +170 -0
  154. planar/routers/object_config_router.py +133 -0
  155. planar/routers/rule.py +108 -0
  156. planar/routers/test_agents_router.py +174 -0
  157. planar/routers/test_object_config_router.py +367 -0
  158. planar/routers/test_routes_security.py +169 -0
  159. planar/routers/test_rule_router.py +470 -0
  160. planar/routers/test_workflow_router.py +274 -0
  161. planar/routers/workflow.py +468 -0
  162. planar/rules/.decorator.py.un~ +0 -0
  163. planar/rules/.runner.py.un~ +0 -0
  164. planar/rules/.test_rules.py.un~ +0 -0
  165. planar/rules/__init__.py +23 -0
  166. planar/rules/decorator.py +184 -0
  167. planar/rules/models.py +355 -0
  168. planar/rules/rule_configuration.py +191 -0
  169. planar/rules/runner.py +64 -0
  170. planar/rules/test_rules.py +750 -0
  171. planar/scaffold_templates/app/__init__.py.j2 +0 -0
  172. planar/scaffold_templates/app/db/entities.py.j2 +11 -0
  173. planar/scaffold_templates/app/flows/process_invoice.py.j2 +67 -0
  174. planar/scaffold_templates/main.py.j2 +13 -0
  175. planar/scaffold_templates/planar.dev.yaml.j2 +34 -0
  176. planar/scaffold_templates/planar.prod.yaml.j2 +28 -0
  177. planar/scaffold_templates/pyproject.toml.j2 +10 -0
  178. planar/security/.jwt_middleware.py.un~ +0 -0
  179. planar/security/auth_context.py +148 -0
  180. planar/security/authorization.py +388 -0
  181. planar/security/default_policies.cedar +77 -0
  182. planar/security/jwt_middleware.py +116 -0
  183. planar/security/security_context.py +18 -0
  184. planar/security/tests/test_authorization_context.py +78 -0
  185. planar/security/tests/test_cedar_basics.py +41 -0
  186. planar/security/tests/test_cedar_policies.py +158 -0
  187. planar/security/tests/test_jwt_principal_context.py +179 -0
  188. planar/session.py +40 -0
  189. planar/sse/.constants.py.un~ +0 -0
  190. planar/sse/.example.html.un~ +0 -0
  191. planar/sse/.hub.py.un~ +0 -0
  192. planar/sse/.model.py.un~ +0 -0
  193. planar/sse/.proxy.py.un~ +0 -0
  194. planar/sse/constants.py +1 -0
  195. planar/sse/example.html +126 -0
  196. planar/sse/hub.py +216 -0
  197. planar/sse/model.py +8 -0
  198. planar/sse/proxy.py +257 -0
  199. planar/task_local.py +37 -0
  200. planar/test_app.py +51 -0
  201. planar/test_cli.py +372 -0
  202. planar/test_config.py +512 -0
  203. planar/test_object_config.py +527 -0
  204. planar/test_object_registry.py +14 -0
  205. planar/test_sqlalchemy.py +158 -0
  206. planar/test_utils.py +105 -0
  207. planar/testing/.client.py.un~ +0 -0
  208. planar/testing/.memory_storage.py.un~ +0 -0
  209. planar/testing/.planar_test_client.py.un~ +0 -0
  210. planar/testing/.predictable_tracer.py.un~ +0 -0
  211. planar/testing/.synchronizable_tracer.py.un~ +0 -0
  212. planar/testing/.test_memory_storage.py.un~ +0 -0
  213. planar/testing/.workflow_observer.py.un~ +0 -0
  214. planar/testing/__init__.py +0 -0
  215. planar/testing/memory_storage.py +78 -0
  216. planar/testing/planar_test_client.py +54 -0
  217. planar/testing/synchronizable_tracer.py +153 -0
  218. planar/testing/test_memory_storage.py +143 -0
  219. planar/testing/workflow_observer.py +73 -0
  220. planar/utils.py +70 -0
  221. planar/workflows/.__init__.py.un~ +0 -0
  222. planar/workflows/.builtin_steps.py.un~ +0 -0
  223. planar/workflows/.concurrency_tracing.py.un~ +0 -0
  224. planar/workflows/.context.py.un~ +0 -0
  225. planar/workflows/.contrib.py.un~ +0 -0
  226. planar/workflows/.decorators.py.un~ +0 -0
  227. planar/workflows/.durable_test.py.un~ +0 -0
  228. planar/workflows/.errors.py.un~ +0 -0
  229. planar/workflows/.events.py.un~ +0 -0
  230. planar/workflows/.exceptions.py.un~ +0 -0
  231. planar/workflows/.execution.py.un~ +0 -0
  232. planar/workflows/.human.py.un~ +0 -0
  233. planar/workflows/.lock.py.un~ +0 -0
  234. planar/workflows/.misc.py.un~ +0 -0
  235. planar/workflows/.model.py.un~ +0 -0
  236. planar/workflows/.models.py.un~ +0 -0
  237. planar/workflows/.notifications.py.un~ +0 -0
  238. planar/workflows/.orchestrator.py.un~ +0 -0
  239. planar/workflows/.runtime.py.un~ +0 -0
  240. planar/workflows/.serialization.py.un~ +0 -0
  241. planar/workflows/.step.py.un~ +0 -0
  242. planar/workflows/.step_core.py.un~ +0 -0
  243. planar/workflows/.sub_workflow_runner.py.un~ +0 -0
  244. planar/workflows/.sub_workflow_scheduler.py.un~ +0 -0
  245. planar/workflows/.test_concurrency.py.un~ +0 -0
  246. planar/workflows/.test_concurrency_detection.py.un~ +0 -0
  247. planar/workflows/.test_human.py.un~ +0 -0
  248. planar/workflows/.test_lock_timeout.py.un~ +0 -0
  249. planar/workflows/.test_orchestrator.py.un~ +0 -0
  250. planar/workflows/.test_race_conditions.py.un~ +0 -0
  251. planar/workflows/.test_serialization.py.un~ +0 -0
  252. planar/workflows/.test_suspend_deserialization.py.un~ +0 -0
  253. planar/workflows/.test_workflow.py.un~ +0 -0
  254. planar/workflows/.tracing.py.un~ +0 -0
  255. planar/workflows/.types.py.un~ +0 -0
  256. planar/workflows/.util.py.un~ +0 -0
  257. planar/workflows/.utils.py.un~ +0 -0
  258. planar/workflows/.workflow.py.un~ +0 -0
  259. planar/workflows/.workflow_wrapper.py.un~ +0 -0
  260. planar/workflows/.wrappers.py.un~ +0 -0
  261. planar/workflows/__init__.py +42 -0
  262. planar/workflows/context.py +44 -0
  263. planar/workflows/contrib.py +190 -0
  264. planar/workflows/decorators.py +217 -0
  265. planar/workflows/events.py +185 -0
  266. planar/workflows/exceptions.py +34 -0
  267. planar/workflows/execution.py +198 -0
  268. planar/workflows/lock.py +229 -0
  269. planar/workflows/misc.py +5 -0
  270. planar/workflows/models.py +154 -0
  271. planar/workflows/notifications.py +96 -0
  272. planar/workflows/orchestrator.py +383 -0
  273. planar/workflows/query.py +256 -0
  274. planar/workflows/serialization.py +409 -0
  275. planar/workflows/step_core.py +373 -0
  276. planar/workflows/step_metadata.py +357 -0
  277. planar/workflows/step_testing_utils.py +86 -0
  278. planar/workflows/sub_workflow_runner.py +191 -0
  279. planar/workflows/test_concurrency_detection.py +120 -0
  280. planar/workflows/test_lock_timeout.py +140 -0
  281. planar/workflows/test_serialization.py +1195 -0
  282. planar/workflows/test_suspend_deserialization.py +231 -0
  283. planar/workflows/test_workflow.py +1967 -0
  284. planar/workflows/tracing.py +106 -0
  285. planar/workflows/wrappers.py +41 -0
  286. planar-0.5.0.dist-info/METADATA +285 -0
  287. planar-0.5.0.dist-info/RECORD +289 -0
  288. planar-0.5.0.dist-info/WHEEL +4 -0
  289. planar-0.5.0.dist-info/entry_points.txt +3 -0
@@ -0,0 +1,28 @@
1
+ """${message}
2
+
3
+ Revision ID: ${up_revision}
4
+ Revises: ${down_revision | comma,n}
5
+ Create Date: ${create_date}
6
+
7
+ """
8
+ from typing import Sequence, Union
9
+
10
+ from alembic import op
11
+ import sqlalchemy as sa
12
+ import sqlmodel.sql.sqltypes
13
+ import planar.object_config.models
14
+ ${imports if imports else ""}
15
+
16
+ # revision identifiers, used by Alembic.
17
+ revision: str = ${repr(up_revision)}
18
+ down_revision: Union[str, None] = ${repr(down_revision)}
19
+ branch_labels: Union[str, Sequence[str], None] = ${repr(branch_labels)}
20
+ depends_on: Union[str, Sequence[str], None] = ${repr(depends_on)}
21
+
22
+
23
+ def upgrade() -> None:
24
+ ${upgrades if upgrades else "pass"}
25
+
26
+
27
+ def downgrade() -> None:
28
+ ${downgrades if downgrades else "pass"}
@@ -0,0 +1,339 @@
1
+ """Initial system tables migration
2
+
3
+ Revision ID: 3476068c153c
4
+ Revises:
5
+ Create Date: 2025-06-16 20:53:26.711664
6
+
7
+ """
8
+
9
+ from typing import Sequence, Union
10
+
11
+ import sqlalchemy as sa
12
+ import sqlmodel.sql.sqltypes
13
+ from alembic import op
14
+
15
+ import planar.object_config.models
16
+
17
+ # revision identifiers, used by Alembic.
18
+ revision: str = "3476068c153c"
19
+ down_revision: Union[str, None] = None
20
+ branch_labels: Union[str, Sequence[str], None] = None
21
+ depends_on: Union[str, Sequence[str], None] = None
22
+
23
+
24
+ def upgrade() -> None:
25
+ # ### commands auto generated by Alembic - please adjust! ###
26
+ op.create_table(
27
+ "human_task",
28
+ sa.Column("created_at", sa.DateTime(), nullable=False),
29
+ sa.Column("updated_at", sa.DateTime(), nullable=False),
30
+ sa.Column("id", sa.Uuid(), nullable=False),
31
+ sa.Column("created_by", sqlmodel.sql.sqltypes.AutoString(), nullable=False),
32
+ sa.Column("updated_by", sqlmodel.sql.sqltypes.AutoString(), nullable=False),
33
+ sa.Column("name", sqlmodel.sql.sqltypes.AutoString(), nullable=False),
34
+ sa.Column("title", sqlmodel.sql.sqltypes.AutoString(), nullable=False),
35
+ sa.Column("description", sqlmodel.sql.sqltypes.AutoString(), nullable=True),
36
+ sa.Column("workflow_id", sa.Uuid(), nullable=False),
37
+ sa.Column("workflow_name", sqlmodel.sql.sqltypes.AutoString(), nullable=False),
38
+ sa.Column("input_schema", sa.JSON(), nullable=True),
39
+ sa.Column("input_data", sa.JSON(), nullable=True),
40
+ sa.Column("message", sqlmodel.sql.sqltypes.AutoString(), nullable=True),
41
+ sa.Column("output_schema", sa.JSON(), nullable=True),
42
+ sa.Column("output_data", sa.JSON(), nullable=True),
43
+ sa.Column("suggested_data", sa.JSON(), nullable=True),
44
+ sa.Column(
45
+ "status",
46
+ sa.Enum(
47
+ "PENDING",
48
+ "COMPLETED",
49
+ "CANCELLED",
50
+ "EXPIRED",
51
+ name="humantaskstatus",
52
+ ),
53
+ nullable=False,
54
+ ),
55
+ sa.Column("completed_by", sqlmodel.sql.sqltypes.AutoString(), nullable=True),
56
+ sa.Column("completed_at", sa.DateTime(), nullable=True),
57
+ sa.Column("deadline", sa.DateTime(), nullable=True),
58
+ sa.PrimaryKeyConstraint("id"),
59
+ schema="planar",
60
+ if_not_exists=True,
61
+ )
62
+ with op.batch_alter_table("human_task", schema="planar") as batch_op:
63
+ batch_op.create_index(
64
+ batch_op.f("ix_planar_human_task_name"),
65
+ ["name"],
66
+ unique=False,
67
+ if_not_exists=True,
68
+ )
69
+ batch_op.create_index(
70
+ batch_op.f("ix_planar_human_task_workflow_id"),
71
+ ["workflow_id"],
72
+ unique=False,
73
+ if_not_exists=True,
74
+ )
75
+
76
+ op.create_table(
77
+ "locked_resource",
78
+ sa.Column("lock_key", sqlmodel.sql.sqltypes.AutoString(), nullable=False),
79
+ sa.Column("lock_until", sa.DateTime(), nullable=True),
80
+ sa.Column("version_id", sa.Integer(), nullable=False),
81
+ sa.PrimaryKeyConstraint("lock_key"),
82
+ schema="planar",
83
+ if_not_exists=True,
84
+ )
85
+ with op.batch_alter_table("locked_resource", schema="planar") as batch_op:
86
+ batch_op.create_index(
87
+ batch_op.f("ix_planar_locked_resource_lock_until"),
88
+ ["lock_until"],
89
+ unique=False,
90
+ if_not_exists=True,
91
+ )
92
+
93
+ op.create_table(
94
+ "object_configuration",
95
+ sa.Column("id", sa.Uuid(), nullable=False),
96
+ sa.Column("object_name", sqlmodel.sql.sqltypes.AutoString(), nullable=False),
97
+ sa.Column(
98
+ "object_type",
99
+ sa.Enum("RULE", "AGENT", name="configurableobjecttype"),
100
+ nullable=False,
101
+ ),
102
+ sa.Column("created_at", sa.DateTime(), nullable=False),
103
+ sa.Column("version", sa.Integer(), nullable=False),
104
+ sa.Column("data", planar.object_config.models.JSONEncodedDict(), nullable=True),
105
+ sa.Column("active", sa.Boolean(), nullable=False),
106
+ sa.PrimaryKeyConstraint("id"),
107
+ sa.UniqueConstraint(
108
+ "object_name",
109
+ "object_type",
110
+ "version",
111
+ name="uq_object_config_name_type_version",
112
+ ),
113
+ schema="planar",
114
+ if_not_exists=True,
115
+ )
116
+ with op.batch_alter_table("object_configuration", schema="planar") as batch_op:
117
+ batch_op.create_index(
118
+ batch_op.f("ix_planar_object_configuration_object_name"),
119
+ ["object_name"],
120
+ unique=False,
121
+ if_not_exists=True,
122
+ )
123
+ batch_op.create_index(
124
+ batch_op.f("ix_planar_object_configuration_object_type"),
125
+ ["object_type"],
126
+ unique=False,
127
+ if_not_exists=True,
128
+ )
129
+
130
+ op.create_table(
131
+ "planar_file_metadata",
132
+ sa.Column("created_at", sa.DateTime(), nullable=False),
133
+ sa.Column("updated_at", sa.DateTime(), nullable=False),
134
+ sa.Column("id", sa.Uuid(), nullable=False),
135
+ sa.Column("filename", sqlmodel.sql.sqltypes.AutoString(), nullable=False),
136
+ sa.Column("content_type", sqlmodel.sql.sqltypes.AutoString(), nullable=False),
137
+ sa.Column("size", sa.Integer(), nullable=False),
138
+ sa.Column("storage_ref", sqlmodel.sql.sqltypes.AutoString(), nullable=False),
139
+ sa.PrimaryKeyConstraint("id"),
140
+ schema="planar",
141
+ if_not_exists=True,
142
+ )
143
+ with op.batch_alter_table("planar_file_metadata", schema="planar") as batch_op:
144
+ batch_op.create_index(
145
+ batch_op.f("ix_planar_planar_file_metadata_storage_ref"),
146
+ ["storage_ref"],
147
+ unique=False,
148
+ if_not_exists=True,
149
+ )
150
+
151
+ op.create_table(
152
+ "workflow",
153
+ sa.Column("created_at", sa.DateTime(), nullable=False),
154
+ sa.Column("updated_at", sa.DateTime(), nullable=False),
155
+ sa.Column("function_name", sqlmodel.sql.sqltypes.AutoString(), nullable=False),
156
+ sa.Column("id", sa.Uuid(), nullable=False),
157
+ sa.Column("parent_id", sa.Uuid(), nullable=True),
158
+ sa.Column(
159
+ "status",
160
+ sa.Enum(
161
+ "PENDING",
162
+ "SUCCEEDED",
163
+ "FAILED",
164
+ name="workflowstatus",
165
+ ),
166
+ nullable=False,
167
+ ),
168
+ sa.Column("args", sa.JSON(), nullable=True),
169
+ sa.Column("kwargs", sa.JSON(), nullable=True),
170
+ sa.Column("result", sa.JSON(), nullable=True),
171
+ sa.Column("error", sa.JSON(), nullable=True),
172
+ sa.Column("wakeup_at", sa.DateTime(), nullable=True),
173
+ sa.Column(
174
+ "waiting_for_event", sqlmodel.sql.sqltypes.AutoString(), nullable=True
175
+ ),
176
+ sa.ForeignKeyConstraint(
177
+ ["parent_id"],
178
+ ["planar.workflow.id"],
179
+ ),
180
+ sa.PrimaryKeyConstraint("id"),
181
+ schema="planar",
182
+ if_not_exists=True,
183
+ )
184
+ with op.batch_alter_table("workflow", schema="planar") as batch_op:
185
+ batch_op.create_index(
186
+ batch_op.f("ix_planar_workflow_parent_id"),
187
+ ["parent_id"],
188
+ unique=False,
189
+ if_not_exists=True,
190
+ )
191
+ batch_op.create_index(
192
+ batch_op.f("ix_planar_workflow_status"),
193
+ ["status"],
194
+ unique=False,
195
+ if_not_exists=True,
196
+ )
197
+ batch_op.create_index(
198
+ batch_op.f("ix_planar_workflow_waiting_for_event"),
199
+ ["waiting_for_event"],
200
+ unique=False,
201
+ if_not_exists=True,
202
+ )
203
+ batch_op.create_index(
204
+ batch_op.f("ix_planar_workflow_wakeup_at"),
205
+ ["wakeup_at"],
206
+ unique=False,
207
+ if_not_exists=True,
208
+ )
209
+
210
+ op.create_table(
211
+ "workflow_event",
212
+ sa.Column("id", sa.Uuid(), nullable=False),
213
+ sa.Column("event_key", sqlmodel.sql.sqltypes.AutoString(), nullable=False),
214
+ sa.Column("workflow_id", sa.Uuid(), nullable=True),
215
+ sa.Column("payload", sa.JSON(), nullable=True),
216
+ sa.Column("timestamp", sa.DateTime(), nullable=False),
217
+ sa.ForeignKeyConstraint(
218
+ ["workflow_id"],
219
+ ["planar.workflow.id"],
220
+ ),
221
+ sa.PrimaryKeyConstraint("id"),
222
+ schema="planar",
223
+ if_not_exists=True,
224
+ )
225
+ with op.batch_alter_table("workflow_event", schema="planar") as batch_op:
226
+ batch_op.create_index(
227
+ batch_op.f("ix_planar_workflow_event_event_key"),
228
+ ["event_key"],
229
+ unique=False,
230
+ if_not_exists=True,
231
+ )
232
+ batch_op.create_index(
233
+ batch_op.f("ix_planar_workflow_event_timestamp"),
234
+ ["timestamp"],
235
+ unique=False,
236
+ if_not_exists=True,
237
+ )
238
+ batch_op.create_index(
239
+ batch_op.f("ix_planar_workflow_event_workflow_id"),
240
+ ["workflow_id"],
241
+ unique=False,
242
+ if_not_exists=True,
243
+ )
244
+
245
+ op.create_table(
246
+ "workflow_step",
247
+ sa.Column("created_at", sa.DateTime(), nullable=False),
248
+ sa.Column("updated_at", sa.DateTime(), nullable=False),
249
+ sa.Column("step_id", sa.Integer(), nullable=False),
250
+ sa.Column("workflow_id", sa.Uuid(), nullable=False),
251
+ sa.Column("parent_step_id", sa.Integer(), nullable=True),
252
+ sa.Column("function_name", sqlmodel.sql.sqltypes.AutoString(), nullable=False),
253
+ sa.Column("display_name", sqlmodel.sql.sqltypes.AutoString(), nullable=True),
254
+ sa.Column(
255
+ "status",
256
+ sa.Enum("SUCCEEDED", "RUNNING", "FAILED", name="stepstatus"),
257
+ nullable=False,
258
+ ),
259
+ sa.Column(
260
+ "step_type",
261
+ sa.Enum(
262
+ "COMPUTE",
263
+ "AGENT",
264
+ "RULE",
265
+ "HUMAN_IN_THE_LOOP",
266
+ "TOOL_CALL",
267
+ name="steptype",
268
+ ),
269
+ nullable=False,
270
+ ),
271
+ sa.Column("args", sa.JSON(), nullable=True),
272
+ sa.Column("kwargs", sa.JSON(), nullable=True),
273
+ sa.Column("result", sa.JSON(), nullable=True),
274
+ sa.Column("sub_step_count", sa.Integer(), nullable=False),
275
+ sa.Column("error", sa.JSON(), nullable=True),
276
+ sa.Column("retry_count", sa.Integer(), nullable=False),
277
+ sa.ForeignKeyConstraint(
278
+ ["workflow_id"],
279
+ ["planar.workflow.id"],
280
+ ),
281
+ sa.PrimaryKeyConstraint("step_id", "workflow_id"),
282
+ schema="planar",
283
+ if_not_exists=True,
284
+ )
285
+ with op.batch_alter_table("workflow_step", schema="planar") as batch_op:
286
+ batch_op.create_index(
287
+ batch_op.f("ix_planar_workflow_step_parent_step_id"),
288
+ ["parent_step_id"],
289
+ unique=False,
290
+ if_not_exists=True,
291
+ )
292
+
293
+ # ### end Alembic commands ###
294
+
295
+
296
+ def downgrade() -> None:
297
+ # ### commands auto generated by Alembic - please adjust! ###
298
+ with op.batch_alter_table("workflow_step", schema="planar") as batch_op:
299
+ batch_op.drop_index(batch_op.f("ix_planar_workflow_step_parent_step_id"))
300
+
301
+ op.drop_table("workflow_step", schema="planar")
302
+ with op.batch_alter_table("workflow_event", schema="planar") as batch_op:
303
+ batch_op.drop_index(batch_op.f("ix_planar_workflow_event_workflow_id"))
304
+ batch_op.drop_index(batch_op.f("ix_planar_workflow_event_timestamp"))
305
+ batch_op.drop_index(batch_op.f("ix_planar_workflow_event_event_key"))
306
+
307
+ op.drop_table("workflow_event", schema="planar")
308
+ with op.batch_alter_table("workflow", schema="planar") as batch_op:
309
+ batch_op.drop_index(batch_op.f("ix_planar_workflow_wakeup_at"))
310
+ batch_op.drop_index(batch_op.f("ix_planar_workflow_waiting_for_event"))
311
+ batch_op.drop_index(batch_op.f("ix_planar_workflow_status"))
312
+ batch_op.drop_index(batch_op.f("ix_planar_workflow_parent_id"))
313
+
314
+ op.drop_table("workflow", schema="planar")
315
+ with op.batch_alter_table("planar_file_metadata", schema="planar") as batch_op:
316
+ batch_op.drop_index(batch_op.f("ix_planar_planar_file_metadata_storage_ref"))
317
+
318
+ op.drop_table("planar_file_metadata", schema="planar")
319
+ with op.batch_alter_table("object_configuration", schema="planar") as batch_op:
320
+ batch_op.drop_index(batch_op.f("ix_planar_object_configuration_object_type"))
321
+ batch_op.drop_index(batch_op.f("ix_planar_object_configuration_object_name"))
322
+
323
+ op.drop_table("object_configuration", schema="planar")
324
+ with op.batch_alter_table("locked_resource", schema="planar") as batch_op:
325
+ batch_op.drop_index(batch_op.f("ix_planar_locked_resource_lock_until"))
326
+
327
+ op.drop_table("locked_resource", schema="planar")
328
+ with op.batch_alter_table("human_task", schema="planar") as batch_op:
329
+ batch_op.drop_index(batch_op.f("ix_planar_human_task_workflow_id"))
330
+ batch_op.drop_index(batch_op.f("ix_planar_human_task_name"))
331
+
332
+ op.drop_table("human_task", schema="planar")
333
+
334
+ # Drop ENUM types if they exist
335
+ op.execute("DROP TYPE IF EXISTS humantaskstatus;")
336
+ op.execute("DROP TYPE IF EXISTS configurableobjecttype;")
337
+ op.execute("DROP TYPE IF EXISTS workflowstatus;")
338
+ op.execute("DROP TYPE IF EXISTS stepstatus;")
339
+ op.execute("DROP TYPE IF EXISTS steptype;")
planar/db/alembic.ini ADDED
@@ -0,0 +1,128 @@
1
+ # A generic, single database configuration.
2
+
3
+ [alembic]
4
+ # path to migration scripts
5
+ script_location = alembic
6
+
7
+ # template used to generate migration file names; The default value is %(rev)s_%(slug)s
8
+ # Uncomment the line below if you want the files to be prepended with date and time
9
+ # file_template = %(year)d_%(month).2d_%(day).2d_%(hour).2d%(minute).2d-%(rev)s_%(slug)s
10
+
11
+ # sys.path path, will be prepended to sys.path if present.
12
+ # defaults to the current working directory.
13
+ prepend_sys_path = .
14
+
15
+ # timezone to use when rendering the date within the migration file
16
+ # as well as the filename.
17
+ # If specified, requires the python-dateutil library that can be
18
+ # installed by adding `alembic[tz]` to the pip requirements
19
+ # string value is passed to dateutil.tz.gettz()
20
+ # leave blank for localtime
21
+ # timezone =
22
+
23
+ # max length of characters to apply to the
24
+ # "slug" field
25
+ # truncate_slug_length = 40
26
+
27
+ # set to 'true' to run the environment during
28
+ # the 'revision' command, regardless of autogenerate
29
+ # revision_environment = false
30
+
31
+ # set to 'true' to allow .pyc and .pyo files without
32
+ # a source .py file to be detected as revisions in the
33
+ # versions/ directory
34
+ # sourceless = false
35
+
36
+ # version number format
37
+ version_num_format = %%04d
38
+
39
+ # version location specification; This defaults
40
+ # to alembic/versions. When using multiple version
41
+ # directories, initial revisions must be specified with --version-path.
42
+ # The path separator used here should be the separator specified by "version_path_separator" below.
43
+ # version_locations = %%(here)s/bar:%%(here)s/bat:alembic/versions
44
+
45
+ # version path separator; As mentioned above, this is the character used to split
46
+ # version_locations. The default within new alembic.ini files is "os", which uses os.pathsep.
47
+ # If this key is omitted entirely, it falls back to the legacy behavior of splitting on spaces and/or commas.
48
+ # Valid values for version_path_separator are:
49
+ #
50
+ # version_path_separator = :
51
+ # version_path_separator = ;
52
+ # version_path_separator = space
53
+ version_path_separator = os
54
+
55
+ # set to 'true' to search source files recursively
56
+ # in each "version_locations" directory
57
+ # new in Alembic version 1.10
58
+ # recursive_version_locations = false
59
+
60
+ # the output encoding used when revision files
61
+ # are written from script.py.mako
62
+ # output_encoding = utf-8
63
+
64
+ # Development database for generating system migrations
65
+ # It's safer for us to use a local postgres database for generating and testing migrations rather than sqlite,
66
+ # to be sure they'll work in production deployments.
67
+ # Using postgres as the dev database for autogenerating revisions also is better because
68
+ # we don't have the weird schema issues of Sqlite. Alembic doesn't fully support `schema_translate_map`
69
+ # feature in SA that we use to remap `planar`->None in SQLite (due to it not supporting schemas),
70
+ # so it sometimes incorrectly thinks it needs to re-generate things (like indices) that already
71
+ # exist in the database from a prior migration. Using postgres obviates that issue.
72
+ # https://github.com/sqlalchemy/alembic/issues/555
73
+ sqlalchemy.url = postgresql+psycopg2://postgres:postgres@localhost:5432/postgres
74
+
75
+ # [post_write_hooks]
76
+ # This section defines scripts or Python functions that are run
77
+ # on newly generated revision scripts. See the documentation for further
78
+ # detail and examples
79
+
80
+ # format using "black" - use the console_scripts runner,
81
+ # against the "black" entrypoint
82
+ # hooks = black
83
+ # black.type = console_scripts
84
+ # black.entrypoint = black
85
+ # black.options = -l 79 REVISION_SCRIPT_FILENAME
86
+
87
+ # lint with attempts to fix using "ruff" - use the exec runner, execute a binary
88
+ # hooks = ruff
89
+ # ruff.type = exec
90
+ # ruff.executable = %(here)s/.venv/bin/ruff
91
+ # ruff.options = check --fix REVISION_SCRIPT_FILENAME
92
+
93
+
94
+ # Logging configuration. This is also consumed by the user-maintained
95
+ # env.py script only.
96
+ [loggers]
97
+ keys = root,sqlalchemy,alembic
98
+
99
+ [handlers]
100
+ keys = console
101
+
102
+ [formatters]
103
+ keys = generic
104
+
105
+ [logger_root]
106
+ level = WARNING
107
+ handlers = console
108
+ qualname =
109
+
110
+ [logger_sqlalchemy]
111
+ level = WARNING
112
+ handlers =
113
+ qualname = sqlalchemy.engine
114
+
115
+ [logger_alembic]
116
+ level = INFO
117
+ handlers =
118
+ qualname = alembic
119
+
120
+ [handler_console]
121
+ class = StreamHandler
122
+ args = (sys.stderr,)
123
+ level = NOTSET
124
+ formatter = generic
125
+
126
+ [formatter_generic]
127
+ format = %(levelname)-5.5s [%(name)s] %(message)s
128
+ datefmt = %H:%M:%S