arize-phoenix 5.5.2__py3-none-any.whl → 5.6.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 (172) hide show
  1. {arize_phoenix-5.5.2.dist-info → arize_phoenix-5.6.0.dist-info}/METADATA +3 -6
  2. {arize_phoenix-5.5.2.dist-info → arize_phoenix-5.6.0.dist-info}/RECORD +171 -171
  3. phoenix/config.py +8 -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/input_types/AddExamplesToDatasetInput.py +2 -2
  72. phoenix/server/api/input_types/AddSpansToDatasetInput.py +2 -2
  73. phoenix/server/api/input_types/ChatCompletionMessageInput.py +13 -1
  74. phoenix/server/api/input_types/ClusterInput.py +2 -2
  75. phoenix/server/api/input_types/DeleteAnnotationsInput.py +1 -3
  76. phoenix/server/api/input_types/DeleteDatasetExamplesInput.py +2 -2
  77. phoenix/server/api/input_types/DeleteExperimentsInput.py +1 -3
  78. phoenix/server/api/input_types/DimensionFilter.py +4 -4
  79. phoenix/server/api/input_types/Granularity.py +1 -1
  80. phoenix/server/api/input_types/InvocationParameters.py +2 -2
  81. phoenix/server/api/input_types/PatchDatasetExamplesInput.py +2 -2
  82. phoenix/server/api/mutations/dataset_mutations.py +4 -4
  83. phoenix/server/api/mutations/experiment_mutations.py +1 -2
  84. phoenix/server/api/mutations/export_events_mutations.py +7 -7
  85. phoenix/server/api/mutations/span_annotations_mutations.py +4 -4
  86. phoenix/server/api/mutations/trace_annotations_mutations.py +4 -4
  87. phoenix/server/api/mutations/user_mutations.py +4 -4
  88. phoenix/server/api/openapi/schema.py +2 -2
  89. phoenix/server/api/queries.py +20 -20
  90. phoenix/server/api/routers/oauth2.py +4 -4
  91. phoenix/server/api/routers/v1/datasets.py +22 -36
  92. phoenix/server/api/routers/v1/evaluations.py +6 -5
  93. phoenix/server/api/routers/v1/experiment_evaluations.py +2 -2
  94. phoenix/server/api/routers/v1/experiment_runs.py +2 -2
  95. phoenix/server/api/routers/v1/experiments.py +4 -4
  96. phoenix/server/api/routers/v1/spans.py +13 -12
  97. phoenix/server/api/routers/v1/traces.py +5 -5
  98. phoenix/server/api/routers/v1/utils.py +5 -5
  99. phoenix/server/api/subscriptions.py +284 -162
  100. phoenix/server/api/types/AnnotationSummary.py +3 -3
  101. phoenix/server/api/types/Cluster.py +8 -7
  102. phoenix/server/api/types/Dataset.py +5 -4
  103. phoenix/server/api/types/Dimension.py +3 -3
  104. phoenix/server/api/types/DocumentEvaluationSummary.py +8 -7
  105. phoenix/server/api/types/EmbeddingDimension.py +6 -5
  106. phoenix/server/api/types/EvaluationSummary.py +3 -3
  107. phoenix/server/api/types/Event.py +7 -7
  108. phoenix/server/api/types/Experiment.py +3 -3
  109. phoenix/server/api/types/ExperimentComparison.py +2 -4
  110. phoenix/server/api/types/Inferences.py +9 -8
  111. phoenix/server/api/types/InferencesRole.py +2 -2
  112. phoenix/server/api/types/Model.py +2 -2
  113. phoenix/server/api/types/Project.py +11 -18
  114. phoenix/server/api/types/Segments.py +3 -3
  115. phoenix/server/api/types/Span.py +8 -7
  116. phoenix/server/api/types/TimeSeries.py +8 -7
  117. phoenix/server/api/types/Trace.py +2 -2
  118. phoenix/server/api/types/UMAPPoints.py +6 -6
  119. phoenix/server/api/types/User.py +3 -3
  120. phoenix/server/api/types/node.py +1 -3
  121. phoenix/server/api/types/pagination.py +4 -4
  122. phoenix/server/api/utils.py +2 -4
  123. phoenix/server/app.py +16 -25
  124. phoenix/server/bearer_auth.py +4 -10
  125. phoenix/server/dml_event.py +3 -3
  126. phoenix/server/dml_event_handler.py +10 -24
  127. phoenix/server/grpc_server.py +3 -2
  128. phoenix/server/jwt_store.py +22 -21
  129. phoenix/server/main.py +3 -3
  130. phoenix/server/oauth2.py +3 -2
  131. phoenix/server/rate_limiters.py +5 -8
  132. phoenix/server/static/.vite/manifest.json +31 -31
  133. phoenix/server/static/assets/components-C70HJiXz.js +1612 -0
  134. phoenix/server/static/assets/{index-DCzakdJq.js → index-DLe1Oo3l.js} +2 -2
  135. phoenix/server/static/assets/{pages-CAL1FDMt.js → pages-C8-Sl7JI.js} +269 -434
  136. phoenix/server/static/assets/{vendor-6IcPAw_j.js → vendor-CtqfhlbC.js} +6 -6
  137. phoenix/server/static/assets/{vendor-arizeai-DRZuoyuF.js → vendor-arizeai-C_3SBz56.js} +2 -2
  138. phoenix/server/static/assets/{vendor-codemirror-DVE2_WBr.js → vendor-codemirror-wfdk9cjp.js} +1 -1
  139. phoenix/server/static/assets/{vendor-recharts-DwrexFA4.js → vendor-recharts-BiVnSv90.js} +1 -1
  140. phoenix/server/thread_server.py +1 -1
  141. phoenix/server/types.py +17 -29
  142. phoenix/services.py +4 -3
  143. phoenix/session/client.py +12 -24
  144. phoenix/session/data_extractor.py +3 -3
  145. phoenix/session/evaluation.py +1 -2
  146. phoenix/session/session.py +11 -20
  147. phoenix/trace/attributes.py +16 -28
  148. phoenix/trace/dsl/filter.py +17 -21
  149. phoenix/trace/dsl/helpers.py +3 -3
  150. phoenix/trace/dsl/query.py +13 -22
  151. phoenix/trace/fixtures.py +11 -17
  152. phoenix/trace/otel.py +5 -15
  153. phoenix/trace/projects.py +3 -2
  154. phoenix/trace/schemas.py +2 -2
  155. phoenix/trace/span_evaluations.py +9 -8
  156. phoenix/trace/span_json_decoder.py +3 -3
  157. phoenix/trace/span_json_encoder.py +2 -2
  158. phoenix/trace/trace_dataset.py +6 -5
  159. phoenix/trace/utils.py +6 -6
  160. phoenix/utilities/deprecation.py +3 -2
  161. phoenix/utilities/error_handling.py +3 -2
  162. phoenix/utilities/json.py +2 -1
  163. phoenix/utilities/logging.py +2 -2
  164. phoenix/utilities/project.py +1 -1
  165. phoenix/utilities/re.py +3 -4
  166. phoenix/utilities/template_formatters.py +5 -4
  167. phoenix/version.py +1 -1
  168. phoenix/server/static/assets/components-hX0LgYz3.js +0 -1428
  169. {arize_phoenix-5.5.2.dist-info → arize_phoenix-5.6.0.dist-info}/WHEEL +0 -0
  170. {arize_phoenix-5.5.2.dist-info → arize_phoenix-5.6.0.dist-info}/entry_points.txt +0 -0
  171. {arize_phoenix-5.5.2.dist-info → arize_phoenix-5.6.0.dist-info}/licenses/IP_NOTICE +0 -0
  172. {arize_phoenix-5.5.2.dist-info → arize_phoenix-5.6.0.dist-info}/licenses/LICENSE +0 -0
@@ -1,20 +1,10 @@
1
1
  import logging
2
+ from collections.abc import Awaitable, Iterable, Iterator, Mapping
2
3
  from dataclasses import dataclass, field
3
4
  from datetime import datetime, timezone
4
5
  from enum import Enum
5
6
  from itertools import chain
6
- from typing import (
7
- Any,
8
- Awaitable,
9
- Dict,
10
- FrozenSet,
11
- Iterable,
12
- Iterator,
13
- Mapping,
14
- Optional,
15
- Union,
16
- cast,
17
- )
7
+ from typing import Any, Optional, Union, cast
18
8
 
19
9
  from sqlalchemy import insert, select
20
10
  from sqlalchemy.ext.asyncio import AsyncSession
@@ -34,9 +24,9 @@ SpanRowId: TypeAlias = int
34
24
 
35
25
  @dataclass(frozen=True)
36
26
  class ExampleContent:
37
- input: Dict[str, Any] = field(default_factory=dict)
38
- output: Dict[str, Any] = field(default_factory=dict)
39
- metadata: Dict[str, Any] = field(default_factory=dict)
27
+ input: dict[str, Any] = field(default_factory=dict)
28
+ output: dict[str, Any] = field(default_factory=dict)
29
+ metadata: dict[str, Any] = field(default_factory=dict)
40
30
 
41
31
 
42
32
  Examples: TypeAlias = Iterable[ExampleContent]
@@ -220,14 +210,14 @@ async def add_dataset_examples(
220
210
 
221
211
  @dataclass(frozen=True)
222
212
  class DatasetKeys:
223
- input: FrozenSet[str]
224
- output: FrozenSet[str]
225
- metadata: FrozenSet[str]
213
+ input: frozenset[str]
214
+ output: frozenset[str]
215
+ metadata: frozenset[str]
226
216
 
227
217
  def __iter__(self) -> Iterator[str]:
228
218
  yield from sorted(set(chain(self.input, self.output, self.metadata)))
229
219
 
230
- def check_differences(self, column_headers_set: FrozenSet[str]) -> None:
220
+ def check_differences(self, column_headers_set: frozenset[str]) -> None:
231
221
  for category, keys in (
232
222
  ("input", self.input),
233
223
  ("output", self.output),
@@ -1,5 +1,6 @@
1
+ from collections.abc import Mapping
1
2
  from datetime import datetime
2
- from typing import Any, List, Mapping, NamedTuple, Optional, Tuple
3
+ from typing import Any, NamedTuple, Optional
3
4
 
4
5
  from sqlalchemy import Row, Select, and_, select, tuple_
5
6
  from sqlalchemy.ext.asyncio import AsyncSession
@@ -24,9 +25,9 @@ _DocumentPosition: TypeAlias = int
24
25
  _AnnoRowId: TypeAlias = int
25
26
  _NumDocs: TypeAlias = int
26
27
 
27
- _Key: TypeAlias = Tuple[_Name, _SpanId, _DocumentPosition]
28
- _UniqueBy: TypeAlias = Tuple[_Name, _SpanRowId, _DocumentPosition]
29
- _Existing: TypeAlias = Tuple[
28
+ _Key: TypeAlias = tuple[_Name, _SpanId, _DocumentPosition]
29
+ _UniqueBy: TypeAlias = tuple[_Name, _SpanRowId, _DocumentPosition]
30
+ _Existing: TypeAlias = tuple[
30
31
  _SpanRowId,
31
32
  _SpanId,
32
33
  _NumDocs,
@@ -51,7 +52,7 @@ class DocumentAnnotationQueueInserter(
51
52
  self,
52
53
  session: AsyncSession,
53
54
  *insertions: Insertables.DocumentAnnotation,
54
- ) -> List[DocumentAnnotationDmlEvent]:
55
+ ) -> list[DocumentAnnotationDmlEvent]:
55
56
  records = [dict(as_kv(ins.row)) for ins in insertions]
56
57
  stmt = self._insert_on_conflict(*records).returning(self.table.id)
57
58
  ids = tuple([_ async for _ in await session.stream_scalars(stmt)])
@@ -61,17 +62,17 @@ class DocumentAnnotationQueueInserter(
61
62
  self,
62
63
  session: AsyncSession,
63
64
  *parcels: Received[Precursors.DocumentAnnotation],
64
- ) -> Tuple[
65
- List[Received[Insertables.DocumentAnnotation]],
66
- List[Postponed[Precursors.DocumentAnnotation]],
67
- List[Received[Precursors.DocumentAnnotation]],
65
+ ) -> tuple[
66
+ list[Received[Insertables.DocumentAnnotation]],
67
+ list[Postponed[Precursors.DocumentAnnotation]],
68
+ list[Received[Precursors.DocumentAnnotation]],
68
69
  ]:
69
- to_insert: List[Received[Insertables.DocumentAnnotation]] = []
70
- to_postpone: List[Postponed[Precursors.DocumentAnnotation]] = []
71
- to_discard: List[Received[Precursors.DocumentAnnotation]] = []
70
+ to_insert: list[Received[Insertables.DocumentAnnotation]] = []
71
+ to_postpone: list[Postponed[Precursors.DocumentAnnotation]] = []
72
+ to_discard: list[Received[Precursors.DocumentAnnotation]] = []
72
73
 
73
74
  stmt = self._select_existing(*map(_key, parcels))
74
- existing: List[Row[_Existing]] = [_ async for _ in await session.stream(stmt)]
75
+ existing: list[Row[_Existing]] = [_ async for _ in await session.stream(stmt)]
75
76
  existing_spans: Mapping[str, _SpanAttr] = {
76
77
  e.span_id: _SpanAttr(e.span_rowid, e.num_docs) for e in existing
77
78
  }
@@ -1,17 +1,7 @@
1
1
  from abc import ABC
2
+ from collections.abc import Awaitable, Callable, Iterable, Iterator, Mapping, Sequence
2
3
  from enum import Enum, auto
3
- from typing import (
4
- Any,
5
- Awaitable,
6
- Callable,
7
- Iterable,
8
- Iterator,
9
- Mapping,
10
- Optional,
11
- Sequence,
12
- Tuple,
13
- Type,
14
- )
4
+ from typing import Any, Optional
15
5
 
16
6
  from sqlalchemy import Insert
17
7
  from sqlalchemy.dialects.postgresql import insert as insert_postgresql
@@ -41,7 +31,7 @@ class OnConflict(Enum):
41
31
 
42
32
  def insert_on_conflict(
43
33
  *records: Mapping[str, Any],
44
- table: Type[Base],
34
+ table: type[Base],
45
35
  dialect: SupportedSQLDialect,
46
36
  unique_by: Sequence[str],
47
37
  on_conflict: OnConflict = OnConflict.DO_UPDATE,
@@ -85,8 +75,8 @@ def insert_on_conflict(
85
75
 
86
76
 
87
77
  def _clean(
88
- kv: Iterable[Tuple[str, KeyedColumnElement[Any]]],
89
- ) -> Iterator[Tuple[str, KeyedColumnElement[Any]]]:
78
+ kv: Iterable[tuple[str, KeyedColumnElement[Any]]],
79
+ ) -> Iterator[tuple[str, KeyedColumnElement[Any]]]:
90
80
  for k, v in kv:
91
81
  if v.primary_key or v.foreign_keys or k == "created_at":
92
82
  continue
@@ -96,7 +86,7 @@ def _clean(
96
86
  yield k, v
97
87
 
98
88
 
99
- def as_kv(obj: models.Base) -> Iterator[Tuple[str, Any]]:
89
+ def as_kv(obj: models.Base) -> Iterator[tuple[str, Any]]:
100
90
  for k, c in obj.__table__.c.items():
101
91
  if k in ["created_at", "updated_at"]:
102
92
  continue
@@ -1,5 +1,6 @@
1
+ from collections.abc import Mapping
1
2
  from datetime import datetime
2
- from typing import Any, List, Mapping, NamedTuple, Optional, Tuple
3
+ from typing import Any, NamedTuple, Optional
3
4
 
4
5
  from sqlalchemy import Row, Select, and_, select, tuple_
5
6
  from sqlalchemy.ext.asyncio import AsyncSession
@@ -22,9 +23,9 @@ _SpanId: TypeAlias = str
22
23
  _SpanRowId: TypeAlias = int
23
24
  _AnnoRowId: TypeAlias = int
24
25
 
25
- _Key: TypeAlias = Tuple[_Name, _SpanId]
26
- _UniqueBy: TypeAlias = Tuple[_Name, _SpanRowId]
27
- _Existing: TypeAlias = Tuple[
26
+ _Key: TypeAlias = tuple[_Name, _SpanId]
27
+ _UniqueBy: TypeAlias = tuple[_Name, _SpanRowId]
28
+ _Existing: TypeAlias = tuple[
28
29
  _SpanRowId,
29
30
  _SpanId,
30
31
  Optional[_AnnoRowId],
@@ -47,7 +48,7 @@ class SpanAnnotationQueueInserter(
47
48
  self,
48
49
  session: AsyncSession,
49
50
  *insertions: Insertables.SpanAnnotation,
50
- ) -> List[SpanAnnotationDmlEvent]:
51
+ ) -> list[SpanAnnotationDmlEvent]:
51
52
  records = [dict(as_kv(ins.row)) for ins in insertions]
52
53
  stmt = self._insert_on_conflict(*records).returning(self.table.id)
53
54
  ids = tuple([_ async for _ in await session.stream_scalars(stmt)])
@@ -57,17 +58,17 @@ class SpanAnnotationQueueInserter(
57
58
  self,
58
59
  session: AsyncSession,
59
60
  *parcels: Received[Precursors.SpanAnnotation],
60
- ) -> Tuple[
61
- List[Received[Insertables.SpanAnnotation]],
62
- List[Postponed[Precursors.SpanAnnotation]],
63
- List[Received[Precursors.SpanAnnotation]],
61
+ ) -> tuple[
62
+ list[Received[Insertables.SpanAnnotation]],
63
+ list[Postponed[Precursors.SpanAnnotation]],
64
+ list[Received[Precursors.SpanAnnotation]],
64
65
  ]:
65
- to_insert: List[Received[Insertables.SpanAnnotation]] = []
66
- to_postpone: List[Postponed[Precursors.SpanAnnotation]] = []
67
- to_discard: List[Received[Precursors.SpanAnnotation]] = []
66
+ to_insert: list[Received[Insertables.SpanAnnotation]] = []
67
+ to_postpone: list[Postponed[Precursors.SpanAnnotation]] = []
68
+ to_discard: list[Received[Precursors.SpanAnnotation]] = []
68
69
 
69
70
  stmt = self._select_existing(*map(_key, parcels))
70
- existing: List[Row[_Existing]] = [_ async for _ in await session.stream(stmt)]
71
+ existing: list[Row[_Existing]] = [_ async for _ in await session.stream(stmt)]
71
72
  existing_spans: Mapping[str, _SpanAttr] = {
72
73
  e.span_id: _SpanAttr(e.span_rowid) for e in existing
73
74
  }
@@ -1,5 +1,6 @@
1
+ from collections.abc import Mapping
1
2
  from datetime import datetime
2
- from typing import Any, List, Mapping, NamedTuple, Optional, Tuple
3
+ from typing import Any, NamedTuple, Optional
3
4
 
4
5
  from sqlalchemy import Row, Select, and_, select, tuple_
5
6
  from sqlalchemy.ext.asyncio import AsyncSession
@@ -22,9 +23,9 @@ _TraceId: TypeAlias = str
22
23
  _TraceRowId: TypeAlias = int
23
24
  _AnnoRowId: TypeAlias = int
24
25
 
25
- _Key: TypeAlias = Tuple[_Name, _TraceId]
26
- _UniqueBy: TypeAlias = Tuple[_Name, _TraceRowId]
27
- _Existing: TypeAlias = Tuple[
26
+ _Key: TypeAlias = tuple[_Name, _TraceId]
27
+ _UniqueBy: TypeAlias = tuple[_Name, _TraceRowId]
28
+ _Existing: TypeAlias = tuple[
28
29
  _TraceRowId,
29
30
  _TraceId,
30
31
  Optional[_AnnoRowId],
@@ -47,7 +48,7 @@ class TraceAnnotationQueueInserter(
47
48
  self,
48
49
  session: AsyncSession,
49
50
  *insertions: Insertables.TraceAnnotation,
50
- ) -> List[TraceAnnotationDmlEvent]:
51
+ ) -> list[TraceAnnotationDmlEvent]:
51
52
  records = [dict(as_kv(ins.row)) for ins in insertions]
52
53
  stmt = self._insert_on_conflict(*records).returning(self.table.id)
53
54
  ids = tuple([_ async for _ in await session.stream_scalars(stmt)])
@@ -57,17 +58,17 @@ class TraceAnnotationQueueInserter(
57
58
  self,
58
59
  session: AsyncSession,
59
60
  *parcels: Received[Precursors.TraceAnnotation],
60
- ) -> Tuple[
61
- List[Received[Insertables.TraceAnnotation]],
62
- List[Postponed[Precursors.TraceAnnotation]],
63
- List[Received[Precursors.TraceAnnotation]],
61
+ ) -> tuple[
62
+ list[Received[Insertables.TraceAnnotation]],
63
+ list[Postponed[Precursors.TraceAnnotation]],
64
+ list[Received[Precursors.TraceAnnotation]],
64
65
  ]:
65
- to_insert: List[Received[Insertables.TraceAnnotation]] = []
66
- to_postpone: List[Postponed[Precursors.TraceAnnotation]] = []
67
- to_discard: List[Received[Precursors.TraceAnnotation]] = []
66
+ to_insert: list[Received[Insertables.TraceAnnotation]] = []
67
+ to_postpone: list[Postponed[Precursors.TraceAnnotation]] = []
68
+ to_discard: list[Received[Precursors.TraceAnnotation]] = []
68
69
 
69
70
  stmt = self._select_existing(*map(_key, parcels))
70
- existing: List[Row[_Existing]] = [_ async for _ in await session.stream(stmt)]
71
+ existing: list[Row[_Existing]] = [_ async for _ in await session.stream(stmt)]
71
72
  existing_traces: Mapping[str, _TraceAttr] = {
72
73
  e.trace_id: _TraceAttr(e.trace_rowid) for e in existing
73
74
  }
@@ -3,22 +3,11 @@ from __future__ import annotations
3
3
  import asyncio
4
4
  import logging
5
5
  from abc import ABC, abstractmethod
6
+ from collections.abc import Mapping, Sequence
6
7
  from copy import copy
7
8
  from dataclasses import dataclass, field
8
9
  from datetime import datetime, timezone
9
- from typing import (
10
- Any,
11
- Generic,
12
- List,
13
- Mapping,
14
- Optional,
15
- Protocol,
16
- Sequence,
17
- Tuple,
18
- Type,
19
- TypeVar,
20
- cast,
21
- )
10
+ from typing import Any, Generic, Optional, Protocol, TypeVar, cast
22
11
 
23
12
  from sqlalchemy.ext.asyncio import AsyncSession
24
13
  from sqlalchemy.sql.dml import Insert
@@ -59,12 +48,12 @@ class Postponed(Received[_AnyT]):
59
48
 
60
49
 
61
50
  class QueueInserter(ABC, Generic[_PrecursorT, _InsertableT, _RowT, _DmlEventT]):
62
- table: Type[_RowT]
51
+ table: type[_RowT]
63
52
  unique_by: Sequence[str]
64
53
 
65
54
  def __init_subclass__(
66
55
  cls,
67
- table: Type[_RowT],
56
+ table: type[_RowT],
68
57
  unique_by: Sequence[str],
69
58
  ) -> None:
70
59
  cls.table = table
@@ -76,7 +65,7 @@ class QueueInserter(ABC, Generic[_PrecursorT, _InsertableT, _RowT, _DmlEventT]):
76
65
  retry_delay_sec: float = DEFAULT_RETRY_DELAY_SEC,
77
66
  retry_allowance: int = DEFAULT_RETRY_ALLOWANCE,
78
67
  ) -> None:
79
- self._queue: List[Received[_PrecursorT]] = []
68
+ self._queue: list[Received[_PrecursorT]] = []
80
69
  self._db = db
81
70
  self._retry_delay_sec = retry_delay_sec
82
71
  self._retry_allowance = retry_allowance
@@ -93,17 +82,17 @@ class QueueInserter(ABC, Generic[_PrecursorT, _InsertableT, _RowT, _DmlEventT]):
93
82
  self,
94
83
  session: AsyncSession,
95
84
  *parcels: Received[_PrecursorT],
96
- ) -> Tuple[
97
- List[Received[_InsertableT]],
98
- List[Postponed[_PrecursorT]],
99
- List[Received[_PrecursorT]],
85
+ ) -> tuple[
86
+ list[Received[_InsertableT]],
87
+ list[Postponed[_PrecursorT]],
88
+ list[Received[_PrecursorT]],
100
89
  ]: ...
101
90
 
102
- async def insert(self) -> Optional[List[_DmlEventT]]:
91
+ async def insert(self) -> Optional[list[_DmlEventT]]:
103
92
  if not self._queue:
104
93
  return None
105
94
  self._queue, parcels = [], self._queue
106
- events: List[_DmlEventT] = []
95
+ events: list[_DmlEventT] = []
107
96
  async with self._db() as session:
108
97
  to_insert, to_postpone, _ = await self._partition(session, *parcels)
109
98
  if to_insert:
@@ -128,20 +117,20 @@ class QueueInserter(ABC, Generic[_PrecursorT, _InsertableT, _RowT, _DmlEventT]):
128
117
  self,
129
118
  session: AsyncSession,
130
119
  *insertions: _InsertableT,
131
- ) -> List[_DmlEventT]: ...
120
+ ) -> list[_DmlEventT]: ...
132
121
 
133
122
  async def _insert(
134
123
  self,
135
124
  session: AsyncSession,
136
125
  *parcels: Received[_InsertableT],
137
- ) -> Tuple[
138
- List[_DmlEventT],
139
- List[Postponed[_PrecursorT]],
140
- List[Received[_InsertableT]],
126
+ ) -> tuple[
127
+ list[_DmlEventT],
128
+ list[Postponed[_PrecursorT]],
129
+ list[Received[_InsertableT]],
141
130
  ]:
142
- to_retry: List[Postponed[_PrecursorT]] = []
143
- failures: List[Received[_InsertableT]] = []
144
- events: List[_DmlEventT] = []
131
+ to_retry: list[Postponed[_PrecursorT]] = []
132
+ failures: list[Received[_InsertableT]] = []
133
+ events: list[_DmlEventT] = []
145
134
  try:
146
135
  async with session.begin_nested():
147
136
  events.extend(await self._events(session, *(p.item for p in parcels)))
@@ -6,7 +6,7 @@ Create Date: 2024-08-03 22:11:28.733133
6
6
 
7
7
  """
8
8
 
9
- from typing import Any, Dict, List, Optional, Sequence, TypedDict, Union
9
+ from typing import Any, Optional, Sequence, TypedDict, Union
10
10
 
11
11
  import sqlalchemy as sa
12
12
  from alembic import op
@@ -51,21 +51,21 @@ JSON_ = (
51
51
  )
52
52
 
53
53
 
54
- class JsonDict(TypeDecorator[Dict[str, Any]]):
54
+ class JsonDict(TypeDecorator[dict[str, Any]]):
55
55
  # See # See https://docs.sqlalchemy.org/en/20/core/custom_types.html
56
56
  cache_ok = True
57
57
  impl = JSON_
58
58
 
59
- def process_bind_param(self, value: Optional[Dict[str, Any]], _: Dialect) -> Dict[str, Any]:
59
+ def process_bind_param(self, value: Optional[dict[str, Any]], _: Dialect) -> dict[str, Any]:
60
60
  return value if isinstance(value, dict) else {}
61
61
 
62
62
 
63
- class JsonList(TypeDecorator[List[Any]]):
63
+ class JsonList(TypeDecorator[list[Any]]):
64
64
  # See # See https://docs.sqlalchemy.org/en/20/core/custom_types.html
65
65
  cache_ok = True
66
66
  impl = JSON_
67
67
 
68
- def process_bind_param(self, value: Optional[List[Any]], _: Dialect) -> List[Any]:
68
+ def process_bind_param(self, value: Optional[list[Any]], _: Dialect) -> list[Any]:
69
69
  return value if isinstance(value, list) else []
70
70
 
71
71
 
@@ -86,8 +86,8 @@ class Base(DeclarativeBase):
86
86
  }
87
87
  )
88
88
  type_annotation_map = {
89
- Dict[str, Any]: JsonDict,
90
- List[Dict[str, Any]]: JsonList,
89
+ dict[str, Any]: JsonDict,
90
+ list[dict[str, Any]]: JsonList,
91
91
  ExperimentRunOutput: JsonDict,
92
92
  }
93
93
 
@@ -95,7 +95,7 @@ class Base(DeclarativeBase):
95
95
  class Span(Base):
96
96
  __tablename__ = "spans"
97
97
  id: Mapped[int] = mapped_column(primary_key=True)
98
- attributes: Mapped[Dict[str, Any]]
98
+ attributes: Mapped[dict[str, Any]]
99
99
  llm_token_count_prompt: Mapped[Optional[int]]
100
100
  llm_token_count_completion: Mapped[Optional[int]]
101
101
 
phoenix/db/models.py CHANGED
@@ -1,6 +1,6 @@
1
1
  from datetime import datetime, timezone
2
2
  from enum import Enum
3
- from typing import Any, Dict, List, Optional, TypedDict
3
+ from typing import Any, Optional, TypedDict
4
4
 
5
5
  from sqlalchemy import (
6
6
  JSON,
@@ -69,21 +69,21 @@ JSON_ = (
69
69
  )
70
70
 
71
71
 
72
- class JsonDict(TypeDecorator[Dict[str, Any]]):
72
+ class JsonDict(TypeDecorator[dict[str, Any]]):
73
73
  # See # See https://docs.sqlalchemy.org/en/20/core/custom_types.html
74
74
  cache_ok = True
75
75
  impl = JSON_
76
76
 
77
- def process_bind_param(self, value: Optional[Dict[str, Any]], _: Dialect) -> Dict[str, Any]:
77
+ def process_bind_param(self, value: Optional[dict[str, Any]], _: Dialect) -> dict[str, Any]:
78
78
  return value if isinstance(value, dict) else {}
79
79
 
80
80
 
81
- class JsonList(TypeDecorator[List[Any]]):
81
+ class JsonList(TypeDecorator[list[Any]]):
82
82
  # See # See https://docs.sqlalchemy.org/en/20/core/custom_types.html
83
83
  cache_ok = True
84
84
  impl = JSON_
85
85
 
86
- def process_bind_param(self, value: Optional[List[Any]], _: Dialect) -> List[Any]:
86
+ def process_bind_param(self, value: Optional[list[Any]], _: Dialect) -> list[Any]:
87
87
  return value if isinstance(value, list) else []
88
88
 
89
89
 
@@ -117,8 +117,8 @@ class Base(DeclarativeBase):
117
117
  },
118
118
  )
119
119
  type_annotation_map = {
120
- Dict[str, Any]: JsonDict,
121
- List[Dict[str, Any]]: JsonList,
120
+ dict[str, Any]: JsonDict,
121
+ list[dict[str, Any]]: JsonList,
122
122
  ExperimentRunOutput: JsonDict,
123
123
  }
124
124
 
@@ -142,7 +142,7 @@ class Project(Base):
142
142
  UtcTimeStamp, server_default=func.now(), onupdate=func.now()
143
143
  )
144
144
 
145
- traces: WriteOnlyMapped[List["Trace"]] = relationship(
145
+ traces: WriteOnlyMapped[list["Trace"]] = relationship(
146
146
  "Trace",
147
147
  back_populates="project",
148
148
  cascade="all, delete-orphan",
@@ -182,13 +182,13 @@ class Trace(Base):
182
182
  "Project",
183
183
  back_populates="traces",
184
184
  )
185
- spans: Mapped[List["Span"]] = relationship(
185
+ spans: Mapped[list["Span"]] = relationship(
186
186
  "Span",
187
187
  back_populates="trace",
188
188
  cascade="all, delete-orphan",
189
189
  uselist=True,
190
190
  )
191
- experiment_runs: Mapped[List["ExperimentRun"]] = relationship(
191
+ experiment_runs: Mapped[list["ExperimentRun"]] = relationship(
192
192
  primaryjoin="foreign(ExperimentRun.trace_id) == Trace.trace_id",
193
193
  back_populates="trace",
194
194
  )
@@ -212,8 +212,8 @@ class Span(Base):
212
212
  span_kind: Mapped[str]
213
213
  start_time: Mapped[datetime] = mapped_column(UtcTimeStamp, index=True)
214
214
  end_time: Mapped[datetime] = mapped_column(UtcTimeStamp)
215
- attributes: Mapped[Dict[str, Any]]
216
- events: Mapped[List[Dict[str, Any]]]
215
+ attributes: Mapped[dict[str, Any]]
216
+ events: Mapped[list[dict[str, Any]]]
217
217
  status_code: Mapped[str] = mapped_column(
218
218
  CheckConstraint("status_code IN ('OK', 'ERROR', 'UNSET')", name="valid_status")
219
219
  )
@@ -248,8 +248,8 @@ class Span(Base):
248
248
  return (self.llm_token_count_prompt or 0) + (self.llm_token_count_completion or 0)
249
249
 
250
250
  trace: Mapped["Trace"] = relationship("Trace", back_populates="spans")
251
- document_annotations: Mapped[List["DocumentAnnotation"]] = relationship(back_populates="span")
252
- dataset_examples: Mapped[List["DatasetExample"]] = relationship(back_populates="span")
251
+ document_annotations: Mapped[list["DocumentAnnotation"]] = relationship(back_populates="span")
252
+ dataset_examples: Mapped[list["DatasetExample"]] = relationship(back_populates="span")
253
253
 
254
254
  __table_args__ = (
255
255
  UniqueConstraint(
@@ -351,7 +351,7 @@ class SpanAnnotation(Base):
351
351
  label: Mapped[Optional[str]] = mapped_column(String, index=True)
352
352
  score: Mapped[Optional[float]] = mapped_column(Float, index=True)
353
353
  explanation: Mapped[Optional[str]]
354
- metadata_: Mapped[Dict[str, Any]] = mapped_column("metadata")
354
+ metadata_: Mapped[dict[str, Any]] = mapped_column("metadata")
355
355
  annotator_kind: Mapped[str] = mapped_column(
356
356
  CheckConstraint("annotator_kind IN ('LLM', 'HUMAN')", name="valid_annotator_kind"),
357
357
  )
@@ -378,7 +378,7 @@ class TraceAnnotation(Base):
378
378
  label: Mapped[Optional[str]] = mapped_column(String, index=True)
379
379
  score: Mapped[Optional[float]] = mapped_column(Float, index=True)
380
380
  explanation: Mapped[Optional[str]]
381
- metadata_: Mapped[Dict[str, Any]] = mapped_column("metadata")
381
+ metadata_: Mapped[dict[str, Any]] = mapped_column("metadata")
382
382
  annotator_kind: Mapped[str] = mapped_column(
383
383
  CheckConstraint("annotator_kind IN ('LLM', 'HUMAN')", name="valid_annotator_kind"),
384
384
  )
@@ -406,7 +406,7 @@ class DocumentAnnotation(Base):
406
406
  label: Mapped[Optional[str]] = mapped_column(String, index=True)
407
407
  score: Mapped[Optional[float]] = mapped_column(Float, index=True)
408
408
  explanation: Mapped[Optional[str]]
409
- metadata_: Mapped[Dict[str, Any]] = mapped_column("metadata")
409
+ metadata_: Mapped[dict[str, Any]] = mapped_column("metadata")
410
410
  annotator_kind: Mapped[str] = mapped_column(
411
411
  CheckConstraint("annotator_kind IN ('LLM', 'HUMAN')", name="valid_annotator_kind"),
412
412
  )
@@ -430,7 +430,7 @@ class Dataset(Base):
430
430
  id: Mapped[int] = mapped_column(primary_key=True)
431
431
  name: Mapped[str] = mapped_column(unique=True)
432
432
  description: Mapped[Optional[str]]
433
- metadata_: Mapped[Dict[str, Any]] = mapped_column("metadata")
433
+ metadata_: Mapped[dict[str, Any]] = mapped_column("metadata")
434
434
  created_at: Mapped[datetime] = mapped_column(UtcTimeStamp, server_default=func.now())
435
435
  updated_at: Mapped[datetime] = mapped_column(
436
436
  UtcTimeStamp, server_default=func.now(), onupdate=func.now()
@@ -493,7 +493,7 @@ class DatasetVersion(Base):
493
493
  index=True,
494
494
  )
495
495
  description: Mapped[Optional[str]]
496
- metadata_: Mapped[Dict[str, Any]] = mapped_column("metadata")
496
+ metadata_: Mapped[dict[str, Any]] = mapped_column("metadata")
497
497
  created_at: Mapped[datetime] = mapped_column(UtcTimeStamp, server_default=func.now())
498
498
 
499
499
 
@@ -525,9 +525,9 @@ class DatasetExampleRevision(Base):
525
525
  ForeignKey("dataset_versions.id", ondelete="CASCADE"),
526
526
  index=True,
527
527
  )
528
- input: Mapped[Dict[str, Any]]
529
- output: Mapped[Dict[str, Any]]
530
- metadata_: Mapped[Dict[str, Any]] = mapped_column("metadata")
528
+ input: Mapped[dict[str, Any]]
529
+ output: Mapped[dict[str, Any]]
530
+ metadata_: Mapped[dict[str, Any]] = mapped_column("metadata")
531
531
  revision_kind: Mapped[str] = mapped_column(
532
532
  CheckConstraint(
533
533
  "revision_kind IN ('CREATE', 'PATCH', 'DELETE')", name="valid_revision_kind"
@@ -557,7 +557,7 @@ class Experiment(Base):
557
557
  name: Mapped[str]
558
558
  description: Mapped[Optional[str]]
559
559
  repetitions: Mapped[int]
560
- metadata_: Mapped[Dict[str, Any]] = mapped_column("metadata")
560
+ metadata_: Mapped[dict[str, Any]] = mapped_column("metadata")
561
561
  project_name: Mapped[Optional[str]] = mapped_column(index=True)
562
562
  created_at: Mapped[datetime] = mapped_column(UtcTimeStamp, server_default=func.now())
563
563
  updated_at: Mapped[datetime] = mapped_column(
@@ -615,7 +615,7 @@ class ExperimentRunAnnotation(Base):
615
615
  explanation: Mapped[Optional[str]]
616
616
  trace_id: Mapped[Optional[str]]
617
617
  error: Mapped[Optional[str]]
618
- metadata_: Mapped[Dict[str, Any]] = mapped_column("metadata")
618
+ metadata_: Mapped[dict[str, Any]] = mapped_column("metadata")
619
619
  start_time: Mapped[datetime] = mapped_column(UtcTimeStamp)
620
620
  end_time: Mapped[datetime] = mapped_column(UtcTimeStamp)
621
621
 
@@ -631,7 +631,7 @@ class UserRole(Base):
631
631
  __tablename__ = "user_roles"
632
632
  id: Mapped[int] = mapped_column(primary_key=True)
633
633
  name: Mapped[str] = mapped_column(unique=True, index=True)
634
- users: Mapped[List["User"]] = relationship("User", back_populates="role")
634
+ users: Mapped[list["User"]] = relationship("User", back_populates="role")
635
635
 
636
636
 
637
637
  class User(Base):
@@ -659,11 +659,11 @@ class User(Base):
659
659
  back_populates="user",
660
660
  uselist=False,
661
661
  )
662
- access_tokens: Mapped[List["AccessToken"]] = relationship("AccessToken", back_populates="user")
663
- refresh_tokens: Mapped[List["RefreshToken"]] = relationship(
662
+ access_tokens: Mapped[list["AccessToken"]] = relationship("AccessToken", back_populates="user")
663
+ refresh_tokens: Mapped[list["RefreshToken"]] = relationship(
664
664
  "RefreshToken", back_populates="user"
665
665
  )
666
- api_keys: Mapped[List["ApiKey"]] = relationship("ApiKey", back_populates="user")
666
+ api_keys: Mapped[list["ApiKey"]] = relationship("ApiKey", back_populates="user")
667
667
 
668
668
  @hybrid_property
669
669
  def auth_method(self) -> Optional[str]:
@@ -1,8 +1,9 @@
1
1
  import functools
2
2
  import inspect
3
3
  from abc import ABC
4
+ from collections.abc import Awaitable, Callable
4
5
  from types import MappingProxyType
5
- from typing import Any, Awaitable, Callable, Optional, Union
6
+ from typing import Any, Optional, Union
6
7
 
7
8
  from typing_extensions import TypeAlias
8
9
 
@@ -4,7 +4,6 @@ import json
4
4
  import re
5
5
  from typing import (
6
6
  Any,
7
- List,
8
7
  Optional,
9
8
  Pattern, # import from re module when we drop support for 3.8
10
9
  Union,
@@ -78,7 +77,7 @@ class ContainsAnyKeyword(CodeEvaluator):
78
77
  An evaluator that checks if any of the keywords are present in the output of an experiment run.
79
78
 
80
79
  Args:
81
- keywords (List[str]): The keywords to search for in the output.
80
+ keywords (list[str]): The keywords to search for in the output.
82
81
  name (str, optional): An optional name for the evaluator. Defaults to
83
82
  "ContainsAny(<keywords>)".
84
83
 
@@ -91,7 +90,7 @@ class ContainsAnyKeyword(CodeEvaluator):
91
90
  run_experiment(dataset, task, evaluators=[ContainsAnyKeyword(["foo", "bar"])])
92
91
  """
93
92
 
94
- def __init__(self, keywords: List[str], name: Optional[str] = None) -> None:
93
+ def __init__(self, keywords: list[str], name: Optional[str] = None) -> None:
95
94
  self.keywords = keywords
96
95
  self._name = name or f"ContainsAny({keywords})"
97
96
 
@@ -113,7 +112,7 @@ class ContainsAllKeywords(CodeEvaluator):
113
112
  An evaluator that checks if all of the keywords are present in the output of an experiment run.
114
113
 
115
114
  Args:
116
- keywords (List[str]): The keywords to search for in the output.
115
+ keywords (list[str]): The keywords to search for in the output.
117
116
  name (str, optional): An optional name for the evaluator. Defaults to
118
117
  "ContainsAll(<keywords>)".
119
118
 
@@ -126,7 +125,7 @@ class ContainsAllKeywords(CodeEvaluator):
126
125
  run_experiment(dataset, task, evaluators=[ContainsAllKeywords(["foo", "bar"])])
127
126
  """
128
127
 
129
- def __init__(self, keywords: List[str], name: Optional[str] = None) -> None:
128
+ def __init__(self, keywords: list[str], name: Optional[str] = None) -> None:
130
129
  self.keywords = keywords
131
130
  self._name = name or f"ContainsAll({keywords})"
132
131