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.
- {arize_phoenix-4.4.4rc6.dist-info → arize_phoenix-4.5.0.dist-info}/METADATA +8 -14
- {arize_phoenix-4.4.4rc6.dist-info → arize_phoenix-4.5.0.dist-info}/RECORD +58 -122
- {arize_phoenix-4.4.4rc6.dist-info → arize_phoenix-4.5.0.dist-info}/WHEEL +1 -1
- phoenix/__init__.py +27 -0
- phoenix/config.py +7 -42
- phoenix/core/model.py +25 -25
- phoenix/core/model_schema.py +62 -64
- phoenix/core/model_schema_adapter.py +25 -27
- phoenix/datetime_utils.py +0 -4
- phoenix/db/bulk_inserter.py +14 -54
- phoenix/db/insertion/evaluation.py +10 -10
- phoenix/db/insertion/helpers.py +14 -17
- phoenix/db/insertion/span.py +3 -3
- phoenix/db/migrations/versions/cf03bd6bae1d_init.py +28 -2
- phoenix/db/models.py +4 -236
- phoenix/inferences/fixtures.py +23 -23
- phoenix/inferences/inferences.py +7 -7
- phoenix/inferences/validation.py +1 -1
- phoenix/server/api/context.py +0 -20
- phoenix/server/api/dataloaders/__init__.py +0 -20
- phoenix/server/api/dataloaders/span_descendants.py +3 -2
- phoenix/server/api/routers/v1/__init__.py +2 -77
- phoenix/server/api/routers/v1/evaluations.py +13 -8
- phoenix/server/api/routers/v1/spans.py +5 -9
- phoenix/server/api/routers/v1/traces.py +4 -1
- phoenix/server/api/schema.py +303 -2
- phoenix/server/api/types/Cluster.py +19 -19
- phoenix/server/api/types/Dataset.py +63 -282
- phoenix/server/api/types/DatasetRole.py +23 -0
- phoenix/server/api/types/Dimension.py +29 -30
- phoenix/server/api/types/EmbeddingDimension.py +34 -40
- phoenix/server/api/types/Event.py +16 -16
- phoenix/server/api/{mutations/export_events_mutations.py → types/ExportEventsMutation.py} +14 -17
- phoenix/server/api/types/Model.py +42 -43
- phoenix/server/api/types/Project.py +12 -26
- phoenix/server/api/types/Span.py +2 -79
- phoenix/server/api/types/TimeSeries.py +6 -6
- phoenix/server/api/types/Trace.py +4 -15
- phoenix/server/api/types/UMAPPoints.py +1 -1
- phoenix/server/api/types/node.py +111 -5
- phoenix/server/api/types/pagination.py +52 -10
- phoenix/server/app.py +49 -103
- phoenix/server/main.py +27 -49
- phoenix/server/openapi/docs.py +0 -3
- phoenix/server/static/index.js +1384 -2390
- phoenix/server/templates/index.html +0 -1
- phoenix/services.py +15 -15
- phoenix/session/client.py +23 -611
- phoenix/session/session.py +37 -47
- phoenix/trace/exporter.py +9 -14
- phoenix/trace/fixtures.py +7 -133
- phoenix/trace/schemas.py +2 -1
- phoenix/trace/span_evaluations.py +3 -3
- phoenix/trace/trace_dataset.py +6 -6
- phoenix/version.py +1 -1
- phoenix/db/insertion/dataset.py +0 -237
- phoenix/db/migrations/types.py +0 -29
- phoenix/db/migrations/versions/10460e46d750_datasets.py +0 -291
- phoenix/experiments/__init__.py +0 -6
- phoenix/experiments/evaluators/__init__.py +0 -29
- phoenix/experiments/evaluators/base.py +0 -153
- phoenix/experiments/evaluators/code_evaluators.py +0 -99
- phoenix/experiments/evaluators/llm_evaluators.py +0 -244
- phoenix/experiments/evaluators/utils.py +0 -189
- phoenix/experiments/functions.py +0 -616
- phoenix/experiments/tracing.py +0 -85
- phoenix/experiments/types.py +0 -722
- phoenix/experiments/utils.py +0 -9
- phoenix/server/api/dataloaders/average_experiment_run_latency.py +0 -54
- phoenix/server/api/dataloaders/dataset_example_revisions.py +0 -100
- phoenix/server/api/dataloaders/dataset_example_spans.py +0 -43
- phoenix/server/api/dataloaders/experiment_annotation_summaries.py +0 -85
- phoenix/server/api/dataloaders/experiment_error_rates.py +0 -43
- phoenix/server/api/dataloaders/experiment_run_counts.py +0 -42
- phoenix/server/api/dataloaders/experiment_sequence_number.py +0 -49
- phoenix/server/api/dataloaders/project_by_name.py +0 -31
- phoenix/server/api/dataloaders/span_projects.py +0 -33
- phoenix/server/api/dataloaders/trace_row_ids.py +0 -39
- phoenix/server/api/helpers/dataset_helpers.py +0 -179
- phoenix/server/api/input_types/AddExamplesToDatasetInput.py +0 -16
- phoenix/server/api/input_types/AddSpansToDatasetInput.py +0 -14
- phoenix/server/api/input_types/ClearProjectInput.py +0 -15
- phoenix/server/api/input_types/CreateDatasetInput.py +0 -12
- phoenix/server/api/input_types/DatasetExampleInput.py +0 -14
- phoenix/server/api/input_types/DatasetSort.py +0 -17
- phoenix/server/api/input_types/DatasetVersionSort.py +0 -16
- phoenix/server/api/input_types/DeleteDatasetExamplesInput.py +0 -13
- phoenix/server/api/input_types/DeleteDatasetInput.py +0 -7
- phoenix/server/api/input_types/DeleteExperimentsInput.py +0 -9
- phoenix/server/api/input_types/PatchDatasetExamplesInput.py +0 -35
- phoenix/server/api/input_types/PatchDatasetInput.py +0 -14
- phoenix/server/api/mutations/__init__.py +0 -13
- phoenix/server/api/mutations/auth.py +0 -11
- phoenix/server/api/mutations/dataset_mutations.py +0 -520
- phoenix/server/api/mutations/experiment_mutations.py +0 -65
- phoenix/server/api/mutations/project_mutations.py +0 -47
- phoenix/server/api/openapi/__init__.py +0 -0
- phoenix/server/api/openapi/main.py +0 -6
- phoenix/server/api/openapi/schema.py +0 -16
- phoenix/server/api/queries.py +0 -503
- phoenix/server/api/routers/v1/dataset_examples.py +0 -178
- phoenix/server/api/routers/v1/datasets.py +0 -965
- phoenix/server/api/routers/v1/experiment_evaluations.py +0 -65
- phoenix/server/api/routers/v1/experiment_runs.py +0 -96
- phoenix/server/api/routers/v1/experiments.py +0 -174
- phoenix/server/api/types/AnnotatorKind.py +0 -10
- phoenix/server/api/types/CreateDatasetPayload.py +0 -8
- phoenix/server/api/types/DatasetExample.py +0 -85
- phoenix/server/api/types/DatasetExampleRevision.py +0 -34
- phoenix/server/api/types/DatasetVersion.py +0 -14
- phoenix/server/api/types/ExampleRevisionInterface.py +0 -14
- phoenix/server/api/types/Experiment.py +0 -147
- phoenix/server/api/types/ExperimentAnnotationSummary.py +0 -13
- phoenix/server/api/types/ExperimentComparison.py +0 -19
- phoenix/server/api/types/ExperimentRun.py +0 -91
- phoenix/server/api/types/ExperimentRunAnnotation.py +0 -57
- phoenix/server/api/types/Inferences.py +0 -80
- phoenix/server/api/types/InferencesRole.py +0 -23
- phoenix/utilities/json.py +0 -61
- phoenix/utilities/re.py +0 -50
- {arize_phoenix-4.4.4rc6.dist-info → arize_phoenix-4.5.0.dist-info}/licenses/IP_NOTICE +0 -0
- {arize_phoenix-4.4.4rc6.dist-info → arize_phoenix-4.5.0.dist-info}/licenses/LICENSE +0 -0
- /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,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]
|