arize-phoenix 4.10.2rc0__py3-none-any.whl → 4.10.2rc1__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.
- {arize_phoenix-4.10.2rc0.dist-info → arize_phoenix-4.10.2rc1.dist-info}/METADATA +4 -3
- {arize_phoenix-4.10.2rc0.dist-info → arize_phoenix-4.10.2rc1.dist-info}/RECORD +27 -35
- phoenix/server/api/context.py +3 -7
- phoenix/server/api/openapi/main.py +18 -2
- phoenix/server/api/openapi/schema.py +12 -12
- phoenix/server/api/routers/v1/__init__.py +36 -83
- phoenix/server/api/routers/v1/dataset_examples.py +102 -123
- phoenix/server/api/routers/v1/datasets.py +389 -507
- phoenix/server/api/routers/v1/evaluations.py +74 -64
- phoenix/server/api/routers/v1/experiment_evaluations.py +67 -91
- phoenix/server/api/routers/v1/experiment_runs.py +97 -155
- phoenix/server/api/routers/v1/experiments.py +131 -181
- phoenix/server/api/routers/v1/spans.py +141 -173
- phoenix/server/api/routers/v1/traces.py +113 -128
- phoenix/server/api/routers/v1/utils.py +94 -0
- phoenix/server/api/types/Span.py +0 -1
- phoenix/server/app.py +148 -192
- phoenix/server/main.py +0 -3
- phoenix/server/static/index.css +6 -0
- phoenix/server/static/index.js +8547 -0
- phoenix/server/templates/index.html +25 -76
- phoenix/server/thread_server.py +2 -2
- phoenix/trace/schemas.py +0 -1
- phoenix/version.py +1 -1
- phoenix/server/openapi/docs.py +0 -221
- phoenix/server/static/.vite/manifest.json +0 -78
- phoenix/server/static/assets/components-C8sm_r1F.js +0 -1142
- phoenix/server/static/assets/index-BEKPzgQs.js +0 -100
- phoenix/server/static/assets/pages-bN7juCjh.js +0 -2885
- phoenix/server/static/assets/vendor-CUDAPm8e.js +0 -641
- phoenix/server/static/assets/vendor-DxkFTwjz.css +0 -1
- phoenix/server/static/assets/vendor-arizeai-Do2HOmcL.js +0 -662
- phoenix/server/static/assets/vendor-codemirror-CrdxOlMs.js +0 -12
- phoenix/server/static/assets/vendor-recharts-PKRvByVe.js +0 -59
- phoenix/server/static/assets/vendor-three-DwGkEfCM.js +0 -2998
- {arize_phoenix-4.10.2rc0.dist-info → arize_phoenix-4.10.2rc1.dist-info}/WHEEL +0 -0
- {arize_phoenix-4.10.2rc0.dist-info → arize_phoenix-4.10.2rc1.dist-info}/licenses/IP_NOTICE +0 -0
- {arize_phoenix-4.10.2rc0.dist-info → arize_phoenix-4.10.2rc1.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,8 +1,11 @@
|
|
|
1
|
+
from datetime import datetime
|
|
1
2
|
from random import getrandbits
|
|
3
|
+
from typing import Any, Dict, Optional
|
|
2
4
|
|
|
5
|
+
from fastapi import APIRouter, HTTPException
|
|
6
|
+
from pydantic import BaseModel, Field
|
|
3
7
|
from sqlalchemy import select
|
|
4
8
|
from starlette.requests import Request
|
|
5
|
-
from starlette.responses import JSONResponse, Response
|
|
6
9
|
from starlette.status import HTTP_404_NOT_FOUND
|
|
7
10
|
from strawberry.relay import GlobalID
|
|
8
11
|
|
|
@@ -11,6 +14,10 @@ from phoenix.db.helpers import SupportedSQLDialect
|
|
|
11
14
|
from phoenix.db.insertion.helpers import insert_on_conflict
|
|
12
15
|
from phoenix.server.api.types.node import from_global_id_with_expected_type
|
|
13
16
|
|
|
17
|
+
from .utils import ResponseBody, add_errors_to_responses
|
|
18
|
+
|
|
19
|
+
router = APIRouter(tags=["experiments"], include_in_schema=False)
|
|
20
|
+
|
|
14
21
|
|
|
15
22
|
def _short_uuid() -> str:
|
|
16
23
|
return str(getrandbits(32).to_bytes(4, "big").hex())
|
|
@@ -24,94 +31,76 @@ def _generate_experiment_name(dataset_name: str) -> str:
|
|
|
24
31
|
return f"{short_ds_name}-{_short_uuid()}"
|
|
25
32
|
|
|
26
33
|
|
|
27
|
-
|
|
34
|
+
class Experiment(BaseModel):
|
|
35
|
+
id: str = Field(description="The ID of the experiment")
|
|
36
|
+
dataset_id: str = Field(description="The ID of the dataset associated with the experiment")
|
|
37
|
+
dataset_version_id: str = Field(
|
|
38
|
+
description="The ID of the dataset version associated with the experiment"
|
|
39
|
+
)
|
|
40
|
+
repetitions: int = Field(description="Number of times the experiment is repeated")
|
|
41
|
+
metadata: Dict[str, Any] = Field(description="Metadata of the experiment")
|
|
42
|
+
project_name: Optional[str] = Field(
|
|
43
|
+
description="The name of the project associated with the experiment"
|
|
44
|
+
)
|
|
45
|
+
created_at: datetime = Field(description="The creation timestamp of the experiment")
|
|
46
|
+
updated_at: datetime = Field(description="The last update timestamp of the experiment")
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class CreateExperimentRequestBody(BaseModel):
|
|
28
50
|
"""
|
|
29
|
-
|
|
30
|
-
operationId: createExperiment
|
|
31
|
-
tags:
|
|
32
|
-
- private
|
|
33
|
-
parameters:
|
|
34
|
-
- in: path
|
|
35
|
-
name: dataset_id
|
|
36
|
-
required: true
|
|
37
|
-
description: The ID of the dataset to create an experiment for
|
|
38
|
-
schema:
|
|
39
|
-
type: string
|
|
40
|
-
requestBody:
|
|
41
|
-
description: Details of the experiment to be created
|
|
42
|
-
required: true
|
|
43
|
-
content:
|
|
44
|
-
application/json:
|
|
45
|
-
schema:
|
|
46
|
-
type: object
|
|
47
|
-
properties:
|
|
48
|
-
repetitions:
|
|
49
|
-
type: integer
|
|
50
|
-
description: Number of times the experiment should be repeated for each example
|
|
51
|
-
default: 1
|
|
52
|
-
metadata:
|
|
53
|
-
type: object
|
|
54
|
-
description: Metadata for the experiment
|
|
55
|
-
additionalProperties:
|
|
56
|
-
type: string
|
|
57
|
-
version_id:
|
|
58
|
-
type: string
|
|
59
|
-
description: ID of the dataset version to use
|
|
60
|
-
responses:
|
|
61
|
-
200:
|
|
62
|
-
description: Experiment retrieved successfully
|
|
63
|
-
content:
|
|
64
|
-
application/json:
|
|
65
|
-
schema:
|
|
66
|
-
type: object
|
|
67
|
-
properties:
|
|
68
|
-
data:
|
|
69
|
-
type: object
|
|
70
|
-
properties:
|
|
71
|
-
id:
|
|
72
|
-
type: string
|
|
73
|
-
description: The ID of the experiment
|
|
74
|
-
dataset_id:
|
|
75
|
-
type: string
|
|
76
|
-
description: The ID of the dataset associated with the experiment
|
|
77
|
-
dataset_version_id:
|
|
78
|
-
type: string
|
|
79
|
-
description: The ID of the dataset version associated with the experiment
|
|
80
|
-
repetitions:
|
|
81
|
-
type: integer
|
|
82
|
-
description: Number of times the experiment is repeated
|
|
83
|
-
metadata:
|
|
84
|
-
type: object
|
|
85
|
-
description: Metadata of the experiment
|
|
86
|
-
additionalProperties:
|
|
87
|
-
type: string
|
|
88
|
-
project_name:
|
|
89
|
-
type: string
|
|
90
|
-
description: The name of the project associated with the experiment
|
|
91
|
-
created_at:
|
|
92
|
-
type: string
|
|
93
|
-
format: date-time
|
|
94
|
-
description: The creation timestamp of the experiment
|
|
95
|
-
updated_at:
|
|
96
|
-
type: string
|
|
97
|
-
format: date-time
|
|
98
|
-
description: The last update timestamp of the experiment
|
|
99
|
-
404:
|
|
100
|
-
description: Dataset or DatasetVersion not found
|
|
51
|
+
Details of the experiment to be created
|
|
101
52
|
"""
|
|
102
|
-
|
|
53
|
+
|
|
54
|
+
name: Optional[str] = Field(
|
|
55
|
+
default=None,
|
|
56
|
+
description=("Name of the experiment (if omitted, a random name will be generated)"),
|
|
57
|
+
)
|
|
58
|
+
description: Optional[str] = Field(
|
|
59
|
+
default=None, description="An optional description of the experiment"
|
|
60
|
+
)
|
|
61
|
+
metadata: Optional[Dict[str, Any]] = Field(
|
|
62
|
+
default=None, description="Metadata for the experiment"
|
|
63
|
+
)
|
|
64
|
+
version_id: Optional[str] = Field(
|
|
65
|
+
default=None,
|
|
66
|
+
description=(
|
|
67
|
+
"ID of the dataset version over which the experiment will be run "
|
|
68
|
+
"(if omitted, the latest version will be used)"
|
|
69
|
+
),
|
|
70
|
+
)
|
|
71
|
+
repetitions: int = Field(
|
|
72
|
+
default=1, description="Number of times the experiment should be repeated for each example"
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
class CreateExperimentResponseBody(ResponseBody[Experiment]):
|
|
77
|
+
pass
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
@router.post(
|
|
81
|
+
"/datasets/{dataset_id}/experiments",
|
|
82
|
+
operation_id="createExperiment",
|
|
83
|
+
summary="Create experiment on a dataset",
|
|
84
|
+
responses=add_errors_to_responses(
|
|
85
|
+
[{"status_code": HTTP_404_NOT_FOUND, "description": "Dataset or DatasetVersion not found"}]
|
|
86
|
+
),
|
|
87
|
+
response_description="Experiment retrieved successfully",
|
|
88
|
+
)
|
|
89
|
+
async def create_experiment(
|
|
90
|
+
request: Request,
|
|
91
|
+
dataset_id: str,
|
|
92
|
+
request_body: CreateExperimentRequestBody,
|
|
93
|
+
) -> CreateExperimentResponseBody:
|
|
94
|
+
dataset_globalid = GlobalID.from_id(dataset_id)
|
|
103
95
|
try:
|
|
104
|
-
|
|
96
|
+
dataset_rowid = from_global_id_with_expected_type(dataset_globalid, "Dataset")
|
|
105
97
|
except ValueError:
|
|
106
|
-
|
|
107
|
-
|
|
98
|
+
raise HTTPException(
|
|
99
|
+
detail="Dataset with ID {dataset_globalid} does not exist",
|
|
108
100
|
status_code=HTTP_404_NOT_FOUND,
|
|
109
101
|
)
|
|
110
102
|
|
|
111
|
-
|
|
112
|
-
repetitions = payload.get("repetitions", 1)
|
|
113
|
-
metadata = payload.get("metadata") or {}
|
|
114
|
-
dataset_version_globalid_str = payload.get("version_id")
|
|
103
|
+
dataset_version_globalid_str = request_body.version_id
|
|
115
104
|
if dataset_version_globalid_str is not None:
|
|
116
105
|
try:
|
|
117
106
|
dataset_version_globalid = GlobalID.from_id(dataset_version_globalid_str)
|
|
@@ -119,31 +108,31 @@ async def create_experiment(request: Request) -> Response:
|
|
|
119
108
|
dataset_version_globalid, "DatasetVersion"
|
|
120
109
|
)
|
|
121
110
|
except ValueError:
|
|
122
|
-
|
|
123
|
-
|
|
111
|
+
raise HTTPException(
|
|
112
|
+
detail="DatasetVersion with ID {dataset_version_globalid} does not exist",
|
|
124
113
|
status_code=HTTP_404_NOT_FOUND,
|
|
125
114
|
)
|
|
126
115
|
|
|
127
116
|
async with request.app.state.db() as session:
|
|
128
117
|
result = (
|
|
129
|
-
await session.execute(select(models.Dataset).where(models.Dataset.id ==
|
|
118
|
+
await session.execute(select(models.Dataset).where(models.Dataset.id == dataset_rowid))
|
|
130
119
|
).scalar()
|
|
131
120
|
if result is None:
|
|
132
|
-
|
|
133
|
-
|
|
121
|
+
raise HTTPException(
|
|
122
|
+
detail=f"Dataset with ID {dataset_globalid} does not exist",
|
|
134
123
|
status_code=HTTP_404_NOT_FOUND,
|
|
135
124
|
)
|
|
136
125
|
dataset_name = result.name
|
|
137
126
|
if dataset_version_globalid_str is None:
|
|
138
127
|
dataset_version_result = await session.execute(
|
|
139
128
|
select(models.DatasetVersion)
|
|
140
|
-
.where(models.DatasetVersion.dataset_id ==
|
|
129
|
+
.where(models.DatasetVersion.dataset_id == dataset_rowid)
|
|
141
130
|
.order_by(models.DatasetVersion.id.desc())
|
|
142
131
|
)
|
|
143
132
|
dataset_version = dataset_version_result.scalar()
|
|
144
133
|
if not dataset_version:
|
|
145
|
-
|
|
146
|
-
|
|
134
|
+
raise HTTPException(
|
|
135
|
+
detail=f"Dataset {dataset_globalid} does not have any versions",
|
|
147
136
|
status_code=HTTP_404_NOT_FOUND,
|
|
148
137
|
)
|
|
149
138
|
dataset_version_id = dataset_version.id
|
|
@@ -154,24 +143,24 @@ async def create_experiment(request: Request) -> Response:
|
|
|
154
143
|
)
|
|
155
144
|
dataset_version = dataset_version.scalar()
|
|
156
145
|
if not dataset_version:
|
|
157
|
-
|
|
158
|
-
|
|
146
|
+
raise HTTPException(
|
|
147
|
+
detail=f"DatasetVersion with ID {dataset_version_globalid} does not exist",
|
|
159
148
|
status_code=HTTP_404_NOT_FOUND,
|
|
160
149
|
)
|
|
161
150
|
|
|
162
151
|
# generate a semi-unique name for the experiment
|
|
163
|
-
experiment_name =
|
|
152
|
+
experiment_name = request_body.name or _generate_experiment_name(dataset_name)
|
|
164
153
|
project_name = f"Experiment-{getrandbits(96).to_bytes(12, 'big').hex()}"
|
|
165
154
|
project_description = (
|
|
166
155
|
f"dataset_id: {dataset_globalid}\ndataset_version_id: {dataset_version_globalid}"
|
|
167
156
|
)
|
|
168
157
|
experiment = models.Experiment(
|
|
169
|
-
dataset_id=int(
|
|
158
|
+
dataset_id=int(dataset_rowid),
|
|
170
159
|
dataset_version_id=int(dataset_version_id),
|
|
171
160
|
name=experiment_name,
|
|
172
|
-
description=
|
|
173
|
-
repetitions=repetitions,
|
|
174
|
-
metadata_=metadata,
|
|
161
|
+
description=request_body.description,
|
|
162
|
+
repetitions=request_body.repetitions,
|
|
163
|
+
metadata_=request_body.metadata or {},
|
|
175
164
|
project_name=project_name,
|
|
176
165
|
)
|
|
177
166
|
session.add(experiment)
|
|
@@ -198,104 +187,65 @@ async def create_experiment(request: Request) -> Response:
|
|
|
198
187
|
dataset_version_globalid = GlobalID(
|
|
199
188
|
"DatasetVersion", str(experiment.dataset_version_id)
|
|
200
189
|
)
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
190
|
+
return CreateExperimentResponseBody(
|
|
191
|
+
data=Experiment(
|
|
192
|
+
id=str(experiment_globalid),
|
|
193
|
+
dataset_id=str(dataset_globalid),
|
|
194
|
+
dataset_version_id=str(dataset_version_globalid),
|
|
195
|
+
repetitions=experiment.repetitions,
|
|
196
|
+
metadata=experiment.metadata_,
|
|
197
|
+
project_name=experiment.project_name,
|
|
198
|
+
created_at=experiment.created_at,
|
|
199
|
+
updated_at=experiment.updated_at,
|
|
200
|
+
)
|
|
201
|
+
)
|
|
212
202
|
|
|
213
203
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
description: Experiment retrieved successfully
|
|
230
|
-
content:
|
|
231
|
-
application/json:
|
|
232
|
-
schema:
|
|
233
|
-
type: object
|
|
234
|
-
properties:
|
|
235
|
-
data:
|
|
236
|
-
type: object
|
|
237
|
-
properties:
|
|
238
|
-
id:
|
|
239
|
-
type: string
|
|
240
|
-
description: The ID of the experiment
|
|
241
|
-
dataset_id:
|
|
242
|
-
type: string
|
|
243
|
-
description: The ID of the dataset associated with the experiment
|
|
244
|
-
dataset_version_id:
|
|
245
|
-
type: string
|
|
246
|
-
description: The ID of the dataset version associated with the experiment
|
|
247
|
-
repetitions:
|
|
248
|
-
type: integer
|
|
249
|
-
description: Number of times the experiment is repeated
|
|
250
|
-
metadata:
|
|
251
|
-
type: object
|
|
252
|
-
description: Metadata of the experiment
|
|
253
|
-
additionalProperties:
|
|
254
|
-
type: string
|
|
255
|
-
project_name:
|
|
256
|
-
type: string
|
|
257
|
-
description: The name of the project associated with the experiment
|
|
258
|
-
created_at:
|
|
259
|
-
type: string
|
|
260
|
-
format: date-time
|
|
261
|
-
description: The creation timestamp of the experiment
|
|
262
|
-
updated_at:
|
|
263
|
-
type: string
|
|
264
|
-
format: date-time
|
|
265
|
-
description: The last update timestamp of the experiment
|
|
266
|
-
404:
|
|
267
|
-
description: Experiment not found
|
|
268
|
-
"""
|
|
269
|
-
experiment_globalid = GlobalID.from_id(request.path_params["experiment_id"])
|
|
204
|
+
class GetExperimentResponseBody(ResponseBody[Experiment]):
|
|
205
|
+
pass
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
@router.get(
|
|
209
|
+
"/experiments/{experiment_id}",
|
|
210
|
+
operation_id="getExperiment",
|
|
211
|
+
summary="Get experiment by ID",
|
|
212
|
+
responses=add_errors_to_responses(
|
|
213
|
+
[{"status_code": HTTP_404_NOT_FOUND, "description": "Experiment not found"}]
|
|
214
|
+
),
|
|
215
|
+
response_description="Experiment retrieved successfully",
|
|
216
|
+
)
|
|
217
|
+
async def get_experiment(request: Request, experiment_id: str) -> GetExperimentResponseBody:
|
|
218
|
+
experiment_globalid = GlobalID.from_id(experiment_id)
|
|
270
219
|
try:
|
|
271
|
-
|
|
220
|
+
experiment_rowid = from_global_id_with_expected_type(experiment_globalid, "Experiment")
|
|
272
221
|
except ValueError:
|
|
273
|
-
|
|
274
|
-
|
|
222
|
+
raise HTTPException(
|
|
223
|
+
detail="Experiment with ID {experiment_globalid} does not exist",
|
|
275
224
|
status_code=HTTP_404_NOT_FOUND,
|
|
276
225
|
)
|
|
277
226
|
|
|
278
227
|
async with request.app.state.db() as session:
|
|
279
228
|
experiment = await session.execute(
|
|
280
|
-
select(models.Experiment).where(models.Experiment.id ==
|
|
229
|
+
select(models.Experiment).where(models.Experiment.id == experiment_rowid)
|
|
281
230
|
)
|
|
282
231
|
experiment = experiment.scalar()
|
|
283
232
|
if not experiment:
|
|
284
|
-
|
|
285
|
-
|
|
233
|
+
raise HTTPException(
|
|
234
|
+
detail=f"Experiment with ID {experiment_globalid} does not exist",
|
|
286
235
|
status_code=HTTP_404_NOT_FOUND,
|
|
287
236
|
)
|
|
288
237
|
|
|
289
238
|
dataset_globalid = GlobalID("Dataset", str(experiment.dataset_id))
|
|
290
239
|
dataset_version_globalid = GlobalID("DatasetVersion", str(experiment.dataset_version_id))
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
240
|
+
return GetExperimentResponseBody(
|
|
241
|
+
data=Experiment(
|
|
242
|
+
id=str(experiment_globalid),
|
|
243
|
+
dataset_id=str(dataset_globalid),
|
|
244
|
+
dataset_version_id=str(dataset_version_globalid),
|
|
245
|
+
repetitions=experiment.repetitions,
|
|
246
|
+
metadata=experiment.metadata_,
|
|
247
|
+
project_name=experiment.project_name,
|
|
248
|
+
created_at=experiment.created_at,
|
|
249
|
+
updated_at=experiment.updated_at,
|
|
250
|
+
)
|
|
251
|
+
)
|