arize-phoenix 4.4.2__py3-none-any.whl → 4.4.4rc0__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.
- {arize_phoenix-4.4.2.dist-info → arize_phoenix-4.4.4rc0.dist-info}/METADATA +12 -11
- {arize_phoenix-4.4.2.dist-info → arize_phoenix-4.4.4rc0.dist-info}/RECORD +110 -57
- phoenix/__init__.py +0 -27
- phoenix/config.py +21 -7
- phoenix/core/model.py +25 -25
- phoenix/core/model_schema.py +66 -64
- phoenix/core/model_schema_adapter.py +27 -25
- phoenix/datasets/__init__.py +0 -0
- phoenix/datasets/evaluators.py +275 -0
- phoenix/datasets/experiments.py +469 -0
- phoenix/datasets/tracing.py +66 -0
- phoenix/datasets/types.py +212 -0
- phoenix/db/bulk_inserter.py +54 -14
- phoenix/db/insertion/dataset.py +234 -0
- phoenix/db/insertion/evaluation.py +6 -6
- phoenix/db/insertion/helpers.py +13 -2
- phoenix/db/migrations/types.py +29 -0
- phoenix/db/migrations/versions/10460e46d750_datasets.py +291 -0
- phoenix/db/migrations/versions/cf03bd6bae1d_init.py +2 -28
- phoenix/db/models.py +230 -3
- phoenix/inferences/fixtures.py +23 -23
- phoenix/inferences/inferences.py +7 -7
- phoenix/inferences/validation.py +1 -1
- phoenix/metrics/binning.py +2 -2
- phoenix/server/api/context.py +16 -0
- phoenix/server/api/dataloaders/__init__.py +16 -0
- phoenix/server/api/dataloaders/dataset_example_revisions.py +100 -0
- phoenix/server/api/dataloaders/dataset_example_spans.py +43 -0
- phoenix/server/api/dataloaders/experiment_annotation_summaries.py +85 -0
- phoenix/server/api/dataloaders/experiment_error_rates.py +43 -0
- phoenix/server/api/dataloaders/experiment_sequence_number.py +49 -0
- phoenix/server/api/dataloaders/project_by_name.py +31 -0
- phoenix/server/api/dataloaders/span_descendants.py +2 -3
- phoenix/server/api/dataloaders/span_projects.py +33 -0
- phoenix/server/api/dataloaders/trace_row_ids.py +39 -0
- phoenix/server/api/helpers/dataset_helpers.py +178 -0
- phoenix/server/api/input_types/AddExamplesToDatasetInput.py +16 -0
- phoenix/server/api/input_types/AddSpansToDatasetInput.py +14 -0
- phoenix/server/api/input_types/CreateDatasetInput.py +12 -0
- phoenix/server/api/input_types/DatasetExampleInput.py +14 -0
- phoenix/server/api/input_types/DatasetSort.py +17 -0
- phoenix/server/api/input_types/DatasetVersionSort.py +16 -0
- phoenix/server/api/input_types/DeleteDatasetExamplesInput.py +13 -0
- phoenix/server/api/input_types/DeleteDatasetInput.py +7 -0
- phoenix/server/api/input_types/DeleteExperimentsInput.py +9 -0
- phoenix/server/api/input_types/PatchDatasetExamplesInput.py +35 -0
- phoenix/server/api/input_types/PatchDatasetInput.py +14 -0
- phoenix/server/api/mutations/__init__.py +13 -0
- phoenix/server/api/mutations/auth.py +11 -0
- phoenix/server/api/mutations/dataset_mutations.py +520 -0
- phoenix/server/api/mutations/experiment_mutations.py +65 -0
- phoenix/server/api/{types/ExportEventsMutation.py → mutations/export_events_mutations.py} +17 -14
- phoenix/server/api/mutations/project_mutations.py +42 -0
- phoenix/server/api/queries.py +503 -0
- phoenix/server/api/routers/v1/__init__.py +77 -2
- phoenix/server/api/routers/v1/dataset_examples.py +178 -0
- phoenix/server/api/routers/v1/datasets.py +861 -0
- phoenix/server/api/routers/v1/evaluations.py +4 -2
- phoenix/server/api/routers/v1/experiment_evaluations.py +65 -0
- phoenix/server/api/routers/v1/experiment_runs.py +108 -0
- phoenix/server/api/routers/v1/experiments.py +174 -0
- phoenix/server/api/routers/v1/spans.py +3 -1
- phoenix/server/api/routers/v1/traces.py +1 -4
- phoenix/server/api/schema.py +2 -303
- phoenix/server/api/types/AnnotatorKind.py +10 -0
- phoenix/server/api/types/Cluster.py +19 -19
- phoenix/server/api/types/CreateDatasetPayload.py +8 -0
- phoenix/server/api/types/Dataset.py +282 -63
- phoenix/server/api/types/DatasetExample.py +85 -0
- phoenix/server/api/types/DatasetExampleRevision.py +34 -0
- phoenix/server/api/types/DatasetVersion.py +14 -0
- phoenix/server/api/types/Dimension.py +30 -29
- phoenix/server/api/types/EmbeddingDimension.py +40 -34
- phoenix/server/api/types/Event.py +16 -16
- phoenix/server/api/types/ExampleRevisionInterface.py +14 -0
- phoenix/server/api/types/Experiment.py +135 -0
- phoenix/server/api/types/ExperimentAnnotationSummary.py +13 -0
- phoenix/server/api/types/ExperimentComparison.py +19 -0
- phoenix/server/api/types/ExperimentRun.py +91 -0
- phoenix/server/api/types/ExperimentRunAnnotation.py +57 -0
- phoenix/server/api/types/Inferences.py +80 -0
- phoenix/server/api/types/InferencesRole.py +23 -0
- phoenix/server/api/types/Model.py +43 -42
- phoenix/server/api/types/Project.py +26 -12
- phoenix/server/api/types/Segments.py +1 -1
- phoenix/server/api/types/Span.py +78 -2
- phoenix/server/api/types/TimeSeries.py +6 -6
- phoenix/server/api/types/Trace.py +15 -4
- phoenix/server/api/types/UMAPPoints.py +1 -1
- phoenix/server/api/types/node.py +5 -111
- phoenix/server/api/types/pagination.py +10 -52
- phoenix/server/app.py +99 -49
- phoenix/server/main.py +49 -27
- phoenix/server/openapi/docs.py +3 -0
- phoenix/server/static/index.js +2246 -1368
- phoenix/server/templates/index.html +1 -0
- phoenix/services.py +15 -15
- phoenix/session/client.py +316 -21
- phoenix/session/session.py +47 -37
- phoenix/trace/exporter.py +14 -9
- phoenix/trace/fixtures.py +133 -7
- phoenix/trace/span_evaluations.py +3 -3
- phoenix/trace/trace_dataset.py +6 -6
- phoenix/utilities/json.py +61 -0
- phoenix/utilities/re.py +50 -0
- phoenix/version.py +1 -1
- phoenix/server/api/types/DatasetRole.py +0 -23
- {arize_phoenix-4.4.2.dist-info → arize_phoenix-4.4.4rc0.dist-info}/WHEEL +0 -0
- {arize_phoenix-4.4.2.dist-info → arize_phoenix-4.4.4rc0.dist-info}/licenses/IP_NOTICE +0 -0
- {arize_phoenix-4.4.2.dist-info → arize_phoenix-4.4.4rc0.dist-info}/licenses/LICENSE +0 -0
- /phoenix/server/api/{helpers.py → helpers/__init__.py} +0 -0
|
@@ -44,7 +44,7 @@ async def post_evaluations(request: Request) -> Response:
|
|
|
44
44
|
summary: Add evaluations to a span, trace, or document
|
|
45
45
|
operationId: addEvaluations
|
|
46
46
|
tags:
|
|
47
|
-
-
|
|
47
|
+
- private
|
|
48
48
|
parameters:
|
|
49
49
|
- name: project-name
|
|
50
50
|
in: query
|
|
@@ -105,7 +105,7 @@ async def get_evaluations(request: Request) -> Response:
|
|
|
105
105
|
summary: Get evaluations from Phoenix
|
|
106
106
|
operationId: getEvaluation
|
|
107
107
|
tags:
|
|
108
|
-
-
|
|
108
|
+
- private
|
|
109
109
|
parameters:
|
|
110
110
|
- name: project-name
|
|
111
111
|
in: query
|
|
@@ -116,6 +116,8 @@ async def get_evaluations(request: Request) -> Response:
|
|
|
116
116
|
responses:
|
|
117
117
|
200:
|
|
118
118
|
description: Success
|
|
119
|
+
403:
|
|
120
|
+
description: Forbidden
|
|
119
121
|
404:
|
|
120
122
|
description: Not found
|
|
121
123
|
"""
|
|
@@ -0,0 +1,65 @@
|
|
|
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.datasets.types import EvaluationResult, ExperimentEvaluationRun
|
|
9
|
+
from phoenix.db import models
|
|
10
|
+
from phoenix.server.api.types.node import from_global_id_with_expected_type
|
|
11
|
+
from phoenix.utilities.json import jsonify
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
async def create_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
|
+
exp_eval_run = models.ExperimentRunAnnotation(
|
|
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,
|
|
44
|
+
start_time=datetime.fromisoformat(start_time),
|
|
45
|
+
end_time=datetime.fromisoformat(end_time),
|
|
46
|
+
)
|
|
47
|
+
session.add(exp_eval_run)
|
|
48
|
+
await session.flush()
|
|
49
|
+
evaluation_gid = GlobalID("ExperimentEvaluation", str(exp_eval_run.id))
|
|
50
|
+
eval_payload = ExperimentEvaluationRun(
|
|
51
|
+
id=str(evaluation_gid),
|
|
52
|
+
experiment_run_id=str(experiment_run_gid),
|
|
53
|
+
start_time=exp_eval_run.start_time,
|
|
54
|
+
end_time=exp_eval_run.end_time,
|
|
55
|
+
name=exp_eval_run.name,
|
|
56
|
+
annotator_kind=exp_eval_run.annotator_kind,
|
|
57
|
+
error=exp_eval_run.error,
|
|
58
|
+
result=EvaluationResult(
|
|
59
|
+
label=exp_eval_run.label,
|
|
60
|
+
score=exp_eval_run.score,
|
|
61
|
+
explanation=exp_eval_run.explanation,
|
|
62
|
+
metadata=exp_eval_run.metadata_,
|
|
63
|
+
),
|
|
64
|
+
)
|
|
65
|
+
return JSONResponse(content=jsonify(eval_payload), status_code=200)
|
|
@@ -0,0 +1,108 @@
|
|
|
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.datasets.types import ExperimentResult, ExperimentRun
|
|
10
|
+
from phoenix.db import models
|
|
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
|
+
|
|
57
|
+
run_gid = GlobalID("ExperimentRun", str(exp_run.id))
|
|
58
|
+
run_payload = ExperimentRun(
|
|
59
|
+
start_time=exp_run.start_time,
|
|
60
|
+
end_time=exp_run.end_time,
|
|
61
|
+
experiment_id=str(experiment_gid),
|
|
62
|
+
dataset_example_id=str(example_gid),
|
|
63
|
+
repetition_number=exp_run.repetition_number,
|
|
64
|
+
output=ExperimentResult(result=exp_run.output),
|
|
65
|
+
error=exp_run.error,
|
|
66
|
+
id=str(run_gid),
|
|
67
|
+
trace_id=exp_run.trace_id,
|
|
68
|
+
)
|
|
69
|
+
return JSONResponse(content=jsonify(run_payload), status_code=200)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
async def list_experiment_runs(request: Request) -> Response:
|
|
73
|
+
experiment_gid = GlobalID.from_id(request.path_params["experiment_id"])
|
|
74
|
+
try:
|
|
75
|
+
experiment_id = from_global_id_with_expected_type(experiment_gid, "Experiment")
|
|
76
|
+
except ValueError:
|
|
77
|
+
return Response(
|
|
78
|
+
content=f"Experiment with ID {experiment_gid} does not exist",
|
|
79
|
+
status_code=HTTP_404_NOT_FOUND,
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
async with request.app.state.db() as session:
|
|
83
|
+
experiment_runs = await session.execute(
|
|
84
|
+
select(models.ExperimentRun)
|
|
85
|
+
.where(models.ExperimentRun.experiment_id == experiment_id)
|
|
86
|
+
# order by dataset_example_id to be consistent with `list_dataset_examples`
|
|
87
|
+
.order_by(models.ExperimentRun.dataset_example_id.asc())
|
|
88
|
+
)
|
|
89
|
+
experiment_runs = experiment_runs.scalars().all()
|
|
90
|
+
runs = []
|
|
91
|
+
for exp_run in experiment_runs:
|
|
92
|
+
run_gid = GlobalID("ExperimentRun", str(exp_run.id))
|
|
93
|
+
experiment_gid = GlobalID("Experiment", str(exp_run.experiment_id))
|
|
94
|
+
example_gid = GlobalID("DatasetExample", str(exp_run.dataset_example_id))
|
|
95
|
+
runs.append(
|
|
96
|
+
ExperimentRun(
|
|
97
|
+
start_time=exp_run.start_time,
|
|
98
|
+
end_time=exp_run.end_time,
|
|
99
|
+
experiment_id=str(experiment_gid),
|
|
100
|
+
dataset_example_id=str(example_gid),
|
|
101
|
+
repetition_number=exp_run.repetition_number,
|
|
102
|
+
output=ExperimentResult(result=exp_run.output),
|
|
103
|
+
error=exp_run.error,
|
|
104
|
+
id=str(run_gid),
|
|
105
|
+
trace_id=exp_run.trace_id,
|
|
106
|
+
)
|
|
107
|
+
)
|
|
108
|
+
return JSONResponse(content=jsonify(runs), status_code=200)
|
|
@@ -0,0 +1,174 @@
|
|
|
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_stmt
|
|
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_stmt(
|
|
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=experiment_payload, status_code=200)
|
|
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=experiment_payload, status_code=200)
|
|
@@ -19,7 +19,7 @@ async def query_spans_handler(request: Request) -> Response:
|
|
|
19
19
|
summary: Query spans using query DSL
|
|
20
20
|
operationId: querySpans
|
|
21
21
|
tags:
|
|
22
|
-
-
|
|
22
|
+
- private
|
|
23
23
|
parameters:
|
|
24
24
|
- name: project-name
|
|
25
25
|
in: query
|
|
@@ -68,6 +68,8 @@ async def query_spans_handler(request: Request) -> Response:
|
|
|
68
68
|
responses:
|
|
69
69
|
200:
|
|
70
70
|
description: Success
|
|
71
|
+
403:
|
|
72
|
+
description: Forbidden
|
|
71
73
|
404:
|
|
72
74
|
description: Not found
|
|
73
75
|
422:
|
|
@@ -11,7 +11,6 @@ from starlette.datastructures import State
|
|
|
11
11
|
from starlette.requests import Request
|
|
12
12
|
from starlette.responses import Response
|
|
13
13
|
from starlette.status import (
|
|
14
|
-
HTTP_403_FORBIDDEN,
|
|
15
14
|
HTTP_415_UNSUPPORTED_MEDIA_TYPE,
|
|
16
15
|
HTTP_422_UNPROCESSABLE_ENTITY,
|
|
17
16
|
)
|
|
@@ -25,7 +24,7 @@ async def post_traces(request: Request) -> Response:
|
|
|
25
24
|
summary: Send traces to Phoenix
|
|
26
25
|
operationId: addTraces
|
|
27
26
|
tags:
|
|
28
|
-
-
|
|
27
|
+
- private
|
|
29
28
|
requestBody:
|
|
30
29
|
required: true
|
|
31
30
|
content:
|
|
@@ -43,8 +42,6 @@ async def post_traces(request: Request) -> Response:
|
|
|
43
42
|
422:
|
|
44
43
|
description: Request body is invalid
|
|
45
44
|
"""
|
|
46
|
-
if request.app.state.read_only:
|
|
47
|
-
return Response(status_code=HTTP_403_FORBIDDEN)
|
|
48
45
|
content_type = request.headers.get("content-type")
|
|
49
46
|
if content_type != "application/x-protobuf":
|
|
50
47
|
return Response(
|