arize-phoenix 10.0.4__py3-none-any.whl → 12.28.1__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 (276) hide show
  1. {arize_phoenix-10.0.4.dist-info → arize_phoenix-12.28.1.dist-info}/METADATA +124 -72
  2. arize_phoenix-12.28.1.dist-info/RECORD +499 -0
  3. {arize_phoenix-10.0.4.dist-info → arize_phoenix-12.28.1.dist-info}/WHEEL +1 -1
  4. {arize_phoenix-10.0.4.dist-info → arize_phoenix-12.28.1.dist-info}/licenses/IP_NOTICE +1 -1
  5. phoenix/__generated__/__init__.py +0 -0
  6. phoenix/__generated__/classification_evaluator_configs/__init__.py +20 -0
  7. phoenix/__generated__/classification_evaluator_configs/_document_relevance_classification_evaluator_config.py +17 -0
  8. phoenix/__generated__/classification_evaluator_configs/_hallucination_classification_evaluator_config.py +17 -0
  9. phoenix/__generated__/classification_evaluator_configs/_models.py +18 -0
  10. phoenix/__generated__/classification_evaluator_configs/_tool_selection_classification_evaluator_config.py +17 -0
  11. phoenix/__init__.py +5 -4
  12. phoenix/auth.py +39 -2
  13. phoenix/config.py +1763 -91
  14. phoenix/datetime_utils.py +120 -2
  15. phoenix/db/README.md +595 -25
  16. phoenix/db/bulk_inserter.py +145 -103
  17. phoenix/db/engines.py +140 -33
  18. phoenix/db/enums.py +3 -12
  19. phoenix/db/facilitator.py +302 -35
  20. phoenix/db/helpers.py +1000 -65
  21. phoenix/db/iam_auth.py +64 -0
  22. phoenix/db/insertion/dataset.py +135 -2
  23. phoenix/db/insertion/document_annotation.py +9 -6
  24. phoenix/db/insertion/evaluation.py +2 -3
  25. phoenix/db/insertion/helpers.py +17 -2
  26. phoenix/db/insertion/session_annotation.py +176 -0
  27. phoenix/db/insertion/span.py +15 -11
  28. phoenix/db/insertion/span_annotation.py +3 -4
  29. phoenix/db/insertion/trace_annotation.py +3 -4
  30. phoenix/db/insertion/types.py +50 -20
  31. phoenix/db/migrations/versions/01a8342c9cdf_add_user_id_on_datasets.py +40 -0
  32. phoenix/db/migrations/versions/0df286449799_add_session_annotations_table.py +105 -0
  33. phoenix/db/migrations/versions/272b66ff50f8_drop_single_indices.py +119 -0
  34. phoenix/db/migrations/versions/58228d933c91_dataset_labels.py +67 -0
  35. phoenix/db/migrations/versions/699f655af132_experiment_tags.py +57 -0
  36. phoenix/db/migrations/versions/735d3d93c33e_add_composite_indices.py +41 -0
  37. phoenix/db/migrations/versions/a20694b15f82_cost.py +196 -0
  38. phoenix/db/migrations/versions/ab513d89518b_add_user_id_on_dataset_versions.py +40 -0
  39. phoenix/db/migrations/versions/d0690a79ea51_users_on_experiments.py +40 -0
  40. phoenix/db/migrations/versions/deb2c81c0bb2_dataset_splits.py +139 -0
  41. phoenix/db/migrations/versions/e76cbd66ffc3_add_experiments_dataset_examples.py +87 -0
  42. phoenix/db/models.py +669 -56
  43. phoenix/db/pg_config.py +10 -0
  44. phoenix/db/types/model_provider.py +4 -0
  45. phoenix/db/types/token_price_customization.py +29 -0
  46. phoenix/db/types/trace_retention.py +23 -15
  47. phoenix/experiments/evaluators/utils.py +3 -3
  48. phoenix/experiments/functions.py +160 -52
  49. phoenix/experiments/tracing.py +2 -2
  50. phoenix/experiments/types.py +1 -1
  51. phoenix/inferences/inferences.py +1 -2
  52. phoenix/server/api/auth.py +38 -7
  53. phoenix/server/api/auth_messages.py +46 -0
  54. phoenix/server/api/context.py +100 -4
  55. phoenix/server/api/dataloaders/__init__.py +79 -5
  56. phoenix/server/api/dataloaders/annotation_configs_by_project.py +31 -0
  57. phoenix/server/api/dataloaders/annotation_summaries.py +60 -8
  58. phoenix/server/api/dataloaders/average_experiment_repeated_run_group_latency.py +50 -0
  59. phoenix/server/api/dataloaders/average_experiment_run_latency.py +17 -24
  60. phoenix/server/api/dataloaders/cache/two_tier_cache.py +1 -2
  61. phoenix/server/api/dataloaders/dataset_dataset_splits.py +52 -0
  62. phoenix/server/api/dataloaders/dataset_example_revisions.py +0 -1
  63. phoenix/server/api/dataloaders/dataset_example_splits.py +40 -0
  64. phoenix/server/api/dataloaders/dataset_examples_and_versions_by_experiment_run.py +47 -0
  65. phoenix/server/api/dataloaders/dataset_labels.py +36 -0
  66. phoenix/server/api/dataloaders/document_evaluation_summaries.py +2 -2
  67. phoenix/server/api/dataloaders/document_evaluations.py +6 -9
  68. phoenix/server/api/dataloaders/experiment_annotation_summaries.py +88 -34
  69. phoenix/server/api/dataloaders/experiment_dataset_splits.py +43 -0
  70. phoenix/server/api/dataloaders/experiment_error_rates.py +21 -28
  71. phoenix/server/api/dataloaders/experiment_repeated_run_group_annotation_summaries.py +77 -0
  72. phoenix/server/api/dataloaders/experiment_repeated_run_groups.py +57 -0
  73. phoenix/server/api/dataloaders/experiment_runs_by_experiment_and_example.py +44 -0
  74. phoenix/server/api/dataloaders/last_used_times_by_generative_model_id.py +35 -0
  75. phoenix/server/api/dataloaders/latency_ms_quantile.py +40 -8
  76. phoenix/server/api/dataloaders/record_counts.py +37 -10
  77. phoenix/server/api/dataloaders/session_annotations_by_session.py +29 -0
  78. phoenix/server/api/dataloaders/span_cost_by_span.py +24 -0
  79. phoenix/server/api/dataloaders/span_cost_detail_summary_entries_by_generative_model.py +56 -0
  80. phoenix/server/api/dataloaders/span_cost_detail_summary_entries_by_project_session.py +57 -0
  81. phoenix/server/api/dataloaders/span_cost_detail_summary_entries_by_span.py +43 -0
  82. phoenix/server/api/dataloaders/span_cost_detail_summary_entries_by_trace.py +56 -0
  83. phoenix/server/api/dataloaders/span_cost_details_by_span_cost.py +27 -0
  84. phoenix/server/api/dataloaders/span_cost_summary_by_experiment.py +57 -0
  85. phoenix/server/api/dataloaders/span_cost_summary_by_experiment_repeated_run_group.py +64 -0
  86. phoenix/server/api/dataloaders/span_cost_summary_by_experiment_run.py +58 -0
  87. phoenix/server/api/dataloaders/span_cost_summary_by_generative_model.py +55 -0
  88. phoenix/server/api/dataloaders/span_cost_summary_by_project.py +152 -0
  89. phoenix/server/api/dataloaders/span_cost_summary_by_project_session.py +56 -0
  90. phoenix/server/api/dataloaders/span_cost_summary_by_trace.py +55 -0
  91. phoenix/server/api/dataloaders/span_costs.py +29 -0
  92. phoenix/server/api/dataloaders/table_fields.py +2 -2
  93. phoenix/server/api/dataloaders/token_prices_by_model.py +30 -0
  94. phoenix/server/api/dataloaders/trace_annotations_by_trace.py +27 -0
  95. phoenix/server/api/dataloaders/types.py +29 -0
  96. phoenix/server/api/exceptions.py +11 -1
  97. phoenix/server/api/helpers/dataset_helpers.py +5 -1
  98. phoenix/server/api/helpers/playground_clients.py +1243 -292
  99. phoenix/server/api/helpers/playground_registry.py +2 -2
  100. phoenix/server/api/helpers/playground_spans.py +8 -4
  101. phoenix/server/api/helpers/playground_users.py +26 -0
  102. phoenix/server/api/helpers/prompts/conversions/aws.py +83 -0
  103. phoenix/server/api/helpers/prompts/conversions/google.py +103 -0
  104. phoenix/server/api/helpers/prompts/models.py +205 -22
  105. phoenix/server/api/input_types/{SpanAnnotationFilter.py → AnnotationFilter.py} +22 -14
  106. phoenix/server/api/input_types/ChatCompletionInput.py +6 -2
  107. phoenix/server/api/input_types/CreateProjectInput.py +27 -0
  108. phoenix/server/api/input_types/CreateProjectSessionAnnotationInput.py +37 -0
  109. phoenix/server/api/input_types/DatasetFilter.py +17 -0
  110. phoenix/server/api/input_types/ExperimentRunSort.py +237 -0
  111. phoenix/server/api/input_types/GenerativeCredentialInput.py +9 -0
  112. phoenix/server/api/input_types/GenerativeModelInput.py +5 -0
  113. phoenix/server/api/input_types/ProjectSessionSort.py +161 -1
  114. phoenix/server/api/input_types/PromptFilter.py +14 -0
  115. phoenix/server/api/input_types/PromptVersionInput.py +52 -1
  116. phoenix/server/api/input_types/SpanSort.py +44 -7
  117. phoenix/server/api/input_types/TimeBinConfig.py +23 -0
  118. phoenix/server/api/input_types/UpdateAnnotationInput.py +34 -0
  119. phoenix/server/api/input_types/UserRoleInput.py +1 -0
  120. phoenix/server/api/mutations/__init__.py +10 -0
  121. phoenix/server/api/mutations/annotation_config_mutations.py +8 -8
  122. phoenix/server/api/mutations/api_key_mutations.py +19 -23
  123. phoenix/server/api/mutations/chat_mutations.py +154 -47
  124. phoenix/server/api/mutations/dataset_label_mutations.py +243 -0
  125. phoenix/server/api/mutations/dataset_mutations.py +21 -16
  126. phoenix/server/api/mutations/dataset_split_mutations.py +351 -0
  127. phoenix/server/api/mutations/experiment_mutations.py +2 -2
  128. phoenix/server/api/mutations/export_events_mutations.py +3 -3
  129. phoenix/server/api/mutations/model_mutations.py +210 -0
  130. phoenix/server/api/mutations/project_mutations.py +49 -10
  131. phoenix/server/api/mutations/project_session_annotations_mutations.py +158 -0
  132. phoenix/server/api/mutations/project_trace_retention_policy_mutations.py +8 -4
  133. phoenix/server/api/mutations/prompt_label_mutations.py +74 -65
  134. phoenix/server/api/mutations/prompt_mutations.py +65 -129
  135. phoenix/server/api/mutations/prompt_version_tag_mutations.py +11 -8
  136. phoenix/server/api/mutations/span_annotations_mutations.py +15 -10
  137. phoenix/server/api/mutations/trace_annotations_mutations.py +14 -10
  138. phoenix/server/api/mutations/trace_mutations.py +47 -3
  139. phoenix/server/api/mutations/user_mutations.py +66 -41
  140. phoenix/server/api/queries.py +768 -293
  141. phoenix/server/api/routers/__init__.py +2 -2
  142. phoenix/server/api/routers/auth.py +154 -88
  143. phoenix/server/api/routers/ldap.py +229 -0
  144. phoenix/server/api/routers/oauth2.py +369 -106
  145. phoenix/server/api/routers/v1/__init__.py +24 -4
  146. phoenix/server/api/routers/v1/annotation_configs.py +23 -31
  147. phoenix/server/api/routers/v1/annotations.py +481 -17
  148. phoenix/server/api/routers/v1/datasets.py +395 -81
  149. phoenix/server/api/routers/v1/documents.py +142 -0
  150. phoenix/server/api/routers/v1/evaluations.py +24 -31
  151. phoenix/server/api/routers/v1/experiment_evaluations.py +19 -8
  152. phoenix/server/api/routers/v1/experiment_runs.py +337 -59
  153. phoenix/server/api/routers/v1/experiments.py +479 -48
  154. phoenix/server/api/routers/v1/models.py +7 -0
  155. phoenix/server/api/routers/v1/projects.py +18 -49
  156. phoenix/server/api/routers/v1/prompts.py +54 -40
  157. phoenix/server/api/routers/v1/sessions.py +108 -0
  158. phoenix/server/api/routers/v1/spans.py +1091 -81
  159. phoenix/server/api/routers/v1/traces.py +132 -78
  160. phoenix/server/api/routers/v1/users.py +389 -0
  161. phoenix/server/api/routers/v1/utils.py +3 -7
  162. phoenix/server/api/subscriptions.py +305 -88
  163. phoenix/server/api/types/Annotation.py +90 -23
  164. phoenix/server/api/types/ApiKey.py +13 -17
  165. phoenix/server/api/types/AuthMethod.py +1 -0
  166. phoenix/server/api/types/ChatCompletionSubscriptionPayload.py +1 -0
  167. phoenix/server/api/types/CostBreakdown.py +12 -0
  168. phoenix/server/api/types/Dataset.py +226 -72
  169. phoenix/server/api/types/DatasetExample.py +88 -18
  170. phoenix/server/api/types/DatasetExperimentAnnotationSummary.py +10 -0
  171. phoenix/server/api/types/DatasetLabel.py +57 -0
  172. phoenix/server/api/types/DatasetSplit.py +98 -0
  173. phoenix/server/api/types/DatasetVersion.py +49 -4
  174. phoenix/server/api/types/DocumentAnnotation.py +212 -0
  175. phoenix/server/api/types/Experiment.py +264 -59
  176. phoenix/server/api/types/ExperimentComparison.py +5 -10
  177. phoenix/server/api/types/ExperimentRepeatedRunGroup.py +155 -0
  178. phoenix/server/api/types/ExperimentRepeatedRunGroupAnnotationSummary.py +9 -0
  179. phoenix/server/api/types/ExperimentRun.py +169 -65
  180. phoenix/server/api/types/ExperimentRunAnnotation.py +158 -39
  181. phoenix/server/api/types/GenerativeModel.py +245 -3
  182. phoenix/server/api/types/GenerativeProvider.py +70 -11
  183. phoenix/server/api/types/{Model.py → InferenceModel.py} +1 -1
  184. phoenix/server/api/types/ModelInterface.py +16 -0
  185. phoenix/server/api/types/PlaygroundModel.py +20 -0
  186. phoenix/server/api/types/Project.py +1278 -216
  187. phoenix/server/api/types/ProjectSession.py +188 -28
  188. phoenix/server/api/types/ProjectSessionAnnotation.py +187 -0
  189. phoenix/server/api/types/ProjectTraceRetentionPolicy.py +1 -1
  190. phoenix/server/api/types/Prompt.py +119 -39
  191. phoenix/server/api/types/PromptLabel.py +42 -25
  192. phoenix/server/api/types/PromptVersion.py +11 -8
  193. phoenix/server/api/types/PromptVersionTag.py +65 -25
  194. phoenix/server/api/types/ServerStatus.py +6 -0
  195. phoenix/server/api/types/Span.py +167 -123
  196. phoenix/server/api/types/SpanAnnotation.py +189 -42
  197. phoenix/server/api/types/SpanCostDetailSummaryEntry.py +10 -0
  198. phoenix/server/api/types/SpanCostSummary.py +10 -0
  199. phoenix/server/api/types/SystemApiKey.py +65 -1
  200. phoenix/server/api/types/TokenPrice.py +16 -0
  201. phoenix/server/api/types/TokenUsage.py +3 -3
  202. phoenix/server/api/types/Trace.py +223 -51
  203. phoenix/server/api/types/TraceAnnotation.py +149 -50
  204. phoenix/server/api/types/User.py +137 -32
  205. phoenix/server/api/types/UserApiKey.py +73 -26
  206. phoenix/server/api/types/node.py +10 -0
  207. phoenix/server/api/types/pagination.py +11 -2
  208. phoenix/server/app.py +290 -45
  209. phoenix/server/authorization.py +38 -3
  210. phoenix/server/bearer_auth.py +34 -24
  211. phoenix/server/cost_tracking/cost_details_calculator.py +196 -0
  212. phoenix/server/cost_tracking/cost_model_lookup.py +179 -0
  213. phoenix/server/cost_tracking/helpers.py +68 -0
  214. phoenix/server/cost_tracking/model_cost_manifest.json +3657 -830
  215. phoenix/server/cost_tracking/regex_specificity.py +397 -0
  216. phoenix/server/cost_tracking/token_cost_calculator.py +57 -0
  217. phoenix/server/daemons/__init__.py +0 -0
  218. phoenix/server/daemons/db_disk_usage_monitor.py +214 -0
  219. phoenix/server/daemons/generative_model_store.py +103 -0
  220. phoenix/server/daemons/span_cost_calculator.py +99 -0
  221. phoenix/server/dml_event.py +17 -0
  222. phoenix/server/dml_event_handler.py +5 -0
  223. phoenix/server/email/sender.py +56 -3
  224. phoenix/server/email/templates/db_disk_usage_notification.html +19 -0
  225. phoenix/server/email/types.py +11 -0
  226. phoenix/server/experiments/__init__.py +0 -0
  227. phoenix/server/experiments/utils.py +14 -0
  228. phoenix/server/grpc_server.py +11 -11
  229. phoenix/server/jwt_store.py +17 -15
  230. phoenix/server/ldap.py +1449 -0
  231. phoenix/server/main.py +26 -10
  232. phoenix/server/oauth2.py +330 -12
  233. phoenix/server/prometheus.py +66 -6
  234. phoenix/server/rate_limiters.py +4 -9
  235. phoenix/server/retention.py +33 -20
  236. phoenix/server/session_filters.py +49 -0
  237. phoenix/server/static/.vite/manifest.json +55 -51
  238. phoenix/server/static/assets/components-BreFUQQa.js +6702 -0
  239. phoenix/server/static/assets/{index-E0M82BdE.js → index-CTQoemZv.js} +140 -56
  240. phoenix/server/static/assets/pages-DBE5iYM3.js +9524 -0
  241. phoenix/server/static/assets/vendor-BGzfc4EU.css +1 -0
  242. phoenix/server/static/assets/vendor-DCE4v-Ot.js +920 -0
  243. phoenix/server/static/assets/vendor-codemirror-D5f205eT.js +25 -0
  244. phoenix/server/static/assets/vendor-recharts-V9cwpXsm.js +37 -0
  245. phoenix/server/static/assets/vendor-shiki-Do--csgv.js +5 -0
  246. phoenix/server/static/assets/vendor-three-CmB8bl_y.js +3840 -0
  247. phoenix/server/templates/index.html +40 -6
  248. phoenix/server/thread_server.py +1 -2
  249. phoenix/server/types.py +14 -4
  250. phoenix/server/utils.py +74 -0
  251. phoenix/session/client.py +56 -3
  252. phoenix/session/data_extractor.py +5 -0
  253. phoenix/session/evaluation.py +14 -5
  254. phoenix/session/session.py +45 -9
  255. phoenix/settings.py +5 -0
  256. phoenix/trace/attributes.py +80 -13
  257. phoenix/trace/dsl/helpers.py +90 -1
  258. phoenix/trace/dsl/query.py +8 -6
  259. phoenix/trace/projects.py +5 -0
  260. phoenix/utilities/template_formatters.py +1 -1
  261. phoenix/version.py +1 -1
  262. arize_phoenix-10.0.4.dist-info/RECORD +0 -405
  263. phoenix/server/api/types/Evaluation.py +0 -39
  264. phoenix/server/cost_tracking/cost_lookup.py +0 -255
  265. phoenix/server/static/assets/components-DULKeDfL.js +0 -4365
  266. phoenix/server/static/assets/pages-Cl0A-0U2.js +0 -7430
  267. phoenix/server/static/assets/vendor-WIZid84E.css +0 -1
  268. phoenix/server/static/assets/vendor-arizeai-Dy-0mSNw.js +0 -649
  269. phoenix/server/static/assets/vendor-codemirror-DBtifKNr.js +0 -33
  270. phoenix/server/static/assets/vendor-oB4u9zuV.js +0 -905
  271. phoenix/server/static/assets/vendor-recharts-D-T4KPz2.js +0 -59
  272. phoenix/server/static/assets/vendor-shiki-BMn4O_9F.js +0 -5
  273. phoenix/server/static/assets/vendor-three-C5WAXd5r.js +0 -2998
  274. phoenix/utilities/deprecation.py +0 -31
  275. {arize_phoenix-10.0.4.dist-info → arize_phoenix-12.28.1.dist-info}/entry_points.txt +0 -0
  276. {arize_phoenix-10.0.4.dist-info → arize_phoenix-12.28.1.dist-info}/licenses/LICENSE +0 -0
@@ -56,7 +56,7 @@ class TraceAnnotationQueueInserter(
56
56
  session: AsyncSession,
57
57
  *insertions: Insertables.TraceAnnotation,
58
58
  ) -> list[TraceAnnotationDmlEvent]:
59
- records = [dict(as_kv(ins.row)) for ins in insertions]
59
+ records = [{**dict(as_kv(ins.row)), "updated_at": ins.row.updated_at} for ins in insertions]
60
60
  stmt = self._insert_on_conflict(*records).returning(self.table.id)
61
61
  ids = tuple([_ async for _ in await session.stream_scalars(stmt)])
62
62
  return [TraceAnnotationDmlEvent(ids)]
@@ -91,7 +91,7 @@ class TraceAnnotationQueueInserter(
91
91
 
92
92
  for p in parcels:
93
93
  if (anno := existing_annos.get(_key(p))) is not None:
94
- if p.received_at <= anno.updated_at:
94
+ if p.item.updated_at <= anno.updated_at:
95
95
  to_discard.append(p)
96
96
  else:
97
97
  to_insert.append(
@@ -99,7 +99,6 @@ class TraceAnnotationQueueInserter(
99
99
  received_at=p.received_at,
100
100
  item=p.item.as_insertable(
101
101
  trace_rowid=anno.trace_rowid,
102
- id_=anno.id_,
103
102
  ),
104
103
  )
105
104
  )
@@ -167,7 +166,7 @@ def _key(p: Received[Precursors.TraceAnnotation]) -> _Key:
167
166
 
168
167
 
169
168
  def _unique_by(p: Received[Insertables.TraceAnnotation]) -> _UniqueBy:
170
- return p.item.obj.name, p.item.trace_rowid, p.item.identifier
169
+ return p.item.obj.name, p.item.trace_rowid, p.item.obj.identifier
171
170
 
172
171
 
173
172
  def _time(p: Received[Any]) -> datetime:
@@ -94,7 +94,10 @@ class QueueInserter(ABC, Generic[_PrecursorT, _InsertableT, _RowT, _DmlEventT]):
94
94
  async def insert(self) -> Optional[list[_DmlEventT]]:
95
95
  if not self._queue:
96
96
  return None
97
- self._queue, parcels = [], self._queue
97
+ parcels = self._queue.copy()
98
+ # IMPORTANT: Use .clear() instead of reassignment, i.e. self._queue = [], to
99
+ # avoid potential race conditions when appending postponed items to the queue.
100
+ self._queue.clear()
98
101
  events: list[_DmlEventT] = []
99
102
  async with self._db() as session:
100
103
  to_insert, to_postpone, _ = await self._partition(session, *parcels)
@@ -104,9 +107,13 @@ class QueueInserter(ABC, Generic[_PrecursorT, _InsertableT, _RowT, _DmlEventT]):
104
107
  to_postpone.extend(to_retry)
105
108
  if to_postpone:
106
109
  loop = asyncio.get_running_loop()
107
- loop.call_later(self._retry_delay_sec, self._queue.extend, to_postpone)
110
+ loop.call_later(self._retry_delay_sec, self._add_postponed_to_queue, to_postpone)
108
111
  return events
109
112
 
113
+ def _add_postponed_to_queue(self, items: list[Postponed[_PrecursorT]]) -> None:
114
+ """Add postponed items back to the queue for retry."""
115
+ self._queue.extend(items)
116
+
110
117
  def _insert_on_conflict(self, *records: Mapping[str, Any]) -> Insert:
111
118
  return insert_on_conflict(
112
119
  *records,
@@ -167,40 +174,41 @@ class QueueInserter(ABC, Generic[_PrecursorT, _InsertableT, _RowT, _DmlEventT]):
167
174
  class Precursors(ABC):
168
175
  @dataclass(frozen=True)
169
176
  class SpanAnnotation:
177
+ updated_at: datetime
170
178
  span_id: str
171
179
  obj: models.SpanAnnotation
172
180
 
173
181
  def as_insertable(
174
182
  self,
175
183
  span_rowid: int,
176
- id_: Optional[int] = None,
177
184
  ) -> Insertables.SpanAnnotation:
178
185
  return Insertables.SpanAnnotation(
186
+ updated_at=self.updated_at,
179
187
  span_id=self.span_id,
180
188
  obj=self.obj,
181
189
  span_rowid=span_rowid,
182
- id_=id_,
183
190
  )
184
191
 
185
192
  @dataclass(frozen=True)
186
193
  class TraceAnnotation:
194
+ updated_at: datetime
187
195
  trace_id: str
188
196
  obj: models.TraceAnnotation
189
197
 
190
198
  def as_insertable(
191
199
  self,
192
200
  trace_rowid: int,
193
- id_: Optional[int] = None,
194
201
  ) -> Insertables.TraceAnnotation:
195
202
  return Insertables.TraceAnnotation(
203
+ updated_at=self.updated_at,
196
204
  trace_id=self.trace_id,
197
205
  obj=self.obj,
198
206
  trace_rowid=trace_rowid,
199
- id_=id_,
200
207
  )
201
208
 
202
209
  @dataclass(frozen=True)
203
210
  class DocumentAnnotation:
211
+ updated_at: datetime
204
212
  span_id: str
205
213
  document_position: int
206
214
  obj: models.DocumentAnnotation
@@ -208,56 +216,78 @@ class Precursors(ABC):
208
216
  def as_insertable(
209
217
  self,
210
218
  span_rowid: int,
211
- id_: Optional[int] = None,
212
219
  ) -> Insertables.DocumentAnnotation:
213
220
  return Insertables.DocumentAnnotation(
221
+ updated_at=self.updated_at,
214
222
  span_id=self.span_id,
215
223
  document_position=self.document_position,
216
224
  obj=self.obj,
217
225
  span_rowid=span_rowid,
218
- id_=id_,
226
+ )
227
+
228
+ @dataclass(frozen=True)
229
+ class SessionAnnotation:
230
+ updated_at: datetime
231
+ session_id: str
232
+ obj: models.ProjectSessionAnnotation
233
+
234
+ def as_insertable(
235
+ self,
236
+ project_session_rowid: int,
237
+ ) -> Insertables.SessionAnnotation:
238
+ return Insertables.SessionAnnotation(
239
+ updated_at=self.updated_at,
240
+ session_id=self.session_id,
241
+ obj=self.obj,
242
+ project_session_rowid=project_session_rowid,
219
243
  )
220
244
 
221
245
 
222
246
  class Insertables(ABC):
223
247
  @dataclass(frozen=True)
224
248
  class SpanAnnotation(Precursors.SpanAnnotation):
249
+ updated_at: datetime
225
250
  span_rowid: int
226
- identifier: str = ""
227
- id_: Optional[int] = None
228
251
 
229
252
  @property
230
253
  def row(self) -> models.SpanAnnotation:
231
254
  obj = copy(self.obj)
232
255
  obj.span_rowid = self.span_rowid
233
- if self.id_ is not None:
234
- obj.id = self.id_
256
+ obj.updated_at = self.updated_at
235
257
  return obj
236
258
 
237
259
  @dataclass(frozen=True)
238
260
  class TraceAnnotation(Precursors.TraceAnnotation):
261
+ updated_at: datetime
239
262
  trace_rowid: int
240
- identifier: str = ""
241
- id_: Optional[int] = None
242
263
 
243
264
  @property
244
265
  def row(self) -> models.TraceAnnotation:
245
266
  obj = copy(self.obj)
246
267
  obj.trace_rowid = self.trace_rowid
247
- if self.id_ is not None:
248
- obj.id = self.id_
268
+ obj.updated_at = self.updated_at
249
269
  return obj
250
270
 
251
271
  @dataclass(frozen=True)
252
272
  class DocumentAnnotation(Precursors.DocumentAnnotation):
273
+ updated_at: datetime
253
274
  span_rowid: int
254
- identifier: str = ""
255
- id_: Optional[int] = None
256
275
 
257
276
  @property
258
277
  def row(self) -> models.DocumentAnnotation:
259
278
  obj = copy(self.obj)
260
279
  obj.span_rowid = self.span_rowid
261
- if self.id_ is not None:
262
- obj.id = self.id_
280
+ obj.updated_at = self.updated_at
281
+ return obj
282
+
283
+ @dataclass(frozen=True)
284
+ class SessionAnnotation(Precursors.SessionAnnotation):
285
+ updated_at: datetime
286
+ project_session_rowid: int
287
+
288
+ @property
289
+ def row(self) -> models.ProjectSessionAnnotation:
290
+ obj = copy(self.obj)
291
+ obj.project_session_id = self.project_session_rowid
292
+ obj.updated_at = self.updated_at
263
293
  return obj
@@ -0,0 +1,40 @@
1
+ """add user_id on datasets
2
+
3
+ Revision ID: 01a8342c9cdf
4
+ Revises: 0df286449799
5
+ Create Date: 2025-09-25 16:08:51.254947
6
+
7
+ """
8
+
9
+ from typing import Sequence, Union
10
+
11
+ import sqlalchemy as sa
12
+ from alembic import op
13
+
14
+ # revision identifiers, used by Alembic.
15
+ revision: str = "01a8342c9cdf"
16
+ down_revision: Union[str, None] = "0df286449799"
17
+ branch_labels: Union[str, Sequence[str], None] = None
18
+ depends_on: Union[str, Sequence[str], None] = None
19
+
20
+ _Integer = sa.Integer().with_variant(
21
+ sa.BigInteger(),
22
+ "postgresql",
23
+ )
24
+
25
+
26
+ def upgrade() -> None:
27
+ with op.batch_alter_table("datasets") as batch_op:
28
+ batch_op.add_column(
29
+ sa.Column(
30
+ "user_id",
31
+ _Integer,
32
+ sa.ForeignKey("users.id", ondelete="SET NULL"),
33
+ nullable=True,
34
+ ),
35
+ )
36
+
37
+
38
+ def downgrade() -> None:
39
+ with op.batch_alter_table("datasets") as batch_op:
40
+ batch_op.drop_column("user_id")
@@ -0,0 +1,105 @@
1
+ """add session annotations table
2
+
3
+ Revision ID: 0df286449799
4
+ Revises: 735d3d93c33e
5
+ Create Date: 2025-08-06 11:27:01.479664
6
+ """
7
+
8
+ from typing import Any, Sequence, Union
9
+
10
+ import sqlalchemy as sa
11
+ from alembic import op
12
+ from sqlalchemy import JSON
13
+ from sqlalchemy.dialects import postgresql
14
+ from sqlalchemy.ext.compiler import compiles
15
+
16
+ # revision identifiers, used by Alembic.
17
+ revision: str = "0df286449799"
18
+ down_revision: Union[str, None] = "735d3d93c33e"
19
+ branch_labels: Union[str, Sequence[str], None] = None
20
+ depends_on: Union[str, Sequence[str], None] = None
21
+
22
+
23
+ class JSONB(JSON):
24
+ # See https://docs.sqlalchemy.org/en/20/core/custom_types.html
25
+ __visit_name__ = "JSONB"
26
+
27
+
28
+ @compiles(JSONB, "sqlite")
29
+ def _(*args: Any, **kwargs: Any) -> str:
30
+ # See https://docs.sqlalchemy.org/en/20/core/custom_types.html
31
+ return "JSONB"
32
+
33
+
34
+ JSON_ = (
35
+ JSON()
36
+ .with_variant(
37
+ postgresql.JSONB(),
38
+ "postgresql",
39
+ )
40
+ .with_variant(
41
+ JSONB(),
42
+ "sqlite",
43
+ )
44
+ )
45
+
46
+ _Integer = sa.Integer().with_variant(
47
+ sa.BigInteger(),
48
+ "postgresql",
49
+ )
50
+
51
+
52
+ def upgrade() -> None:
53
+ op.create_table(
54
+ "project_session_annotations",
55
+ sa.Column("id", _Integer, primary_key=True),
56
+ sa.Column(
57
+ "project_session_id",
58
+ _Integer,
59
+ sa.ForeignKey("project_sessions.id", ondelete="CASCADE"),
60
+ nullable=False,
61
+ index=True,
62
+ ),
63
+ sa.Column("name", sa.String, nullable=False),
64
+ sa.Column("label", sa.String),
65
+ sa.Column("score", sa.Float),
66
+ sa.Column("explanation", sa.String),
67
+ sa.Column("metadata", JSON_, nullable=False),
68
+ sa.Column(
69
+ "annotator_kind",
70
+ sa.String,
71
+ sa.CheckConstraint(
72
+ "annotator_kind IN ('LLM', 'CODE', 'HUMAN')",
73
+ name="valid_annotator_kind",
74
+ ),
75
+ nullable=False,
76
+ ),
77
+ sa.Column(
78
+ "user_id",
79
+ _Integer,
80
+ sa.ForeignKey("users.id", ondelete="SET NULL"),
81
+ nullable=True,
82
+ ),
83
+ sa.Column("identifier", sa.String, server_default="", nullable=False),
84
+ sa.Column(
85
+ "source",
86
+ sa.String,
87
+ sa.CheckConstraint("source IN ('API', 'APP')", name="valid_source"),
88
+ nullable=False,
89
+ ),
90
+ sa.Column(
91
+ "created_at", sa.TIMESTAMP(timezone=True), server_default=sa.func.now(), nullable=False
92
+ ),
93
+ sa.Column(
94
+ "updated_at",
95
+ sa.TIMESTAMP(timezone=True),
96
+ server_default=sa.func.now(),
97
+ onupdate=sa.func.now(),
98
+ nullable=False,
99
+ ),
100
+ sa.UniqueConstraint("name", "project_session_id", "identifier"),
101
+ )
102
+
103
+
104
+ def downgrade() -> None:
105
+ op.drop_table("project_session_annotations")
@@ -0,0 +1,119 @@
1
+ """drop single indices from traces, project_sessions, and experiment_runs
2
+
3
+ Revision ID: 272b66ff50f8
4
+ Revises: a20694b15f82
5
+ Create Date: 2025-08-11 20:37:46.941940
6
+
7
+ """
8
+
9
+ from typing import Sequence, Union
10
+
11
+ from alembic import op
12
+
13
+ # revision identifiers, used by Alembic.
14
+ revision: str = "272b66ff50f8"
15
+ down_revision: Union[str, None] = "a20694b15f82"
16
+ branch_labels: Union[str, Sequence[str], None] = None
17
+ depends_on: Union[str, Sequence[str], None] = None
18
+
19
+
20
+ def upgrade() -> None:
21
+ op.drop_index(
22
+ "ix_traces_project_rowid",
23
+ table_name="traces",
24
+ if_exists=True,
25
+ )
26
+ op.drop_index(
27
+ "ix_traces_start_time",
28
+ table_name="traces",
29
+ if_exists=True,
30
+ )
31
+
32
+ op.drop_index(
33
+ "ix_project_sessions_project_id",
34
+ table_name="project_sessions",
35
+ if_exists=True,
36
+ )
37
+ op.drop_index(
38
+ "ix_project_sessions_start_time",
39
+ table_name="project_sessions",
40
+ if_exists=True,
41
+ )
42
+
43
+ op.drop_index(
44
+ "ix_experiment_runs_experiment_id",
45
+ table_name="experiment_runs",
46
+ if_exists=True,
47
+ )
48
+ op.drop_index(
49
+ "ix_experiment_run_annotations_experiment_run_id",
50
+ table_name="experiment_run_annotations",
51
+ if_exists=True,
52
+ )
53
+
54
+ op.drop_index(
55
+ "ix_dataset_example_revisions_dataset_example_id",
56
+ table_name="dataset_example_revisions",
57
+ if_exists=True,
58
+ )
59
+
60
+ op.drop_index(
61
+ "ix_span_cost_details_span_cost_id",
62
+ table_name="span_cost_details",
63
+ if_exists=True,
64
+ )
65
+
66
+
67
+ def downgrade() -> None:
68
+ op.create_index(
69
+ "ix_traces_project_rowid",
70
+ "traces",
71
+ ["project_rowid"],
72
+ if_not_exists=True,
73
+ )
74
+ op.create_index(
75
+ "ix_traces_start_time",
76
+ "traces",
77
+ ["start_time"],
78
+ if_not_exists=True,
79
+ )
80
+
81
+ op.create_index(
82
+ "ix_project_sessions_project_id",
83
+ "project_sessions",
84
+ ["project_id"],
85
+ if_not_exists=True,
86
+ )
87
+ op.create_index(
88
+ "ix_project_sessions_start_time",
89
+ "project_sessions",
90
+ ["start_time"],
91
+ if_not_exists=True,
92
+ )
93
+
94
+ op.create_index(
95
+ "ix_experiment_runs_experiment_id",
96
+ "experiment_runs",
97
+ ["experiment_id"],
98
+ if_not_exists=True,
99
+ )
100
+ op.create_index(
101
+ "ix_experiment_run_annotations_experiment_run_id",
102
+ "experiment_run_annotations",
103
+ ["experiment_run_id"],
104
+ if_not_exists=True,
105
+ )
106
+
107
+ op.create_index(
108
+ "ix_dataset_example_revisions_dataset_example_id",
109
+ "dataset_example_revisions",
110
+ ["dataset_example_id"],
111
+ if_not_exists=True,
112
+ )
113
+
114
+ op.create_index(
115
+ "ix_span_cost_details_span_cost_id",
116
+ "span_cost_details",
117
+ ["span_cost_id"],
118
+ if_not_exists=True,
119
+ )
@@ -0,0 +1,67 @@
1
+ """dataset_labels
2
+
3
+ Revision ID: 58228d933c91
4
+ Revises: 699f655af132
5
+ Create Date: 2025-09-05 17:47:34.637329
6
+
7
+ """
8
+
9
+ from typing import Sequence, Union
10
+
11
+ import sqlalchemy as sa
12
+ from alembic import op
13
+
14
+ # revision identifiers, used by Alembic.
15
+ revision: str = "58228d933c91"
16
+ down_revision: Union[str, None] = "699f655af132"
17
+ branch_labels: Union[str, Sequence[str], None] = None
18
+ depends_on: Union[str, Sequence[str], None] = None
19
+
20
+ _Integer = sa.Integer().with_variant(
21
+ sa.BigInteger(),
22
+ "postgresql",
23
+ )
24
+
25
+
26
+ def upgrade() -> None:
27
+ op.create_table(
28
+ "dataset_labels",
29
+ sa.Column("id", _Integer, primary_key=True),
30
+ sa.Column("name", sa.String, nullable=False, unique=True),
31
+ sa.Column("description", sa.String, nullable=True),
32
+ sa.Column("color", sa.String, nullable=False),
33
+ sa.Column(
34
+ "user_id",
35
+ _Integer,
36
+ sa.ForeignKey("users.id", ondelete="SET NULL"),
37
+ nullable=True,
38
+ index=True,
39
+ ),
40
+ )
41
+
42
+ op.create_table(
43
+ "datasets_dataset_labels",
44
+ sa.Column(
45
+ "dataset_id",
46
+ _Integer,
47
+ sa.ForeignKey("datasets.id", ondelete="CASCADE"),
48
+ nullable=False,
49
+ ),
50
+ sa.Column(
51
+ "dataset_label_id",
52
+ _Integer,
53
+ sa.ForeignKey("dataset_labels.id", ondelete="CASCADE"),
54
+ nullable=False,
55
+ # index on the second element of the composite primary key
56
+ index=True,
57
+ ),
58
+ sa.PrimaryKeyConstraint(
59
+ "dataset_id",
60
+ "dataset_label_id",
61
+ ),
62
+ )
63
+
64
+
65
+ def downgrade() -> None:
66
+ op.drop_table("datasets_dataset_labels")
67
+ op.drop_table("dataset_labels")
@@ -0,0 +1,57 @@
1
+ """experiment_tags
2
+
3
+ Revision ID: 699f655af132
4
+ Revises: d0690a79ea51
5
+ Create Date: 2025-09-05 13:14:22.676233
6
+
7
+ """
8
+
9
+ from typing import Sequence, Union
10
+
11
+ import sqlalchemy as sa
12
+ from alembic import op
13
+
14
+ # revision identifiers, used by Alembic.
15
+ revision: str = "699f655af132"
16
+ down_revision: Union[str, None] = "d0690a79ea51"
17
+ branch_labels: Union[str, Sequence[str], None] = None
18
+ depends_on: Union[str, Sequence[str], None] = None
19
+
20
+ _Integer = sa.Integer().with_variant(
21
+ sa.BigInteger(),
22
+ "postgresql",
23
+ )
24
+
25
+
26
+ def upgrade() -> None:
27
+ op.create_table(
28
+ "experiment_tags",
29
+ sa.Column("id", _Integer, primary_key=True),
30
+ sa.Column(
31
+ "experiment_id",
32
+ _Integer,
33
+ sa.ForeignKey("experiments.id", ondelete="CASCADE"),
34
+ nullable=False,
35
+ index=True,
36
+ ),
37
+ sa.Column(
38
+ "dataset_id",
39
+ _Integer,
40
+ sa.ForeignKey("datasets.id", ondelete="CASCADE"),
41
+ nullable=False,
42
+ ),
43
+ sa.Column(
44
+ "user_id",
45
+ _Integer,
46
+ sa.ForeignKey("users.id", ondelete="SET NULL"),
47
+ nullable=True,
48
+ index=True,
49
+ ),
50
+ sa.Column("name", sa.String, nullable=False),
51
+ sa.Column("description", sa.String, nullable=True),
52
+ sa.UniqueConstraint("dataset_id", "name"),
53
+ )
54
+
55
+
56
+ def downgrade() -> None:
57
+ op.drop_table("experiment_tags")
@@ -0,0 +1,41 @@
1
+ """add composite indices to traces and project_sessions
2
+
3
+ Revision ID: 735d3d93c33e
4
+ Revises: 272b66ff50f8
5
+ Create Date: 2025-08-11 20:52:47.477712
6
+
7
+ """
8
+
9
+ from typing import Sequence, Union
10
+
11
+ from alembic import op
12
+
13
+ # revision identifiers, used by Alembic.
14
+ revision: str = "735d3d93c33e"
15
+ down_revision: Union[str, None] = "272b66ff50f8"
16
+ branch_labels: Union[str, Sequence[str], None] = None
17
+ depends_on: Union[str, Sequence[str], None] = None
18
+
19
+
20
+ def upgrade() -> None:
21
+ op.execute(
22
+ "CREATE INDEX IF NOT EXISTS ix_traces_project_rowid_start_time "
23
+ "ON traces (project_rowid, start_time DESC)"
24
+ )
25
+ op.execute(
26
+ "CREATE INDEX IF NOT EXISTS ix_project_sessions_project_id_start_time "
27
+ "ON project_sessions (project_id, start_time DESC)"
28
+ )
29
+
30
+
31
+ def downgrade() -> None:
32
+ op.drop_index(
33
+ "ix_traces_project_rowid_start_time",
34
+ table_name="traces",
35
+ if_exists=True,
36
+ )
37
+ op.drop_index(
38
+ "ix_project_sessions_project_id_start_time",
39
+ table_name="project_sessions",
40
+ if_exists=True,
41
+ )