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
@@ -1,65 +0,0 @@
1
- from datetime import datetime
2
-
3
- from starlette.requests import Request
4
- from starlette.responses import JSONResponse, Response
5
- from starlette.status import HTTP_404_NOT_FOUND
6
- from strawberry.relay import GlobalID
7
-
8
- from phoenix.db import models
9
- from phoenix.db.helpers import SupportedSQLDialect
10
- from phoenix.db.insertion.helpers import OnConflict, insert_on_conflict
11
- from phoenix.server.api.types.node import from_global_id_with_expected_type
12
-
13
-
14
- async def upsert_experiment_evaluation(request: Request) -> Response:
15
- payload = await request.json()
16
- experiment_run_gid = GlobalID.from_id(payload["experiment_run_id"])
17
- try:
18
- experiment_run_id = from_global_id_with_expected_type(experiment_run_gid, "ExperimentRun")
19
- except ValueError:
20
- return Response(
21
- content=f"ExperimentRun with ID {experiment_run_gid} does not exist",
22
- status_code=HTTP_404_NOT_FOUND,
23
- )
24
- name = payload["name"]
25
- annotator_kind = payload["annotator_kind"]
26
- result = payload.get("result")
27
- label = result.get("label") if result else None
28
- score = result.get("score") if result else None
29
- explanation = result.get("explanation") if result else None
30
- error = payload.get("error")
31
- metadata = payload.get("metadata") or {}
32
- start_time = payload["start_time"]
33
- end_time = payload["end_time"]
34
- async with request.app.state.db() as session:
35
- values = dict(
36
- experiment_run_id=experiment_run_id,
37
- name=name,
38
- annotator_kind=annotator_kind,
39
- label=label,
40
- score=score,
41
- explanation=explanation,
42
- error=error,
43
- metadata_=metadata, # `metadata_` must match database
44
- start_time=datetime.fromisoformat(start_time),
45
- end_time=datetime.fromisoformat(end_time),
46
- trace_id=payload.get("trace_id"),
47
- )
48
- set_ = {
49
- **{k: v for k, v in values.items() if k != "metadata_"},
50
- "metadata": values["metadata_"], # `metadata` must match database
51
- }
52
- dialect = SupportedSQLDialect(session.bind.dialect.name)
53
- exp_eval_run = await session.scalar(
54
- insert_on_conflict(
55
- dialect=dialect,
56
- table=models.ExperimentRunAnnotation,
57
- values=values,
58
- constraint="uq_experiment_run_annotations_experiment_run_id_name",
59
- column_names=("experiment_run_id", "name"),
60
- on_conflict=OnConflict.DO_UPDATE,
61
- set_=set_,
62
- ).returning(models.ExperimentRunAnnotation)
63
- )
64
- evaluation_gid = GlobalID("ExperimentEvaluation", str(exp_eval_run.id))
65
- return JSONResponse(content={"data": {"id": str(evaluation_gid)}})
@@ -1,96 +0,0 @@
1
- from datetime import datetime
2
-
3
- from sqlalchemy import select
4
- from starlette.requests import Request
5
- from starlette.responses import JSONResponse, Response
6
- from starlette.status import HTTP_404_NOT_FOUND
7
- from strawberry.relay import GlobalID
8
-
9
- from phoenix.db import models
10
- from phoenix.experiments.types import ExperimentResult, ExperimentRun
11
- from phoenix.server.api.types.node import from_global_id_with_expected_type
12
- from phoenix.utilities.json import jsonify
13
-
14
-
15
- async def create_experiment_run(request: Request) -> Response:
16
- experiment_gid = GlobalID.from_id(request.path_params["experiment_id"])
17
- try:
18
- experiment_id = from_global_id_with_expected_type(experiment_gid, "Experiment")
19
- except ValueError:
20
- return Response(
21
- content=f"Experiment with ID {experiment_gid} does not exist",
22
- status_code=HTTP_404_NOT_FOUND,
23
- )
24
-
25
- payload = await request.json()
26
-
27
- example_gid = GlobalID.from_id(payload["dataset_example_id"])
28
- try:
29
- dataset_example_id = from_global_id_with_expected_type(example_gid, "DatasetExample")
30
- except ValueError:
31
- return Response(
32
- content=f"DatasetExample with ID {example_gid} does not exist",
33
- status_code=HTTP_404_NOT_FOUND,
34
- )
35
-
36
- trace_id = payload.get("trace_id", None)
37
- output = payload["output"]
38
- repetition_number = payload["repetition_number"]
39
- start_time = payload["start_time"]
40
- end_time = payload["end_time"]
41
- error = payload.get("error")
42
-
43
- async with request.app.state.db() as session:
44
- exp_run = models.ExperimentRun(
45
- experiment_id=experiment_id,
46
- dataset_example_id=dataset_example_id,
47
- trace_id=trace_id,
48
- output=output,
49
- repetition_number=repetition_number,
50
- start_time=datetime.fromisoformat(start_time),
51
- end_time=datetime.fromisoformat(end_time),
52
- error=error,
53
- )
54
- session.add(exp_run)
55
- await session.flush()
56
- run_gid = GlobalID("ExperimentRun", str(exp_run.id))
57
- return JSONResponse(content={"data": {"id": str(run_gid)}})
58
-
59
-
60
- async def list_experiment_runs(request: Request) -> Response:
61
- experiment_gid = GlobalID.from_id(request.path_params["experiment_id"])
62
- try:
63
- experiment_id = from_global_id_with_expected_type(experiment_gid, "Experiment")
64
- except ValueError:
65
- return Response(
66
- content=f"Experiment with ID {experiment_gid} does not exist",
67
- status_code=HTTP_404_NOT_FOUND,
68
- )
69
-
70
- async with request.app.state.db() as session:
71
- experiment_runs = await session.execute(
72
- select(models.ExperimentRun)
73
- .where(models.ExperimentRun.experiment_id == experiment_id)
74
- # order by dataset_example_id to be consistent with `list_dataset_examples`
75
- .order_by(models.ExperimentRun.dataset_example_id.asc())
76
- )
77
- experiment_runs = experiment_runs.scalars().all()
78
- runs = []
79
- for exp_run in experiment_runs:
80
- run_gid = GlobalID("ExperimentRun", str(exp_run.id))
81
- experiment_gid = GlobalID("Experiment", str(exp_run.experiment_id))
82
- example_gid = GlobalID("DatasetExample", str(exp_run.dataset_example_id))
83
- runs.append(
84
- ExperimentRun(
85
- start_time=exp_run.start_time,
86
- end_time=exp_run.end_time,
87
- experiment_id=str(experiment_gid),
88
- dataset_example_id=str(example_gid),
89
- repetition_number=exp_run.repetition_number,
90
- output=ExperimentResult.from_dict(exp_run.output) if exp_run.output else None,
91
- error=exp_run.error,
92
- id=str(run_gid),
93
- trace_id=exp_run.trace_id,
94
- )
95
- )
96
- return JSONResponse(content={"data": jsonify(runs)}, status_code=200)
@@ -1,174 +0,0 @@
1
- from random import getrandbits
2
-
3
- from sqlalchemy import select
4
- from starlette.requests import Request
5
- from starlette.responses import JSONResponse, Response
6
- from starlette.status import HTTP_404_NOT_FOUND
7
- from strawberry.relay import GlobalID
8
-
9
- from phoenix.db import models
10
- from phoenix.db.helpers import SupportedSQLDialect
11
- from phoenix.db.insertion.helpers import insert_on_conflict
12
- from phoenix.server.api.types.node import from_global_id_with_expected_type
13
-
14
-
15
- def _short_uuid() -> str:
16
- return str(getrandbits(32).to_bytes(4, "big").hex())
17
-
18
-
19
- def _generate_experiment_name(dataset_name: str) -> str:
20
- """
21
- Generate a semi-unique name for the experiment.
22
- """
23
- short_ds_name = dataset_name[:8].replace(" ", "-")
24
- return f"{short_ds_name}-{_short_uuid()}"
25
-
26
-
27
- async def create_experiment(request: Request) -> Response:
28
- dataset_globalid = GlobalID.from_id(request.path_params["dataset_id"])
29
- try:
30
- dataset_id = from_global_id_with_expected_type(dataset_globalid, "Dataset")
31
- except ValueError:
32
- return Response(
33
- content="Dataset with ID {dataset_globalid} does not exist",
34
- status_code=HTTP_404_NOT_FOUND,
35
- )
36
-
37
- payload = await request.json()
38
- repetitions = payload.get("repetitions", 1)
39
- metadata = payload.get("metadata") or {}
40
- dataset_version_globalid_str = payload.get("version_id")
41
- if dataset_version_globalid_str is not None:
42
- try:
43
- dataset_version_globalid = GlobalID.from_id(dataset_version_globalid_str)
44
- dataset_version_id = from_global_id_with_expected_type(
45
- dataset_version_globalid, "DatasetVersion"
46
- )
47
- except ValueError:
48
- return Response(
49
- content="DatasetVersion with ID {dataset_version_globalid} does not exist",
50
- status_code=HTTP_404_NOT_FOUND,
51
- )
52
-
53
- async with request.app.state.db() as session:
54
- result = (
55
- await session.execute(select(models.Dataset).where(models.Dataset.id == dataset_id))
56
- ).scalar()
57
- if result is None:
58
- return Response(
59
- content=f"Dataset with ID {dataset_globalid} does not exist",
60
- status_code=HTTP_404_NOT_FOUND,
61
- )
62
- dataset_name = result.name
63
- if dataset_version_globalid_str is None:
64
- dataset_version_result = await session.execute(
65
- select(models.DatasetVersion)
66
- .where(models.DatasetVersion.dataset_id == dataset_id)
67
- .order_by(models.DatasetVersion.id.desc())
68
- )
69
- dataset_version = dataset_version_result.scalar()
70
- if not dataset_version:
71
- return Response(
72
- content=f"Dataset {dataset_globalid} does not have any versions",
73
- status_code=HTTP_404_NOT_FOUND,
74
- )
75
- dataset_version_id = dataset_version.id
76
- dataset_version_globalid = GlobalID("DatasetVersion", str(dataset_version_id))
77
- else:
78
- dataset_version = await session.execute(
79
- select(models.DatasetVersion).where(models.DatasetVersion.id == dataset_version_id)
80
- )
81
- dataset_version = dataset_version.scalar()
82
- if not dataset_version:
83
- return Response(
84
- content=f"DatasetVersion with ID {dataset_version_globalid} does not exist",
85
- status_code=HTTP_404_NOT_FOUND,
86
- )
87
-
88
- # generate a semi-unique name for the experiment
89
- experiment_name = payload.get("name") or _generate_experiment_name(dataset_name)
90
- project_name = f"Experiment-{getrandbits(96).to_bytes(12, 'big').hex()}"
91
- project_description = (
92
- f"dataset_id: {dataset_globalid}\ndataset_version_id: {dataset_version_globalid}"
93
- )
94
- experiment = models.Experiment(
95
- dataset_id=int(dataset_id),
96
- dataset_version_id=int(dataset_version_id),
97
- name=experiment_name,
98
- description=payload.get("description"),
99
- repetitions=repetitions,
100
- metadata_=metadata,
101
- project_name=project_name,
102
- )
103
- session.add(experiment)
104
- await session.flush()
105
-
106
- dialect = SupportedSQLDialect(session.bind.dialect.name)
107
- project_rowid = await session.scalar(
108
- insert_on_conflict(
109
- dialect=dialect,
110
- table=models.Project,
111
- constraint="uq_projects_name",
112
- column_names=("name",),
113
- values=dict(
114
- name=project_name,
115
- description=project_description,
116
- created_at=experiment.created_at,
117
- updated_at=experiment.updated_at,
118
- ),
119
- ).returning(models.Project.id)
120
- )
121
- assert project_rowid is not None
122
-
123
- experiment_globalid = GlobalID("Experiment", str(experiment.id))
124
- if dataset_version_globalid_str is None:
125
- dataset_version_globalid = GlobalID(
126
- "DatasetVersion", str(experiment.dataset_version_id)
127
- )
128
- experiment_payload = {
129
- "id": str(experiment_globalid),
130
- "dataset_id": str(dataset_globalid),
131
- "dataset_version_id": str(dataset_version_globalid),
132
- "repetitions": experiment.repetitions,
133
- "metadata": experiment.metadata_,
134
- "project_name": experiment.project_name,
135
- "created_at": experiment.created_at.isoformat(),
136
- "updated_at": experiment.updated_at.isoformat(),
137
- }
138
- return JSONResponse(content={"data": experiment_payload})
139
-
140
-
141
- async def read_experiment(request: Request) -> Response:
142
- experiment_globalid = GlobalID.from_id(request.path_params["experiment_id"])
143
- try:
144
- experiment_id = from_global_id_with_expected_type(experiment_globalid, "Experiment")
145
- except ValueError:
146
- return Response(
147
- content="Experiment with ID {experiment_globalid} does not exist",
148
- status_code=HTTP_404_NOT_FOUND,
149
- )
150
-
151
- async with request.app.state.db() as session:
152
- experiment = await session.execute(
153
- select(models.Experiment).where(models.Experiment.id == experiment_id)
154
- )
155
- experiment = experiment.scalar()
156
- if not experiment:
157
- return Response(
158
- content=f"Experiment with ID {experiment_globalid} does not exist",
159
- status_code=HTTP_404_NOT_FOUND,
160
- )
161
-
162
- dataset_globalid = GlobalID("Dataset", str(experiment.dataset_id))
163
- dataset_version_globalid = GlobalID("DatasetVersion", str(experiment.dataset_version_id))
164
- experiment_payload = {
165
- "id": str(experiment_globalid),
166
- "dataset_id": str(dataset_globalid),
167
- "dataset_version_id": str(dataset_version_globalid),
168
- "repetitions": experiment.repetitions,
169
- "metadata": experiment.metadata_,
170
- "project_name": experiment.project_name,
171
- "created_at": experiment.created_at.isoformat(),
172
- "updated_at": experiment.updated_at.isoformat(),
173
- }
174
- return JSONResponse(content={"data": experiment_payload})
@@ -1,10 +0,0 @@
1
- from enum import Enum
2
-
3
- import strawberry
4
-
5
-
6
- @strawberry.enum
7
- class AnnotatorKind(Enum):
8
- LLM = "LLM"
9
- HUMAN = "HUMAN"
10
- CODE = "CODE"
@@ -1,8 +0,0 @@
1
- import strawberry
2
-
3
- from phoenix.server.api.types.Dataset import Dataset
4
-
5
-
6
- @strawberry.type
7
- class CreateDatasetPayload:
8
- dataset: Dataset
@@ -1,85 +0,0 @@
1
- from datetime import datetime
2
- from typing import Optional
3
-
4
- import strawberry
5
- from sqlalchemy import select
6
- from sqlalchemy.orm import joinedload
7
- from strawberry import UNSET
8
- from strawberry.relay.types import Connection, GlobalID, Node, NodeID
9
- from strawberry.types import Info
10
-
11
- from phoenix.db import models
12
- from phoenix.server.api.context import Context
13
- from phoenix.server.api.types.DatasetExampleRevision import DatasetExampleRevision
14
- from phoenix.server.api.types.DatasetVersion import DatasetVersion
15
- from phoenix.server.api.types.ExperimentRun import ExperimentRun, to_gql_experiment_run
16
- from phoenix.server.api.types.node import from_global_id_with_expected_type
17
- from phoenix.server.api.types.pagination import (
18
- ConnectionArgs,
19
- CursorString,
20
- connection_from_list,
21
- )
22
- from phoenix.server.api.types.Span import Span, to_gql_span
23
-
24
-
25
- @strawberry.type
26
- class DatasetExample(Node):
27
- id_attr: NodeID[int]
28
- created_at: datetime
29
- version_id: strawberry.Private[Optional[int]] = None
30
-
31
- @strawberry.field
32
- async def revision(
33
- self,
34
- info: Info[Context, None],
35
- dataset_version_id: Optional[GlobalID] = UNSET,
36
- ) -> DatasetExampleRevision:
37
- example_id = self.id_attr
38
- version_id: Optional[int] = None
39
- if dataset_version_id:
40
- version_id = from_global_id_with_expected_type(
41
- global_id=dataset_version_id, expected_type_name=DatasetVersion.__name__
42
- )
43
- elif self.version_id is not None:
44
- version_id = self.version_id
45
- return await info.context.data_loaders.dataset_example_revisions.load(
46
- (example_id, version_id)
47
- )
48
-
49
- @strawberry.field
50
- async def span(
51
- self,
52
- info: Info[Context, None],
53
- ) -> Optional[Span]:
54
- return (
55
- to_gql_span(span)
56
- if (span := await info.context.data_loaders.dataset_example_spans.load(self.id_attr))
57
- else None
58
- )
59
-
60
- @strawberry.field
61
- async def experiment_runs(
62
- self,
63
- info: Info[Context, None],
64
- first: Optional[int] = 50,
65
- last: Optional[int] = UNSET,
66
- after: Optional[CursorString] = UNSET,
67
- before: Optional[CursorString] = UNSET,
68
- ) -> Connection[ExperimentRun]:
69
- args = ConnectionArgs(
70
- first=first,
71
- after=after if isinstance(after, CursorString) else None,
72
- last=last,
73
- before=before if isinstance(before, CursorString) else None,
74
- )
75
- example_id = self.id_attr
76
- query = (
77
- select(models.ExperimentRun)
78
- .options(joinedload(models.ExperimentRun.trace).load_only(models.Trace.trace_id))
79
- .join(models.Experiment, models.Experiment.id == models.ExperimentRun.experiment_id)
80
- .where(models.ExperimentRun.dataset_example_id == example_id)
81
- .order_by(models.Experiment.id.desc())
82
- )
83
- async with info.context.db() as session:
84
- runs = (await session.scalars(query)).all()
85
- return connection_from_list([to_gql_experiment_run(run) for run in runs], args)
@@ -1,34 +0,0 @@
1
- from datetime import datetime
2
- from enum import Enum
3
-
4
- import strawberry
5
-
6
- from phoenix.db import models
7
- from phoenix.server.api.types.ExampleRevisionInterface import ExampleRevision
8
-
9
-
10
- @strawberry.enum
11
- class RevisionKind(Enum):
12
- CREATE = "CREATE"
13
- PATCH = "PATCH"
14
- DELETE = "DELETE"
15
-
16
-
17
- @strawberry.type
18
- class DatasetExampleRevision(ExampleRevision):
19
- """
20
- Represents a revision (i.e., update or alteration) of a dataset example.
21
- """
22
-
23
- revision_kind: RevisionKind
24
- created_at: datetime
25
-
26
- @classmethod
27
- def from_orm_revision(cls, revision: models.DatasetExampleRevision) -> "DatasetExampleRevision":
28
- return cls(
29
- input=revision.input,
30
- output=revision.output,
31
- metadata=revision.metadata_,
32
- revision_kind=RevisionKind(revision.revision_kind),
33
- created_at=revision.created_at,
34
- )
@@ -1,14 +0,0 @@
1
- from datetime import datetime
2
- from typing import Optional
3
-
4
- import strawberry
5
- from strawberry.relay import Node, NodeID
6
- from strawberry.scalars import JSON
7
-
8
-
9
- @strawberry.type
10
- class DatasetVersion(Node):
11
- id_attr: NodeID[int]
12
- description: Optional[str]
13
- metadata: JSON
14
- created_at: datetime
@@ -1,14 +0,0 @@
1
- import strawberry
2
- from strawberry.scalars import JSON
3
-
4
-
5
- @strawberry.interface
6
- class ExampleRevision:
7
- """
8
- Represents an example revision for generative tasks.
9
- For example, you might have text -> text, text -> labels, etc.
10
- """
11
-
12
- input: JSON
13
- output: JSON
14
- metadata: JSON
@@ -1,147 +0,0 @@
1
- from datetime import datetime
2
- from typing import List, Optional
3
-
4
- import strawberry
5
- from sqlalchemy import select
6
- from sqlalchemy.orm import joinedload
7
- from strawberry import UNSET, Private
8
- from strawberry.relay import Connection, Node, NodeID
9
- from strawberry.scalars import JSON
10
- from strawberry.types import Info
11
-
12
- from phoenix.db import models
13
- from phoenix.server.api.context import Context
14
- from phoenix.server.api.types.ExperimentAnnotationSummary import ExperimentAnnotationSummary
15
- from phoenix.server.api.types.ExperimentRun import ExperimentRun, to_gql_experiment_run
16
- from phoenix.server.api.types.pagination import (
17
- ConnectionArgs,
18
- CursorString,
19
- connection_from_list,
20
- )
21
- from phoenix.server.api.types.Project import Project
22
-
23
-
24
- @strawberry.type
25
- class Experiment(Node):
26
- cached_sequence_number: Private[Optional[int]] = None
27
- id_attr: NodeID[int]
28
- name: str
29
- project_name: Optional[str]
30
- description: Optional[str]
31
- metadata: JSON
32
- created_at: datetime
33
- updated_at: datetime
34
-
35
- @strawberry.field(
36
- description="Sequence number (1-based) of experiments belonging to the same dataset"
37
- ) # type: ignore
38
- async def sequence_number(
39
- self,
40
- info: Info[Context, None],
41
- ) -> int:
42
- if self.cached_sequence_number is None:
43
- seq_num = await info.context.data_loaders.experiment_sequence_number.load(self.id_attr)
44
- if seq_num is None:
45
- raise ValueError(f"invalid experiment: id={self.id_attr}")
46
- self.cached_sequence_number = seq_num
47
- return self.cached_sequence_number
48
-
49
- @strawberry.field
50
- async def runs(
51
- self,
52
- info: Info[Context, None],
53
- first: Optional[int] = 50,
54
- last: Optional[int] = UNSET,
55
- after: Optional[CursorString] = UNSET,
56
- before: Optional[CursorString] = UNSET,
57
- ) -> Connection[ExperimentRun]:
58
- args = ConnectionArgs(
59
- first=first,
60
- after=after if isinstance(after, CursorString) else None,
61
- last=last,
62
- before=before if isinstance(before, CursorString) else None,
63
- )
64
- experiment_id = self.id_attr
65
- async with info.context.db() as session:
66
- runs = (
67
- await session.scalars(
68
- select(models.ExperimentRun)
69
- .where(models.ExperimentRun.experiment_id == experiment_id)
70
- .order_by(models.ExperimentRun.id.desc())
71
- .options(
72
- joinedload(models.ExperimentRun.trace).load_only(models.Trace.trace_id)
73
- )
74
- )
75
- ).all()
76
- return connection_from_list([to_gql_experiment_run(run) for run in runs], args)
77
-
78
- @strawberry.field
79
- async def run_count(self, info: Info[Context, None]) -> int:
80
- experiment_id = self.id_attr
81
- return await info.context.data_loaders.experiment_run_counts.load(experiment_id)
82
-
83
- @strawberry.field
84
- async def annotation_summaries(
85
- self, info: Info[Context, None]
86
- ) -> List[ExperimentAnnotationSummary]:
87
- experiment_id = self.id_attr
88
- return [
89
- ExperimentAnnotationSummary(
90
- annotation_name=summary.annotation_name,
91
- min_score=summary.min_score,
92
- max_score=summary.max_score,
93
- mean_score=summary.mean_score,
94
- count=summary.count,
95
- error_count=summary.error_count,
96
- )
97
- for summary in await info.context.data_loaders.experiment_annotation_summaries.load(
98
- experiment_id
99
- )
100
- ]
101
-
102
- @strawberry.field
103
- async def error_rate(self, info: Info[Context, None]) -> Optional[float]:
104
- return await info.context.data_loaders.experiment_error_rates.load(self.id_attr)
105
-
106
- @strawberry.field
107
- async def average_run_latency_ms(self, info: Info[Context, None]) -> float:
108
- latency_seconds = await info.context.data_loaders.average_experiment_run_latency.load(
109
- self.id_attr
110
- )
111
- return latency_seconds * 1000
112
-
113
- @strawberry.field
114
- async def project(self, info: Info[Context, None]) -> Optional[Project]:
115
- if self.project_name is None:
116
- return None
117
-
118
- db_project = await info.context.data_loaders.project_by_name.load(self.project_name)
119
-
120
- if db_project is None:
121
- return None
122
-
123
- return Project(
124
- id_attr=db_project.id,
125
- name=db_project.name,
126
- gradient_start_color=db_project.gradient_start_color,
127
- gradient_end_color=db_project.gradient_end_color,
128
- )
129
-
130
-
131
- def to_gql_experiment(
132
- experiment: models.Experiment,
133
- sequence_number: Optional[int] = None,
134
- ) -> Experiment:
135
- """
136
- Converts an ORM experiment to a GraphQL Experiment.
137
- """
138
- return Experiment(
139
- cached_sequence_number=sequence_number,
140
- id_attr=experiment.id,
141
- name=experiment.name,
142
- project_name=experiment.project_name,
143
- description=experiment.description,
144
- metadata=experiment.metadata_,
145
- created_at=experiment.created_at,
146
- updated_at=experiment.updated_at,
147
- )
@@ -1,13 +0,0 @@
1
- from typing import Optional
2
-
3
- import strawberry
4
-
5
-
6
- @strawberry.type
7
- class ExperimentAnnotationSummary:
8
- annotation_name: str
9
- min_score: Optional[float]
10
- max_score: Optional[float]
11
- mean_score: Optional[float]
12
- count: int
13
- error_count: int
@@ -1,19 +0,0 @@
1
- from typing import List
2
-
3
- import strawberry
4
- from strawberry.relay import GlobalID
5
-
6
- from phoenix.server.api.types.DatasetExample import DatasetExample
7
- from phoenix.server.api.types.ExperimentRun import ExperimentRun
8
-
9
-
10
- @strawberry.type
11
- class RunComparisonItem:
12
- experiment_id: GlobalID
13
- runs: List[ExperimentRun]
14
-
15
-
16
- @strawberry.type
17
- class ExperimentComparison:
18
- example: DatasetExample
19
- run_comparison_items: List[RunComparisonItem]