arize-phoenix 12.8.0__py3-none-any.whl → 12.9.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of arize-phoenix might be problematic. Click here for more details.

Files changed (70) hide show
  1. {arize_phoenix-12.8.0.dist-info → arize_phoenix-12.9.0.dist-info}/METADATA +3 -1
  2. {arize_phoenix-12.8.0.dist-info → arize_phoenix-12.9.0.dist-info}/RECORD +70 -67
  3. phoenix/config.py +131 -9
  4. phoenix/db/engines.py +127 -14
  5. phoenix/db/iam_auth.py +64 -0
  6. phoenix/db/pg_config.py +10 -0
  7. phoenix/server/api/context.py +23 -0
  8. phoenix/server/api/dataloaders/__init__.py +6 -0
  9. phoenix/server/api/dataloaders/experiment_repeated_run_groups.py +0 -2
  10. phoenix/server/api/dataloaders/experiment_runs_by_experiment_and_example.py +44 -0
  11. phoenix/server/api/dataloaders/span_costs.py +3 -9
  12. phoenix/server/api/dataloaders/token_prices_by_model.py +30 -0
  13. phoenix/server/api/helpers/playground_clients.py +3 -3
  14. phoenix/server/api/input_types/PromptVersionInput.py +47 -1
  15. phoenix/server/api/mutations/annotation_config_mutations.py +2 -2
  16. phoenix/server/api/mutations/api_key_mutations.py +2 -15
  17. phoenix/server/api/mutations/chat_mutations.py +3 -2
  18. phoenix/server/api/mutations/dataset_label_mutations.py +12 -6
  19. phoenix/server/api/mutations/dataset_mutations.py +8 -8
  20. phoenix/server/api/mutations/dataset_split_mutations.py +13 -9
  21. phoenix/server/api/mutations/model_mutations.py +4 -4
  22. phoenix/server/api/mutations/project_session_annotations_mutations.py +4 -7
  23. phoenix/server/api/mutations/prompt_label_mutations.py +3 -3
  24. phoenix/server/api/mutations/prompt_mutations.py +24 -117
  25. phoenix/server/api/mutations/prompt_version_tag_mutations.py +8 -5
  26. phoenix/server/api/mutations/span_annotations_mutations.py +10 -5
  27. phoenix/server/api/mutations/trace_annotations_mutations.py +9 -4
  28. phoenix/server/api/mutations/user_mutations.py +4 -4
  29. phoenix/server/api/queries.py +65 -210
  30. phoenix/server/api/subscriptions.py +4 -4
  31. phoenix/server/api/types/Annotation.py +90 -23
  32. phoenix/server/api/types/ApiKey.py +13 -17
  33. phoenix/server/api/types/Dataset.py +88 -48
  34. phoenix/server/api/types/DatasetExample.py +34 -30
  35. phoenix/server/api/types/DatasetLabel.py +47 -13
  36. phoenix/server/api/types/DatasetSplit.py +87 -21
  37. phoenix/server/api/types/DatasetVersion.py +49 -4
  38. phoenix/server/api/types/DocumentAnnotation.py +182 -62
  39. phoenix/server/api/types/Experiment.py +146 -55
  40. phoenix/server/api/types/ExperimentRepeatedRunGroup.py +10 -1
  41. phoenix/server/api/types/ExperimentRun.py +118 -61
  42. phoenix/server/api/types/ExperimentRunAnnotation.py +158 -39
  43. phoenix/server/api/types/GenerativeModel.py +95 -42
  44. phoenix/server/api/types/ModelInterface.py +7 -2
  45. phoenix/server/api/types/PlaygroundModel.py +12 -2
  46. phoenix/server/api/types/Project.py +70 -75
  47. phoenix/server/api/types/ProjectSession.py +69 -37
  48. phoenix/server/api/types/ProjectSessionAnnotation.py +166 -47
  49. phoenix/server/api/types/ProjectTraceRetentionPolicy.py +1 -1
  50. phoenix/server/api/types/Prompt.py +82 -44
  51. phoenix/server/api/types/PromptLabel.py +47 -13
  52. phoenix/server/api/types/PromptVersion.py +11 -8
  53. phoenix/server/api/types/PromptVersionTag.py +65 -25
  54. phoenix/server/api/types/Span.py +116 -115
  55. phoenix/server/api/types/SpanAnnotation.py +189 -42
  56. phoenix/server/api/types/SystemApiKey.py +65 -1
  57. phoenix/server/api/types/Trace.py +45 -44
  58. phoenix/server/api/types/TraceAnnotation.py +144 -48
  59. phoenix/server/api/types/User.py +103 -33
  60. phoenix/server/api/types/UserApiKey.py +73 -26
  61. phoenix/server/app.py +29 -0
  62. phoenix/server/static/.vite/manifest.json +9 -9
  63. phoenix/server/static/assets/{components-Bem6_7MW.js → components-v927s3NF.js} +427 -397
  64. phoenix/server/static/assets/{index-NdiXbuNL.js → index-DrD9eSrN.js} +9 -5
  65. phoenix/server/static/assets/{pages-CEJgMVKU.js → pages-GVybXa_W.js} +489 -486
  66. phoenix/version.py +1 -1
  67. {arize_phoenix-12.8.0.dist-info → arize_phoenix-12.9.0.dist-info}/WHEEL +0 -0
  68. {arize_phoenix-12.8.0.dist-info → arize_phoenix-12.9.0.dist-info}/entry_points.txt +0 -0
  69. {arize_phoenix-12.8.0.dist-info → arize_phoenix-12.9.0.dist-info}/licenses/IP_NOTICE +0 -0
  70. {arize_phoenix-12.8.0.dist-info → arize_phoenix-12.9.0.dist-info}/licenses/LICENSE +0 -0
@@ -34,17 +34,14 @@ from phoenix.server.api.input_types.SpanAnnotationSort import (
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 (
38
- DocumentAnnotation,
39
- to_gql_document_annotation,
40
- )
37
+ from phoenix.server.api.types.DocumentAnnotation import DocumentAnnotation
41
38
  from phoenix.server.api.types.DocumentRetrievalMetrics import DocumentRetrievalMetrics
42
39
  from phoenix.server.api.types.ExampleRevisionInterface import ExampleRevision
43
40
  from phoenix.server.api.types.GenerativeProvider import GenerativeProvider
44
41
  from phoenix.server.api.types.MimeType import MimeType
45
42
  from phoenix.server.api.types.pagination import ConnectionArgs, CursorString, connection_from_list
46
43
  from phoenix.server.api.types.SortDir import SortDir
47
- from phoenix.server.api.types.SpanAnnotation import SpanAnnotation, to_gql_span_annotation
44
+ from phoenix.server.api.types.SpanAnnotation import SpanAnnotation
48
45
  from phoenix.server.api.types.SpanCostDetailSummaryEntry import SpanCostDetailSummaryEntry
49
46
  from phoenix.server.api.types.SpanCostSummary import SpanCostSummary
50
47
  from phoenix.server.api.types.SpanIOValue import SpanIOValue, truncate_value
@@ -126,11 +123,11 @@ SpanRowId: TypeAlias = int
126
123
 
127
124
  @strawberry.type
128
125
  class Span(Node):
129
- span_rowid: NodeID[SpanRowId]
130
- db_span: strawberry.Private[models.Span] = UNSET
126
+ id: NodeID[SpanRowId]
127
+ db_record: strawberry.Private[Optional[models.Span]] = None
131
128
 
132
129
  def __post_init__(self) -> None:
133
- if self.db_span and self.span_rowid != self.db_span.id:
130
+ if self.db_record and self.id != self.db_record.id:
134
131
  raise ValueError("Span ID mismatch")
135
132
 
136
133
  @strawberry.field
@@ -138,10 +135,10 @@ class Span(Node):
138
135
  self,
139
136
  info: Info[Context, None],
140
137
  ) -> str:
141
- if self.db_span:
142
- return self.db_span.name
138
+ if self.db_record:
139
+ return self.db_record.name
143
140
  value = await info.context.data_loaders.span_fields.load(
144
- (self.span_rowid, models.Span.name),
141
+ (self.id, models.Span.name),
145
142
  )
146
143
  return str(value)
147
144
 
@@ -150,11 +147,11 @@ class Span(Node):
150
147
  self,
151
148
  info: Info[Context, None],
152
149
  ) -> SpanStatusCode:
153
- if self.db_span:
154
- value = self.db_span.status_code
150
+ if self.db_record:
151
+ value = self.db_record.status_code
155
152
  else:
156
153
  value = await info.context.data_loaders.span_fields.load(
157
- (self.span_rowid, models.Span.status_code),
154
+ (self.id, models.Span.status_code),
158
155
  )
159
156
  return SpanStatusCode(value)
160
157
 
@@ -163,10 +160,10 @@ class Span(Node):
163
160
  self,
164
161
  info: Info[Context, None],
165
162
  ) -> str:
166
- if self.db_span:
167
- return self.db_span.status_message
163
+ if self.db_record:
164
+ return self.db_record.status_message
168
165
  value = await info.context.data_loaders.span_fields.load(
169
- (self.span_rowid, models.Span.status_message),
166
+ (self.id, models.Span.status_message),
170
167
  )
171
168
  return str(value)
172
169
 
@@ -175,10 +172,10 @@ class Span(Node):
175
172
  self,
176
173
  info: Info[Context, None],
177
174
  ) -> datetime:
178
- if self.db_span:
179
- return self.db_span.start_time
175
+ if self.db_record:
176
+ return self.db_record.start_time
180
177
  value = await info.context.data_loaders.span_fields.load(
181
- (self.span_rowid, models.Span.start_time),
178
+ (self.id, models.Span.start_time),
182
179
  )
183
180
  return cast(datetime, value)
184
181
 
@@ -187,10 +184,10 @@ class Span(Node):
187
184
  self,
188
185
  info: Info[Context, None],
189
186
  ) -> Optional[datetime]:
190
- if self.db_span:
191
- return self.db_span.end_time
187
+ if self.db_record:
188
+ return self.db_record.end_time
192
189
  value = await info.context.data_loaders.span_fields.load(
193
- (self.span_rowid, models.Span.end_time),
190
+ (self.id, models.Span.end_time),
194
191
  )
195
192
  return cast(datetime, value)
196
193
 
@@ -199,10 +196,10 @@ class Span(Node):
199
196
  self,
200
197
  info: Info[Context, None],
201
198
  ) -> Optional[float]:
202
- if self.db_span:
203
- return self.db_span.latency_ms
199
+ if self.db_record:
200
+ return self.db_record.latency_ms
204
201
  value = await info.context.data_loaders.span_fields.load(
205
- (self.span_rowid, models.Span.latency_ms),
202
+ (self.id, models.Span.latency_ms),
206
203
  )
207
204
  return cast(float, value)
208
205
 
@@ -213,11 +210,11 @@ class Span(Node):
213
210
  self,
214
211
  info: Info[Context, None],
215
212
  ) -> Optional[ID]:
216
- if self.db_span:
217
- value = self.db_span.parent_id
213
+ if self.db_record:
214
+ value = self.db_record.parent_id
218
215
  else:
219
216
  value = await info.context.data_loaders.span_fields.load(
220
- (self.span_rowid, models.Span.parent_id),
217
+ (self.id, models.Span.parent_id),
221
218
  )
222
219
  return None if value is None else ID(value)
223
220
 
@@ -226,11 +223,11 @@ class Span(Node):
226
223
  self,
227
224
  info: Info[Context, None],
228
225
  ) -> SpanKind:
229
- if self.db_span:
230
- value = self.db_span.span_kind
226
+ if self.db_record:
227
+ value = self.db_record.span_kind
231
228
  else:
232
229
  value = await info.context.data_loaders.span_fields.load(
233
- (self.span_rowid, models.Span.span_kind),
230
+ (self.id, models.Span.span_kind),
234
231
  )
235
232
  return SpanKind(value)
236
233
 
@@ -239,11 +236,11 @@ class Span(Node):
239
236
  self,
240
237
  info: Info[Context, None],
241
238
  ) -> ID:
242
- if self.db_span:
243
- span_id = self.db_span.span_id
239
+ if self.db_record:
240
+ span_id = self.db_record.span_id
244
241
  else:
245
242
  span_id = await info.context.data_loaders.span_fields.load(
246
- (self.span_rowid, models.Span.span_id),
243
+ (self.id, models.Span.span_id),
247
244
  )
248
245
  return ID(span_id)
249
246
 
@@ -252,31 +249,31 @@ class Span(Node):
252
249
  self,
253
250
  info: Info[Context, None],
254
251
  ) -> Annotated["Trace", strawberry.lazy(".Trace")]:
255
- if self.db_span:
256
- trace_rowid = self.db_span.trace_rowid
252
+ if self.db_record:
253
+ trace_rowid = self.db_record.trace_rowid
257
254
  else:
258
255
  trace_rowid = await info.context.data_loaders.span_fields.load(
259
- (self.span_rowid, models.Span.trace_rowid),
256
+ (self.id, models.Span.trace_rowid),
260
257
  )
261
258
  from phoenix.server.api.types.Trace import Trace
262
259
 
263
- return Trace(trace_rowid=trace_rowid)
260
+ return Trace(id=trace_rowid)
264
261
 
265
262
  @strawberry.field
266
263
  async def context(
267
264
  self,
268
265
  info: Info[Context, None],
269
266
  ) -> SpanContext:
270
- if self.db_span:
271
- trace_id = self.db_span.trace.trace_id
272
- 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
273
270
  else:
274
271
  span_id, trace_id = await gather(
275
272
  info.context.data_loaders.span_fields.load(
276
- (self.span_rowid, models.Span.span_id),
273
+ (self.id, models.Span.span_id),
277
274
  ),
278
275
  info.context.data_loaders.span_fields.load(
279
- (self.span_rowid, models.Trace.trace_id),
276
+ (self.id, models.Trace.trace_id),
280
277
  ),
281
278
  )
282
279
  return SpanContext(trace_id=ID(trace_id), span_id=ID(span_id))
@@ -288,11 +285,11 @@ class Span(Node):
288
285
  self,
289
286
  info: Info[Context, None],
290
287
  ) -> str:
291
- if self.db_span:
292
- value = self.db_span.attributes
288
+ if self.db_record:
289
+ value = self.db_record.attributes
293
290
  else:
294
291
  value = await info.context.data_loaders.span_fields.load(
295
- (self.span_rowid, models.Span.attributes),
292
+ (self.id, models.Span.attributes),
296
293
  )
297
294
  return json.dumps(_hide_embedding_vectors(value), cls=_JSONEncoder)
298
295
 
@@ -303,11 +300,11 @@ class Span(Node):
303
300
  self,
304
301
  info: Info[Context, None],
305
302
  ) -> Optional[str]:
306
- if self.db_span:
307
- value = self.db_span.metadata_
303
+ if self.db_record:
304
+ value = self.db_record.metadata_
308
305
  else:
309
306
  value = await info.context.data_loaders.span_fields.load(
310
- (self.span_rowid, models.Span.metadata_),
307
+ (self.id, models.Span.metadata_),
311
308
  )
312
309
  return _convert_metadata_to_string(value)
313
310
 
@@ -316,10 +313,10 @@ class Span(Node):
316
313
  self,
317
314
  info: Info[Context, None],
318
315
  ) -> Optional[int]:
319
- if self.db_span:
320
- return self.db_span.num_documents
316
+ if self.db_record:
317
+ return self.db_record.num_documents
321
318
  value = await info.context.data_loaders.span_fields.load(
322
- (self.span_rowid, models.Span.num_documents),
319
+ (self.id, models.Span.num_documents),
323
320
  )
324
321
  return cast(int, value)
325
322
 
@@ -328,10 +325,10 @@ class Span(Node):
328
325
  self,
329
326
  info: Info[Context, None],
330
327
  ) -> Optional[int]:
331
- if self.db_span:
332
- return self.db_span.llm_token_count_total
328
+ if self.db_record:
329
+ return self.db_record.llm_token_count_total
333
330
  value = await info.context.data_loaders.span_fields.load(
334
- (self.span_rowid, models.Span.llm_token_count_total),
331
+ (self.id, models.Span.llm_token_count_total),
335
332
  )
336
333
  return cast(Optional[int], value)
337
334
 
@@ -340,10 +337,10 @@ class Span(Node):
340
337
  self,
341
338
  info: Info[Context, None],
342
339
  ) -> Optional[int]:
343
- if self.db_span:
344
- return self.db_span.llm_token_count_prompt
340
+ if self.db_record:
341
+ return self.db_record.llm_token_count_prompt
345
342
  value = await info.context.data_loaders.span_fields.load(
346
- (self.span_rowid, models.Span.llm_token_count_prompt),
343
+ (self.id, models.Span.llm_token_count_prompt),
347
344
  )
348
345
  return cast(Optional[int], value)
349
346
 
@@ -352,10 +349,10 @@ class Span(Node):
352
349
  self,
353
350
  info: Info[Context, None],
354
351
  ) -> Optional[int]:
355
- if self.db_span:
356
- return self.db_span.llm_token_count_completion
352
+ if self.db_record:
353
+ return self.db_record.llm_token_count_completion
357
354
  value = await info.context.data_loaders.span_fields.load(
358
- (self.span_rowid, models.Span.llm_token_count_completion),
355
+ (self.id, models.Span.llm_token_count_completion),
359
356
  )
360
357
  return cast(Optional[int], value)
361
358
 
@@ -364,11 +361,11 @@ class Span(Node):
364
361
  self,
365
362
  info: Info[Context, None],
366
363
  ) -> TokenCountPromptDetails:
367
- if self.db_span:
368
- attributes = self.db_span.attributes
364
+ if self.db_record:
365
+ attributes = self.db_record.attributes
369
366
  else:
370
367
  attributes = await info.context.data_loaders.span_fields.load(
371
- (self.span_rowid, models.Span.attributes),
368
+ (self.id, models.Span.attributes),
372
369
  )
373
370
 
374
371
  cache_read: Optional[int] = None
@@ -406,28 +403,28 @@ class Span(Node):
406
403
  self,
407
404
  info: Info[Context, None],
408
405
  ) -> Optional[SpanIOValue]:
409
- if self.db_span:
410
- input_value = self.db_span.input_value
406
+ if self.db_record:
407
+ input_value = self.db_record.input_value
411
408
  if input_value is None or input_value == "":
412
409
  return None
413
410
  input_value = str(input_value)
414
- mime_type = self.db_span.input_mime_type
411
+ mime_type = self.db_record.input_mime_type
415
412
  return SpanIOValue(
416
413
  cached_value=input_value,
417
414
  mime_type=MimeType(mime_type),
418
415
  )
419
416
  mime_type, input_value_first_101_chars = await gather(
420
417
  info.context.data_loaders.span_fields.load(
421
- (self.span_rowid, models.Span.input_mime_type),
418
+ (self.id, models.Span.input_mime_type),
422
419
  ),
423
420
  info.context.data_loaders.span_fields.load(
424
- (self.span_rowid, models.Span.input_value_first_101_chars),
421
+ (self.id, models.Span.input_value_first_101_chars),
425
422
  ),
426
423
  )
427
424
  if not input_value_first_101_chars:
428
425
  return None
429
426
  return SpanIOValue(
430
- span_rowid=self.span_rowid,
427
+ span_rowid=self.id,
431
428
  attr=models.Span.input_value,
432
429
  truncated_value=truncate_value(input_value_first_101_chars),
433
430
  mime_type=MimeType(mime_type),
@@ -438,28 +435,28 @@ class Span(Node):
438
435
  self,
439
436
  info: Info[Context, None],
440
437
  ) -> Optional[SpanIOValue]:
441
- if self.db_span:
442
- output_value = self.db_span.output_value
438
+ if self.db_record:
439
+ output_value = self.db_record.output_value
443
440
  if output_value is None or output_value == "":
444
441
  return None
445
442
  output_value = str(output_value)
446
- mime_type = self.db_span.output_mime_type
443
+ mime_type = self.db_record.output_mime_type
447
444
  return SpanIOValue(
448
445
  cached_value=output_value,
449
446
  mime_type=MimeType(mime_type),
450
447
  )
451
448
  mime_type, output_value_first_101_chars = await gather(
452
449
  info.context.data_loaders.span_fields.load(
453
- (self.span_rowid, models.Span.output_mime_type),
450
+ (self.id, models.Span.output_mime_type),
454
451
  ),
455
452
  info.context.data_loaders.span_fields.load(
456
- (self.span_rowid, models.Span.output_value_first_101_chars),
453
+ (self.id, models.Span.output_value_first_101_chars),
457
454
  ),
458
455
  )
459
456
  if not output_value_first_101_chars:
460
457
  return None
461
458
  return SpanIOValue(
462
- span_rowid=self.span_rowid,
459
+ span_rowid=self.id,
463
460
  attr=models.Span.output_value,
464
461
  truncated_value=truncate_value(output_value_first_101_chars),
465
462
  mime_type=MimeType(mime_type),
@@ -470,10 +467,10 @@ class Span(Node):
470
467
  self,
471
468
  info: Info[Context, None],
472
469
  ) -> list[SpanEvent]:
473
- if self.db_span:
474
- 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]
475
472
  value = await info.context.data_loaders.span_fields.load(
476
- (self.span_rowid, models.Span.events),
473
+ (self.id, models.Span.events),
477
474
  )
478
475
  return [SpanEvent.from_dict(event) for event in value]
479
476
 
@@ -485,10 +482,10 @@ class Span(Node):
485
482
  self,
486
483
  info: Info[Context, None],
487
484
  ) -> Optional[int]:
488
- if self.db_span:
489
- return self.db_span.cumulative_llm_token_count_total
485
+ if self.db_record:
486
+ return self.db_record.cumulative_llm_token_count_total
490
487
  value = await info.context.data_loaders.span_fields.load(
491
- (self.span_rowid, models.Span.cumulative_llm_token_count_total),
488
+ (self.id, models.Span.cumulative_llm_token_count_total),
492
489
  )
493
490
  return cast(Optional[int], value)
494
491
 
@@ -500,10 +497,10 @@ class Span(Node):
500
497
  self,
501
498
  info: Info[Context, None],
502
499
  ) -> Optional[int]:
503
- if self.db_span:
504
- return self.db_span.cumulative_llm_token_count_prompt
500
+ if self.db_record:
501
+ return self.db_record.cumulative_llm_token_count_prompt
505
502
  value = await info.context.data_loaders.span_fields.load(
506
- (self.span_rowid, models.Span.cumulative_llm_token_count_prompt),
503
+ (self.id, models.Span.cumulative_llm_token_count_prompt),
507
504
  )
508
505
  return cast(Optional[int], value)
509
506
 
@@ -515,10 +512,10 @@ class Span(Node):
515
512
  self,
516
513
  info: Info[Context, None],
517
514
  ) -> Optional[int]:
518
- if self.db_span:
519
- return self.db_span.cumulative_llm_token_count_completion
515
+ if self.db_record:
516
+ return self.db_record.cumulative_llm_token_count_completion
520
517
  value = await info.context.data_loaders.span_fields.load(
521
- (self.span_rowid, models.Span.cumulative_llm_token_count_completion),
518
+ (self.id, models.Span.cumulative_llm_token_count_completion),
522
519
  )
523
520
  return cast(Optional[int], value)
524
521
 
@@ -530,11 +527,11 @@ class Span(Node):
530
527
  self,
531
528
  info: Info[Context, None],
532
529
  ) -> SpanStatusCode:
533
- if self.db_span:
534
- value = self.db_span.cumulative_error_count
530
+ if self.db_record:
531
+ value = self.db_record.cumulative_error_count
535
532
  else:
536
533
  value = await info.context.data_loaders.span_fields.load(
537
- (self.span_rowid, models.Span.cumulative_error_count),
534
+ (self.id, models.Span.cumulative_error_count),
538
535
  )
539
536
  return SpanStatusCode.ERROR if value else SpanStatusCode.OK
540
537
 
@@ -549,7 +546,7 @@ class Span(Node):
549
546
  sort: Optional[SpanAnnotationSort] = UNSET,
550
547
  filter: Optional[AnnotationFilter] = None,
551
548
  ) -> list[SpanAnnotation]:
552
- span_id = self.span_rowid
549
+ span_id = self.id
553
550
  annotations = await info.context.data_loaders.span_annotations.load(span_id)
554
551
  sort_key = SpanAnnotationColumn.name.value
555
552
  sort_descending = False
@@ -563,18 +560,22 @@ class Span(Node):
563
560
  annotations.sort(
564
561
  key=lambda annotation: getattr(annotation, sort_key), reverse=sort_descending
565
562
  )
566
- 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
+ ]
567
566
 
568
567
  @strawberry.field(description=("Notes associated with the span.")) # type: ignore
569
568
  async def span_notes(
570
569
  self,
571
570
  info: Info[Context, None],
572
571
  ) -> list[SpanAnnotation]:
573
- span_id = self.span_rowid
572
+ span_id = self.id
574
573
  annotations = await info.context.data_loaders.span_annotations.load(span_id)
575
574
  annotations = [annotation for annotation in annotations if annotation.name == "note"]
576
575
  annotations.sort(key=lambda annotation: getattr(annotation, "created_at"), reverse=False)
577
- 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
+ ]
578
579
 
579
580
  @strawberry.field(description="Summarizes each annotation (by name) associated with the span") # type: ignore
580
581
  async def span_annotation_summaries(
@@ -599,7 +600,7 @@ class Span(Node):
599
600
  - data: A list of dictionaries with label statistics
600
601
  """
601
602
  # Load all annotations for this span from the data loader
602
- annotations = await info.context.data_loaders.span_annotations.load(self.span_rowid)
603
+ annotations = await info.context.data_loaders.span_annotations.load(self.id)
603
604
 
604
605
  # Apply filter if provided to narrow down the annotations
605
606
  if filter:
@@ -643,8 +644,8 @@ class Span(Node):
643
644
  info: Info[Context, None],
644
645
  ) -> list[DocumentAnnotation]:
645
646
  return [
646
- to_gql_document_annotation(anno)
647
- for anno in await info.context.data_loaders.document_evaluations.load(self.span_rowid)
647
+ DocumentAnnotation(id=anno.id, db_record=anno)
648
+ for anno in await info.context.data_loaders.document_evaluations.load(self.id)
648
649
  ]
649
650
 
650
651
  @strawberry.field(
@@ -656,21 +657,21 @@ class Span(Node):
656
657
  evaluation_name: Optional[str] = UNSET,
657
658
  ) -> list[DocumentRetrievalMetrics]:
658
659
  num_documents = (
659
- self.db_span.num_documents
660
- if self.db_span
660
+ self.db_record.num_documents
661
+ if self.db_record
661
662
  else await info.context.data_loaders.span_fields.load(
662
- (self.span_rowid, models.Span.num_documents),
663
+ (self.id, models.Span.num_documents),
663
664
  )
664
665
  )
665
666
  if not num_documents:
666
667
  return []
667
668
  return await info.context.data_loaders.document_retrieval_metrics.load(
668
- (self.span_rowid, evaluation_name or None, num_documents),
669
+ (self.id, evaluation_name or None, num_documents),
669
670
  )
670
671
 
671
672
  @strawberry.field
672
673
  async def num_child_spans(self, info: Info[Context, None]) -> int:
673
- 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)
674
675
 
675
676
  @strawberry.field(
676
677
  description="All descendant spans (children, grandchildren, etc.)",
@@ -699,9 +700,9 @@ class Span(Node):
699
700
  before=before if isinstance(before, CursorString) else None,
700
701
  )
701
702
  span_rowids: Iterable[int] = await info.context.data_loaders.span_descendants.load(
702
- (self.span_rowid, max_depth or None),
703
+ (self.id, max_depth or None),
703
704
  )
704
- data = [Span(span_rowid=span_rowid) for span_rowid in span_rowids]
705
+ data = [Span(id=span_rowid) for span_rowid in span_rowids]
705
706
  return connection_from_list(data=data, args=args)
706
707
 
707
708
  @strawberry.field(
@@ -712,9 +713,9 @@ class Span(Node):
712
713
  info: Info[Context, None],
713
714
  ) -> SpanAsExampleRevision:
714
715
  span = (
715
- self.db_span
716
- if self.db_span
717
- 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)
718
719
  )
719
720
 
720
721
  # Fetch annotations associated with this span
@@ -749,16 +750,16 @@ class Span(Node):
749
750
  ]: # use lazy types to avoid circular import: https://strawberry.rocks/docs/types/lazy
750
751
  from phoenix.server.api.types.Project import Project
751
752
 
752
- span_id = self.span_rowid
753
+ span_id = self.id
753
754
  project = await info.context.data_loaders.span_projects.load(span_id)
754
- return Project(project_rowid=project.id, db_project=project)
755
+ return Project(id=project.id, db_record=project)
755
756
 
756
757
  @strawberry.field(description="Indicates if the span is contained in any dataset") # type: ignore
757
758
  async def contained_in_dataset(
758
759
  self,
759
760
  info: Info[Context, None],
760
761
  ) -> bool:
761
- 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)
762
763
  return bool(examples)
763
764
 
764
765
  @strawberry.field(description="Invocation parameters for the span") # type: ignore
@@ -769,7 +770,7 @@ class Span(Node):
769
770
  from phoenix.server.api.helpers.playground_clients import OpenAIStreamingClient
770
771
  from phoenix.server.api.helpers.playground_registry import PLAYGROUND_CLIENT_REGISTRY
771
772
 
772
- 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)
773
774
  attributes = db_span.attributes
774
775
  llm_provider = GenerativeProvider.get_model_provider_from_attributes(attributes)
775
776
  if llm_provider is None:
@@ -800,7 +801,7 @@ class Span(Node):
800
801
 
801
802
  @strawberry.field
802
803
  async def cost_summary(self, info: Info[Context, None]) -> Optional[SpanCostSummary]:
803
- 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)
804
805
  if span_cost is None:
805
806
  return None
806
807
  return SpanCostSummary(
@@ -823,7 +824,7 @@ class Span(Node):
823
824
  self, info: Info[Context, None]
824
825
  ) -> list[SpanCostDetailSummaryEntry]:
825
826
  loader = info.context.data_loaders.span_cost_detail_summary_entries_by_span
826
- entries = await loader.load(self.span_rowid)
827
+ entries = await loader.load(self.id)
827
828
  return [
828
829
  SpanCostDetailSummaryEntry(
829
830
  token_type=entry.token_type,