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