arize-phoenix 4.4.4rc6__py3-none-any.whl → 4.5.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 (123) hide show
  1. {arize_phoenix-4.4.4rc6.dist-info → arize_phoenix-4.5.0.dist-info}/METADATA +8 -14
  2. {arize_phoenix-4.4.4rc6.dist-info → arize_phoenix-4.5.0.dist-info}/RECORD +58 -122
  3. {arize_phoenix-4.4.4rc6.dist-info → arize_phoenix-4.5.0.dist-info}/WHEEL +1 -1
  4. phoenix/__init__.py +27 -0
  5. phoenix/config.py +7 -42
  6. phoenix/core/model.py +25 -25
  7. phoenix/core/model_schema.py +62 -64
  8. phoenix/core/model_schema_adapter.py +25 -27
  9. phoenix/datetime_utils.py +0 -4
  10. phoenix/db/bulk_inserter.py +14 -54
  11. phoenix/db/insertion/evaluation.py +10 -10
  12. phoenix/db/insertion/helpers.py +14 -17
  13. phoenix/db/insertion/span.py +3 -3
  14. phoenix/db/migrations/versions/cf03bd6bae1d_init.py +28 -2
  15. phoenix/db/models.py +4 -236
  16. phoenix/inferences/fixtures.py +23 -23
  17. phoenix/inferences/inferences.py +7 -7
  18. phoenix/inferences/validation.py +1 -1
  19. phoenix/server/api/context.py +0 -20
  20. phoenix/server/api/dataloaders/__init__.py +0 -20
  21. phoenix/server/api/dataloaders/span_descendants.py +3 -2
  22. phoenix/server/api/routers/v1/__init__.py +2 -77
  23. phoenix/server/api/routers/v1/evaluations.py +13 -8
  24. phoenix/server/api/routers/v1/spans.py +5 -9
  25. phoenix/server/api/routers/v1/traces.py +4 -1
  26. phoenix/server/api/schema.py +303 -2
  27. phoenix/server/api/types/Cluster.py +19 -19
  28. phoenix/server/api/types/Dataset.py +63 -282
  29. phoenix/server/api/types/DatasetRole.py +23 -0
  30. phoenix/server/api/types/Dimension.py +29 -30
  31. phoenix/server/api/types/EmbeddingDimension.py +34 -40
  32. phoenix/server/api/types/Event.py +16 -16
  33. phoenix/server/api/{mutations/export_events_mutations.py → types/ExportEventsMutation.py} +14 -17
  34. phoenix/server/api/types/Model.py +42 -43
  35. phoenix/server/api/types/Project.py +12 -26
  36. phoenix/server/api/types/Span.py +2 -79
  37. phoenix/server/api/types/TimeSeries.py +6 -6
  38. phoenix/server/api/types/Trace.py +4 -15
  39. phoenix/server/api/types/UMAPPoints.py +1 -1
  40. phoenix/server/api/types/node.py +111 -5
  41. phoenix/server/api/types/pagination.py +52 -10
  42. phoenix/server/app.py +49 -103
  43. phoenix/server/main.py +27 -49
  44. phoenix/server/openapi/docs.py +0 -3
  45. phoenix/server/static/index.js +1384 -2390
  46. phoenix/server/templates/index.html +0 -1
  47. phoenix/services.py +15 -15
  48. phoenix/session/client.py +23 -611
  49. phoenix/session/session.py +37 -47
  50. phoenix/trace/exporter.py +9 -14
  51. phoenix/trace/fixtures.py +7 -133
  52. phoenix/trace/schemas.py +2 -1
  53. phoenix/trace/span_evaluations.py +3 -3
  54. phoenix/trace/trace_dataset.py +6 -6
  55. phoenix/version.py +1 -1
  56. phoenix/db/insertion/dataset.py +0 -237
  57. phoenix/db/migrations/types.py +0 -29
  58. phoenix/db/migrations/versions/10460e46d750_datasets.py +0 -291
  59. phoenix/experiments/__init__.py +0 -6
  60. phoenix/experiments/evaluators/__init__.py +0 -29
  61. phoenix/experiments/evaluators/base.py +0 -153
  62. phoenix/experiments/evaluators/code_evaluators.py +0 -99
  63. phoenix/experiments/evaluators/llm_evaluators.py +0 -244
  64. phoenix/experiments/evaluators/utils.py +0 -189
  65. phoenix/experiments/functions.py +0 -616
  66. phoenix/experiments/tracing.py +0 -85
  67. phoenix/experiments/types.py +0 -722
  68. phoenix/experiments/utils.py +0 -9
  69. phoenix/server/api/dataloaders/average_experiment_run_latency.py +0 -54
  70. phoenix/server/api/dataloaders/dataset_example_revisions.py +0 -100
  71. phoenix/server/api/dataloaders/dataset_example_spans.py +0 -43
  72. phoenix/server/api/dataloaders/experiment_annotation_summaries.py +0 -85
  73. phoenix/server/api/dataloaders/experiment_error_rates.py +0 -43
  74. phoenix/server/api/dataloaders/experiment_run_counts.py +0 -42
  75. phoenix/server/api/dataloaders/experiment_sequence_number.py +0 -49
  76. phoenix/server/api/dataloaders/project_by_name.py +0 -31
  77. phoenix/server/api/dataloaders/span_projects.py +0 -33
  78. phoenix/server/api/dataloaders/trace_row_ids.py +0 -39
  79. phoenix/server/api/helpers/dataset_helpers.py +0 -179
  80. phoenix/server/api/input_types/AddExamplesToDatasetInput.py +0 -16
  81. phoenix/server/api/input_types/AddSpansToDatasetInput.py +0 -14
  82. phoenix/server/api/input_types/ClearProjectInput.py +0 -15
  83. phoenix/server/api/input_types/CreateDatasetInput.py +0 -12
  84. phoenix/server/api/input_types/DatasetExampleInput.py +0 -14
  85. phoenix/server/api/input_types/DatasetSort.py +0 -17
  86. phoenix/server/api/input_types/DatasetVersionSort.py +0 -16
  87. phoenix/server/api/input_types/DeleteDatasetExamplesInput.py +0 -13
  88. phoenix/server/api/input_types/DeleteDatasetInput.py +0 -7
  89. phoenix/server/api/input_types/DeleteExperimentsInput.py +0 -9
  90. phoenix/server/api/input_types/PatchDatasetExamplesInput.py +0 -35
  91. phoenix/server/api/input_types/PatchDatasetInput.py +0 -14
  92. phoenix/server/api/mutations/__init__.py +0 -13
  93. phoenix/server/api/mutations/auth.py +0 -11
  94. phoenix/server/api/mutations/dataset_mutations.py +0 -520
  95. phoenix/server/api/mutations/experiment_mutations.py +0 -65
  96. phoenix/server/api/mutations/project_mutations.py +0 -47
  97. phoenix/server/api/openapi/__init__.py +0 -0
  98. phoenix/server/api/openapi/main.py +0 -6
  99. phoenix/server/api/openapi/schema.py +0 -16
  100. phoenix/server/api/queries.py +0 -503
  101. phoenix/server/api/routers/v1/dataset_examples.py +0 -178
  102. phoenix/server/api/routers/v1/datasets.py +0 -965
  103. phoenix/server/api/routers/v1/experiment_evaluations.py +0 -65
  104. phoenix/server/api/routers/v1/experiment_runs.py +0 -96
  105. phoenix/server/api/routers/v1/experiments.py +0 -174
  106. phoenix/server/api/types/AnnotatorKind.py +0 -10
  107. phoenix/server/api/types/CreateDatasetPayload.py +0 -8
  108. phoenix/server/api/types/DatasetExample.py +0 -85
  109. phoenix/server/api/types/DatasetExampleRevision.py +0 -34
  110. phoenix/server/api/types/DatasetVersion.py +0 -14
  111. phoenix/server/api/types/ExampleRevisionInterface.py +0 -14
  112. phoenix/server/api/types/Experiment.py +0 -147
  113. phoenix/server/api/types/ExperimentAnnotationSummary.py +0 -13
  114. phoenix/server/api/types/ExperimentComparison.py +0 -19
  115. phoenix/server/api/types/ExperimentRun.py +0 -91
  116. phoenix/server/api/types/ExperimentRunAnnotation.py +0 -57
  117. phoenix/server/api/types/Inferences.py +0 -80
  118. phoenix/server/api/types/InferencesRole.py +0 -23
  119. phoenix/utilities/json.py +0 -61
  120. phoenix/utilities/re.py +0 -50
  121. {arize_phoenix-4.4.4rc6.dist-info → arize_phoenix-4.5.0.dist-info}/licenses/IP_NOTICE +0 -0
  122. {arize_phoenix-4.4.4rc6.dist-info → arize_phoenix-4.5.0.dist-info}/licenses/LICENSE +0 -0
  123. /phoenix/server/api/{helpers/__init__.py → helpers.py} +0 -0
@@ -6,7 +6,7 @@ from typing_extensions import assert_never
6
6
 
7
7
  from phoenix.db import models
8
8
  from phoenix.db.helpers import SupportedSQLDialect, num_docs_col
9
- from phoenix.db.insertion.helpers import OnConflict, insert_on_conflict
9
+ from phoenix.db.insertion.helpers import OnConflict, insert_stmt
10
10
  from phoenix.exceptions import PhoenixException
11
11
  from phoenix.trace import v1 as pb
12
12
 
@@ -15,24 +15,24 @@ class InsertEvaluationError(PhoenixException):
15
15
  pass
16
16
 
17
17
 
18
- class EvaluationInsertionEvent(NamedTuple):
18
+ class EvaluationInsertionResult(NamedTuple):
19
19
  project_rowid: int
20
20
  evaluation_name: str
21
21
 
22
22
 
23
- class SpanEvaluationInsertionEvent(EvaluationInsertionEvent): ...
23
+ class SpanEvaluationInsertionEvent(EvaluationInsertionResult): ...
24
24
 
25
25
 
26
- class TraceEvaluationInsertionEvent(EvaluationInsertionEvent): ...
26
+ class TraceEvaluationInsertionEvent(EvaluationInsertionResult): ...
27
27
 
28
28
 
29
- class DocumentEvaluationInsertionEvent(EvaluationInsertionEvent): ...
29
+ class DocumentEvaluationInsertionEvent(EvaluationInsertionResult): ...
30
30
 
31
31
 
32
32
  async def insert_evaluation(
33
33
  session: AsyncSession,
34
34
  evaluation: pb.Evaluation,
35
- ) -> Optional[EvaluationInsertionEvent]:
35
+ ) -> Optional[EvaluationInsertionResult]:
36
36
  evaluation_name = evaluation.name
37
37
  result = evaluation.result
38
38
  label = result.label.value if result.HasField("label") else None
@@ -91,7 +91,7 @@ async def _insert_trace_evaluation(
91
91
  set_.pop("metadata_")
92
92
  set_["metadata"] = values["metadata_"] # `metadata` must match database
93
93
  await session.execute(
94
- insert_on_conflict(
94
+ insert_stmt(
95
95
  dialect=dialect,
96
96
  table=models.TraceAnnotation,
97
97
  values=values,
@@ -139,7 +139,7 @@ async def _insert_span_evaluation(
139
139
  set_.pop("metadata_")
140
140
  set_["metadata"] = values["metadata_"] # `metadata` must match database
141
141
  await session.execute(
142
- insert_on_conflict(
142
+ insert_stmt(
143
143
  dialect=dialect,
144
144
  table=models.SpanAnnotation,
145
145
  values=values,
@@ -160,7 +160,7 @@ async def _insert_document_evaluation(
160
160
  label: Optional[str],
161
161
  score: Optional[float],
162
162
  explanation: Optional[str],
163
- ) -> EvaluationInsertionEvent:
163
+ ) -> EvaluationInsertionResult:
164
164
  dialect = SupportedSQLDialect(session.bind.dialect.name)
165
165
  stmt = (
166
166
  select(
@@ -196,7 +196,7 @@ async def _insert_document_evaluation(
196
196
  set_.pop("metadata_")
197
197
  set_["metadata"] = values["metadata_"] # `metadata` must match database
198
198
  await session.execute(
199
- insert_on_conflict(
199
+ insert_stmt(
200
200
  dialect=dialect,
201
201
  table=models.DocumentAnnotation,
202
202
  values=values,
@@ -1,42 +1,39 @@
1
- from abc import ABC
2
1
  from enum import Enum, auto
3
- from typing import Any, Awaitable, Callable, Mapping, Optional, Sequence
2
+ from typing import Any, Mapping, Optional, Sequence
4
3
 
5
- from sqlalchemy import Insert
4
+ from sqlalchemy import Insert, insert
6
5
  from sqlalchemy.dialects.postgresql import insert as insert_postgresql
7
6
  from sqlalchemy.dialects.sqlite import insert as insert_sqlite
8
- from sqlalchemy.ext.asyncio import AsyncSession
9
- from typing_extensions import TypeAlias, assert_never
7
+ from typing_extensions import assert_never
10
8
 
11
9
  from phoenix.db.helpers import SupportedSQLDialect
12
10
 
13
11
 
14
- class DataManipulationEvent(ABC):
15
- """
16
- Execution of DML (Data Manipulation Language) statements.
17
- """
18
-
19
-
20
- DataManipulation: TypeAlias = Callable[[AsyncSession], Awaitable[Optional[DataManipulationEvent]]]
21
-
22
-
23
12
  class OnConflict(Enum):
24
13
  DO_NOTHING = auto()
25
14
  DO_UPDATE = auto()
26
15
 
27
16
 
28
- def insert_on_conflict(
17
+ def insert_stmt(
29
18
  dialect: SupportedSQLDialect,
30
19
  table: Any,
31
20
  values: Mapping[str, Any],
32
- constraint: str,
33
- column_names: Sequence[str],
21
+ constraint: Optional[str] = None,
22
+ column_names: Sequence[str] = (),
34
23
  on_conflict: OnConflict = OnConflict.DO_NOTHING,
35
24
  set_: Optional[Mapping[str, Any]] = None,
36
25
  ) -> Insert:
37
26
  """
38
27
  Dialect specific insertion statement using ON CONFLICT DO syntax.
39
28
  """
29
+ if bool(constraint) != bool(column_names):
30
+ raise ValueError(
31
+ "Both `constraint` and `column_names` must be provided or omitted at the same time."
32
+ )
33
+ if (dialect is SupportedSQLDialect.POSTGRESQL and constraint is None) or (
34
+ dialect is SupportedSQLDialect.SQLITE and not column_names
35
+ ):
36
+ return insert(table).values(values)
40
37
  if dialect is SupportedSQLDialect.POSTGRESQL:
41
38
  stmt_postgresql = insert_postgresql(table).values(values)
42
39
  if on_conflict is OnConflict.DO_NOTHING or not set_:
@@ -7,7 +7,7 @@ from sqlalchemy.ext.asyncio import AsyncSession
7
7
 
8
8
  from phoenix.db import models
9
9
  from phoenix.db.helpers import SupportedSQLDialect
10
- from phoenix.db.insertion.helpers import OnConflict, insert_on_conflict
10
+ from phoenix.db.insertion.helpers import OnConflict, insert_stmt
11
11
  from phoenix.trace.attributes import get_attribute_value
12
12
  from phoenix.trace.schemas import Span, SpanStatusCode
13
13
 
@@ -27,7 +27,7 @@ async def insert_span(
27
27
  ) -> Optional[SpanInsertionEvent]:
28
28
  dialect = SupportedSQLDialect(session.bind.dialect.name)
29
29
  project_rowid = await session.scalar(
30
- insert_on_conflict(
30
+ insert_stmt(
31
31
  dialect=dialect,
32
32
  table=models.Project,
33
33
  constraint="uq_projects_name",
@@ -87,7 +87,7 @@ async def insert_span(
87
87
  cumulative_llm_token_count_prompt += cast(int, accumulation[1] or 0)
88
88
  cumulative_llm_token_count_completion += cast(int, accumulation[2] or 0)
89
89
  span_rowid = await session.scalar(
90
- insert_on_conflict(
90
+ insert_stmt(
91
91
  dialect=dialect,
92
92
  table=models.Span,
93
93
  constraint="uq_spans_span_id",
@@ -6,11 +6,13 @@ Create Date: 2024-04-03 19:41:48.871555
6
6
 
7
7
  """
8
8
 
9
- from typing import Sequence, Union
9
+ from typing import Any, Sequence, Union
10
10
 
11
11
  import sqlalchemy as sa
12
12
  from alembic import op
13
- from phoenix.db.migrations.types import JSON_
13
+ from sqlalchemy import JSON
14
+ from sqlalchemy.dialects import postgresql
15
+ from sqlalchemy.ext.compiler import compiles
14
16
 
15
17
  # revision identifiers, used by Alembic.
16
18
  revision: str = "cf03bd6bae1d"
@@ -19,6 +21,30 @@ branch_labels: Union[str, Sequence[str], None] = None
19
21
  depends_on: Union[str, Sequence[str], None] = None
20
22
 
21
23
 
24
+ class JSONB(JSON):
25
+ # See https://docs.sqlalchemy.org/en/20/core/custom_types.html
26
+ __visit_name__ = "JSONB"
27
+
28
+
29
+ @compiles(JSONB, "sqlite") # type: ignore
30
+ def _(*args: Any, **kwargs: Any) -> str:
31
+ # See https://docs.sqlalchemy.org/en/20/core/custom_types.html
32
+ return "JSONB"
33
+
34
+
35
+ JSON_ = (
36
+ JSON()
37
+ .with_variant(
38
+ postgresql.JSONB(), # type: ignore
39
+ "postgresql",
40
+ )
41
+ .with_variant(
42
+ JSONB(),
43
+ "sqlite",
44
+ )
45
+ )
46
+
47
+
22
48
  def upgrade() -> None:
23
49
  projects_table = op.create_table(
24
50
  "projects",
phoenix/db/models.py CHANGED
@@ -1,5 +1,5 @@
1
1
  from datetime import datetime, timezone
2
- from typing import Any, Dict, List, Optional, TypedDict
2
+ from typing import Any, Dict, List, Optional
3
3
 
4
4
  from sqlalchemy import (
5
5
  JSON,
@@ -15,14 +15,12 @@ from sqlalchemy import (
15
15
  String,
16
16
  TypeDecorator,
17
17
  UniqueConstraint,
18
- case,
19
18
  func,
20
19
  insert,
21
- select,
22
20
  text,
23
21
  )
24
22
  from sqlalchemy.dialects import postgresql
25
- from sqlalchemy.ext.asyncio import AsyncEngine, AsyncSession
23
+ from sqlalchemy.ext.asyncio import AsyncEngine
26
24
  from sqlalchemy.ext.compiler import compiles
27
25
  from sqlalchemy.ext.hybrid import hybrid_property
28
26
  from sqlalchemy.orm import (
@@ -61,24 +59,6 @@ JSON_ = (
61
59
  )
62
60
 
63
61
 
64
- class JsonDict(TypeDecorator[Dict[str, Any]]):
65
- # See # See https://docs.sqlalchemy.org/en/20/core/custom_types.html
66
- cache_ok = True
67
- impl = JSON_
68
-
69
- def process_bind_param(self, value: Optional[Dict[str, Any]], _: Dialect) -> Dict[str, Any]:
70
- return value if isinstance(value, dict) else {}
71
-
72
-
73
- class JsonList(TypeDecorator[List[Any]]):
74
- # See # See https://docs.sqlalchemy.org/en/20/core/custom_types.html
75
- cache_ok = True
76
- impl = JSON_
77
-
78
- def process_bind_param(self, value: Optional[List[Any]], _: Dialect) -> List[Any]:
79
- return value if isinstance(value, list) else []
80
-
81
-
82
62
  class UtcTimeStamp(TypeDecorator[datetime]):
83
63
  # See # See https://docs.sqlalchemy.org/en/20/core/custom_types.html
84
64
  cache_ok = True
@@ -91,10 +71,6 @@ class UtcTimeStamp(TypeDecorator[datetime]):
91
71
  return normalize_datetime(value, timezone.utc)
92
72
 
93
73
 
94
- class ExperimentResult(TypedDict, total=False):
95
- result: Any
96
-
97
-
98
74
  class Base(DeclarativeBase):
99
75
  # Enforce best practices for naming constraints
100
76
  # https://alembic.sqlalchemy.org/en/latest/naming.html#integration-of-naming-conventions-into-operations-autogenerate
@@ -108,9 +84,8 @@ class Base(DeclarativeBase):
108
84
  }
109
85
  )
110
86
  type_annotation_map = {
111
- Dict[str, Any]: JsonDict,
112
- List[Dict[str, Any]]: JsonList,
113
- ExperimentResult: JsonDict,
87
+ Dict[str, Any]: JSON_,
88
+ List[Dict[str, Any]]: JSON_,
114
89
  }
115
90
 
116
91
 
@@ -179,10 +154,6 @@ class Trace(Base):
179
154
  cascade="all, delete-orphan",
180
155
  uselist=True,
181
156
  )
182
- experiment_runs: Mapped[List["ExperimentRun"]] = relationship(
183
- primaryjoin="foreign(ExperimentRun.trace_id) == Trace.trace_id",
184
- back_populates="trace",
185
- )
186
157
  __table_args__ = (
187
158
  UniqueConstraint(
188
159
  "trace_id",
@@ -232,7 +203,6 @@ class Span(Base):
232
203
 
233
204
  trace: Mapped["Trace"] = relationship("Trace", back_populates="spans")
234
205
  document_annotations: Mapped[List["DocumentAnnotation"]] = relationship(back_populates="span")
235
- dataset_examples: Mapped[List["DatasetExample"]] = relationship(back_populates="span")
236
206
 
237
207
  __table_args__ = (
238
208
  UniqueConstraint(
@@ -406,205 +376,3 @@ class DocumentAnnotation(Base):
406
376
  "document_position",
407
377
  ),
408
378
  )
409
-
410
-
411
- class Dataset(Base):
412
- __tablename__ = "datasets"
413
- id: Mapped[int] = mapped_column(primary_key=True)
414
- name: Mapped[str] = mapped_column(unique=True)
415
- description: Mapped[Optional[str]]
416
- metadata_: Mapped[Dict[str, Any]] = mapped_column("metadata")
417
- created_at: Mapped[datetime] = mapped_column(UtcTimeStamp, server_default=func.now())
418
- updated_at: Mapped[datetime] = mapped_column(
419
- UtcTimeStamp, server_default=func.now(), onupdate=func.now()
420
- )
421
-
422
- @hybrid_property
423
- def example_count(self) -> Optional[int]:
424
- if hasattr(self, "_example_count_value"):
425
- assert isinstance(self._example_count_value, int)
426
- return self._example_count_value
427
- return None
428
-
429
- @example_count.inplace.expression
430
- def _example_count(cls) -> ColumnElement[int]:
431
- return (
432
- select(
433
- func.sum(
434
- case(
435
- (DatasetExampleRevision.revision_kind == "CREATE", 1),
436
- (DatasetExampleRevision.revision_kind == "DELETE", -1),
437
- else_=0,
438
- )
439
- )
440
- )
441
- .select_from(DatasetExampleRevision)
442
- .join(
443
- DatasetExample,
444
- onclause=DatasetExample.id == DatasetExampleRevision.dataset_example_id,
445
- )
446
- .filter(DatasetExample.dataset_id == cls.id)
447
- .label("example_count")
448
- )
449
-
450
- async def load_example_count(self, session: AsyncSession) -> None:
451
- if not hasattr(self, "_example_count_value"):
452
- self._example_count_value = await session.scalar(
453
- select(
454
- func.sum(
455
- case(
456
- (DatasetExampleRevision.revision_kind == "CREATE", 1),
457
- (DatasetExampleRevision.revision_kind == "DELETE", -1),
458
- else_=0,
459
- )
460
- )
461
- )
462
- .select_from(DatasetExampleRevision)
463
- .join(
464
- DatasetExample,
465
- onclause=DatasetExample.id == DatasetExampleRevision.dataset_example_id,
466
- )
467
- .filter(DatasetExample.dataset_id == self.id)
468
- )
469
-
470
-
471
- class DatasetVersion(Base):
472
- __tablename__ = "dataset_versions"
473
- id: Mapped[int] = mapped_column(primary_key=True)
474
- dataset_id: Mapped[int] = mapped_column(
475
- ForeignKey("datasets.id", ondelete="CASCADE"),
476
- index=True,
477
- )
478
- description: Mapped[Optional[str]]
479
- metadata_: Mapped[Dict[str, Any]] = mapped_column("metadata")
480
- created_at: Mapped[datetime] = mapped_column(UtcTimeStamp, server_default=func.now())
481
-
482
-
483
- class DatasetExample(Base):
484
- __tablename__ = "dataset_examples"
485
- id: Mapped[int] = mapped_column(primary_key=True)
486
- dataset_id: Mapped[int] = mapped_column(
487
- ForeignKey("datasets.id", ondelete="CASCADE"),
488
- index=True,
489
- )
490
- span_rowid: Mapped[Optional[int]] = mapped_column(
491
- ForeignKey("spans.id", ondelete="SET NULL"),
492
- index=True,
493
- nullable=True,
494
- )
495
- created_at: Mapped[datetime] = mapped_column(UtcTimeStamp, server_default=func.now())
496
-
497
- span: Mapped[Optional[Span]] = relationship(back_populates="dataset_examples")
498
-
499
-
500
- class DatasetExampleRevision(Base):
501
- __tablename__ = "dataset_example_revisions"
502
- id: Mapped[int] = mapped_column(primary_key=True)
503
- dataset_example_id: Mapped[int] = mapped_column(
504
- ForeignKey("dataset_examples.id", ondelete="CASCADE"),
505
- index=True,
506
- )
507
- dataset_version_id: Mapped[int] = mapped_column(
508
- ForeignKey("dataset_versions.id", ondelete="CASCADE"),
509
- index=True,
510
- )
511
- input: Mapped[Dict[str, Any]]
512
- output: Mapped[Dict[str, Any]]
513
- metadata_: Mapped[Dict[str, Any]] = mapped_column("metadata")
514
- revision_kind: Mapped[str] = mapped_column(
515
- CheckConstraint(
516
- "revision_kind IN ('CREATE', 'PATCH', 'DELETE')", name="valid_revision_kind"
517
- ),
518
- )
519
- created_at: Mapped[datetime] = mapped_column(UtcTimeStamp, server_default=func.now())
520
-
521
- __table_args__ = (
522
- UniqueConstraint(
523
- "dataset_example_id",
524
- "dataset_version_id",
525
- ),
526
- )
527
-
528
-
529
- class Experiment(Base):
530
- __tablename__ = "experiments"
531
- id: Mapped[int] = mapped_column(primary_key=True)
532
- dataset_id: Mapped[int] = mapped_column(
533
- ForeignKey("datasets.id", ondelete="CASCADE"),
534
- index=True,
535
- )
536
- dataset_version_id: Mapped[int] = mapped_column(
537
- ForeignKey("dataset_versions.id", ondelete="CASCADE"),
538
- index=True,
539
- )
540
- name: Mapped[str]
541
- description: Mapped[Optional[str]]
542
- repetitions: Mapped[int]
543
- metadata_: Mapped[Dict[str, Any]] = mapped_column("metadata")
544
- project_name: Mapped[Optional[str]] = mapped_column(index=True)
545
- created_at: Mapped[datetime] = mapped_column(UtcTimeStamp, server_default=func.now())
546
- updated_at: Mapped[datetime] = mapped_column(
547
- UtcTimeStamp, server_default=func.now(), onupdate=func.now()
548
- )
549
-
550
-
551
- class ExperimentRun(Base):
552
- __tablename__ = "experiment_runs"
553
- id: Mapped[int] = mapped_column(primary_key=True)
554
- experiment_id: Mapped[int] = mapped_column(
555
- ForeignKey("experiments.id", ondelete="CASCADE"),
556
- index=True,
557
- )
558
- dataset_example_id: Mapped[int] = mapped_column(
559
- ForeignKey("dataset_examples.id", ondelete="CASCADE"),
560
- index=True,
561
- )
562
- repetition_number: Mapped[int]
563
- trace_id: Mapped[Optional[str]]
564
- output: Mapped[ExperimentResult]
565
- start_time: Mapped[datetime] = mapped_column(UtcTimeStamp)
566
- end_time: Mapped[datetime] = mapped_column(UtcTimeStamp)
567
- prompt_token_count: Mapped[Optional[int]]
568
- completion_token_count: Mapped[Optional[int]]
569
- error: Mapped[Optional[str]]
570
-
571
- trace: Mapped["Trace"] = relationship(
572
- primaryjoin="foreign(ExperimentRun.trace_id) == Trace.trace_id",
573
- back_populates="experiment_runs",
574
- )
575
-
576
- __table_args__ = (
577
- UniqueConstraint(
578
- "experiment_id",
579
- "dataset_example_id",
580
- "repetition_number",
581
- ),
582
- )
583
-
584
-
585
- class ExperimentRunAnnotation(Base):
586
- __tablename__ = "experiment_run_annotations"
587
- id: Mapped[int] = mapped_column(primary_key=True)
588
- experiment_run_id: Mapped[int] = mapped_column(
589
- ForeignKey("experiment_runs.id", ondelete="CASCADE"),
590
- index=True,
591
- )
592
- name: Mapped[str]
593
- annotator_kind: Mapped[str] = mapped_column(
594
- CheckConstraint("annotator_kind IN ('LLM', 'CODE', 'HUMAN')", name="valid_annotator_kind"),
595
- )
596
- label: Mapped[Optional[str]]
597
- score: Mapped[Optional[float]]
598
- explanation: Mapped[Optional[str]]
599
- trace_id: Mapped[Optional[str]]
600
- error: Mapped[Optional[str]]
601
- metadata_: Mapped[Dict[str, Any]] = mapped_column("metadata")
602
- start_time: Mapped[datetime] = mapped_column(UtcTimeStamp)
603
- end_time: Mapped[datetime] = mapped_column(UtcTimeStamp)
604
-
605
- __table_args__ = (
606
- UniqueConstraint(
607
- "experiment_run_id",
608
- "name",
609
- ),
610
- )
@@ -9,7 +9,7 @@ from urllib.parse import quote, urljoin
9
9
 
10
10
  from pandas import read_parquet
11
11
 
12
- from phoenix.config import INFERENCES_DIR
12
+ from phoenix.config import DATASET_DIR
13
13
  from phoenix.inferences.inferences import Inferences
14
14
  from phoenix.inferences.schema import (
15
15
  EmbeddingColumnNames,
@@ -20,7 +20,7 @@ from phoenix.inferences.schema import (
20
20
  logger = logging.getLogger(__name__)
21
21
 
22
22
 
23
- class InferencesRole(Enum):
23
+ class DatasetRole(Enum):
24
24
  PRIMARY = auto()
25
25
  REFERENCE = auto()
26
26
  CORPUS = auto()
@@ -39,11 +39,11 @@ class Fixture:
39
39
  corpus_file_name: Optional[str] = None
40
40
  corpus_schema: Optional[Schema] = None
41
41
 
42
- def paths(self) -> Iterator[Tuple[InferencesRole, Path]]:
42
+ def paths(self) -> Iterator[Tuple[DatasetRole, Path]]:
43
43
  return (
44
44
  (role, Path(self.prefix) / name)
45
45
  for role, name in zip(
46
- InferencesRole,
46
+ DatasetRole,
47
47
  (
48
48
  self.primary_file_name,
49
49
  self.reference_file_name,
@@ -413,41 +413,41 @@ FIXTURES: Tuple[Fixture, ...] = (
413
413
  NAME_TO_FIXTURE = {fixture.name: fixture for fixture in FIXTURES}
414
414
 
415
415
 
416
- def get_inferences(
416
+ def get_datasets(
417
417
  fixture_name: str,
418
418
  no_internet: bool = False,
419
419
  ) -> Tuple[Inferences, Optional[Inferences], Optional[Inferences]]:
420
420
  """
421
- Downloads primary and reference inferences for a fixture if they are not found
421
+ Downloads primary and reference datasets for a fixture if they are not found
422
422
  locally.
423
423
  """
424
424
  fixture = _get_fixture_by_name(fixture_name=fixture_name)
425
425
  if no_internet:
426
- paths = {role: INFERENCES_DIR / path for role, path in fixture.paths()}
426
+ paths = {role: DATASET_DIR / path for role, path in fixture.paths()}
427
427
  else:
428
- paths = dict(_download(fixture, INFERENCES_DIR))
429
- primary_inferences = Inferences(
430
- read_parquet(paths[InferencesRole.PRIMARY]),
428
+ paths = dict(_download(fixture, DATASET_DIR))
429
+ primary_dataset = Inferences(
430
+ read_parquet(paths[DatasetRole.PRIMARY]),
431
431
  fixture.primary_schema,
432
432
  "production",
433
433
  )
434
- reference_inferences = None
434
+ reference_dataset = None
435
435
  if fixture.reference_file_name is not None:
436
- reference_inferences = Inferences(
437
- read_parquet(paths[InferencesRole.REFERENCE]),
436
+ reference_dataset = Inferences(
437
+ read_parquet(paths[DatasetRole.REFERENCE]),
438
438
  fixture.reference_schema
439
439
  if fixture.reference_schema is not None
440
440
  else fixture.primary_schema,
441
441
  "training",
442
442
  )
443
- corpus_inferences = None
443
+ corpus_dataset = None
444
444
  if fixture.corpus_file_name is not None:
445
- corpus_inferences = Inferences(
446
- read_parquet(paths[InferencesRole.CORPUS]),
445
+ corpus_dataset = Inferences(
446
+ read_parquet(paths[DatasetRole.CORPUS]),
447
447
  fixture.corpus_schema,
448
448
  "knowledge_base",
449
449
  )
450
- return primary_inferences, reference_inferences, corpus_inferences
450
+ return primary_dataset, reference_dataset, corpus_dataset
451
451
 
452
452
 
453
453
  def _get_fixture_by_name(fixture_name: str) -> Fixture:
@@ -496,14 +496,14 @@ def load_example(use_case: str) -> ExampleInferences:
496
496
 
497
497
  """
498
498
  fixture = _get_fixture_by_name(use_case)
499
- primary_inferences, reference_inferences, corpus_inferences = get_inferences(use_case)
499
+ primary_dataset, reference_dataset, corpus_dataset = get_datasets(use_case)
500
500
  print(f"📥 Loaded {use_case} example datasets.")
501
501
  print("ℹ️ About this use-case:")
502
502
  print(fixture.description)
503
503
  return ExampleInferences(
504
- primary=primary_inferences,
505
- reference=reference_inferences,
506
- corpus=corpus_inferences,
504
+ primary=primary_dataset,
505
+ reference=reference_dataset,
506
+ corpus=corpus_dataset,
507
507
  )
508
508
 
509
509
 
@@ -544,7 +544,7 @@ class GCSAssets(NamedTuple):
544
544
  )
545
545
 
546
546
 
547
- def _download(fixture: Fixture, location: Path) -> Iterator[Tuple[InferencesRole, Path]]:
547
+ def _download(fixture: Fixture, location: Path) -> Iterator[Tuple[DatasetRole, Path]]:
548
548
  for role, path in fixture.paths():
549
549
  yield role, GCSAssets().metadata(path).save_artifact(location)
550
550
 
@@ -556,5 +556,5 @@ if __name__ == "__main__":
556
556
  for fixture in FIXTURES:
557
557
  start_time = time.time()
558
558
  print(f"getting {fixture.name}", end="...")
559
- dict(_download(fixture, INFERENCES_DIR))
559
+ dict(_download(fixture, DATASET_DIR))
560
560
  print(f"done ({time.time() - start_time:.2f}s)")
@@ -15,7 +15,7 @@ from pandas.api.types import (
15
15
  )
16
16
  from typing_extensions import TypeAlias
17
17
 
18
- from phoenix.config import GENERATED_INFERENCES_NAME_PREFIX, INFERENCES_DIR
18
+ from phoenix.config import DATASET_DIR, GENERATED_DATASET_NAME_PREFIX
19
19
  from phoenix.datetime_utils import normalize_timestamps
20
20
  from phoenix.utilities.deprecation import deprecated
21
21
 
@@ -31,7 +31,7 @@ from .schema import (
31
31
  SchemaFieldName,
32
32
  SchemaFieldValue,
33
33
  )
34
- from .validation import validate_inferences_inputs
34
+ from .validation import validate_dataset_inputs
35
35
 
36
36
  logger = logging.getLogger(__name__)
37
37
 
@@ -62,7 +62,7 @@ class Inferences:
62
62
 
63
63
  Examples
64
64
  --------
65
- >>> primary_inferences = px.Inferences(
65
+ >>> primary_dataset = px.Inferences(
66
66
  >>> dataframe=production_dataframe, schema=schema, name="primary"
67
67
  >>> )
68
68
  """
@@ -81,7 +81,7 @@ class Inferences:
81
81
  # allow for schema like objects
82
82
  if not isinstance(schema, Schema):
83
83
  schema = _get_schema_from_unknown_schema_param(schema)
84
- errors = validate_inferences_inputs(
84
+ errors = validate_dataset_inputs(
85
85
  dataframe=dataframe,
86
86
  schema=schema,
87
87
  )
@@ -95,7 +95,7 @@ class Inferences:
95
95
  self.__dataframe: DataFrame = dataframe
96
96
  self.__schema: Schema = schema
97
97
  self.__name: str = (
98
- name if name is not None else f"{GENERATED_INFERENCES_NAME_PREFIX}{str(uuid.uuid4())}"
98
+ name if name is not None else f"{GENERATED_DATASET_NAME_PREFIX}{str(uuid.uuid4())}"
99
99
  )
100
100
  self._is_empty = self.dataframe.empty
101
101
  logger.info(f"""Dataset: {self.__name} initialized""")
@@ -118,7 +118,7 @@ class Inferences:
118
118
  @classmethod
119
119
  def from_name(cls, name: str) -> "Inferences":
120
120
  """Retrieves a dataset by name from the file system"""
121
- directory = INFERENCES_DIR / name
121
+ directory = DATASET_DIR / name
122
122
  df = read_parquet(directory / cls._data_file_name)
123
123
  with open(directory / cls._schema_file_name) as schema_file:
124
124
  schema_json = schema_file.read()
@@ -127,7 +127,7 @@ class Inferences:
127
127
 
128
128
  def to_disc(self) -> None:
129
129
  """writes the data and schema to disc"""
130
- directory = INFERENCES_DIR / self.name
130
+ directory = DATASET_DIR / self.name
131
131
  directory.mkdir(parents=True, exist_ok=True)
132
132
  self.dataframe.to_parquet(
133
133
  directory / self._data_file_name,
@@ -34,7 +34,7 @@ def _check_valid_schema(schema: Schema) -> List[err.ValidationError]:
34
34
  return []
35
35
 
36
36
 
37
- def validate_inferences_inputs(dataframe: DataFrame, schema: Schema) -> List[err.ValidationError]:
37
+ def validate_dataset_inputs(dataframe: DataFrame, schema: Schema) -> List[err.ValidationError]:
38
38
  errors = _check_missing_columns(dataframe, schema)
39
39
  if errors:
40
40
  return errors