arize-phoenix 11.23.1__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 (221) hide show
  1. {arize_phoenix-11.23.1.dist-info → arize_phoenix-12.28.1.dist-info}/METADATA +61 -36
  2. {arize_phoenix-11.23.1.dist-info → arize_phoenix-12.28.1.dist-info}/RECORD +212 -162
  3. {arize_phoenix-11.23.1.dist-info → arize_phoenix-12.28.1.dist-info}/WHEEL +1 -1
  4. {arize_phoenix-11.23.1.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 +2 -1
  12. phoenix/auth.py +27 -2
  13. phoenix/config.py +1594 -81
  14. phoenix/db/README.md +546 -28
  15. phoenix/db/bulk_inserter.py +119 -116
  16. phoenix/db/engines.py +140 -33
  17. phoenix/db/facilitator.py +22 -1
  18. phoenix/db/helpers.py +818 -65
  19. phoenix/db/iam_auth.py +64 -0
  20. phoenix/db/insertion/dataset.py +133 -1
  21. phoenix/db/insertion/document_annotation.py +9 -6
  22. phoenix/db/insertion/evaluation.py +2 -3
  23. phoenix/db/insertion/helpers.py +2 -2
  24. phoenix/db/insertion/session_annotation.py +176 -0
  25. phoenix/db/insertion/span_annotation.py +3 -4
  26. phoenix/db/insertion/trace_annotation.py +3 -4
  27. phoenix/db/insertion/types.py +41 -18
  28. phoenix/db/migrations/versions/01a8342c9cdf_add_user_id_on_datasets.py +40 -0
  29. phoenix/db/migrations/versions/0df286449799_add_session_annotations_table.py +105 -0
  30. phoenix/db/migrations/versions/272b66ff50f8_drop_single_indices.py +119 -0
  31. phoenix/db/migrations/versions/58228d933c91_dataset_labels.py +67 -0
  32. phoenix/db/migrations/versions/699f655af132_experiment_tags.py +57 -0
  33. phoenix/db/migrations/versions/735d3d93c33e_add_composite_indices.py +41 -0
  34. phoenix/db/migrations/versions/ab513d89518b_add_user_id_on_dataset_versions.py +40 -0
  35. phoenix/db/migrations/versions/d0690a79ea51_users_on_experiments.py +40 -0
  36. phoenix/db/migrations/versions/deb2c81c0bb2_dataset_splits.py +139 -0
  37. phoenix/db/migrations/versions/e76cbd66ffc3_add_experiments_dataset_examples.py +87 -0
  38. phoenix/db/models.py +364 -56
  39. phoenix/db/pg_config.py +10 -0
  40. phoenix/db/types/trace_retention.py +7 -6
  41. phoenix/experiments/functions.py +69 -19
  42. phoenix/inferences/inferences.py +1 -2
  43. phoenix/server/api/auth.py +9 -0
  44. phoenix/server/api/auth_messages.py +46 -0
  45. phoenix/server/api/context.py +60 -0
  46. phoenix/server/api/dataloaders/__init__.py +36 -0
  47. phoenix/server/api/dataloaders/annotation_summaries.py +60 -8
  48. phoenix/server/api/dataloaders/average_experiment_repeated_run_group_latency.py +50 -0
  49. phoenix/server/api/dataloaders/average_experiment_run_latency.py +17 -24
  50. phoenix/server/api/dataloaders/cache/two_tier_cache.py +1 -2
  51. phoenix/server/api/dataloaders/dataset_dataset_splits.py +52 -0
  52. phoenix/server/api/dataloaders/dataset_example_revisions.py +0 -1
  53. phoenix/server/api/dataloaders/dataset_example_splits.py +40 -0
  54. phoenix/server/api/dataloaders/dataset_examples_and_versions_by_experiment_run.py +47 -0
  55. phoenix/server/api/dataloaders/dataset_labels.py +36 -0
  56. phoenix/server/api/dataloaders/document_evaluation_summaries.py +2 -2
  57. phoenix/server/api/dataloaders/document_evaluations.py +6 -9
  58. phoenix/server/api/dataloaders/experiment_annotation_summaries.py +88 -34
  59. phoenix/server/api/dataloaders/experiment_dataset_splits.py +43 -0
  60. phoenix/server/api/dataloaders/experiment_error_rates.py +21 -28
  61. phoenix/server/api/dataloaders/experiment_repeated_run_group_annotation_summaries.py +77 -0
  62. phoenix/server/api/dataloaders/experiment_repeated_run_groups.py +57 -0
  63. phoenix/server/api/dataloaders/experiment_runs_by_experiment_and_example.py +44 -0
  64. phoenix/server/api/dataloaders/latency_ms_quantile.py +40 -8
  65. phoenix/server/api/dataloaders/record_counts.py +37 -10
  66. phoenix/server/api/dataloaders/session_annotations_by_session.py +29 -0
  67. phoenix/server/api/dataloaders/span_cost_summary_by_experiment_repeated_run_group.py +64 -0
  68. phoenix/server/api/dataloaders/span_cost_summary_by_project.py +28 -14
  69. phoenix/server/api/dataloaders/span_costs.py +3 -9
  70. phoenix/server/api/dataloaders/table_fields.py +2 -2
  71. phoenix/server/api/dataloaders/token_prices_by_model.py +30 -0
  72. phoenix/server/api/dataloaders/trace_annotations_by_trace.py +27 -0
  73. phoenix/server/api/exceptions.py +5 -1
  74. phoenix/server/api/helpers/playground_clients.py +263 -83
  75. phoenix/server/api/helpers/playground_spans.py +2 -1
  76. phoenix/server/api/helpers/playground_users.py +26 -0
  77. phoenix/server/api/helpers/prompts/conversions/google.py +103 -0
  78. phoenix/server/api/helpers/prompts/models.py +61 -19
  79. phoenix/server/api/input_types/{SpanAnnotationFilter.py → AnnotationFilter.py} +22 -14
  80. phoenix/server/api/input_types/ChatCompletionInput.py +3 -0
  81. phoenix/server/api/input_types/CreateProjectSessionAnnotationInput.py +37 -0
  82. phoenix/server/api/input_types/DatasetFilter.py +5 -2
  83. phoenix/server/api/input_types/ExperimentRunSort.py +237 -0
  84. phoenix/server/api/input_types/GenerativeModelInput.py +3 -0
  85. phoenix/server/api/input_types/ProjectSessionSort.py +158 -1
  86. phoenix/server/api/input_types/PromptVersionInput.py +47 -1
  87. phoenix/server/api/input_types/SpanSort.py +3 -2
  88. phoenix/server/api/input_types/UpdateAnnotationInput.py +34 -0
  89. phoenix/server/api/input_types/UserRoleInput.py +1 -0
  90. phoenix/server/api/mutations/__init__.py +8 -0
  91. phoenix/server/api/mutations/annotation_config_mutations.py +8 -8
  92. phoenix/server/api/mutations/api_key_mutations.py +15 -20
  93. phoenix/server/api/mutations/chat_mutations.py +106 -37
  94. phoenix/server/api/mutations/dataset_label_mutations.py +243 -0
  95. phoenix/server/api/mutations/dataset_mutations.py +21 -16
  96. phoenix/server/api/mutations/dataset_split_mutations.py +351 -0
  97. phoenix/server/api/mutations/experiment_mutations.py +2 -2
  98. phoenix/server/api/mutations/export_events_mutations.py +3 -3
  99. phoenix/server/api/mutations/model_mutations.py +11 -9
  100. phoenix/server/api/mutations/project_mutations.py +4 -4
  101. phoenix/server/api/mutations/project_session_annotations_mutations.py +158 -0
  102. phoenix/server/api/mutations/project_trace_retention_policy_mutations.py +8 -4
  103. phoenix/server/api/mutations/prompt_label_mutations.py +74 -65
  104. phoenix/server/api/mutations/prompt_mutations.py +65 -129
  105. phoenix/server/api/mutations/prompt_version_tag_mutations.py +11 -8
  106. phoenix/server/api/mutations/span_annotations_mutations.py +15 -10
  107. phoenix/server/api/mutations/trace_annotations_mutations.py +13 -8
  108. phoenix/server/api/mutations/trace_mutations.py +3 -3
  109. phoenix/server/api/mutations/user_mutations.py +55 -26
  110. phoenix/server/api/queries.py +501 -617
  111. phoenix/server/api/routers/__init__.py +2 -2
  112. phoenix/server/api/routers/auth.py +141 -87
  113. phoenix/server/api/routers/ldap.py +229 -0
  114. phoenix/server/api/routers/oauth2.py +349 -101
  115. phoenix/server/api/routers/v1/__init__.py +22 -4
  116. phoenix/server/api/routers/v1/annotation_configs.py +19 -30
  117. phoenix/server/api/routers/v1/annotations.py +455 -13
  118. phoenix/server/api/routers/v1/datasets.py +355 -68
  119. phoenix/server/api/routers/v1/documents.py +142 -0
  120. phoenix/server/api/routers/v1/evaluations.py +20 -28
  121. phoenix/server/api/routers/v1/experiment_evaluations.py +16 -6
  122. phoenix/server/api/routers/v1/experiment_runs.py +335 -59
  123. phoenix/server/api/routers/v1/experiments.py +475 -47
  124. phoenix/server/api/routers/v1/projects.py +16 -50
  125. phoenix/server/api/routers/v1/prompts.py +50 -39
  126. phoenix/server/api/routers/v1/sessions.py +108 -0
  127. phoenix/server/api/routers/v1/spans.py +156 -96
  128. phoenix/server/api/routers/v1/traces.py +51 -77
  129. phoenix/server/api/routers/v1/users.py +64 -24
  130. phoenix/server/api/routers/v1/utils.py +3 -7
  131. phoenix/server/api/subscriptions.py +257 -93
  132. phoenix/server/api/types/Annotation.py +90 -23
  133. phoenix/server/api/types/ApiKey.py +13 -17
  134. phoenix/server/api/types/AuthMethod.py +1 -0
  135. phoenix/server/api/types/ChatCompletionSubscriptionPayload.py +1 -0
  136. phoenix/server/api/types/Dataset.py +199 -72
  137. phoenix/server/api/types/DatasetExample.py +88 -18
  138. phoenix/server/api/types/DatasetExperimentAnnotationSummary.py +10 -0
  139. phoenix/server/api/types/DatasetLabel.py +57 -0
  140. phoenix/server/api/types/DatasetSplit.py +98 -0
  141. phoenix/server/api/types/DatasetVersion.py +49 -4
  142. phoenix/server/api/types/DocumentAnnotation.py +212 -0
  143. phoenix/server/api/types/Experiment.py +215 -68
  144. phoenix/server/api/types/ExperimentComparison.py +3 -9
  145. phoenix/server/api/types/ExperimentRepeatedRunGroup.py +155 -0
  146. phoenix/server/api/types/ExperimentRepeatedRunGroupAnnotationSummary.py +9 -0
  147. phoenix/server/api/types/ExperimentRun.py +120 -70
  148. phoenix/server/api/types/ExperimentRunAnnotation.py +158 -39
  149. phoenix/server/api/types/GenerativeModel.py +95 -42
  150. phoenix/server/api/types/GenerativeProvider.py +1 -1
  151. phoenix/server/api/types/ModelInterface.py +7 -2
  152. phoenix/server/api/types/PlaygroundModel.py +12 -2
  153. phoenix/server/api/types/Project.py +218 -185
  154. phoenix/server/api/types/ProjectSession.py +146 -29
  155. phoenix/server/api/types/ProjectSessionAnnotation.py +187 -0
  156. phoenix/server/api/types/ProjectTraceRetentionPolicy.py +1 -1
  157. phoenix/server/api/types/Prompt.py +119 -39
  158. phoenix/server/api/types/PromptLabel.py +42 -25
  159. phoenix/server/api/types/PromptVersion.py +11 -8
  160. phoenix/server/api/types/PromptVersionTag.py +65 -25
  161. phoenix/server/api/types/Span.py +130 -123
  162. phoenix/server/api/types/SpanAnnotation.py +189 -42
  163. phoenix/server/api/types/SystemApiKey.py +65 -1
  164. phoenix/server/api/types/Trace.py +184 -53
  165. phoenix/server/api/types/TraceAnnotation.py +149 -50
  166. phoenix/server/api/types/User.py +128 -33
  167. phoenix/server/api/types/UserApiKey.py +73 -26
  168. phoenix/server/api/types/node.py +10 -0
  169. phoenix/server/api/types/pagination.py +11 -2
  170. phoenix/server/app.py +154 -36
  171. phoenix/server/authorization.py +5 -4
  172. phoenix/server/bearer_auth.py +13 -5
  173. phoenix/server/cost_tracking/cost_model_lookup.py +42 -14
  174. phoenix/server/cost_tracking/model_cost_manifest.json +1085 -194
  175. phoenix/server/daemons/generative_model_store.py +61 -9
  176. phoenix/server/daemons/span_cost_calculator.py +10 -8
  177. phoenix/server/dml_event.py +13 -0
  178. phoenix/server/email/sender.py +29 -2
  179. phoenix/server/grpc_server.py +9 -9
  180. phoenix/server/jwt_store.py +8 -6
  181. phoenix/server/ldap.py +1449 -0
  182. phoenix/server/main.py +9 -3
  183. phoenix/server/oauth2.py +330 -12
  184. phoenix/server/prometheus.py +43 -6
  185. phoenix/server/rate_limiters.py +4 -9
  186. phoenix/server/retention.py +33 -20
  187. phoenix/server/session_filters.py +49 -0
  188. phoenix/server/static/.vite/manifest.json +51 -53
  189. phoenix/server/static/assets/components-BreFUQQa.js +6702 -0
  190. phoenix/server/static/assets/{index-BPCwGQr8.js → index-CTQoemZv.js} +42 -35
  191. phoenix/server/static/assets/pages-DBE5iYM3.js +9524 -0
  192. phoenix/server/static/assets/vendor-BGzfc4EU.css +1 -0
  193. phoenix/server/static/assets/vendor-DCE4v-Ot.js +920 -0
  194. phoenix/server/static/assets/vendor-codemirror-D5f205eT.js +25 -0
  195. phoenix/server/static/assets/{vendor-recharts-Bw30oz1A.js → vendor-recharts-V9cwpXsm.js} +7 -7
  196. phoenix/server/static/assets/{vendor-shiki-DZajAPeq.js → vendor-shiki-Do--csgv.js} +1 -1
  197. phoenix/server/static/assets/vendor-three-CmB8bl_y.js +3840 -0
  198. phoenix/server/templates/index.html +7 -1
  199. phoenix/server/thread_server.py +1 -2
  200. phoenix/server/utils.py +74 -0
  201. phoenix/session/client.py +55 -1
  202. phoenix/session/data_extractor.py +5 -0
  203. phoenix/session/evaluation.py +8 -4
  204. phoenix/session/session.py +44 -8
  205. phoenix/settings.py +2 -0
  206. phoenix/trace/attributes.py +80 -13
  207. phoenix/trace/dsl/query.py +2 -0
  208. phoenix/trace/projects.py +5 -0
  209. phoenix/utilities/template_formatters.py +1 -1
  210. phoenix/version.py +1 -1
  211. phoenix/server/api/types/Evaluation.py +0 -39
  212. phoenix/server/static/assets/components-D0DWAf0l.js +0 -5650
  213. phoenix/server/static/assets/pages-Creyamao.js +0 -8612
  214. phoenix/server/static/assets/vendor-CU36oj8y.js +0 -905
  215. phoenix/server/static/assets/vendor-CqDb5u4o.css +0 -1
  216. phoenix/server/static/assets/vendor-arizeai-Ctgw0e1G.js +0 -168
  217. phoenix/server/static/assets/vendor-codemirror-Cojjzqb9.js +0 -25
  218. phoenix/server/static/assets/vendor-three-BLWp5bic.js +0 -2998
  219. phoenix/utilities/deprecation.py +0 -31
  220. {arize_phoenix-11.23.1.dist-info → arize_phoenix-12.28.1.dist-info}/entry_points.txt +0 -0
  221. {arize_phoenix-11.23.1.dist-info → arize_phoenix-12.28.1.dist-info}/licenses/LICENSE +0 -0
@@ -23,25 +23,25 @@ from phoenix.server.api.helpers.dataset_helpers import (
23
23
  get_dataset_example_input,
24
24
  get_dataset_example_output,
25
25
  )
26
- from phoenix.server.api.input_types.InvocationParameters import InvocationParameter
27
- from phoenix.server.api.input_types.SpanAnnotationFilter import (
28
- SpanAnnotationFilter,
26
+ from phoenix.server.api.input_types.AnnotationFilter import (
27
+ AnnotationFilter,
29
28
  satisfies_filter,
30
29
  )
30
+ from phoenix.server.api.input_types.InvocationParameters import InvocationParameter
31
31
  from phoenix.server.api.input_types.SpanAnnotationSort import (
32
32
  SpanAnnotationColumn,
33
33
  SpanAnnotationSort,
34
34
  )
35
35
  from phoenix.server.api.types.AnnotationSummary import AnnotationSummary
36
36
  from phoenix.server.api.types.CostBreakdown import CostBreakdown
37
+ from phoenix.server.api.types.DocumentAnnotation import DocumentAnnotation
37
38
  from phoenix.server.api.types.DocumentRetrievalMetrics import DocumentRetrievalMetrics
38
- from phoenix.server.api.types.Evaluation import DocumentEvaluation
39
39
  from phoenix.server.api.types.ExampleRevisionInterface import ExampleRevision
40
40
  from phoenix.server.api.types.GenerativeProvider import GenerativeProvider
41
41
  from phoenix.server.api.types.MimeType import MimeType
42
42
  from phoenix.server.api.types.pagination import ConnectionArgs, CursorString, connection_from_list
43
43
  from phoenix.server.api.types.SortDir import SortDir
44
- from phoenix.server.api.types.SpanAnnotation import SpanAnnotation, to_gql_span_annotation
44
+ from phoenix.server.api.types.SpanAnnotation import SpanAnnotation
45
45
  from phoenix.server.api.types.SpanCostDetailSummaryEntry import SpanCostDetailSummaryEntry
46
46
  from phoenix.server.api.types.SpanCostSummary import SpanCostSummary
47
47
  from phoenix.server.api.types.SpanIOValue import SpanIOValue, truncate_value
@@ -123,11 +123,11 @@ SpanRowId: TypeAlias = int
123
123
 
124
124
  @strawberry.type
125
125
  class Span(Node):
126
- span_rowid: NodeID[SpanRowId]
127
- db_span: strawberry.Private[models.Span] = UNSET
126
+ id: NodeID[SpanRowId]
127
+ db_record: strawberry.Private[Optional[models.Span]] = None
128
128
 
129
129
  def __post_init__(self) -> None:
130
- if self.db_span and self.span_rowid != self.db_span.id:
130
+ if self.db_record and self.id != self.db_record.id:
131
131
  raise ValueError("Span ID mismatch")
132
132
 
133
133
  @strawberry.field
@@ -135,10 +135,10 @@ class Span(Node):
135
135
  self,
136
136
  info: Info[Context, None],
137
137
  ) -> str:
138
- if self.db_span:
139
- return self.db_span.name
138
+ if self.db_record:
139
+ return self.db_record.name
140
140
  value = await info.context.data_loaders.span_fields.load(
141
- (self.span_rowid, models.Span.name),
141
+ (self.id, models.Span.name),
142
142
  )
143
143
  return str(value)
144
144
 
@@ -147,11 +147,11 @@ class Span(Node):
147
147
  self,
148
148
  info: Info[Context, None],
149
149
  ) -> SpanStatusCode:
150
- if self.db_span:
151
- value = self.db_span.status_code
150
+ if self.db_record:
151
+ value = self.db_record.status_code
152
152
  else:
153
153
  value = await info.context.data_loaders.span_fields.load(
154
- (self.span_rowid, models.Span.status_code),
154
+ (self.id, models.Span.status_code),
155
155
  )
156
156
  return SpanStatusCode(value)
157
157
 
@@ -160,10 +160,10 @@ class Span(Node):
160
160
  self,
161
161
  info: Info[Context, None],
162
162
  ) -> str:
163
- if self.db_span:
164
- return self.db_span.status_message
163
+ if self.db_record:
164
+ return self.db_record.status_message
165
165
  value = await info.context.data_loaders.span_fields.load(
166
- (self.span_rowid, models.Span.status_message),
166
+ (self.id, models.Span.status_message),
167
167
  )
168
168
  return str(value)
169
169
 
@@ -172,10 +172,10 @@ class Span(Node):
172
172
  self,
173
173
  info: Info[Context, None],
174
174
  ) -> datetime:
175
- if self.db_span:
176
- return self.db_span.start_time
175
+ if self.db_record:
176
+ return self.db_record.start_time
177
177
  value = await info.context.data_loaders.span_fields.load(
178
- (self.span_rowid, models.Span.start_time),
178
+ (self.id, models.Span.start_time),
179
179
  )
180
180
  return cast(datetime, value)
181
181
 
@@ -184,10 +184,10 @@ class Span(Node):
184
184
  self,
185
185
  info: Info[Context, None],
186
186
  ) -> Optional[datetime]:
187
- if self.db_span:
188
- return self.db_span.end_time
187
+ if self.db_record:
188
+ return self.db_record.end_time
189
189
  value = await info.context.data_loaders.span_fields.load(
190
- (self.span_rowid, models.Span.end_time),
190
+ (self.id, models.Span.end_time),
191
191
  )
192
192
  return cast(datetime, value)
193
193
 
@@ -196,10 +196,10 @@ class Span(Node):
196
196
  self,
197
197
  info: Info[Context, None],
198
198
  ) -> Optional[float]:
199
- if self.db_span:
200
- return self.db_span.latency_ms
199
+ if self.db_record:
200
+ return self.db_record.latency_ms
201
201
  value = await info.context.data_loaders.span_fields.load(
202
- (self.span_rowid, models.Span.latency_ms),
202
+ (self.id, models.Span.latency_ms),
203
203
  )
204
204
  return cast(float, value)
205
205
 
@@ -210,11 +210,11 @@ class Span(Node):
210
210
  self,
211
211
  info: Info[Context, None],
212
212
  ) -> Optional[ID]:
213
- if self.db_span:
214
- value = self.db_span.parent_id
213
+ if self.db_record:
214
+ value = self.db_record.parent_id
215
215
  else:
216
216
  value = await info.context.data_loaders.span_fields.load(
217
- (self.span_rowid, models.Span.parent_id),
217
+ (self.id, models.Span.parent_id),
218
218
  )
219
219
  return None if value is None else ID(value)
220
220
 
@@ -223,11 +223,11 @@ class Span(Node):
223
223
  self,
224
224
  info: Info[Context, None],
225
225
  ) -> SpanKind:
226
- if self.db_span:
227
- value = self.db_span.span_kind
226
+ if self.db_record:
227
+ value = self.db_record.span_kind
228
228
  else:
229
229
  value = await info.context.data_loaders.span_fields.load(
230
- (self.span_rowid, models.Span.span_kind),
230
+ (self.id, models.Span.span_kind),
231
231
  )
232
232
  return SpanKind(value)
233
233
 
@@ -236,11 +236,11 @@ class Span(Node):
236
236
  self,
237
237
  info: Info[Context, None],
238
238
  ) -> ID:
239
- if self.db_span:
240
- span_id = self.db_span.span_id
239
+ if self.db_record:
240
+ span_id = self.db_record.span_id
241
241
  else:
242
242
  span_id = await info.context.data_loaders.span_fields.load(
243
- (self.span_rowid, models.Span.span_id),
243
+ (self.id, models.Span.span_id),
244
244
  )
245
245
  return ID(span_id)
246
246
 
@@ -249,31 +249,31 @@ class Span(Node):
249
249
  self,
250
250
  info: Info[Context, None],
251
251
  ) -> Annotated["Trace", strawberry.lazy(".Trace")]:
252
- if self.db_span:
253
- trace_rowid = self.db_span.trace_rowid
252
+ if self.db_record:
253
+ trace_rowid = self.db_record.trace_rowid
254
254
  else:
255
255
  trace_rowid = await info.context.data_loaders.span_fields.load(
256
- (self.span_rowid, models.Span.trace_rowid),
256
+ (self.id, models.Span.trace_rowid),
257
257
  )
258
258
  from phoenix.server.api.types.Trace import Trace
259
259
 
260
- return Trace(trace_rowid=trace_rowid)
260
+ return Trace(id=trace_rowid)
261
261
 
262
262
  @strawberry.field
263
263
  async def context(
264
264
  self,
265
265
  info: Info[Context, None],
266
266
  ) -> SpanContext:
267
- if self.db_span:
268
- trace_id = self.db_span.trace.trace_id
269
- span_id = self.db_span.span_id
267
+ if self.db_record:
268
+ trace_id = self.db_record.trace.trace_id
269
+ span_id = self.db_record.span_id
270
270
  else:
271
271
  span_id, trace_id = await gather(
272
272
  info.context.data_loaders.span_fields.load(
273
- (self.span_rowid, models.Span.span_id),
273
+ (self.id, models.Span.span_id),
274
274
  ),
275
275
  info.context.data_loaders.span_fields.load(
276
- (self.span_rowid, models.Trace.trace_id),
276
+ (self.id, models.Trace.trace_id),
277
277
  ),
278
278
  )
279
279
  return SpanContext(trace_id=ID(trace_id), span_id=ID(span_id))
@@ -285,11 +285,11 @@ class Span(Node):
285
285
  self,
286
286
  info: Info[Context, None],
287
287
  ) -> str:
288
- if self.db_span:
289
- value = self.db_span.attributes
288
+ if self.db_record:
289
+ value = self.db_record.attributes
290
290
  else:
291
291
  value = await info.context.data_loaders.span_fields.load(
292
- (self.span_rowid, models.Span.attributes),
292
+ (self.id, models.Span.attributes),
293
293
  )
294
294
  return json.dumps(_hide_embedding_vectors(value), cls=_JSONEncoder)
295
295
 
@@ -300,11 +300,11 @@ class Span(Node):
300
300
  self,
301
301
  info: Info[Context, None],
302
302
  ) -> Optional[str]:
303
- if self.db_span:
304
- value = self.db_span.metadata_
303
+ if self.db_record:
304
+ value = self.db_record.metadata_
305
305
  else:
306
306
  value = await info.context.data_loaders.span_fields.load(
307
- (self.span_rowid, models.Span.metadata_),
307
+ (self.id, models.Span.metadata_),
308
308
  )
309
309
  return _convert_metadata_to_string(value)
310
310
 
@@ -313,10 +313,10 @@ class Span(Node):
313
313
  self,
314
314
  info: Info[Context, None],
315
315
  ) -> Optional[int]:
316
- if self.db_span:
317
- return self.db_span.num_documents
316
+ if self.db_record:
317
+ return self.db_record.num_documents
318
318
  value = await info.context.data_loaders.span_fields.load(
319
- (self.span_rowid, models.Span.num_documents),
319
+ (self.id, models.Span.num_documents),
320
320
  )
321
321
  return cast(int, value)
322
322
 
@@ -325,10 +325,10 @@ class Span(Node):
325
325
  self,
326
326
  info: Info[Context, None],
327
327
  ) -> Optional[int]:
328
- if self.db_span:
329
- return self.db_span.llm_token_count_total
328
+ if self.db_record:
329
+ return self.db_record.llm_token_count_total
330
330
  value = await info.context.data_loaders.span_fields.load(
331
- (self.span_rowid, models.Span.llm_token_count_total),
331
+ (self.id, models.Span.llm_token_count_total),
332
332
  )
333
333
  return cast(Optional[int], value)
334
334
 
@@ -337,10 +337,10 @@ class Span(Node):
337
337
  self,
338
338
  info: Info[Context, None],
339
339
  ) -> Optional[int]:
340
- if self.db_span:
341
- return self.db_span.llm_token_count_prompt
340
+ if self.db_record:
341
+ return self.db_record.llm_token_count_prompt
342
342
  value = await info.context.data_loaders.span_fields.load(
343
- (self.span_rowid, models.Span.llm_token_count_prompt),
343
+ (self.id, models.Span.llm_token_count_prompt),
344
344
  )
345
345
  return cast(Optional[int], value)
346
346
 
@@ -349,10 +349,10 @@ class Span(Node):
349
349
  self,
350
350
  info: Info[Context, None],
351
351
  ) -> Optional[int]:
352
- if self.db_span:
353
- return self.db_span.llm_token_count_completion
352
+ if self.db_record:
353
+ return self.db_record.llm_token_count_completion
354
354
  value = await info.context.data_loaders.span_fields.load(
355
- (self.span_rowid, models.Span.llm_token_count_completion),
355
+ (self.id, models.Span.llm_token_count_completion),
356
356
  )
357
357
  return cast(Optional[int], value)
358
358
 
@@ -361,11 +361,11 @@ class Span(Node):
361
361
  self,
362
362
  info: Info[Context, None],
363
363
  ) -> TokenCountPromptDetails:
364
- if self.db_span:
365
- attributes = self.db_span.attributes
364
+ if self.db_record:
365
+ attributes = self.db_record.attributes
366
366
  else:
367
367
  attributes = await info.context.data_loaders.span_fields.load(
368
- (self.span_rowid, models.Span.attributes),
368
+ (self.id, models.Span.attributes),
369
369
  )
370
370
 
371
371
  cache_read: Optional[int] = None
@@ -403,28 +403,28 @@ class Span(Node):
403
403
  self,
404
404
  info: Info[Context, None],
405
405
  ) -> Optional[SpanIOValue]:
406
- if self.db_span:
407
- input_value = self.db_span.input_value
406
+ if self.db_record:
407
+ input_value = self.db_record.input_value
408
408
  if input_value is None or input_value == "":
409
409
  return None
410
410
  input_value = str(input_value)
411
- mime_type = self.db_span.input_mime_type
411
+ mime_type = self.db_record.input_mime_type
412
412
  return SpanIOValue(
413
413
  cached_value=input_value,
414
414
  mime_type=MimeType(mime_type),
415
415
  )
416
416
  mime_type, input_value_first_101_chars = await gather(
417
417
  info.context.data_loaders.span_fields.load(
418
- (self.span_rowid, models.Span.input_mime_type),
418
+ (self.id, models.Span.input_mime_type),
419
419
  ),
420
420
  info.context.data_loaders.span_fields.load(
421
- (self.span_rowid, models.Span.input_value_first_101_chars),
421
+ (self.id, models.Span.input_value_first_101_chars),
422
422
  ),
423
423
  )
424
424
  if not input_value_first_101_chars:
425
425
  return None
426
426
  return SpanIOValue(
427
- span_rowid=self.span_rowid,
427
+ span_rowid=self.id,
428
428
  attr=models.Span.input_value,
429
429
  truncated_value=truncate_value(input_value_first_101_chars),
430
430
  mime_type=MimeType(mime_type),
@@ -435,28 +435,28 @@ class Span(Node):
435
435
  self,
436
436
  info: Info[Context, None],
437
437
  ) -> Optional[SpanIOValue]:
438
- if self.db_span:
439
- output_value = self.db_span.output_value
438
+ if self.db_record:
439
+ output_value = self.db_record.output_value
440
440
  if output_value is None or output_value == "":
441
441
  return None
442
442
  output_value = str(output_value)
443
- mime_type = self.db_span.output_mime_type
443
+ mime_type = self.db_record.output_mime_type
444
444
  return SpanIOValue(
445
445
  cached_value=output_value,
446
446
  mime_type=MimeType(mime_type),
447
447
  )
448
448
  mime_type, output_value_first_101_chars = await gather(
449
449
  info.context.data_loaders.span_fields.load(
450
- (self.span_rowid, models.Span.output_mime_type),
450
+ (self.id, models.Span.output_mime_type),
451
451
  ),
452
452
  info.context.data_loaders.span_fields.load(
453
- (self.span_rowid, models.Span.output_value_first_101_chars),
453
+ (self.id, models.Span.output_value_first_101_chars),
454
454
  ),
455
455
  )
456
456
  if not output_value_first_101_chars:
457
457
  return None
458
458
  return SpanIOValue(
459
- span_rowid=self.span_rowid,
459
+ span_rowid=self.id,
460
460
  attr=models.Span.output_value,
461
461
  truncated_value=truncate_value(output_value_first_101_chars),
462
462
  mime_type=MimeType(mime_type),
@@ -467,10 +467,10 @@ class Span(Node):
467
467
  self,
468
468
  info: Info[Context, None],
469
469
  ) -> list[SpanEvent]:
470
- if self.db_span:
471
- return [SpanEvent.from_dict(event) for event in self.db_span.events]
470
+ if self.db_record:
471
+ return [SpanEvent.from_dict(event) for event in self.db_record.events]
472
472
  value = await info.context.data_loaders.span_fields.load(
473
- (self.span_rowid, models.Span.events),
473
+ (self.id, models.Span.events),
474
474
  )
475
475
  return [SpanEvent.from_dict(event) for event in value]
476
476
 
@@ -481,13 +481,13 @@ class Span(Node):
481
481
  async def cumulative_token_count_total(
482
482
  self,
483
483
  info: Info[Context, None],
484
- ) -> Optional[int]:
485
- if self.db_span:
486
- return self.db_span.cumulative_llm_token_count_total
484
+ ) -> Optional[float]:
485
+ if self.db_record:
486
+ return float(self.db_record.cumulative_llm_token_count_total)
487
487
  value = await info.context.data_loaders.span_fields.load(
488
- (self.span_rowid, models.Span.cumulative_llm_token_count_total),
488
+ (self.id, models.Span.cumulative_llm_token_count_total),
489
489
  )
490
- return cast(Optional[int], value)
490
+ return float(value) if value is not None else None
491
491
 
492
492
  @strawberry.field(
493
493
  description="Cumulative (prompt) token count from self and all descendant "
@@ -496,13 +496,13 @@ class Span(Node):
496
496
  async def cumulative_token_count_prompt(
497
497
  self,
498
498
  info: Info[Context, None],
499
- ) -> Optional[int]:
500
- if self.db_span:
501
- return self.db_span.cumulative_llm_token_count_prompt
499
+ ) -> Optional[float]:
500
+ if self.db_record:
501
+ return float(self.db_record.cumulative_llm_token_count_prompt)
502
502
  value = await info.context.data_loaders.span_fields.load(
503
- (self.span_rowid, models.Span.cumulative_llm_token_count_prompt),
503
+ (self.id, models.Span.cumulative_llm_token_count_prompt),
504
504
  )
505
- return cast(Optional[int], value)
505
+ return float(value) if value is not None else None
506
506
 
507
507
  @strawberry.field(
508
508
  description="Cumulative (completion) token count from self and all descendant "
@@ -511,13 +511,13 @@ class Span(Node):
511
511
  async def cumulative_token_count_completion(
512
512
  self,
513
513
  info: Info[Context, None],
514
- ) -> Optional[int]:
515
- if self.db_span:
516
- return self.db_span.cumulative_llm_token_count_completion
514
+ ) -> Optional[float]:
515
+ if self.db_record:
516
+ return float(self.db_record.cumulative_llm_token_count_completion)
517
517
  value = await info.context.data_loaders.span_fields.load(
518
- (self.span_rowid, models.Span.cumulative_llm_token_count_completion),
518
+ (self.id, models.Span.cumulative_llm_token_count_completion),
519
519
  )
520
- return cast(Optional[int], value)
520
+ return float(value) if value is not None else None
521
521
 
522
522
  @strawberry.field(
523
523
  description="Propagated status code that percolates up error status codes from "
@@ -527,11 +527,11 @@ class Span(Node):
527
527
  self,
528
528
  info: Info[Context, None],
529
529
  ) -> SpanStatusCode:
530
- if self.db_span:
531
- value = self.db_span.cumulative_error_count
530
+ if self.db_record:
531
+ value = self.db_record.cumulative_error_count
532
532
  else:
533
533
  value = await info.context.data_loaders.span_fields.load(
534
- (self.span_rowid, models.Span.cumulative_error_count),
534
+ (self.id, models.Span.cumulative_error_count),
535
535
  )
536
536
  return SpanStatusCode.ERROR if value else SpanStatusCode.OK
537
537
 
@@ -544,9 +544,9 @@ class Span(Node):
544
544
  self,
545
545
  info: Info[Context, None],
546
546
  sort: Optional[SpanAnnotationSort] = UNSET,
547
- filter: Optional[SpanAnnotationFilter] = None,
547
+ filter: Optional[AnnotationFilter] = None,
548
548
  ) -> list[SpanAnnotation]:
549
- span_id = self.span_rowid
549
+ span_id = self.id
550
550
  annotations = await info.context.data_loaders.span_annotations.load(span_id)
551
551
  sort_key = SpanAnnotationColumn.name.value
552
552
  sort_descending = False
@@ -560,24 +560,28 @@ class Span(Node):
560
560
  annotations.sort(
561
561
  key=lambda annotation: getattr(annotation, sort_key), reverse=sort_descending
562
562
  )
563
- return [to_gql_span_annotation(annotation) for annotation in annotations]
563
+ return [
564
+ SpanAnnotation(id=annotation.id, db_record=annotation) for annotation in annotations
565
+ ]
564
566
 
565
567
  @strawberry.field(description=("Notes associated with the span.")) # type: ignore
566
568
  async def span_notes(
567
569
  self,
568
570
  info: Info[Context, None],
569
571
  ) -> list[SpanAnnotation]:
570
- span_id = self.span_rowid
572
+ span_id = self.id
571
573
  annotations = await info.context.data_loaders.span_annotations.load(span_id)
572
574
  annotations = [annotation for annotation in annotations if annotation.name == "note"]
573
575
  annotations.sort(key=lambda annotation: getattr(annotation, "created_at"), reverse=False)
574
- return [to_gql_span_annotation(annotation) for annotation in annotations]
576
+ return [
577
+ SpanAnnotation(id=annotation.id, db_record=annotation) for annotation in annotations
578
+ ]
575
579
 
576
580
  @strawberry.field(description="Summarizes each annotation (by name) associated with the span") # type: ignore
577
581
  async def span_annotation_summaries(
578
582
  self,
579
583
  info: Info[Context, None],
580
- filter: Optional[SpanAnnotationFilter] = None,
584
+ filter: Optional[AnnotationFilter] = None,
581
585
  ) -> list[AnnotationSummary]:
582
586
  """
583
587
  Retrieves and summarizes annotations associated with this span.
@@ -596,7 +600,7 @@ class Span(Node):
596
600
  - data: A list of dictionaries with label statistics
597
601
  """
598
602
  # Load all annotations for this span from the data loader
599
- annotations = await info.context.data_loaders.span_annotations.load(self.span_rowid)
603
+ annotations = await info.context.data_loaders.span_annotations.load(self.id)
600
604
 
601
605
  # Apply filter if provided to narrow down the annotations
602
606
  if filter:
@@ -638,8 +642,11 @@ class Span(Node):
638
642
  async def document_evaluations(
639
643
  self,
640
644
  info: Info[Context, None],
641
- ) -> list[DocumentEvaluation]:
642
- return await info.context.data_loaders.document_evaluations.load(self.span_rowid)
645
+ ) -> list[DocumentAnnotation]:
646
+ return [
647
+ DocumentAnnotation(id=anno.id, db_record=anno)
648
+ for anno in await info.context.data_loaders.document_evaluations.load(self.id)
649
+ ]
643
650
 
644
651
  @strawberry.field(
645
652
  description="Retrieval metrics: NDCG@K, Precision@K, Reciprocal Rank, etc.",
@@ -650,21 +657,21 @@ class Span(Node):
650
657
  evaluation_name: Optional[str] = UNSET,
651
658
  ) -> list[DocumentRetrievalMetrics]:
652
659
  num_documents = (
653
- self.db_span.num_documents
654
- if self.db_span
660
+ self.db_record.num_documents
661
+ if self.db_record
655
662
  else await info.context.data_loaders.span_fields.load(
656
- (self.span_rowid, models.Span.num_documents),
663
+ (self.id, models.Span.num_documents),
657
664
  )
658
665
  )
659
666
  if not num_documents:
660
667
  return []
661
668
  return await info.context.data_loaders.document_retrieval_metrics.load(
662
- (self.span_rowid, evaluation_name or None, num_documents),
669
+ (self.id, evaluation_name or None, num_documents),
663
670
  )
664
671
 
665
672
  @strawberry.field
666
673
  async def num_child_spans(self, info: Info[Context, None]) -> int:
667
- return await info.context.data_loaders.num_child_spans.load(self.span_rowid)
674
+ return await info.context.data_loaders.num_child_spans.load(self.id)
668
675
 
669
676
  @strawberry.field(
670
677
  description="All descendant spans (children, grandchildren, etc.)",
@@ -693,9 +700,9 @@ class Span(Node):
693
700
  before=before if isinstance(before, CursorString) else None,
694
701
  )
695
702
  span_rowids: Iterable[int] = await info.context.data_loaders.span_descendants.load(
696
- (self.span_rowid, max_depth or None),
703
+ (self.id, max_depth or None),
697
704
  )
698
- data = [Span(span_rowid=span_rowid) for span_rowid in span_rowids]
705
+ data = [Span(id=span_rowid) for span_rowid in span_rowids]
699
706
  return connection_from_list(data=data, args=args)
700
707
 
701
708
  @strawberry.field(
@@ -706,9 +713,9 @@ class Span(Node):
706
713
  info: Info[Context, None],
707
714
  ) -> SpanAsExampleRevision:
708
715
  span = (
709
- self.db_span
710
- if self.db_span
711
- else await info.context.data_loaders.span_by_id.load(self.span_rowid)
716
+ self.db_record
717
+ if self.db_record
718
+ else await info.context.data_loaders.span_by_id.load(self.id)
712
719
  )
713
720
 
714
721
  # Fetch annotations associated with this span
@@ -743,16 +750,16 @@ class Span(Node):
743
750
  ]: # use lazy types to avoid circular import: https://strawberry.rocks/docs/types/lazy
744
751
  from phoenix.server.api.types.Project import Project
745
752
 
746
- span_id = self.span_rowid
753
+ span_id = self.id
747
754
  project = await info.context.data_loaders.span_projects.load(span_id)
748
- return Project(project_rowid=project.id, db_project=project)
755
+ return Project(id=project.id, db_record=project)
749
756
 
750
757
  @strawberry.field(description="Indicates if the span is contained in any dataset") # type: ignore
751
758
  async def contained_in_dataset(
752
759
  self,
753
760
  info: Info[Context, None],
754
761
  ) -> bool:
755
- examples = await info.context.data_loaders.span_dataset_examples.load(self.span_rowid)
762
+ examples = await info.context.data_loaders.span_dataset_examples.load(self.id)
756
763
  return bool(examples)
757
764
 
758
765
  @strawberry.field(description="Invocation parameters for the span") # type: ignore
@@ -763,7 +770,7 @@ class Span(Node):
763
770
  from phoenix.server.api.helpers.playground_clients import OpenAIStreamingClient
764
771
  from phoenix.server.api.helpers.playground_registry import PLAYGROUND_CLIENT_REGISTRY
765
772
 
766
- db_span: models.Span = await info.context.data_loaders.span_by_id.load(self.span_rowid)
773
+ db_span: models.Span = await info.context.data_loaders.span_by_id.load(self.id)
767
774
  attributes = db_span.attributes
768
775
  llm_provider = GenerativeProvider.get_model_provider_from_attributes(attributes)
769
776
  if llm_provider is None:
@@ -794,7 +801,7 @@ class Span(Node):
794
801
 
795
802
  @strawberry.field
796
803
  async def cost_summary(self, info: Info[Context, None]) -> Optional[SpanCostSummary]:
797
- span_cost = await info.context.data_loaders.span_cost_by_span.load(self.span_rowid)
804
+ span_cost = await info.context.data_loaders.span_cost_by_span.load(self.id)
798
805
  if span_cost is None:
799
806
  return None
800
807
  return SpanCostSummary(
@@ -817,7 +824,7 @@ class Span(Node):
817
824
  self, info: Info[Context, None]
818
825
  ) -> list[SpanCostDetailSummaryEntry]:
819
826
  loader = info.context.data_loaders.span_cost_detail_summary_entries_by_span
820
- entries = await loader.load(self.span_rowid)
827
+ entries = await loader.load(self.id)
821
828
  return [
822
829
  SpanCostDetailSummaryEntry(
823
830
  token_type=entry.token_type,