arize-phoenix 12.2.0__py3-none-any.whl → 12.4.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-12.2.0.dist-info → arize_phoenix-12.4.0.dist-info}/METADATA +2 -1
- {arize_phoenix-12.2.0.dist-info → arize_phoenix-12.4.0.dist-info}/RECORD +45 -44
- phoenix/auth.py +19 -0
- phoenix/config.py +302 -53
- phoenix/db/README.md +546 -28
- phoenix/server/api/auth_messages.py +46 -0
- phoenix/server/api/routers/auth.py +21 -30
- phoenix/server/api/routers/oauth2.py +255 -46
- phoenix/server/api/routers/v1/__init__.py +2 -3
- phoenix/server/api/routers/v1/annotation_configs.py +12 -29
- phoenix/server/api/routers/v1/annotations.py +21 -22
- phoenix/server/api/routers/v1/datasets.py +38 -56
- phoenix/server/api/routers/v1/documents.py +2 -3
- phoenix/server/api/routers/v1/evaluations.py +12 -24
- phoenix/server/api/routers/v1/experiment_evaluations.py +2 -3
- phoenix/server/api/routers/v1/experiment_runs.py +9 -10
- phoenix/server/api/routers/v1/experiments.py +16 -17
- phoenix/server/api/routers/v1/projects.py +15 -21
- phoenix/server/api/routers/v1/prompts.py +30 -31
- phoenix/server/api/routers/v1/sessions.py +2 -5
- phoenix/server/api/routers/v1/spans.py +35 -26
- phoenix/server/api/routers/v1/traces.py +11 -19
- phoenix/server/api/routers/v1/users.py +14 -23
- phoenix/server/api/routers/v1/utils.py +3 -7
- phoenix/server/app.py +6 -2
- phoenix/server/authorization.py +2 -3
- phoenix/server/bearer_auth.py +4 -5
- phoenix/server/cost_tracking/model_cost_manifest.json +54 -54
- phoenix/server/oauth2.py +174 -9
- phoenix/server/static/.vite/manifest.json +39 -39
- phoenix/server/static/assets/{components-BG6v0EM8.js → components-BvsExS75.js} +422 -387
- phoenix/server/static/assets/{index-CSVcULw1.js → index-iq8WDxat.js} +12 -12
- phoenix/server/static/assets/{pages-DgaM7kpM.js → pages-Ckg4SLQ9.js} +542 -488
- phoenix/server/static/assets/vendor-D2eEI-6h.js +914 -0
- phoenix/server/static/assets/{vendor-arizeai-DlOj0PQQ.js → vendor-arizeai-kfOei7nf.js} +2 -2
- phoenix/server/static/assets/{vendor-codemirror-B2PHH5yZ.js → vendor-codemirror-1bq_t1Ec.js} +3 -3
- phoenix/server/static/assets/{vendor-recharts-CKsi4IjN.js → vendor-recharts-DQ4xfrf4.js} +1 -1
- phoenix/server/static/assets/{vendor-shiki-DN26BkKE.js → vendor-shiki-GGmcIQxA.js} +1 -1
- phoenix/server/templates/index.html +1 -0
- phoenix/trace/attributes.py +80 -13
- phoenix/version.py +1 -1
- phoenix/server/static/assets/vendor-BqTEkGQU.js +0 -903
- {arize_phoenix-12.2.0.dist-info → arize_phoenix-12.4.0.dist-info}/WHEEL +0 -0
- {arize_phoenix-12.2.0.dist-info → arize_phoenix-12.4.0.dist-info}/entry_points.txt +0 -0
- {arize_phoenix-12.2.0.dist-info → arize_phoenix-12.4.0.dist-info}/licenses/IP_NOTICE +0 -0
- {arize_phoenix-12.2.0.dist-info → arize_phoenix-12.4.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -7,7 +7,6 @@ from sqlalchemy import select
|
|
|
7
7
|
from sqlalchemy.exc import IntegrityError as PostgreSQLIntegrityError
|
|
8
8
|
from sqlean.dbapi2 import IntegrityError as SQLiteIntegrityError # type: ignore[import-untyped]
|
|
9
9
|
from starlette.requests import Request
|
|
10
|
-
from starlette.status import HTTP_404_NOT_FOUND, HTTP_409_CONFLICT, HTTP_422_UNPROCESSABLE_ENTITY
|
|
11
10
|
from strawberry.relay import GlobalID
|
|
12
11
|
|
|
13
12
|
from phoenix.db import models
|
|
@@ -60,11 +59,11 @@ class CreateExperimentRunResponseBody(ResponseBody[CreateExperimentRunResponseBo
|
|
|
60
59
|
responses=add_errors_to_responses(
|
|
61
60
|
[
|
|
62
61
|
{
|
|
63
|
-
"status_code":
|
|
62
|
+
"status_code": 404,
|
|
64
63
|
"description": "Experiment or dataset example not found",
|
|
65
64
|
},
|
|
66
65
|
{
|
|
67
|
-
"status_code":
|
|
66
|
+
"status_code": 409,
|
|
68
67
|
"description": "This experiment run has already been submitted",
|
|
69
68
|
},
|
|
70
69
|
]
|
|
@@ -79,7 +78,7 @@ async def create_experiment_run(
|
|
|
79
78
|
except ValueError:
|
|
80
79
|
raise HTTPException(
|
|
81
80
|
detail=f"Experiment with ID {experiment_gid} does not exist",
|
|
82
|
-
status_code=
|
|
81
|
+
status_code=404,
|
|
83
82
|
)
|
|
84
83
|
|
|
85
84
|
example_gid = GlobalID.from_id(request_body.dataset_example_id)
|
|
@@ -88,7 +87,7 @@ async def create_experiment_run(
|
|
|
88
87
|
except ValueError:
|
|
89
88
|
raise HTTPException(
|
|
90
89
|
detail=f"DatasetExample with ID {example_gid} does not exist",
|
|
91
|
-
status_code=
|
|
90
|
+
status_code=404,
|
|
92
91
|
)
|
|
93
92
|
|
|
94
93
|
trace_id = request_body.trace_id
|
|
@@ -115,7 +114,7 @@ async def create_experiment_run(
|
|
|
115
114
|
except (PostgreSQLIntegrityError, SQLiteIntegrityError):
|
|
116
115
|
raise HTTPException(
|
|
117
116
|
detail="This experiment run has already been submitted",
|
|
118
|
-
status_code=
|
|
117
|
+
status_code=409,
|
|
119
118
|
)
|
|
120
119
|
request.state.event_queue.put(ExperimentRunInsertEvent((exp_run.id,)))
|
|
121
120
|
run_gid = GlobalID("ExperimentRun", str(exp_run.id))
|
|
@@ -141,8 +140,8 @@ class ListExperimentRunsResponseBody(PaginatedResponseBody[ExperimentRunResponse
|
|
|
141
140
|
response_description="Experiment runs retrieved successfully",
|
|
142
141
|
responses=add_errors_to_responses(
|
|
143
142
|
[
|
|
144
|
-
{"status_code":
|
|
145
|
-
{"status_code":
|
|
143
|
+
{"status_code": 404, "description": "Experiment not found"},
|
|
144
|
+
{"status_code": 422, "description": "Invalid cursor format"},
|
|
146
145
|
]
|
|
147
146
|
),
|
|
148
147
|
)
|
|
@@ -166,7 +165,7 @@ async def list_experiment_runs(
|
|
|
166
165
|
except ValueError:
|
|
167
166
|
raise HTTPException(
|
|
168
167
|
detail=f"Experiment with ID {experiment_gid} does not exist",
|
|
169
|
-
status_code=
|
|
168
|
+
status_code=404,
|
|
170
169
|
)
|
|
171
170
|
|
|
172
171
|
stmt = (
|
|
@@ -182,7 +181,7 @@ async def list_experiment_runs(
|
|
|
182
181
|
except ValueError:
|
|
183
182
|
raise HTTPException(
|
|
184
183
|
detail=f"Invalid cursor format: {cursor}",
|
|
185
|
-
status_code=
|
|
184
|
+
status_code=422,
|
|
186
185
|
)
|
|
187
186
|
|
|
188
187
|
# Apply limit only if specified for pagination
|
|
@@ -11,7 +11,6 @@ from sqlalchemy.ext.asyncio import AsyncSession
|
|
|
11
11
|
from sqlalchemy.orm import joinedload
|
|
12
12
|
from starlette.requests import Request
|
|
13
13
|
from starlette.responses import PlainTextResponse
|
|
14
|
-
from starlette.status import HTTP_200_OK, HTTP_404_NOT_FOUND, HTTP_422_UNPROCESSABLE_ENTITY
|
|
15
14
|
from strawberry.relay import GlobalID
|
|
16
15
|
|
|
17
16
|
from phoenix.db import models
|
|
@@ -96,7 +95,7 @@ class CreateExperimentResponseBody(ResponseBody[Experiment]):
|
|
|
96
95
|
operation_id="createExperiment",
|
|
97
96
|
summary="Create experiment on a dataset",
|
|
98
97
|
responses=add_errors_to_responses(
|
|
99
|
-
[{"status_code":
|
|
98
|
+
[{"status_code": 404, "description": "Dataset or DatasetVersion not found"}]
|
|
100
99
|
),
|
|
101
100
|
response_description="Experiment retrieved successfully",
|
|
102
101
|
)
|
|
@@ -111,7 +110,7 @@ async def create_experiment(
|
|
|
111
110
|
except ValueError:
|
|
112
111
|
raise HTTPException(
|
|
113
112
|
detail="Dataset with ID {dataset_globalid} does not exist",
|
|
114
|
-
status_code=
|
|
113
|
+
status_code=404,
|
|
115
114
|
)
|
|
116
115
|
|
|
117
116
|
dataset_version_globalid_str = request_body.version_id
|
|
@@ -124,7 +123,7 @@ async def create_experiment(
|
|
|
124
123
|
except ValueError:
|
|
125
124
|
raise HTTPException(
|
|
126
125
|
detail=f"DatasetVersion with ID {dataset_version_globalid_str} does not exist",
|
|
127
|
-
status_code=
|
|
126
|
+
status_code=404,
|
|
128
127
|
)
|
|
129
128
|
|
|
130
129
|
async with request.app.state.db() as session:
|
|
@@ -134,7 +133,7 @@ async def create_experiment(
|
|
|
134
133
|
if result is None:
|
|
135
134
|
raise HTTPException(
|
|
136
135
|
detail=f"Dataset with ID {dataset_globalid} does not exist",
|
|
137
|
-
status_code=
|
|
136
|
+
status_code=404,
|
|
138
137
|
)
|
|
139
138
|
dataset_name = result.name
|
|
140
139
|
if dataset_version_globalid_str is None:
|
|
@@ -147,7 +146,7 @@ async def create_experiment(
|
|
|
147
146
|
if not dataset_version:
|
|
148
147
|
raise HTTPException(
|
|
149
148
|
detail=f"Dataset {dataset_globalid} does not have any versions",
|
|
150
|
-
status_code=
|
|
149
|
+
status_code=404,
|
|
151
150
|
)
|
|
152
151
|
dataset_version_id = dataset_version.id
|
|
153
152
|
dataset_version_globalid = GlobalID("DatasetVersion", str(dataset_version_id))
|
|
@@ -159,7 +158,7 @@ async def create_experiment(
|
|
|
159
158
|
if not dataset_version:
|
|
160
159
|
raise HTTPException(
|
|
161
160
|
detail=f"DatasetVersion with ID {dataset_version_globalid} does not exist",
|
|
162
|
-
status_code=
|
|
161
|
+
status_code=404,
|
|
163
162
|
)
|
|
164
163
|
user_id: Optional[int] = None
|
|
165
164
|
if request.app.state.authentication_enabled and isinstance(request.user, PhoenixUser):
|
|
@@ -228,7 +227,7 @@ class GetExperimentResponseBody(ResponseBody[Experiment]):
|
|
|
228
227
|
operation_id="getExperiment",
|
|
229
228
|
summary="Get experiment by ID",
|
|
230
229
|
responses=add_errors_to_responses(
|
|
231
|
-
[{"status_code":
|
|
230
|
+
[{"status_code": 404, "description": "Experiment not found"}]
|
|
232
231
|
),
|
|
233
232
|
response_description="Experiment retrieved successfully",
|
|
234
233
|
)
|
|
@@ -239,7 +238,7 @@ async def get_experiment(request: Request, experiment_id: str) -> GetExperimentR
|
|
|
239
238
|
except ValueError:
|
|
240
239
|
raise HTTPException(
|
|
241
240
|
detail="Experiment with ID {experiment_globalid} does not exist",
|
|
242
|
-
status_code=
|
|
241
|
+
status_code=404,
|
|
243
242
|
)
|
|
244
243
|
|
|
245
244
|
async with request.app.state.db() as session:
|
|
@@ -250,7 +249,7 @@ async def get_experiment(request: Request, experiment_id: str) -> GetExperimentR
|
|
|
250
249
|
if not experiment:
|
|
251
250
|
raise HTTPException(
|
|
252
251
|
detail=f"Experiment with ID {experiment_globalid} does not exist",
|
|
253
|
-
status_code=
|
|
252
|
+
status_code=404,
|
|
254
253
|
)
|
|
255
254
|
|
|
256
255
|
dataset_globalid = GlobalID("Dataset", str(experiment.dataset_id))
|
|
@@ -289,7 +288,7 @@ async def list_experiments(
|
|
|
289
288
|
except ValueError:
|
|
290
289
|
raise HTTPException(
|
|
291
290
|
detail=f"Dataset with ID {dataset_gid} does not exist",
|
|
292
|
-
status_code=
|
|
291
|
+
status_code=404,
|
|
293
292
|
)
|
|
294
293
|
async with request.app.state.db() as session:
|
|
295
294
|
query = (
|
|
@@ -328,7 +327,7 @@ async def _get_experiment_runs_and_revisions(
|
|
|
328
327
|
) -> tuple[models.Experiment, tuple[models.ExperimentRun], tuple[models.DatasetExampleRevision]]:
|
|
329
328
|
experiment = await session.get(models.Experiment, experiment_rowid)
|
|
330
329
|
if not experiment:
|
|
331
|
-
raise HTTPException(detail="Experiment not found", status_code=
|
|
330
|
+
raise HTTPException(detail="Experiment not found", status_code=404)
|
|
332
331
|
revision_ids = (
|
|
333
332
|
select(func.max(models.DatasetExampleRevision.id))
|
|
334
333
|
.join(
|
|
@@ -377,7 +376,7 @@ async def _get_experiment_runs_and_revisions(
|
|
|
377
376
|
if not runs_and_revisions:
|
|
378
377
|
raise HTTPException(
|
|
379
378
|
detail="Experiment has no runs",
|
|
380
|
-
status_code=
|
|
379
|
+
status_code=404,
|
|
381
380
|
)
|
|
382
381
|
runs, revisions = zip(*runs_and_revisions)
|
|
383
382
|
return experiment, runs, revisions
|
|
@@ -390,7 +389,7 @@ async def _get_experiment_runs_and_revisions(
|
|
|
390
389
|
response_class=PlainTextResponse,
|
|
391
390
|
responses=add_errors_to_responses(
|
|
392
391
|
[
|
|
393
|
-
{"status_code":
|
|
392
|
+
{"status_code": 404, "description": "Experiment not found"},
|
|
394
393
|
]
|
|
395
394
|
),
|
|
396
395
|
)
|
|
@@ -404,7 +403,7 @@ async def get_experiment_json(
|
|
|
404
403
|
except ValueError:
|
|
405
404
|
raise HTTPException(
|
|
406
405
|
detail=f"Invalid experiment ID: {experiment_globalid}",
|
|
407
|
-
status_code=
|
|
406
|
+
status_code=422,
|
|
408
407
|
)
|
|
409
408
|
|
|
410
409
|
async with request.app.state.db() as session:
|
|
@@ -459,7 +458,7 @@ async def get_experiment_json(
|
|
|
459
458
|
"/experiments/{experiment_id}/csv",
|
|
460
459
|
operation_id="getExperimentCSV",
|
|
461
460
|
summary="Download experiment runs as a CSV file",
|
|
462
|
-
responses={**add_text_csv_content_to_responses(
|
|
461
|
+
responses={**add_text_csv_content_to_responses(200)},
|
|
463
462
|
)
|
|
464
463
|
async def get_experiment_csv(
|
|
465
464
|
request: Request,
|
|
@@ -471,7 +470,7 @@ async def get_experiment_csv(
|
|
|
471
470
|
except ValueError:
|
|
472
471
|
raise HTTPException(
|
|
473
472
|
detail=f"Invalid experiment ID: {experiment_globalid}",
|
|
474
|
-
status_code=
|
|
473
|
+
status_code=422,
|
|
475
474
|
)
|
|
476
475
|
|
|
477
476
|
async with request.app.state.db() as session:
|
|
@@ -4,12 +4,6 @@ from fastapi import APIRouter, Depends, HTTPException, Path, Query
|
|
|
4
4
|
from pydantic import Field
|
|
5
5
|
from sqlalchemy import select
|
|
6
6
|
from starlette.requests import Request
|
|
7
|
-
from starlette.status import (
|
|
8
|
-
HTTP_204_NO_CONTENT,
|
|
9
|
-
HTTP_403_FORBIDDEN,
|
|
10
|
-
HTTP_404_NOT_FOUND,
|
|
11
|
-
HTTP_422_UNPROCESSABLE_ENTITY,
|
|
12
|
-
)
|
|
13
7
|
from strawberry.relay import GlobalID
|
|
14
8
|
|
|
15
9
|
from phoenix.config import DEFAULT_PROJECT_NAME
|
|
@@ -70,7 +64,7 @@ class UpdateProjectResponseBody(ResponseBody[Project]):
|
|
|
70
64
|
response_description="A list of projects with pagination information", # noqa: E501
|
|
71
65
|
responses=add_errors_to_responses(
|
|
72
66
|
[
|
|
73
|
-
|
|
67
|
+
422,
|
|
74
68
|
]
|
|
75
69
|
),
|
|
76
70
|
)
|
|
@@ -115,7 +109,7 @@ async def get_projects(
|
|
|
115
109
|
except ValueError:
|
|
116
110
|
raise HTTPException(
|
|
117
111
|
detail=f"Invalid cursor format: {cursor}",
|
|
118
|
-
status_code=
|
|
112
|
+
status_code=422,
|
|
119
113
|
)
|
|
120
114
|
|
|
121
115
|
stmt = stmt.limit(limit + 1)
|
|
@@ -142,8 +136,8 @@ async def get_projects(
|
|
|
142
136
|
response_description="The requested project", # noqa: E501
|
|
143
137
|
responses=add_errors_to_responses(
|
|
144
138
|
[
|
|
145
|
-
|
|
146
|
-
|
|
139
|
+
404,
|
|
140
|
+
422,
|
|
147
141
|
]
|
|
148
142
|
),
|
|
149
143
|
)
|
|
@@ -182,7 +176,7 @@ async def get_project(
|
|
|
182
176
|
response_description="The newly created project", # noqa: E501
|
|
183
177
|
responses=add_errors_to_responses(
|
|
184
178
|
[
|
|
185
|
-
|
|
179
|
+
422,
|
|
186
180
|
]
|
|
187
181
|
),
|
|
188
182
|
)
|
|
@@ -223,9 +217,9 @@ async def create_project(
|
|
|
223
217
|
response_description="The updated project", # noqa: E501
|
|
224
218
|
responses=add_errors_to_responses(
|
|
225
219
|
[
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
220
|
+
403,
|
|
221
|
+
404,
|
|
222
|
+
422,
|
|
229
223
|
]
|
|
230
224
|
),
|
|
231
225
|
)
|
|
@@ -262,7 +256,7 @@ async def update_project(
|
|
|
262
256
|
role_name: UserRoleName = await session.scalar(stmt)
|
|
263
257
|
if role_name != "ADMIN" and role_name != "SYSTEM":
|
|
264
258
|
raise HTTPException(
|
|
265
|
-
status_code=
|
|
259
|
+
status_code=403,
|
|
266
260
|
detail="Only admins can update projects",
|
|
267
261
|
)
|
|
268
262
|
async with request.app.state.db() as session:
|
|
@@ -282,12 +276,12 @@ async def update_project(
|
|
|
282
276
|
summary="Delete a project by ID or name", # noqa: E501
|
|
283
277
|
description="Delete an existing project and all its associated data. The project identifier is either project ID or project name. The default project cannot be deleted. Note: When using a project name as the identifier, it cannot contain slash (/), question mark (?), or pound sign (#) characters.", # noqa: E501
|
|
284
278
|
response_description="No content returned on successful deletion", # noqa: E501
|
|
285
|
-
status_code=
|
|
279
|
+
status_code=204,
|
|
286
280
|
responses=add_errors_to_responses(
|
|
287
281
|
[
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
282
|
+
403,
|
|
283
|
+
404,
|
|
284
|
+
422,
|
|
291
285
|
]
|
|
292
286
|
),
|
|
293
287
|
)
|
|
@@ -322,7 +316,7 @@ async def delete_project(
|
|
|
322
316
|
role_name: UserRoleName = await session.scalar(stmt)
|
|
323
317
|
if role_name != "ADMIN" and role_name != "SYSTEM":
|
|
324
318
|
raise HTTPException(
|
|
325
|
-
status_code=
|
|
319
|
+
status_code=403,
|
|
326
320
|
detail="Only admins can delete projects",
|
|
327
321
|
)
|
|
328
322
|
async with request.app.state.db() as session:
|
|
@@ -331,7 +325,7 @@ async def delete_project(
|
|
|
331
325
|
# The default project must not be deleted - it's forbidden
|
|
332
326
|
if project.name == DEFAULT_PROJECT_NAME:
|
|
333
327
|
raise HTTPException(
|
|
334
|
-
status_code=
|
|
328
|
+
status_code=403,
|
|
335
329
|
detail="The default project cannot be deleted",
|
|
336
330
|
)
|
|
337
331
|
|
|
@@ -6,7 +6,6 @@ from pydantic import ValidationError, model_validator
|
|
|
6
6
|
from sqlalchemy import select
|
|
7
7
|
from sqlalchemy.sql import Select
|
|
8
8
|
from starlette.requests import Request
|
|
9
|
-
from starlette.status import HTTP_204_NO_CONTENT, HTTP_404_NOT_FOUND, HTTP_422_UNPROCESSABLE_ENTITY
|
|
10
9
|
from strawberry.relay import GlobalID
|
|
11
10
|
from typing_extensions import Self, TypeAlias, assert_never
|
|
12
11
|
|
|
@@ -110,7 +109,7 @@ router = APIRouter(tags=["prompts"])
|
|
|
110
109
|
response_description="A list of prompts with pagination information",
|
|
111
110
|
responses=add_errors_to_responses(
|
|
112
111
|
[
|
|
113
|
-
|
|
112
|
+
422,
|
|
114
113
|
]
|
|
115
114
|
),
|
|
116
115
|
)
|
|
@@ -154,7 +153,7 @@ async def get_prompts(
|
|
|
154
153
|
except ValueError:
|
|
155
154
|
raise HTTPException(
|
|
156
155
|
detail=f"Invalid cursor format: {cursor}",
|
|
157
|
-
status_code=
|
|
156
|
+
status_code=422,
|
|
158
157
|
)
|
|
159
158
|
|
|
160
159
|
query = query.limit(limit + 1)
|
|
@@ -181,7 +180,7 @@ async def get_prompts(
|
|
|
181
180
|
description="Retrieve all versions of a specific prompt with pagination support. Each prompt "
|
|
182
181
|
"can have multiple versions with different configurations.",
|
|
183
182
|
response_description="A list of prompt versions with pagination information",
|
|
184
|
-
responses=add_errors_to_responses([
|
|
183
|
+
responses=add_errors_to_responses([422, 404]),
|
|
185
184
|
response_model_by_alias=True,
|
|
186
185
|
response_model_exclude_defaults=True,
|
|
187
186
|
response_model_exclude_unset=True,
|
|
@@ -226,7 +225,7 @@ async def list_prompt_versions(
|
|
|
226
225
|
except ValueError:
|
|
227
226
|
raise HTTPException(
|
|
228
227
|
detail=f"Invalid cursor format: {cursor}",
|
|
229
|
-
status_code=
|
|
228
|
+
status_code=422,
|
|
230
229
|
)
|
|
231
230
|
|
|
232
231
|
query = query.limit(limit + 1)
|
|
@@ -255,8 +254,8 @@ async def list_prompt_versions(
|
|
|
255
254
|
response_description="The requested prompt version",
|
|
256
255
|
responses=add_errors_to_responses(
|
|
257
256
|
[
|
|
258
|
-
|
|
259
|
-
|
|
257
|
+
404,
|
|
258
|
+
422,
|
|
260
259
|
]
|
|
261
260
|
),
|
|
262
261
|
response_model_by_alias=True,
|
|
@@ -286,11 +285,11 @@ async def get_prompt_version_by_prompt_version_id(
|
|
|
286
285
|
PromptVersionNodeType.__name__,
|
|
287
286
|
)
|
|
288
287
|
except ValueError:
|
|
289
|
-
raise HTTPException(
|
|
288
|
+
raise HTTPException(422, "Invalid prompt version ID")
|
|
290
289
|
async with request.app.state.db() as session:
|
|
291
290
|
prompt_version = await session.get(models.PromptVersion, id_)
|
|
292
291
|
if prompt_version is None:
|
|
293
|
-
raise HTTPException(
|
|
292
|
+
raise HTTPException(404)
|
|
294
293
|
data = _prompt_version_from_orm_version(prompt_version)
|
|
295
294
|
return GetPromptResponseBody(data=data)
|
|
296
295
|
|
|
@@ -304,8 +303,8 @@ async def get_prompt_version_by_prompt_version_id(
|
|
|
304
303
|
response_description="The prompt version with the specified tag",
|
|
305
304
|
responses=add_errors_to_responses(
|
|
306
305
|
[
|
|
307
|
-
|
|
308
|
-
|
|
306
|
+
404,
|
|
307
|
+
422,
|
|
309
308
|
]
|
|
310
309
|
),
|
|
311
310
|
response_model_by_alias=True,
|
|
@@ -334,7 +333,7 @@ async def get_prompt_version_by_tag_name(
|
|
|
334
333
|
try:
|
|
335
334
|
name = Identifier.model_validate(tag_name)
|
|
336
335
|
except ValidationError:
|
|
337
|
-
raise HTTPException(
|
|
336
|
+
raise HTTPException(422, "Invalid tag name")
|
|
338
337
|
stmt = (
|
|
339
338
|
select(models.PromptVersion)
|
|
340
339
|
.join_from(models.PromptVersion, models.PromptVersionTag)
|
|
@@ -344,7 +343,7 @@ async def get_prompt_version_by_tag_name(
|
|
|
344
343
|
async with request.app.state.db() as session:
|
|
345
344
|
prompt_version: models.PromptVersion = await session.scalar(stmt)
|
|
346
345
|
if prompt_version is None:
|
|
347
|
-
raise HTTPException(
|
|
346
|
+
raise HTTPException(404)
|
|
348
347
|
data = _prompt_version_from_orm_version(prompt_version)
|
|
349
348
|
return GetPromptResponseBody(data=data)
|
|
350
349
|
|
|
@@ -357,8 +356,8 @@ async def get_prompt_version_by_tag_name(
|
|
|
357
356
|
response_description="The latest version of the specified prompt",
|
|
358
357
|
responses=add_errors_to_responses(
|
|
359
358
|
[
|
|
360
|
-
|
|
361
|
-
|
|
359
|
+
404,
|
|
360
|
+
422,
|
|
362
361
|
]
|
|
363
362
|
),
|
|
364
363
|
response_model_by_alias=True,
|
|
@@ -387,7 +386,7 @@ async def get_prompt_version_by_latest(
|
|
|
387
386
|
async with request.app.state.db() as session:
|
|
388
387
|
prompt_version: models.PromptVersion = await session.scalar(stmt)
|
|
389
388
|
if prompt_version is None:
|
|
390
|
-
raise HTTPException(
|
|
389
|
+
raise HTTPException(404)
|
|
391
390
|
data = _prompt_version_from_orm_version(prompt_version)
|
|
392
391
|
return GetPromptResponseBody(data=data)
|
|
393
392
|
|
|
@@ -401,7 +400,7 @@ async def get_prompt_version_by_latest(
|
|
|
401
400
|
response_description="The newly created prompt version",
|
|
402
401
|
responses=add_errors_to_responses(
|
|
403
402
|
[
|
|
404
|
-
|
|
403
|
+
422,
|
|
405
404
|
]
|
|
406
405
|
),
|
|
407
406
|
response_model_by_alias=True,
|
|
@@ -431,7 +430,7 @@ async def create_prompt(
|
|
|
431
430
|
or request_body.version.template_type != PromptTemplateType.CHAT
|
|
432
431
|
):
|
|
433
432
|
raise HTTPException(
|
|
434
|
-
|
|
433
|
+
422,
|
|
435
434
|
"Only CHAT template type is supported for prompts",
|
|
436
435
|
)
|
|
437
436
|
prompt = request_body.prompt
|
|
@@ -439,7 +438,7 @@ async def create_prompt(
|
|
|
439
438
|
name = Identifier.model_validate(prompt.name)
|
|
440
439
|
except ValidationError as e:
|
|
441
440
|
raise HTTPException(
|
|
442
|
-
|
|
441
|
+
422,
|
|
443
442
|
"Invalid name identifier for prompt: " + e.errors()[0]["msg"],
|
|
444
443
|
)
|
|
445
444
|
version = request_body.version
|
|
@@ -496,8 +495,8 @@ class GetPromptVersionTagsResponseBody(PaginatedResponseBody[PromptVersionTag]):
|
|
|
496
495
|
response_description="A list of tags associated with the prompt version",
|
|
497
496
|
responses=add_errors_to_responses(
|
|
498
497
|
[
|
|
499
|
-
|
|
500
|
-
|
|
498
|
+
404,
|
|
499
|
+
422,
|
|
501
500
|
]
|
|
502
501
|
),
|
|
503
502
|
response_model_by_alias=True,
|
|
@@ -537,7 +536,7 @@ async def list_prompt_version_tags(
|
|
|
537
536
|
PromptVersionNodeType.__name__,
|
|
538
537
|
)
|
|
539
538
|
except ValueError:
|
|
540
|
-
raise HTTPException(
|
|
539
|
+
raise HTTPException(422, "Invalid prompt version ID")
|
|
541
540
|
|
|
542
541
|
# Build the query for tags
|
|
543
542
|
stmt = (
|
|
@@ -560,7 +559,7 @@ async def list_prompt_version_tags(
|
|
|
560
559
|
except ValueError:
|
|
561
560
|
raise HTTPException(
|
|
562
561
|
detail=f"Invalid cursor format: {cursor}",
|
|
563
|
-
status_code=
|
|
562
|
+
status_code=422,
|
|
564
563
|
)
|
|
565
564
|
|
|
566
565
|
# Apply limit
|
|
@@ -571,7 +570,7 @@ async def list_prompt_version_tags(
|
|
|
571
570
|
|
|
572
571
|
# Check if prompt version exists
|
|
573
572
|
if not result:
|
|
574
|
-
raise HTTPException(
|
|
573
|
+
raise HTTPException(404, "Prompt version not found")
|
|
575
574
|
|
|
576
575
|
# Check if there are any tags
|
|
577
576
|
has_tags = any(id_ is not None for _, id_, _, _ in result)
|
|
@@ -610,11 +609,11 @@ async def list_prompt_version_tags(
|
|
|
610
609
|
description="Add a new tag to a specific prompt version. Tags help identify and categorize "
|
|
611
610
|
"different versions of a prompt.",
|
|
612
611
|
response_description="No content returned on successful tag creation",
|
|
613
|
-
status_code=
|
|
612
|
+
status_code=204,
|
|
614
613
|
responses=add_errors_to_responses(
|
|
615
614
|
[
|
|
616
|
-
|
|
617
|
-
|
|
615
|
+
404,
|
|
616
|
+
422,
|
|
618
617
|
]
|
|
619
618
|
),
|
|
620
619
|
response_model_by_alias=True,
|
|
@@ -647,7 +646,7 @@ async def create_prompt_version_tag(
|
|
|
647
646
|
PromptVersionNodeType.__name__,
|
|
648
647
|
)
|
|
649
648
|
except ValueError:
|
|
650
|
-
raise HTTPException(
|
|
649
|
+
raise HTTPException(422, "Invalid prompt version ID")
|
|
651
650
|
user_id: Optional[int] = None
|
|
652
651
|
if request.app.state.authentication_enabled:
|
|
653
652
|
assert isinstance(user := request.user, PhoenixUser)
|
|
@@ -655,7 +654,7 @@ async def create_prompt_version_tag(
|
|
|
655
654
|
async with request.app.state.db() as session:
|
|
656
655
|
prompt_id = await session.scalar(select(models.PromptVersion.prompt_id).filter_by(id=id_))
|
|
657
656
|
if prompt_id is None:
|
|
658
|
-
raise HTTPException(
|
|
657
|
+
raise HTTPException(404)
|
|
659
658
|
dialect = SupportedSQLDialect(session.bind.dialect.name)
|
|
660
659
|
values = dict(
|
|
661
660
|
name=request_body.name,
|
|
@@ -686,7 +685,7 @@ def _parse_prompt_identifier(
|
|
|
686
685
|
prompt_identifier: str,
|
|
687
686
|
) -> _PromptIdentifier:
|
|
688
687
|
if not prompt_identifier:
|
|
689
|
-
raise HTTPException(
|
|
688
|
+
raise HTTPException(422, "Invalid prompt identifier")
|
|
690
689
|
try:
|
|
691
690
|
prompt_id = from_global_id_with_expected_type(
|
|
692
691
|
GlobalID.from_id(prompt_identifier),
|
|
@@ -696,7 +695,7 @@ def _parse_prompt_identifier(
|
|
|
696
695
|
try:
|
|
697
696
|
return Identifier.model_validate(prompt_identifier)
|
|
698
697
|
except ValidationError:
|
|
699
|
-
raise HTTPException(
|
|
698
|
+
raise HTTPException(422, "Invalid prompt name")
|
|
700
699
|
return _PromptId(prompt_id)
|
|
701
700
|
|
|
702
701
|
|
|
@@ -7,7 +7,6 @@ from fastapi import APIRouter, Depends, HTTPException, Query
|
|
|
7
7
|
from pydantic import Field
|
|
8
8
|
from sqlalchemy import select
|
|
9
9
|
from starlette.requests import Request
|
|
10
|
-
from starlette.status import HTTP_404_NOT_FOUND
|
|
11
10
|
|
|
12
11
|
from phoenix.db import models
|
|
13
12
|
from phoenix.db.helpers import SupportedSQLDialect
|
|
@@ -39,9 +38,7 @@ class AnnotateSessionsResponseBody(ResponseBody[list[InsertedSessionAnnotation]]
|
|
|
39
38
|
dependencies=[Depends(is_not_locked)],
|
|
40
39
|
operation_id="annotateSessions",
|
|
41
40
|
summary="Create session annotations",
|
|
42
|
-
responses=add_errors_to_responses(
|
|
43
|
-
[{"status_code": HTTP_404_NOT_FOUND, "description": "Session not found"}]
|
|
44
|
-
),
|
|
41
|
+
responses=add_errors_to_responses([{"status_code": 404, "description": "Session not found"}]),
|
|
45
42
|
response_description="Session annotations inserted successfully",
|
|
46
43
|
include_in_schema=True,
|
|
47
44
|
)
|
|
@@ -88,7 +85,7 @@ async def annotate_sessions(
|
|
|
88
85
|
if missing_session_ids:
|
|
89
86
|
raise HTTPException(
|
|
90
87
|
detail=f"Sessions with IDs {', '.join(missing_session_ids)} do not exist.",
|
|
91
|
-
status_code=
|
|
88
|
+
status_code=404,
|
|
92
89
|
)
|
|
93
90
|
|
|
94
91
|
async with request.app.state.db() as session:
|