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
@@ -23,24 +23,27 @@ 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
+ from phoenix.server.api.types.CostBreakdown import CostBreakdown
37
+ from phoenix.server.api.types.DocumentAnnotation import DocumentAnnotation
36
38
  from phoenix.server.api.types.DocumentRetrievalMetrics import DocumentRetrievalMetrics
37
- from phoenix.server.api.types.Evaluation import DocumentEvaluation
38
39
  from phoenix.server.api.types.ExampleRevisionInterface import ExampleRevision
39
40
  from phoenix.server.api.types.GenerativeProvider import GenerativeProvider
40
41
  from phoenix.server.api.types.MimeType import MimeType
41
42
  from phoenix.server.api.types.pagination import ConnectionArgs, CursorString, connection_from_list
42
43
  from phoenix.server.api.types.SortDir import SortDir
43
- from phoenix.server.api.types.SpanAnnotation import SpanAnnotation, to_gql_span_annotation
44
+ from phoenix.server.api.types.SpanAnnotation import SpanAnnotation
45
+ from phoenix.server.api.types.SpanCostDetailSummaryEntry import SpanCostDetailSummaryEntry
46
+ from phoenix.server.api.types.SpanCostSummary import SpanCostSummary
44
47
  from phoenix.server.api.types.SpanIOValue import SpanIOValue, truncate_value
45
48
  from phoenix.trace.attributes import get_attribute_value
46
49
 
@@ -120,11 +123,11 @@ SpanRowId: TypeAlias = int
120
123
 
121
124
  @strawberry.type
122
125
  class Span(Node):
123
- span_rowid: NodeID[SpanRowId]
124
- db_span: strawberry.Private[models.Span] = UNSET
126
+ id: NodeID[SpanRowId]
127
+ db_record: strawberry.Private[Optional[models.Span]] = None
125
128
 
126
129
  def __post_init__(self) -> None:
127
- if self.db_span and self.span_rowid != self.db_span.id:
130
+ if self.db_record and self.id != self.db_record.id:
128
131
  raise ValueError("Span ID mismatch")
129
132
 
130
133
  @strawberry.field
@@ -132,10 +135,10 @@ class Span(Node):
132
135
  self,
133
136
  info: Info[Context, None],
134
137
  ) -> str:
135
- if self.db_span:
136
- return self.db_span.name
138
+ if self.db_record:
139
+ return self.db_record.name
137
140
  value = await info.context.data_loaders.span_fields.load(
138
- (self.span_rowid, models.Span.name),
141
+ (self.id, models.Span.name),
139
142
  )
140
143
  return str(value)
141
144
 
@@ -144,11 +147,11 @@ class Span(Node):
144
147
  self,
145
148
  info: Info[Context, None],
146
149
  ) -> SpanStatusCode:
147
- if self.db_span:
148
- value = self.db_span.status_code
150
+ if self.db_record:
151
+ value = self.db_record.status_code
149
152
  else:
150
153
  value = await info.context.data_loaders.span_fields.load(
151
- (self.span_rowid, models.Span.status_code),
154
+ (self.id, models.Span.status_code),
152
155
  )
153
156
  return SpanStatusCode(value)
154
157
 
@@ -157,10 +160,10 @@ class Span(Node):
157
160
  self,
158
161
  info: Info[Context, None],
159
162
  ) -> str:
160
- if self.db_span:
161
- return self.db_span.status_message
163
+ if self.db_record:
164
+ return self.db_record.status_message
162
165
  value = await info.context.data_loaders.span_fields.load(
163
- (self.span_rowid, models.Span.status_message),
166
+ (self.id, models.Span.status_message),
164
167
  )
165
168
  return str(value)
166
169
 
@@ -169,10 +172,10 @@ class Span(Node):
169
172
  self,
170
173
  info: Info[Context, None],
171
174
  ) -> datetime:
172
- if self.db_span:
173
- return self.db_span.start_time
175
+ if self.db_record:
176
+ return self.db_record.start_time
174
177
  value = await info.context.data_loaders.span_fields.load(
175
- (self.span_rowid, models.Span.start_time),
178
+ (self.id, models.Span.start_time),
176
179
  )
177
180
  return cast(datetime, value)
178
181
 
@@ -181,10 +184,10 @@ class Span(Node):
181
184
  self,
182
185
  info: Info[Context, None],
183
186
  ) -> Optional[datetime]:
184
- if self.db_span:
185
- return self.db_span.end_time
187
+ if self.db_record:
188
+ return self.db_record.end_time
186
189
  value = await info.context.data_loaders.span_fields.load(
187
- (self.span_rowid, models.Span.end_time),
190
+ (self.id, models.Span.end_time),
188
191
  )
189
192
  return cast(datetime, value)
190
193
 
@@ -193,10 +196,10 @@ class Span(Node):
193
196
  self,
194
197
  info: Info[Context, None],
195
198
  ) -> Optional[float]:
196
- if self.db_span:
197
- return self.db_span.latency_ms
199
+ if self.db_record:
200
+ return self.db_record.latency_ms
198
201
  value = await info.context.data_loaders.span_fields.load(
199
- (self.span_rowid, models.Span.latency_ms),
202
+ (self.id, models.Span.latency_ms),
200
203
  )
201
204
  return cast(float, value)
202
205
 
@@ -207,11 +210,11 @@ class Span(Node):
207
210
  self,
208
211
  info: Info[Context, None],
209
212
  ) -> Optional[ID]:
210
- if self.db_span:
211
- value = self.db_span.parent_id
213
+ if self.db_record:
214
+ value = self.db_record.parent_id
212
215
  else:
213
216
  value = await info.context.data_loaders.span_fields.load(
214
- (self.span_rowid, models.Span.parent_id),
217
+ (self.id, models.Span.parent_id),
215
218
  )
216
219
  return None if value is None else ID(value)
217
220
 
@@ -220,11 +223,11 @@ class Span(Node):
220
223
  self,
221
224
  info: Info[Context, None],
222
225
  ) -> SpanKind:
223
- if self.db_span:
224
- value = self.db_span.span_kind
226
+ if self.db_record:
227
+ value = self.db_record.span_kind
225
228
  else:
226
229
  value = await info.context.data_loaders.span_fields.load(
227
- (self.span_rowid, models.Span.span_kind),
230
+ (self.id, models.Span.span_kind),
228
231
  )
229
232
  return SpanKind(value)
230
233
 
@@ -233,11 +236,11 @@ class Span(Node):
233
236
  self,
234
237
  info: Info[Context, None],
235
238
  ) -> ID:
236
- if self.db_span:
237
- span_id = self.db_span.span_id
239
+ if self.db_record:
240
+ span_id = self.db_record.span_id
238
241
  else:
239
242
  span_id = await info.context.data_loaders.span_fields.load(
240
- (self.span_rowid, models.Span.span_id),
243
+ (self.id, models.Span.span_id),
241
244
  )
242
245
  return ID(span_id)
243
246
 
@@ -246,31 +249,31 @@ class Span(Node):
246
249
  self,
247
250
  info: Info[Context, None],
248
251
  ) -> Annotated["Trace", strawberry.lazy(".Trace")]:
249
- if self.db_span:
250
- trace_rowid = self.db_span.trace_rowid
252
+ if self.db_record:
253
+ trace_rowid = self.db_record.trace_rowid
251
254
  else:
252
255
  trace_rowid = await info.context.data_loaders.span_fields.load(
253
- (self.span_rowid, models.Span.trace_rowid),
256
+ (self.id, models.Span.trace_rowid),
254
257
  )
255
258
  from phoenix.server.api.types.Trace import Trace
256
259
 
257
- return Trace(trace_rowid=trace_rowid)
260
+ return Trace(id=trace_rowid)
258
261
 
259
262
  @strawberry.field
260
263
  async def context(
261
264
  self,
262
265
  info: Info[Context, None],
263
266
  ) -> SpanContext:
264
- if self.db_span:
265
- trace_id = self.db_span.trace.trace_id
266
- 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
267
270
  else:
268
271
  span_id, trace_id = await gather(
269
272
  info.context.data_loaders.span_fields.load(
270
- (self.span_rowid, models.Span.span_id),
273
+ (self.id, models.Span.span_id),
271
274
  ),
272
275
  info.context.data_loaders.span_fields.load(
273
- (self.span_rowid, models.Trace.trace_id),
276
+ (self.id, models.Trace.trace_id),
274
277
  ),
275
278
  )
276
279
  return SpanContext(trace_id=ID(trace_id), span_id=ID(span_id))
@@ -282,11 +285,11 @@ class Span(Node):
282
285
  self,
283
286
  info: Info[Context, None],
284
287
  ) -> str:
285
- if self.db_span:
286
- value = self.db_span.attributes
288
+ if self.db_record:
289
+ value = self.db_record.attributes
287
290
  else:
288
291
  value = await info.context.data_loaders.span_fields.load(
289
- (self.span_rowid, models.Span.attributes),
292
+ (self.id, models.Span.attributes),
290
293
  )
291
294
  return json.dumps(_hide_embedding_vectors(value), cls=_JSONEncoder)
292
295
 
@@ -297,11 +300,11 @@ class Span(Node):
297
300
  self,
298
301
  info: Info[Context, None],
299
302
  ) -> Optional[str]:
300
- if self.db_span:
301
- value = self.db_span.metadata_
303
+ if self.db_record:
304
+ value = self.db_record.metadata_
302
305
  else:
303
306
  value = await info.context.data_loaders.span_fields.load(
304
- (self.span_rowid, models.Span.metadata_),
307
+ (self.id, models.Span.metadata_),
305
308
  )
306
309
  return _convert_metadata_to_string(value)
307
310
 
@@ -310,10 +313,10 @@ class Span(Node):
310
313
  self,
311
314
  info: Info[Context, None],
312
315
  ) -> Optional[int]:
313
- if self.db_span:
314
- return self.db_span.num_documents
316
+ if self.db_record:
317
+ return self.db_record.num_documents
315
318
  value = await info.context.data_loaders.span_fields.load(
316
- (self.span_rowid, models.Span.num_documents),
319
+ (self.id, models.Span.num_documents),
317
320
  )
318
321
  return cast(int, value)
319
322
 
@@ -322,10 +325,10 @@ class Span(Node):
322
325
  self,
323
326
  info: Info[Context, None],
324
327
  ) -> Optional[int]:
325
- if self.db_span:
326
- return self.db_span.llm_token_count_total
328
+ if self.db_record:
329
+ return self.db_record.llm_token_count_total
327
330
  value = await info.context.data_loaders.span_fields.load(
328
- (self.span_rowid, models.Span.llm_token_count_total),
331
+ (self.id, models.Span.llm_token_count_total),
329
332
  )
330
333
  return cast(Optional[int], value)
331
334
 
@@ -334,10 +337,10 @@ class Span(Node):
334
337
  self,
335
338
  info: Info[Context, None],
336
339
  ) -> Optional[int]:
337
- if self.db_span:
338
- return self.db_span.llm_token_count_prompt
340
+ if self.db_record:
341
+ return self.db_record.llm_token_count_prompt
339
342
  value = await info.context.data_loaders.span_fields.load(
340
- (self.span_rowid, models.Span.llm_token_count_prompt),
343
+ (self.id, models.Span.llm_token_count_prompt),
341
344
  )
342
345
  return cast(Optional[int], value)
343
346
 
@@ -346,10 +349,10 @@ class Span(Node):
346
349
  self,
347
350
  info: Info[Context, None],
348
351
  ) -> Optional[int]:
349
- if self.db_span:
350
- return self.db_span.llm_token_count_completion
352
+ if self.db_record:
353
+ return self.db_record.llm_token_count_completion
351
354
  value = await info.context.data_loaders.span_fields.load(
352
- (self.span_rowid, models.Span.llm_token_count_completion),
355
+ (self.id, models.Span.llm_token_count_completion),
353
356
  )
354
357
  return cast(Optional[int], value)
355
358
 
@@ -358,11 +361,11 @@ class Span(Node):
358
361
  self,
359
362
  info: Info[Context, None],
360
363
  ) -> TokenCountPromptDetails:
361
- if self.db_span:
362
- attributes = self.db_span.attributes
364
+ if self.db_record:
365
+ attributes = self.db_record.attributes
363
366
  else:
364
367
  attributes = await info.context.data_loaders.span_fields.load(
365
- (self.span_rowid, models.Span.attributes),
368
+ (self.id, models.Span.attributes),
366
369
  )
367
370
 
368
371
  cache_read: Optional[int] = None
@@ -400,28 +403,28 @@ class Span(Node):
400
403
  self,
401
404
  info: Info[Context, None],
402
405
  ) -> Optional[SpanIOValue]:
403
- if self.db_span:
404
- input_value = self.db_span.input_value
406
+ if self.db_record:
407
+ input_value = self.db_record.input_value
405
408
  if input_value is None or input_value == "":
406
409
  return None
407
410
  input_value = str(input_value)
408
- mime_type = self.db_span.input_mime_type
411
+ mime_type = self.db_record.input_mime_type
409
412
  return SpanIOValue(
410
413
  cached_value=input_value,
411
414
  mime_type=MimeType(mime_type),
412
415
  )
413
416
  mime_type, input_value_first_101_chars = await gather(
414
417
  info.context.data_loaders.span_fields.load(
415
- (self.span_rowid, models.Span.input_mime_type),
418
+ (self.id, models.Span.input_mime_type),
416
419
  ),
417
420
  info.context.data_loaders.span_fields.load(
418
- (self.span_rowid, models.Span.input_value_first_101_chars),
421
+ (self.id, models.Span.input_value_first_101_chars),
419
422
  ),
420
423
  )
421
424
  if not input_value_first_101_chars:
422
425
  return None
423
426
  return SpanIOValue(
424
- span_rowid=self.span_rowid,
427
+ span_rowid=self.id,
425
428
  attr=models.Span.input_value,
426
429
  truncated_value=truncate_value(input_value_first_101_chars),
427
430
  mime_type=MimeType(mime_type),
@@ -432,28 +435,28 @@ class Span(Node):
432
435
  self,
433
436
  info: Info[Context, None],
434
437
  ) -> Optional[SpanIOValue]:
435
- if self.db_span:
436
- output_value = self.db_span.output_value
438
+ if self.db_record:
439
+ output_value = self.db_record.output_value
437
440
  if output_value is None or output_value == "":
438
441
  return None
439
442
  output_value = str(output_value)
440
- mime_type = self.db_span.output_mime_type
443
+ mime_type = self.db_record.output_mime_type
441
444
  return SpanIOValue(
442
445
  cached_value=output_value,
443
446
  mime_type=MimeType(mime_type),
444
447
  )
445
448
  mime_type, output_value_first_101_chars = await gather(
446
449
  info.context.data_loaders.span_fields.load(
447
- (self.span_rowid, models.Span.output_mime_type),
450
+ (self.id, models.Span.output_mime_type),
448
451
  ),
449
452
  info.context.data_loaders.span_fields.load(
450
- (self.span_rowid, models.Span.output_value_first_101_chars),
453
+ (self.id, models.Span.output_value_first_101_chars),
451
454
  ),
452
455
  )
453
456
  if not output_value_first_101_chars:
454
457
  return None
455
458
  return SpanIOValue(
456
- span_rowid=self.span_rowid,
459
+ span_rowid=self.id,
457
460
  attr=models.Span.output_value,
458
461
  truncated_value=truncate_value(output_value_first_101_chars),
459
462
  mime_type=MimeType(mime_type),
@@ -464,10 +467,10 @@ class Span(Node):
464
467
  self,
465
468
  info: Info[Context, None],
466
469
  ) -> list[SpanEvent]:
467
- if self.db_span:
468
- 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]
469
472
  value = await info.context.data_loaders.span_fields.load(
470
- (self.span_rowid, models.Span.events),
473
+ (self.id, models.Span.events),
471
474
  )
472
475
  return [SpanEvent.from_dict(event) for event in value]
473
476
 
@@ -478,13 +481,13 @@ class Span(Node):
478
481
  async def cumulative_token_count_total(
479
482
  self,
480
483
  info: Info[Context, None],
481
- ) -> Optional[int]:
482
- if self.db_span:
483
- 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)
484
487
  value = await info.context.data_loaders.span_fields.load(
485
- (self.span_rowid, models.Span.cumulative_llm_token_count_total),
488
+ (self.id, models.Span.cumulative_llm_token_count_total),
486
489
  )
487
- return cast(Optional[int], value)
490
+ return float(value) if value is not None else None
488
491
 
489
492
  @strawberry.field(
490
493
  description="Cumulative (prompt) token count from self and all descendant "
@@ -493,13 +496,13 @@ class Span(Node):
493
496
  async def cumulative_token_count_prompt(
494
497
  self,
495
498
  info: Info[Context, None],
496
- ) -> Optional[int]:
497
- if self.db_span:
498
- 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)
499
502
  value = await info.context.data_loaders.span_fields.load(
500
- (self.span_rowid, models.Span.cumulative_llm_token_count_prompt),
503
+ (self.id, models.Span.cumulative_llm_token_count_prompt),
501
504
  )
502
- return cast(Optional[int], value)
505
+ return float(value) if value is not None else None
503
506
 
504
507
  @strawberry.field(
505
508
  description="Cumulative (completion) token count from self and all descendant "
@@ -508,13 +511,13 @@ class Span(Node):
508
511
  async def cumulative_token_count_completion(
509
512
  self,
510
513
  info: Info[Context, None],
511
- ) -> Optional[int]:
512
- if self.db_span:
513
- 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)
514
517
  value = await info.context.data_loaders.span_fields.load(
515
- (self.span_rowid, models.Span.cumulative_llm_token_count_completion),
518
+ (self.id, models.Span.cumulative_llm_token_count_completion),
516
519
  )
517
- return cast(Optional[int], value)
520
+ return float(value) if value is not None else None
518
521
 
519
522
  @strawberry.field(
520
523
  description="Propagated status code that percolates up error status codes from "
@@ -524,27 +527,26 @@ class Span(Node):
524
527
  self,
525
528
  info: Info[Context, None],
526
529
  ) -> SpanStatusCode:
527
- if self.db_span:
528
- value = self.db_span.cumulative_error_count
530
+ if self.db_record:
531
+ value = self.db_record.cumulative_error_count
529
532
  else:
530
533
  value = await info.context.data_loaders.span_fields.load(
531
- (self.span_rowid, models.Span.cumulative_error_count),
534
+ (self.id, models.Span.cumulative_error_count),
532
535
  )
533
536
  return SpanStatusCode.ERROR if value else SpanStatusCode.OK
534
537
 
535
538
  @strawberry.field(
536
539
  description=(
537
- "Annotations associated with the span. This encompasses both "
538
- "LLM and human annotations."
540
+ "Annotations associated with the span. This encompasses both LLM and human annotations."
539
541
  )
540
542
  ) # type: ignore
541
543
  async def span_annotations(
542
544
  self,
543
545
  info: Info[Context, None],
544
546
  sort: Optional[SpanAnnotationSort] = UNSET,
545
- filter: Optional[SpanAnnotationFilter] = None,
547
+ filter: Optional[AnnotationFilter] = None,
546
548
  ) -> list[SpanAnnotation]:
547
- span_id = self.span_rowid
549
+ span_id = self.id
548
550
  annotations = await info.context.data_loaders.span_annotations.load(span_id)
549
551
  sort_key = SpanAnnotationColumn.name.value
550
552
  sort_descending = False
@@ -558,24 +560,28 @@ class Span(Node):
558
560
  annotations.sort(
559
561
  key=lambda annotation: getattr(annotation, sort_key), reverse=sort_descending
560
562
  )
561
- 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
+ ]
562
566
 
563
567
  @strawberry.field(description=("Notes associated with the span.")) # type: ignore
564
568
  async def span_notes(
565
569
  self,
566
570
  info: Info[Context, None],
567
571
  ) -> list[SpanAnnotation]:
568
- span_id = self.span_rowid
572
+ span_id = self.id
569
573
  annotations = await info.context.data_loaders.span_annotations.load(span_id)
570
574
  annotations = [annotation for annotation in annotations if annotation.name == "note"]
571
575
  annotations.sort(key=lambda annotation: getattr(annotation, "created_at"), reverse=False)
572
- 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
+ ]
573
579
 
574
580
  @strawberry.field(description="Summarizes each annotation (by name) associated with the span") # type: ignore
575
581
  async def span_annotation_summaries(
576
582
  self,
577
583
  info: Info[Context, None],
578
- filter: Optional[SpanAnnotationFilter] = None,
584
+ filter: Optional[AnnotationFilter] = None,
579
585
  ) -> list[AnnotationSummary]:
580
586
  """
581
587
  Retrieves and summarizes annotations associated with this span.
@@ -594,7 +600,7 @@ class Span(Node):
594
600
  - data: A list of dictionaries with label statistics
595
601
  """
596
602
  # Load all annotations for this span from the data loader
597
- annotations = await info.context.data_loaders.span_annotations.load(self.span_rowid)
603
+ annotations = await info.context.data_loaders.span_annotations.load(self.id)
598
604
 
599
605
  # Apply filter if provided to narrow down the annotations
600
606
  if filter:
@@ -636,8 +642,11 @@ class Span(Node):
636
642
  async def document_evaluations(
637
643
  self,
638
644
  info: Info[Context, None],
639
- ) -> list[DocumentEvaluation]:
640
- 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
+ ]
641
650
 
642
651
  @strawberry.field(
643
652
  description="Retrieval metrics: NDCG@K, Precision@K, Reciprocal Rank, etc.",
@@ -648,21 +657,21 @@ class Span(Node):
648
657
  evaluation_name: Optional[str] = UNSET,
649
658
  ) -> list[DocumentRetrievalMetrics]:
650
659
  num_documents = (
651
- self.db_span.num_documents
652
- if self.db_span
660
+ self.db_record.num_documents
661
+ if self.db_record
653
662
  else await info.context.data_loaders.span_fields.load(
654
- (self.span_rowid, models.Span.num_documents),
663
+ (self.id, models.Span.num_documents),
655
664
  )
656
665
  )
657
666
  if not num_documents:
658
667
  return []
659
668
  return await info.context.data_loaders.document_retrieval_metrics.load(
660
- (self.span_rowid, evaluation_name or None, num_documents),
669
+ (self.id, evaluation_name or None, num_documents),
661
670
  )
662
671
 
663
672
  @strawberry.field
664
673
  async def num_child_spans(self, info: Info[Context, None]) -> int:
665
- 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)
666
675
 
667
676
  @strawberry.field(
668
677
  description="All descendant spans (children, grandchildren, etc.)",
@@ -691,9 +700,9 @@ class Span(Node):
691
700
  before=before if isinstance(before, CursorString) else None,
692
701
  )
693
702
  span_rowids: Iterable[int] = await info.context.data_loaders.span_descendants.load(
694
- (self.span_rowid, max_depth or None),
703
+ (self.id, max_depth or None),
695
704
  )
696
- data = [Span(span_rowid=span_rowid) for span_rowid in span_rowids]
705
+ data = [Span(id=span_rowid) for span_rowid in span_rowids]
697
706
  return connection_from_list(data=data, args=args)
698
707
 
699
708
  @strawberry.field(
@@ -704,9 +713,9 @@ class Span(Node):
704
713
  info: Info[Context, None],
705
714
  ) -> SpanAsExampleRevision:
706
715
  span = (
707
- self.db_span
708
- if self.db_span
709
- 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)
710
719
  )
711
720
 
712
721
  # Fetch annotations associated with this span
@@ -741,16 +750,16 @@ class Span(Node):
741
750
  ]: # use lazy types to avoid circular import: https://strawberry.rocks/docs/types/lazy
742
751
  from phoenix.server.api.types.Project import Project
743
752
 
744
- span_id = self.span_rowid
753
+ span_id = self.id
745
754
  project = await info.context.data_loaders.span_projects.load(span_id)
746
- return Project(project_rowid=project.id, db_project=project)
755
+ return Project(id=project.id, db_record=project)
747
756
 
748
757
  @strawberry.field(description="Indicates if the span is contained in any dataset") # type: ignore
749
758
  async def contained_in_dataset(
750
759
  self,
751
760
  info: Info[Context, None],
752
761
  ) -> bool:
753
- 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)
754
763
  return bool(examples)
755
764
 
756
765
  @strawberry.field(description="Invocation parameters for the span") # type: ignore
@@ -761,7 +770,7 @@ class Span(Node):
761
770
  from phoenix.server.api.helpers.playground_clients import OpenAIStreamingClient
762
771
  from phoenix.server.api.helpers.playground_registry import PLAYGROUND_CLIENT_REGISTRY
763
772
 
764
- 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)
765
774
  attributes = db_span.attributes
766
775
  llm_provider = GenerativeProvider.get_model_provider_from_attributes(attributes)
767
776
  if llm_provider is None:
@@ -790,6 +799,41 @@ class Span(Node):
790
799
  )
791
800
  ]
792
801
 
802
+ @strawberry.field
803
+ async def cost_summary(self, info: Info[Context, None]) -> Optional[SpanCostSummary]:
804
+ span_cost = await info.context.data_loaders.span_cost_by_span.load(self.id)
805
+ if span_cost is None:
806
+ return None
807
+ return SpanCostSummary(
808
+ prompt=CostBreakdown(
809
+ tokens=span_cost.prompt_tokens,
810
+ cost=span_cost.prompt_cost,
811
+ ),
812
+ completion=CostBreakdown(
813
+ tokens=span_cost.completion_tokens,
814
+ cost=span_cost.completion_cost,
815
+ ),
816
+ total=CostBreakdown(
817
+ tokens=span_cost.total_tokens,
818
+ cost=span_cost.total_cost,
819
+ ),
820
+ )
821
+
822
+ @strawberry.field
823
+ async def cost_detail_summary_entries(
824
+ self, info: Info[Context, None]
825
+ ) -> list[SpanCostDetailSummaryEntry]:
826
+ loader = info.context.data_loaders.span_cost_detail_summary_entries_by_span
827
+ entries = await loader.load(self.id)
828
+ return [
829
+ SpanCostDetailSummaryEntry(
830
+ token_type=entry.token_type,
831
+ is_prompt=entry.is_prompt,
832
+ value=CostBreakdown(tokens=entry.value.tokens, cost=entry.value.cost),
833
+ )
834
+ for entry in entries
835
+ ]
836
+
793
837
 
794
838
  def _hide_embedding_vectors(attributes: Mapping[str, Any]) -> Mapping[str, Any]:
795
839
  if not (