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.

Files changed (46) hide show
  1. {arize_phoenix-12.2.0.dist-info → arize_phoenix-12.4.0.dist-info}/METADATA +2 -1
  2. {arize_phoenix-12.2.0.dist-info → arize_phoenix-12.4.0.dist-info}/RECORD +45 -44
  3. phoenix/auth.py +19 -0
  4. phoenix/config.py +302 -53
  5. phoenix/db/README.md +546 -28
  6. phoenix/server/api/auth_messages.py +46 -0
  7. phoenix/server/api/routers/auth.py +21 -30
  8. phoenix/server/api/routers/oauth2.py +255 -46
  9. phoenix/server/api/routers/v1/__init__.py +2 -3
  10. phoenix/server/api/routers/v1/annotation_configs.py +12 -29
  11. phoenix/server/api/routers/v1/annotations.py +21 -22
  12. phoenix/server/api/routers/v1/datasets.py +38 -56
  13. phoenix/server/api/routers/v1/documents.py +2 -3
  14. phoenix/server/api/routers/v1/evaluations.py +12 -24
  15. phoenix/server/api/routers/v1/experiment_evaluations.py +2 -3
  16. phoenix/server/api/routers/v1/experiment_runs.py +9 -10
  17. phoenix/server/api/routers/v1/experiments.py +16 -17
  18. phoenix/server/api/routers/v1/projects.py +15 -21
  19. phoenix/server/api/routers/v1/prompts.py +30 -31
  20. phoenix/server/api/routers/v1/sessions.py +2 -5
  21. phoenix/server/api/routers/v1/spans.py +35 -26
  22. phoenix/server/api/routers/v1/traces.py +11 -19
  23. phoenix/server/api/routers/v1/users.py +14 -23
  24. phoenix/server/api/routers/v1/utils.py +3 -7
  25. phoenix/server/app.py +6 -2
  26. phoenix/server/authorization.py +2 -3
  27. phoenix/server/bearer_auth.py +4 -5
  28. phoenix/server/cost_tracking/model_cost_manifest.json +54 -54
  29. phoenix/server/oauth2.py +174 -9
  30. phoenix/server/static/.vite/manifest.json +39 -39
  31. phoenix/server/static/assets/{components-BG6v0EM8.js → components-BvsExS75.js} +422 -387
  32. phoenix/server/static/assets/{index-CSVcULw1.js → index-iq8WDxat.js} +12 -12
  33. phoenix/server/static/assets/{pages-DgaM7kpM.js → pages-Ckg4SLQ9.js} +542 -488
  34. phoenix/server/static/assets/vendor-D2eEI-6h.js +914 -0
  35. phoenix/server/static/assets/{vendor-arizeai-DlOj0PQQ.js → vendor-arizeai-kfOei7nf.js} +2 -2
  36. phoenix/server/static/assets/{vendor-codemirror-B2PHH5yZ.js → vendor-codemirror-1bq_t1Ec.js} +3 -3
  37. phoenix/server/static/assets/{vendor-recharts-CKsi4IjN.js → vendor-recharts-DQ4xfrf4.js} +1 -1
  38. phoenix/server/static/assets/{vendor-shiki-DN26BkKE.js → vendor-shiki-GGmcIQxA.js} +1 -1
  39. phoenix/server/templates/index.html +1 -0
  40. phoenix/trace/attributes.py +80 -13
  41. phoenix/version.py +1 -1
  42. phoenix/server/static/assets/vendor-BqTEkGQU.js +0 -903
  43. {arize_phoenix-12.2.0.dist-info → arize_phoenix-12.4.0.dist-info}/WHEEL +0 -0
  44. {arize_phoenix-12.2.0.dist-info → arize_phoenix-12.4.0.dist-info}/entry_points.txt +0 -0
  45. {arize_phoenix-12.2.0.dist-info → arize_phoenix-12.4.0.dist-info}/licenses/IP_NOTICE +0 -0
  46. {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": HTTP_404_NOT_FOUND,
62
+ "status_code": 404,
64
63
  "description": "Experiment or dataset example not found",
65
64
  },
66
65
  {
67
- "status_code": HTTP_409_CONFLICT,
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=HTTP_404_NOT_FOUND,
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=HTTP_404_NOT_FOUND,
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=HTTP_409_CONFLICT,
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": HTTP_404_NOT_FOUND, "description": "Experiment not found"},
145
- {"status_code": HTTP_422_UNPROCESSABLE_ENTITY, "description": "Invalid cursor format"},
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=HTTP_404_NOT_FOUND,
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=HTTP_422_UNPROCESSABLE_ENTITY,
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": HTTP_404_NOT_FOUND, "description": "Dataset or DatasetVersion not found"}]
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=HTTP_404_NOT_FOUND,
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=HTTP_404_NOT_FOUND,
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=HTTP_404_NOT_FOUND,
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=HTTP_404_NOT_FOUND,
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=HTTP_404_NOT_FOUND,
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": HTTP_404_NOT_FOUND, "description": "Experiment not found"}]
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=HTTP_404_NOT_FOUND,
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=HTTP_404_NOT_FOUND,
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=HTTP_404_NOT_FOUND,
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=HTTP_404_NOT_FOUND)
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=HTTP_404_NOT_FOUND,
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": HTTP_404_NOT_FOUND, "description": "Experiment not found"},
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=HTTP_422_UNPROCESSABLE_ENTITY,
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(HTTP_200_OK)},
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=HTTP_422_UNPROCESSABLE_ENTITY,
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
- HTTP_422_UNPROCESSABLE_ENTITY,
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=HTTP_422_UNPROCESSABLE_ENTITY,
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
- HTTP_404_NOT_FOUND,
146
- HTTP_422_UNPROCESSABLE_ENTITY,
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
- HTTP_422_UNPROCESSABLE_ENTITY,
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
- HTTP_403_FORBIDDEN,
227
- HTTP_404_NOT_FOUND,
228
- HTTP_422_UNPROCESSABLE_ENTITY,
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=HTTP_403_FORBIDDEN,
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=HTTP_204_NO_CONTENT,
279
+ status_code=204,
286
280
  responses=add_errors_to_responses(
287
281
  [
288
- HTTP_403_FORBIDDEN,
289
- HTTP_404_NOT_FOUND,
290
- HTTP_422_UNPROCESSABLE_ENTITY,
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=HTTP_403_FORBIDDEN,
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=HTTP_403_FORBIDDEN,
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
- HTTP_422_UNPROCESSABLE_ENTITY,
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=HTTP_422_UNPROCESSABLE_ENTITY,
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([HTTP_422_UNPROCESSABLE_ENTITY, HTTP_404_NOT_FOUND]),
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=HTTP_422_UNPROCESSABLE_ENTITY,
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
- HTTP_404_NOT_FOUND,
259
- HTTP_422_UNPROCESSABLE_ENTITY,
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(HTTP_422_UNPROCESSABLE_ENTITY, "Invalid prompt version ID")
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(HTTP_404_NOT_FOUND)
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
- HTTP_404_NOT_FOUND,
308
- HTTP_422_UNPROCESSABLE_ENTITY,
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(HTTP_422_UNPROCESSABLE_ENTITY, "Invalid tag name")
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(HTTP_404_NOT_FOUND)
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
- HTTP_404_NOT_FOUND,
361
- HTTP_422_UNPROCESSABLE_ENTITY,
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(HTTP_404_NOT_FOUND)
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
- HTTP_422_UNPROCESSABLE_ENTITY,
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
- HTTP_422_UNPROCESSABLE_ENTITY,
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
- HTTP_422_UNPROCESSABLE_ENTITY,
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
- HTTP_404_NOT_FOUND,
500
- HTTP_422_UNPROCESSABLE_ENTITY,
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(HTTP_422_UNPROCESSABLE_ENTITY, "Invalid prompt version ID")
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=HTTP_422_UNPROCESSABLE_ENTITY,
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(HTTP_404_NOT_FOUND, "Prompt version not found")
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=HTTP_204_NO_CONTENT,
612
+ status_code=204,
614
613
  responses=add_errors_to_responses(
615
614
  [
616
- HTTP_404_NOT_FOUND,
617
- HTTP_422_UNPROCESSABLE_ENTITY,
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(HTTP_422_UNPROCESSABLE_ENTITY, "Invalid prompt version ID")
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(HTTP_404_NOT_FOUND)
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(HTTP_422_UNPROCESSABLE_ENTITY, "Invalid prompt identifier")
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(HTTP_422_UNPROCESSABLE_ENTITY, "Invalid prompt name")
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=HTTP_404_NOT_FOUND,
88
+ status_code=404,
92
89
  )
93
90
 
94
91
  async with request.app.state.db() as session: