arize-phoenix 5.5.2__py3-none-any.whl → 5.7.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 (186) hide show
  1. {arize_phoenix-5.5.2.dist-info → arize_phoenix-5.7.0.dist-info}/METADATA +4 -7
  2. arize_phoenix-5.7.0.dist-info/RECORD +330 -0
  3. phoenix/config.py +50 -8
  4. phoenix/core/model.py +3 -3
  5. phoenix/core/model_schema.py +41 -50
  6. phoenix/core/model_schema_adapter.py +17 -16
  7. phoenix/datetime_utils.py +2 -2
  8. phoenix/db/bulk_inserter.py +10 -20
  9. phoenix/db/engines.py +2 -1
  10. phoenix/db/enums.py +2 -2
  11. phoenix/db/helpers.py +8 -7
  12. phoenix/db/insertion/dataset.py +9 -19
  13. phoenix/db/insertion/document_annotation.py +14 -13
  14. phoenix/db/insertion/helpers.py +6 -16
  15. phoenix/db/insertion/span_annotation.py +14 -13
  16. phoenix/db/insertion/trace_annotation.py +14 -13
  17. phoenix/db/insertion/types.py +19 -30
  18. phoenix/db/migrations/versions/3be8647b87d8_add_token_columns_to_spans_table.py +8 -8
  19. phoenix/db/models.py +28 -28
  20. phoenix/experiments/evaluators/base.py +2 -1
  21. phoenix/experiments/evaluators/code_evaluators.py +4 -5
  22. phoenix/experiments/evaluators/llm_evaluators.py +157 -4
  23. phoenix/experiments/evaluators/utils.py +3 -2
  24. phoenix/experiments/functions.py +10 -21
  25. phoenix/experiments/tracing.py +2 -1
  26. phoenix/experiments/types.py +20 -29
  27. phoenix/experiments/utils.py +2 -1
  28. phoenix/inferences/errors.py +6 -5
  29. phoenix/inferences/fixtures.py +6 -5
  30. phoenix/inferences/inferences.py +37 -37
  31. phoenix/inferences/schema.py +11 -10
  32. phoenix/inferences/validation.py +13 -14
  33. phoenix/logging/_formatter.py +3 -3
  34. phoenix/metrics/__init__.py +5 -4
  35. phoenix/metrics/binning.py +2 -1
  36. phoenix/metrics/metrics.py +2 -1
  37. phoenix/metrics/mixins.py +7 -6
  38. phoenix/metrics/retrieval_metrics.py +2 -1
  39. phoenix/metrics/timeseries.py +5 -4
  40. phoenix/metrics/wrappers.py +2 -2
  41. phoenix/pointcloud/clustering.py +3 -4
  42. phoenix/pointcloud/pointcloud.py +7 -5
  43. phoenix/pointcloud/umap_parameters.py +2 -1
  44. phoenix/server/api/dataloaders/annotation_summaries.py +12 -19
  45. phoenix/server/api/dataloaders/average_experiment_run_latency.py +2 -2
  46. phoenix/server/api/dataloaders/cache/two_tier_cache.py +3 -2
  47. phoenix/server/api/dataloaders/dataset_example_revisions.py +3 -8
  48. phoenix/server/api/dataloaders/dataset_example_spans.py +2 -5
  49. phoenix/server/api/dataloaders/document_evaluation_summaries.py +12 -18
  50. phoenix/server/api/dataloaders/document_evaluations.py +3 -7
  51. phoenix/server/api/dataloaders/document_retrieval_metrics.py +6 -13
  52. phoenix/server/api/dataloaders/experiment_annotation_summaries.py +4 -8
  53. phoenix/server/api/dataloaders/experiment_error_rates.py +2 -5
  54. phoenix/server/api/dataloaders/experiment_run_annotations.py +3 -7
  55. phoenix/server/api/dataloaders/experiment_run_counts.py +1 -5
  56. phoenix/server/api/dataloaders/experiment_sequence_number.py +2 -5
  57. phoenix/server/api/dataloaders/latency_ms_quantile.py +21 -30
  58. phoenix/server/api/dataloaders/min_start_or_max_end_times.py +7 -13
  59. phoenix/server/api/dataloaders/project_by_name.py +3 -3
  60. phoenix/server/api/dataloaders/record_counts.py +11 -18
  61. phoenix/server/api/dataloaders/span_annotations.py +3 -7
  62. phoenix/server/api/dataloaders/span_dataset_examples.py +3 -8
  63. phoenix/server/api/dataloaders/span_descendants.py +3 -7
  64. phoenix/server/api/dataloaders/span_projects.py +2 -2
  65. phoenix/server/api/dataloaders/token_counts.py +12 -19
  66. phoenix/server/api/dataloaders/trace_row_ids.py +3 -7
  67. phoenix/server/api/dataloaders/user_roles.py +3 -3
  68. phoenix/server/api/dataloaders/users.py +3 -3
  69. phoenix/server/api/helpers/__init__.py +4 -3
  70. phoenix/server/api/helpers/dataset_helpers.py +10 -9
  71. phoenix/server/api/helpers/playground_clients.py +671 -0
  72. phoenix/server/api/helpers/playground_registry.py +70 -0
  73. phoenix/server/api/helpers/playground_spans.py +325 -0
  74. phoenix/server/api/input_types/AddExamplesToDatasetInput.py +2 -2
  75. phoenix/server/api/input_types/AddSpansToDatasetInput.py +2 -2
  76. phoenix/server/api/input_types/ChatCompletionInput.py +38 -0
  77. phoenix/server/api/input_types/ChatCompletionMessageInput.py +13 -1
  78. phoenix/server/api/input_types/ClusterInput.py +2 -2
  79. phoenix/server/api/input_types/DeleteAnnotationsInput.py +1 -3
  80. phoenix/server/api/input_types/DeleteDatasetExamplesInput.py +2 -2
  81. phoenix/server/api/input_types/DeleteExperimentsInput.py +1 -3
  82. phoenix/server/api/input_types/DimensionFilter.py +4 -4
  83. phoenix/server/api/input_types/GenerativeModelInput.py +17 -0
  84. phoenix/server/api/input_types/Granularity.py +1 -1
  85. phoenix/server/api/input_types/InvocationParameters.py +156 -13
  86. phoenix/server/api/input_types/PatchDatasetExamplesInput.py +2 -2
  87. phoenix/server/api/input_types/TemplateOptions.py +10 -0
  88. phoenix/server/api/mutations/__init__.py +4 -0
  89. phoenix/server/api/mutations/chat_mutations.py +374 -0
  90. phoenix/server/api/mutations/dataset_mutations.py +4 -4
  91. phoenix/server/api/mutations/experiment_mutations.py +1 -2
  92. phoenix/server/api/mutations/export_events_mutations.py +7 -7
  93. phoenix/server/api/mutations/span_annotations_mutations.py +4 -4
  94. phoenix/server/api/mutations/trace_annotations_mutations.py +4 -4
  95. phoenix/server/api/mutations/user_mutations.py +4 -4
  96. phoenix/server/api/openapi/schema.py +2 -2
  97. phoenix/server/api/queries.py +61 -72
  98. phoenix/server/api/routers/oauth2.py +4 -4
  99. phoenix/server/api/routers/v1/datasets.py +22 -36
  100. phoenix/server/api/routers/v1/evaluations.py +6 -5
  101. phoenix/server/api/routers/v1/experiment_evaluations.py +2 -2
  102. phoenix/server/api/routers/v1/experiment_runs.py +2 -2
  103. phoenix/server/api/routers/v1/experiments.py +4 -4
  104. phoenix/server/api/routers/v1/spans.py +13 -12
  105. phoenix/server/api/routers/v1/traces.py +5 -5
  106. phoenix/server/api/routers/v1/utils.py +5 -5
  107. phoenix/server/api/schema.py +42 -10
  108. phoenix/server/api/subscriptions.py +347 -494
  109. phoenix/server/api/types/AnnotationSummary.py +3 -3
  110. phoenix/server/api/types/ChatCompletionSubscriptionPayload.py +44 -0
  111. phoenix/server/api/types/Cluster.py +8 -7
  112. phoenix/server/api/types/Dataset.py +5 -4
  113. phoenix/server/api/types/Dimension.py +3 -3
  114. phoenix/server/api/types/DocumentEvaluationSummary.py +8 -7
  115. phoenix/server/api/types/EmbeddingDimension.py +6 -5
  116. phoenix/server/api/types/EvaluationSummary.py +3 -3
  117. phoenix/server/api/types/Event.py +7 -7
  118. phoenix/server/api/types/Experiment.py +3 -3
  119. phoenix/server/api/types/ExperimentComparison.py +2 -4
  120. phoenix/server/api/types/GenerativeProvider.py +27 -3
  121. phoenix/server/api/types/Inferences.py +9 -8
  122. phoenix/server/api/types/InferencesRole.py +2 -2
  123. phoenix/server/api/types/Model.py +2 -2
  124. phoenix/server/api/types/Project.py +11 -18
  125. phoenix/server/api/types/Segments.py +3 -3
  126. phoenix/server/api/types/Span.py +45 -7
  127. phoenix/server/api/types/TemplateLanguage.py +9 -0
  128. phoenix/server/api/types/TimeSeries.py +8 -7
  129. phoenix/server/api/types/Trace.py +2 -2
  130. phoenix/server/api/types/UMAPPoints.py +6 -6
  131. phoenix/server/api/types/User.py +3 -3
  132. phoenix/server/api/types/node.py +1 -3
  133. phoenix/server/api/types/pagination.py +4 -4
  134. phoenix/server/api/utils.py +2 -4
  135. phoenix/server/app.py +76 -37
  136. phoenix/server/bearer_auth.py +4 -10
  137. phoenix/server/dml_event.py +3 -3
  138. phoenix/server/dml_event_handler.py +10 -24
  139. phoenix/server/grpc_server.py +3 -2
  140. phoenix/server/jwt_store.py +22 -21
  141. phoenix/server/main.py +17 -4
  142. phoenix/server/oauth2.py +3 -2
  143. phoenix/server/rate_limiters.py +5 -8
  144. phoenix/server/static/.vite/manifest.json +31 -31
  145. phoenix/server/static/assets/components-Csu8UKOs.js +1612 -0
  146. phoenix/server/static/assets/{index-DCzakdJq.js → index-Bk5C9EA7.js} +2 -2
  147. phoenix/server/static/assets/{pages-CAL1FDMt.js → pages-UeWaKXNs.js} +337 -442
  148. phoenix/server/static/assets/{vendor-6IcPAw_j.js → vendor-CtqfhlbC.js} +6 -6
  149. phoenix/server/static/assets/{vendor-arizeai-DRZuoyuF.js → vendor-arizeai-C_3SBz56.js} +2 -2
  150. phoenix/server/static/assets/{vendor-codemirror-DVE2_WBr.js → vendor-codemirror-wfdk9cjp.js} +1 -1
  151. phoenix/server/static/assets/{vendor-recharts-DwrexFA4.js → vendor-recharts-BiVnSv90.js} +1 -1
  152. phoenix/server/templates/index.html +1 -0
  153. phoenix/server/thread_server.py +1 -1
  154. phoenix/server/types.py +17 -29
  155. phoenix/services.py +8 -3
  156. phoenix/session/client.py +12 -24
  157. phoenix/session/data_extractor.py +3 -3
  158. phoenix/session/evaluation.py +1 -2
  159. phoenix/session/session.py +26 -21
  160. phoenix/trace/attributes.py +16 -28
  161. phoenix/trace/dsl/filter.py +17 -21
  162. phoenix/trace/dsl/helpers.py +3 -3
  163. phoenix/trace/dsl/query.py +13 -22
  164. phoenix/trace/fixtures.py +11 -17
  165. phoenix/trace/otel.py +5 -15
  166. phoenix/trace/projects.py +3 -2
  167. phoenix/trace/schemas.py +2 -2
  168. phoenix/trace/span_evaluations.py +9 -8
  169. phoenix/trace/span_json_decoder.py +3 -3
  170. phoenix/trace/span_json_encoder.py +2 -2
  171. phoenix/trace/trace_dataset.py +6 -5
  172. phoenix/trace/utils.py +6 -6
  173. phoenix/utilities/deprecation.py +3 -2
  174. phoenix/utilities/error_handling.py +3 -2
  175. phoenix/utilities/json.py +2 -1
  176. phoenix/utilities/logging.py +2 -2
  177. phoenix/utilities/project.py +1 -1
  178. phoenix/utilities/re.py +3 -4
  179. phoenix/utilities/template_formatters.py +16 -5
  180. phoenix/version.py +1 -1
  181. arize_phoenix-5.5.2.dist-info/RECORD +0 -321
  182. phoenix/server/static/assets/components-hX0LgYz3.js +0 -1428
  183. {arize_phoenix-5.5.2.dist-info → arize_phoenix-5.7.0.dist-info}/WHEEL +0 -0
  184. {arize_phoenix-5.5.2.dist-info → arize_phoenix-5.7.0.dist-info}/entry_points.txt +0 -0
  185. {arize_phoenix-5.5.2.dist-info → arize_phoenix-5.7.0.dist-info}/licenses/IP_NOTICE +0 -0
  186. {arize_phoenix-5.5.2.dist-info → arize_phoenix-5.7.0.dist-info}/licenses/LICENSE +0 -0
@@ -1,4 +1,4 @@
1
- from typing import List, Optional, Union, cast
1
+ from typing import Optional, Union, cast
2
2
 
3
3
  import pandas as pd
4
4
  import strawberry
@@ -22,11 +22,11 @@ class AnnotationSummary:
22
22
  return cast(int, self.df.record_count.sum())
23
23
 
24
24
  @strawberry.field
25
- def labels(self) -> List[str]:
25
+ def labels(self) -> list[str]:
26
26
  return self.df.label.dropna().tolist()
27
27
 
28
28
  @strawberry.field
29
- def label_fractions(self) -> List[LabelFraction]:
29
+ def label_fractions(self) -> list[LabelFraction]:
30
30
  if not (n := self.df.label_count.sum()):
31
31
  return []
32
32
  return [
@@ -0,0 +1,44 @@
1
+ from typing import Optional
2
+
3
+ import strawberry
4
+ from strawberry.relay import GlobalID
5
+
6
+ from .Experiment import Experiment
7
+ from .Span import Span
8
+
9
+
10
+ @strawberry.interface
11
+ class ChatCompletionSubscriptionPayload:
12
+ dataset_example_id: Optional[GlobalID] = None
13
+
14
+
15
+ @strawberry.type
16
+ class TextChunk(ChatCompletionSubscriptionPayload):
17
+ content: str
18
+
19
+
20
+ @strawberry.type
21
+ class FunctionCallChunk(ChatCompletionSubscriptionPayload):
22
+ name: str
23
+ arguments: str
24
+
25
+
26
+ @strawberry.type
27
+ class ToolCallChunk(ChatCompletionSubscriptionPayload):
28
+ id: str
29
+ function: FunctionCallChunk
30
+
31
+
32
+ @strawberry.type
33
+ class FinishedChatCompletion(ChatCompletionSubscriptionPayload):
34
+ span: Span
35
+
36
+
37
+ @strawberry.type
38
+ class ChatCompletionSubscriptionError(ChatCompletionSubscriptionPayload):
39
+ message: str
40
+
41
+
42
+ @strawberry.type
43
+ class ChatCompletionOverDatasetSubscriptionResult(ChatCompletionSubscriptionPayload):
44
+ experiment: Experiment
@@ -1,5 +1,6 @@
1
1
  from collections import Counter, defaultdict
2
- from typing import Dict, List, Mapping, Optional, Set
2
+ from collections.abc import Mapping
3
+ from typing import Optional
3
4
 
4
5
  import strawberry
5
6
  from strawberry import ID
@@ -22,7 +23,7 @@ class Cluster:
22
23
  description="The ID of the cluster",
23
24
  )
24
25
 
25
- event_ids: List[ID] = strawberry.field(
26
+ event_ids: list[ID] = strawberry.field(
26
27
  description="The event IDs of the points in the cluster",
27
28
  )
28
29
 
@@ -94,7 +95,7 @@ class Cluster:
94
95
  metric: DataQualityMetricInput,
95
96
  ) -> DatasetValues:
96
97
  model = info.context.model
97
- row_ids: Dict[InferencesRole, List[int]] = defaultdict(list)
98
+ row_ids: dict[InferencesRole, list[int]] = defaultdict(list)
98
99
  for row_id, inferences_role in map(unpack_event_id, self.event_ids):
99
100
  if not isinstance(inferences_role, InferencesRole):
100
101
  continue
@@ -120,7 +121,7 @@ class Cluster:
120
121
  metric: PerformanceMetricInput,
121
122
  ) -> DatasetValues:
122
123
  model = info.context.model
123
- row_ids: Dict[InferencesRole, List[int]] = defaultdict(list)
124
+ row_ids: dict[InferencesRole, list[int]] = defaultdict(list)
124
125
  for row_id, inferences_role in map(unpack_event_id, self.event_ids):
125
126
  if not isinstance(inferences_role, InferencesRole):
126
127
  continue
@@ -139,15 +140,15 @@ class Cluster:
139
140
 
140
141
 
141
142
  def to_gql_clusters(
142
- clustered_events: Mapping[str, Set[ID]],
143
- ) -> List[Cluster]:
143
+ clustered_events: Mapping[str, set[ID]],
144
+ ) -> list[Cluster]:
144
145
  """
145
146
  Converts a dictionary of event IDs to cluster IDs to a list of clusters
146
147
  for the graphQL response
147
148
 
148
149
  Parameters
149
150
  ----------
150
- clustered_events: Mapping[str, Set[ID]]
151
+ clustered_events: Mapping[str, set[ID]]
151
152
  A mapping of cluster ID to its set of event IDs
152
153
  """
153
154
 
@@ -1,5 +1,6 @@
1
+ from collections.abc import AsyncIterable
1
2
  from datetime import datetime
2
- from typing import AsyncIterable, ClassVar, List, Optional, Tuple, Type, cast
3
+ from typing import ClassVar, Optional, cast
3
4
 
4
5
  import strawberry
5
6
  from sqlalchemy import and_, func, select
@@ -27,7 +28,7 @@ from phoenix.server.api.types.SortDir import SortDir
27
28
 
28
29
  @strawberry.type
29
30
  class Dataset(Node):
30
- _table: ClassVar[Type[models.Base]] = models.Experiment
31
+ _table: ClassVar[type[models.Base]] = models.Experiment
31
32
  id_attr: NodeID[int]
32
33
  name: str
33
34
  description: Optional[str]
@@ -233,7 +234,7 @@ class Dataset(Node):
233
234
  experiments = [
234
235
  to_gql_experiment(experiment, sequence_number)
235
236
  async for experiment, sequence_number in cast(
236
- AsyncIterable[Tuple[models.Experiment, int]],
237
+ AsyncIterable[tuple[models.Experiment, int]],
237
238
  await session.stream(query),
238
239
  )
239
240
  ]
@@ -242,7 +243,7 @@ class Dataset(Node):
242
243
  @strawberry.field
243
244
  async def experiment_annotation_summaries(
244
245
  self, info: Info[Context, None]
245
- ) -> List[ExperimentAnnotationSummary]:
246
+ ) -> list[ExperimentAnnotationSummary]:
246
247
  dataset_id = self.id_attr
247
248
  query = (
248
249
  select(
@@ -1,5 +1,5 @@
1
1
  from collections import defaultdict
2
- from typing import Any, Dict, List, Optional
2
+ from typing import Any, Optional
3
3
 
4
4
  import pandas as pd
5
5
  import strawberry
@@ -123,7 +123,7 @@ class Dimension(Node):
123
123
  " Missing values are excluded. Non-categorical dimensions return an empty list."
124
124
  )
125
125
  ) # type: ignore # https://github.com/strawberry-graphql/strawberry/issues/1929
126
- def categories(self) -> List[str]:
126
+ def categories(self) -> list[str]:
127
127
  return list(self.dimension.categories)
128
128
 
129
129
  @strawberry.field(
@@ -250,7 +250,7 @@ class Dimension(Node):
250
250
  if isinstance(binning_method, binning.IntervalBinning) and binning_method.bins is not None:
251
251
  all_bins = all_bins.union(binning_method.bins)
252
252
  for bin in all_bins:
253
- values: Dict[ms.InferencesRole, Any] = defaultdict(lambda: None)
253
+ values: dict[ms.InferencesRole, Any] = defaultdict(lambda: None)
254
254
  for role in ms.InferencesRole:
255
255
  if model[role].empty:
256
256
  continue
@@ -1,6 +1,7 @@
1
1
  import math
2
+ from collections.abc import Iterable
2
3
  from functools import cached_property
3
- from typing import Any, Dict, Iterable, Optional, Tuple
4
+ from typing import Any, Optional
4
5
 
5
6
  import pandas as pd
6
7
  import strawberry
@@ -24,8 +25,8 @@ class DocumentEvaluationSummary:
24
25
  ) -> None:
25
26
  self.evaluation_name = evaluation_name
26
27
  self.metrics_collection = pd.Series(metrics_collection, dtype=object)
27
- self._cached_average_ndcg_results: Dict[Optional[int], Tuple[float, int]] = {}
28
- self._cached_average_precision_results: Dict[Optional[int], Tuple[float, int]] = {}
28
+ self._cached_average_ndcg_results: dict[Optional[int], tuple[float, int]] = {}
29
+ self._cached_average_precision_results: dict[Optional[int], tuple[float, int]] = {}
29
30
 
30
31
  @strawberry.field
31
32
  def average_ndcg(self, k: Optional[int] = UNSET) -> Optional[float]:
@@ -67,7 +68,7 @@ class DocumentEvaluationSummary:
67
68
  _, count = self._average_hit
68
69
  return count
69
70
 
70
- def _average_ndcg(self, k: Optional[int] = None) -> Tuple[float, int]:
71
+ def _average_ndcg(self, k: Optional[int] = None) -> tuple[float, int]:
71
72
  if (result := self._cached_average_ndcg_results.get(k)) is not None:
72
73
  return result
73
74
  values = self.metrics_collection.apply(lambda m: m.ndcg(k))
@@ -75,7 +76,7 @@ class DocumentEvaluationSummary:
75
76
  self._cached_average_ndcg_results[k] = result
76
77
  return result
77
78
 
78
- def _average_precision(self, k: Optional[int] = None) -> Tuple[float, int]:
79
+ def _average_precision(self, k: Optional[int] = None) -> tuple[float, int]:
79
80
  if (result := self._cached_average_precision_results.get(k)) is not None:
80
81
  return result
81
82
  values = self.metrics_collection.apply(lambda m: m.precision(k))
@@ -84,11 +85,11 @@ class DocumentEvaluationSummary:
84
85
  return result
85
86
 
86
87
  @cached_property
87
- def _average_reciprocal_rank(self) -> Tuple[float, int]:
88
+ def _average_reciprocal_rank(self) -> tuple[float, int]:
88
89
  values = self.metrics_collection.apply(lambda m: m.reciprocal_rank())
89
90
  return values.mean(), values.count()
90
91
 
91
92
  @cached_property
92
- def _average_hit(self) -> Tuple[float, int]:
93
+ def _average_hit(self) -> tuple[float, int]:
93
94
  values = self.metrics_collection.apply(lambda m: m.hit())
94
95
  return values.mean(), values.count()
@@ -1,7 +1,8 @@
1
1
  from collections import defaultdict
2
+ from collections.abc import Iterable, Iterator
2
3
  from datetime import timedelta
3
4
  from itertools import chain, repeat
4
- from typing import Any, Dict, Iterable, Iterator, List, Optional, Tuple, Union, cast
5
+ from typing import Any, Optional, Union, cast
5
6
 
6
7
  import numpy as np
7
8
  import numpy.typing as npt
@@ -313,8 +314,8 @@ class EmbeddingDimension(Node):
313
314
  ] = DEFAULT_CLUSTER_SELECTION_EPSILON,
314
315
  ) -> UMAPPoints:
315
316
  model = info.context.model
316
- data: Dict[ID, npt.NDArray[np.float64]] = {}
317
- retrievals: List[Tuple[ID, Any, Any]] = []
317
+ data: dict[ID, npt.NDArray[np.float64]] = {}
318
+ retrievals: list[tuple[ID, Any, Any]] = []
318
319
  for inferences in model[Inferences]:
319
320
  inferences_id = inferences.role
320
321
  row_id_start, row_id_stop = 0, len(inferences)
@@ -353,7 +354,7 @@ class EmbeddingDimension(Node):
353
354
  )
354
355
  )
355
356
 
356
- context_retrievals: List[Retrieval] = []
357
+ context_retrievals: list[Retrieval] = []
357
358
  if isinstance(
358
359
  self.dimension,
359
360
  ms.RetrievalEmbeddingDimension,
@@ -414,7 +415,7 @@ class EmbeddingDimension(Node):
414
415
  ),
415
416
  ).generate(data, n_components=n_components)
416
417
 
417
- points: Dict[Union[InferencesRole, AncillaryInferencesRole], List[UMAPPoint]] = defaultdict(
418
+ points: dict[Union[InferencesRole, AncillaryInferencesRole], list[UMAPPoint]] = defaultdict(
418
419
  list
419
420
  )
420
421
  for event_id, vector in vectors.items():
@@ -1,4 +1,4 @@
1
- from typing import List, Optional, Union, cast
1
+ from typing import Optional, Union, cast
2
2
 
3
3
  import pandas as pd
4
4
  import strawberry
@@ -22,11 +22,11 @@ class EvaluationSummary:
22
22
  return cast(int, self.df.record_count.sum())
23
23
 
24
24
  @strawberry.field
25
- def labels(self) -> List[str]:
25
+ def labels(self) -> list[str]:
26
26
  return self.df.label.dropna().tolist()
27
27
 
28
28
  @strawberry.field
29
- def label_fractions(self) -> List[LabelFraction]:
29
+ def label_fractions(self) -> list[LabelFraction]:
30
30
  if not (n := self.df.label_count.sum()):
31
31
  return []
32
32
  return [
@@ -1,6 +1,6 @@
1
1
  import math
2
2
  from collections import defaultdict
3
- from typing import Dict, List, Optional, Tuple, Union, cast
3
+ from typing import Optional, Union, cast
4
4
 
5
5
  import strawberry
6
6
  from strawberry import ID
@@ -28,7 +28,7 @@ from .PromptResponse import PromptResponse
28
28
  class Event:
29
29
  id: strawberry.ID
30
30
  eventMetadata: EventMetadata
31
- dimensions: List[DimensionWithValue]
31
+ dimensions: list[DimensionWithValue]
32
32
  prompt_and_response: Optional[PromptResponse] = strawberry.field(
33
33
  description="The prompt and response pair associated with the event",
34
34
  default=GqlValueMediator(),
@@ -53,7 +53,7 @@ def create_event_id(
53
53
 
54
54
  def unpack_event_id(
55
55
  event_id: ID,
56
- ) -> Tuple[int, Union[InferencesRole, AncillaryInferencesRole]]:
56
+ ) -> tuple[int, Union[InferencesRole, AncillaryInferencesRole]]:
57
57
  row_id_str, inferences_role_str = str(event_id).split(":")
58
58
  row_id = int(row_id_str)
59
59
  inferences_role = STR_TO_INFEREENCES_ROLE[inferences_role_str]
@@ -61,12 +61,12 @@ def unpack_event_id(
61
61
 
62
62
 
63
63
  def parse_event_ids_by_inferences_role(
64
- event_ids: List[ID],
65
- ) -> Dict[Union[InferencesRole, AncillaryInferencesRole], List[int]]:
64
+ event_ids: list[ID],
65
+ ) -> dict[Union[InferencesRole, AncillaryInferencesRole], list[int]]:
66
66
  """
67
67
  Parses event IDs and returns the corresponding row indexes.
68
68
  """
69
- row_indexes: Dict[Union[InferencesRole, AncillaryInferencesRole], List[int]] = defaultdict(list)
69
+ row_indexes: dict[Union[InferencesRole, AncillaryInferencesRole], list[int]] = defaultdict(list)
70
70
  for event_id in event_ids:
71
71
  row_id, inferences_role = unpack_event_id(event_id)
72
72
  row_indexes[inferences_role].append(row_id)
@@ -76,7 +76,7 @@ def parse_event_ids_by_inferences_role(
76
76
  def create_event(
77
77
  event_id: ID,
78
78
  event: ms.Event,
79
- dimensions: List[Dimension],
79
+ dimensions: list[Dimension],
80
80
  is_document_record: bool = False,
81
81
  ) -> Event:
82
82
  """
@@ -1,5 +1,5 @@
1
1
  from datetime import datetime
2
- from typing import ClassVar, List, Optional, Type
2
+ from typing import ClassVar, Optional
3
3
 
4
4
  import strawberry
5
5
  from sqlalchemy import select
@@ -23,7 +23,7 @@ from phoenix.server.api.types.Project import Project
23
23
 
24
24
  @strawberry.type
25
25
  class Experiment(Node):
26
- _table: ClassVar[Type[models.Base]] = models.Experiment
26
+ _table: ClassVar[type[models.Base]] = models.Experiment
27
27
  cached_sequence_number: Private[Optional[int]] = None
28
28
  id_attr: NodeID[int]
29
29
  name: str
@@ -84,7 +84,7 @@ class Experiment(Node):
84
84
  @strawberry.field
85
85
  async def annotation_summaries(
86
86
  self, info: Info[Context, None]
87
- ) -> List[ExperimentAnnotationSummary]:
87
+ ) -> list[ExperimentAnnotationSummary]:
88
88
  experiment_id = self.id_attr
89
89
  return [
90
90
  ExperimentAnnotationSummary(
@@ -1,5 +1,3 @@
1
- from typing import List
2
-
3
1
  import strawberry
4
2
  from strawberry.relay import GlobalID
5
3
 
@@ -10,10 +8,10 @@ from phoenix.server.api.types.ExperimentRun import ExperimentRun
10
8
  @strawberry.type
11
9
  class RunComparisonItem:
12
10
  experiment_id: GlobalID
13
- runs: List[ExperimentRun]
11
+ runs: list[ExperimentRun]
14
12
 
15
13
 
16
14
  @strawberry.type
17
15
  class ExperimentComparison:
18
16
  example: DatasetExample
19
- run_comparison_items: List[RunComparisonItem]
17
+ run_comparison_items: list[RunComparisonItem]
@@ -5,12 +5,36 @@ import strawberry
5
5
 
6
6
  @strawberry.enum
7
7
  class GenerativeProviderKey(Enum):
8
- OPENAI = "OPENAI"
9
- ANTHROPIC = "ANTHROPIC"
10
- AZURE_OPENAI = "AZURE_OPENAI"
8
+ OPENAI = "OpenAI"
9
+ ANTHROPIC = "Anthropic"
10
+ AZURE_OPENAI = "Azure OpenAI"
11
11
 
12
12
 
13
13
  @strawberry.type
14
14
  class GenerativeProvider:
15
15
  name: str
16
16
  key: GenerativeProviderKey
17
+
18
+ @strawberry.field
19
+ async def dependencies(self) -> list[str]:
20
+ from phoenix.server.api.helpers.playground_registry import (
21
+ PLAYGROUND_CLIENT_REGISTRY,
22
+ PROVIDER_DEFAULT,
23
+ )
24
+
25
+ default_client = PLAYGROUND_CLIENT_REGISTRY.get_client(self.key, PROVIDER_DEFAULT)
26
+ if default_client:
27
+ return default_client.dependencies()
28
+ return []
29
+
30
+ @strawberry.field
31
+ async def dependencies_installed(self) -> bool:
32
+ from phoenix.server.api.helpers.playground_registry import (
33
+ PLAYGROUND_CLIENT_REGISTRY,
34
+ PROVIDER_DEFAULT,
35
+ )
36
+
37
+ default_client = PLAYGROUND_CLIENT_REGISTRY.get_client(self.key, PROVIDER_DEFAULT)
38
+ if default_client:
39
+ return default_client.dependencies_are_installed()
40
+ return False
@@ -1,5 +1,6 @@
1
+ from collections.abc import Iterable
1
2
  from datetime import datetime
2
- from typing import Iterable, List, Optional, Set, Union
3
+ from typing import Optional, Union
3
4
 
4
5
  import strawberry
5
6
  from strawberry import ID, UNSET
@@ -30,9 +31,9 @@ class Inferences:
30
31
  @strawberry.field
31
32
  def events(
32
33
  self,
33
- event_ids: List[ID],
34
- dimensions: Optional[List[DimensionInput]] = UNSET,
35
- ) -> List[Event]:
34
+ event_ids: list[ID],
35
+ dimensions: Optional[list[DimensionInput]] = UNSET,
36
+ ) -> list[Event]:
36
37
  """
37
38
  Returns events for specific event IDs and dimensions. If no input
38
39
  dimensions are provided, returns all features and tags.
@@ -62,16 +63,16 @@ class Inferences:
62
63
 
63
64
  def _get_requested_features_and_tags(
64
65
  core_dimensions: Iterable[ScalarDimension],
65
- requested_dimension_names: Optional[Set[str]] = UNSET,
66
- ) -> List[Dimension]:
66
+ requested_dimension_names: Optional[set[str]] = UNSET,
67
+ ) -> list[Dimension]:
67
68
  """
68
69
  Returns requested features and tags as a list of strawberry Inferences. If no
69
70
  dimensions are explicitly requested, returns all features and tags.
70
71
  """
71
- requested_features_and_tags: List[Dimension] = []
72
+ requested_features_and_tags: list[Dimension] = []
72
73
  for id, dim in enumerate(core_dimensions):
73
74
  is_requested = (
74
- not isinstance(requested_dimension_names, Set)
75
+ not isinstance(requested_dimension_names, set)
75
76
  ) or dim.name in requested_dimension_names
76
77
  is_feature_or_tag = dim.role in (FEATURE, TAG)
77
78
  if is_requested and is_feature_or_tag:
@@ -1,5 +1,5 @@
1
1
  from enum import Enum
2
- from typing import Dict, Union
2
+ from typing import Union
3
3
 
4
4
  import strawberry
5
5
 
@@ -16,7 +16,7 @@ class AncillaryInferencesRole(Enum):
16
16
  corpus = "InferencesRole.CORPUS"
17
17
 
18
18
 
19
- STR_TO_INFEREENCES_ROLE: Dict[str, Union[InferencesRole, AncillaryInferencesRole]] = {
19
+ STR_TO_INFEREENCES_ROLE: dict[str, Union[InferencesRole, AncillaryInferencesRole]] = {
20
20
  str(InferencesRole.primary.value): InferencesRole.primary,
21
21
  str(InferencesRole.reference.value): InferencesRole.reference,
22
22
  str(AncillaryInferencesRole.corpus.value): AncillaryInferencesRole.corpus,
@@ -1,5 +1,5 @@
1
1
  import asyncio
2
- from typing import List, Optional
2
+ from typing import Optional
3
3
 
4
4
  import strawberry
5
5
  from strawberry import UNSET, Info
@@ -135,7 +135,7 @@ class Model:
135
135
  async def exported_files(
136
136
  self,
137
137
  info: Info[Context, None],
138
- ) -> List[ExportedFile]:
138
+ ) -> list[ExportedFile]:
139
139
  loop = asyncio.get_running_loop()
140
140
  return [
141
141
  ExportedFile(file_name=path.stem)
@@ -3,9 +3,7 @@ from datetime import datetime
3
3
  from typing import (
4
4
  Any,
5
5
  ClassVar,
6
- List,
7
6
  Optional,
8
- Type,
9
7
  )
10
8
 
11
9
  import strawberry
@@ -39,7 +37,7 @@ from phoenix.trace.dsl import SpanFilter
39
37
 
40
38
  @strawberry.type
41
39
  class Project(Node):
42
- _table: ClassVar[Type[models.Base]] = models.Project
40
+ _table: ClassVar[type[models.Base]] = models.Project
43
41
  id_attr: NodeID[int]
44
42
  name: str
45
43
  gradient_start_color: str
@@ -223,18 +221,13 @@ class Project(Node):
223
221
  span_records = await session.execute(stmt)
224
222
  async for span_record in islice(span_records, first):
225
223
  span = span_record[0]
226
- sort_column_value = span_record[1] if len(span_record) > 1 else None
227
- cursor = Cursor(
228
- rowid=span.id,
229
- sort_column=(
230
- CursorSortColumn(
231
- type=sort_config.column_data_type,
232
- value=sort_column_value,
233
- )
234
- if sort_config
235
- else None
236
- ),
237
- )
224
+ cursor = Cursor(rowid=span.id)
225
+ if sort_config:
226
+ assert len(span_record) > 1
227
+ cursor.sort_column = CursorSortColumn(
228
+ type=sort_config.column_data_type,
229
+ value=span_record[1],
230
+ )
238
231
  cursors_and_nodes.append((cursor, to_gql_span(span)))
239
232
  has_next_page = True
240
233
  try:
@@ -255,7 +248,7 @@ class Project(Node):
255
248
  async def trace_annotations_names(
256
249
  self,
257
250
  info: Info[Context, None],
258
- ) -> List[str]:
251
+ ) -> list[str]:
259
252
  stmt = (
260
253
  select(distinct(models.TraceAnnotation.name))
261
254
  .join(models.Trace)
@@ -271,7 +264,7 @@ class Project(Node):
271
264
  async def span_annotation_names(
272
265
  self,
273
266
  info: Info[Context, None],
274
- ) -> List[str]:
267
+ ) -> list[str]:
275
268
  stmt = (
276
269
  select(distinct(models.SpanAnnotation.name))
277
270
  .join(models.Span)
@@ -288,7 +281,7 @@ class Project(Node):
288
281
  self,
289
282
  info: Info[Context, None],
290
283
  span_id: Optional[ID] = UNSET,
291
- ) -> List[str]:
284
+ ) -> list[str]:
292
285
  stmt = (
293
286
  select(distinct(models.DocumentAnnotation.name))
294
287
  .join(models.Span)
@@ -1,6 +1,6 @@
1
1
  import math
2
2
  from dataclasses import dataclass
3
- from typing import Any, List, Optional, Union, overload
3
+ from typing import Any, Optional, Union, overload
4
4
 
5
5
  import numpy as np
6
6
  import pandas as pd
@@ -72,12 +72,12 @@ class Segment:
72
72
  default_factory=DatasetValues,
73
73
  )
74
74
  # TODO add support for a "z" metric list
75
- # values: List[Optional[float]]
75
+ # values: list[Optional[float]]
76
76
 
77
77
 
78
78
  @strawberry.type
79
79
  class Segments:
80
- segments: List[Segment] = strawberry.field(default_factory=list)
80
+ segments: list[Segment] = strawberry.field(default_factory=list)
81
81
  total_counts: DatasetValues = strawberry.field(
82
82
  default_factory=DatasetValues,
83
83
  )