arize-phoenix 10.0.4__py3-none-any.whl → 12.28.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (276) hide show
  1. {arize_phoenix-10.0.4.dist-info → arize_phoenix-12.28.1.dist-info}/METADATA +124 -72
  2. arize_phoenix-12.28.1.dist-info/RECORD +499 -0
  3. {arize_phoenix-10.0.4.dist-info → arize_phoenix-12.28.1.dist-info}/WHEEL +1 -1
  4. {arize_phoenix-10.0.4.dist-info → arize_phoenix-12.28.1.dist-info}/licenses/IP_NOTICE +1 -1
  5. phoenix/__generated__/__init__.py +0 -0
  6. phoenix/__generated__/classification_evaluator_configs/__init__.py +20 -0
  7. phoenix/__generated__/classification_evaluator_configs/_document_relevance_classification_evaluator_config.py +17 -0
  8. phoenix/__generated__/classification_evaluator_configs/_hallucination_classification_evaluator_config.py +17 -0
  9. phoenix/__generated__/classification_evaluator_configs/_models.py +18 -0
  10. phoenix/__generated__/classification_evaluator_configs/_tool_selection_classification_evaluator_config.py +17 -0
  11. phoenix/__init__.py +5 -4
  12. phoenix/auth.py +39 -2
  13. phoenix/config.py +1763 -91
  14. phoenix/datetime_utils.py +120 -2
  15. phoenix/db/README.md +595 -25
  16. phoenix/db/bulk_inserter.py +145 -103
  17. phoenix/db/engines.py +140 -33
  18. phoenix/db/enums.py +3 -12
  19. phoenix/db/facilitator.py +302 -35
  20. phoenix/db/helpers.py +1000 -65
  21. phoenix/db/iam_auth.py +64 -0
  22. phoenix/db/insertion/dataset.py +135 -2
  23. phoenix/db/insertion/document_annotation.py +9 -6
  24. phoenix/db/insertion/evaluation.py +2 -3
  25. phoenix/db/insertion/helpers.py +17 -2
  26. phoenix/db/insertion/session_annotation.py +176 -0
  27. phoenix/db/insertion/span.py +15 -11
  28. phoenix/db/insertion/span_annotation.py +3 -4
  29. phoenix/db/insertion/trace_annotation.py +3 -4
  30. phoenix/db/insertion/types.py +50 -20
  31. phoenix/db/migrations/versions/01a8342c9cdf_add_user_id_on_datasets.py +40 -0
  32. phoenix/db/migrations/versions/0df286449799_add_session_annotations_table.py +105 -0
  33. phoenix/db/migrations/versions/272b66ff50f8_drop_single_indices.py +119 -0
  34. phoenix/db/migrations/versions/58228d933c91_dataset_labels.py +67 -0
  35. phoenix/db/migrations/versions/699f655af132_experiment_tags.py +57 -0
  36. phoenix/db/migrations/versions/735d3d93c33e_add_composite_indices.py +41 -0
  37. phoenix/db/migrations/versions/a20694b15f82_cost.py +196 -0
  38. phoenix/db/migrations/versions/ab513d89518b_add_user_id_on_dataset_versions.py +40 -0
  39. phoenix/db/migrations/versions/d0690a79ea51_users_on_experiments.py +40 -0
  40. phoenix/db/migrations/versions/deb2c81c0bb2_dataset_splits.py +139 -0
  41. phoenix/db/migrations/versions/e76cbd66ffc3_add_experiments_dataset_examples.py +87 -0
  42. phoenix/db/models.py +669 -56
  43. phoenix/db/pg_config.py +10 -0
  44. phoenix/db/types/model_provider.py +4 -0
  45. phoenix/db/types/token_price_customization.py +29 -0
  46. phoenix/db/types/trace_retention.py +23 -15
  47. phoenix/experiments/evaluators/utils.py +3 -3
  48. phoenix/experiments/functions.py +160 -52
  49. phoenix/experiments/tracing.py +2 -2
  50. phoenix/experiments/types.py +1 -1
  51. phoenix/inferences/inferences.py +1 -2
  52. phoenix/server/api/auth.py +38 -7
  53. phoenix/server/api/auth_messages.py +46 -0
  54. phoenix/server/api/context.py +100 -4
  55. phoenix/server/api/dataloaders/__init__.py +79 -5
  56. phoenix/server/api/dataloaders/annotation_configs_by_project.py +31 -0
  57. phoenix/server/api/dataloaders/annotation_summaries.py +60 -8
  58. phoenix/server/api/dataloaders/average_experiment_repeated_run_group_latency.py +50 -0
  59. phoenix/server/api/dataloaders/average_experiment_run_latency.py +17 -24
  60. phoenix/server/api/dataloaders/cache/two_tier_cache.py +1 -2
  61. phoenix/server/api/dataloaders/dataset_dataset_splits.py +52 -0
  62. phoenix/server/api/dataloaders/dataset_example_revisions.py +0 -1
  63. phoenix/server/api/dataloaders/dataset_example_splits.py +40 -0
  64. phoenix/server/api/dataloaders/dataset_examples_and_versions_by_experiment_run.py +47 -0
  65. phoenix/server/api/dataloaders/dataset_labels.py +36 -0
  66. phoenix/server/api/dataloaders/document_evaluation_summaries.py +2 -2
  67. phoenix/server/api/dataloaders/document_evaluations.py +6 -9
  68. phoenix/server/api/dataloaders/experiment_annotation_summaries.py +88 -34
  69. phoenix/server/api/dataloaders/experiment_dataset_splits.py +43 -0
  70. phoenix/server/api/dataloaders/experiment_error_rates.py +21 -28
  71. phoenix/server/api/dataloaders/experiment_repeated_run_group_annotation_summaries.py +77 -0
  72. phoenix/server/api/dataloaders/experiment_repeated_run_groups.py +57 -0
  73. phoenix/server/api/dataloaders/experiment_runs_by_experiment_and_example.py +44 -0
  74. phoenix/server/api/dataloaders/last_used_times_by_generative_model_id.py +35 -0
  75. phoenix/server/api/dataloaders/latency_ms_quantile.py +40 -8
  76. phoenix/server/api/dataloaders/record_counts.py +37 -10
  77. phoenix/server/api/dataloaders/session_annotations_by_session.py +29 -0
  78. phoenix/server/api/dataloaders/span_cost_by_span.py +24 -0
  79. phoenix/server/api/dataloaders/span_cost_detail_summary_entries_by_generative_model.py +56 -0
  80. phoenix/server/api/dataloaders/span_cost_detail_summary_entries_by_project_session.py +57 -0
  81. phoenix/server/api/dataloaders/span_cost_detail_summary_entries_by_span.py +43 -0
  82. phoenix/server/api/dataloaders/span_cost_detail_summary_entries_by_trace.py +56 -0
  83. phoenix/server/api/dataloaders/span_cost_details_by_span_cost.py +27 -0
  84. phoenix/server/api/dataloaders/span_cost_summary_by_experiment.py +57 -0
  85. phoenix/server/api/dataloaders/span_cost_summary_by_experiment_repeated_run_group.py +64 -0
  86. phoenix/server/api/dataloaders/span_cost_summary_by_experiment_run.py +58 -0
  87. phoenix/server/api/dataloaders/span_cost_summary_by_generative_model.py +55 -0
  88. phoenix/server/api/dataloaders/span_cost_summary_by_project.py +152 -0
  89. phoenix/server/api/dataloaders/span_cost_summary_by_project_session.py +56 -0
  90. phoenix/server/api/dataloaders/span_cost_summary_by_trace.py +55 -0
  91. phoenix/server/api/dataloaders/span_costs.py +29 -0
  92. phoenix/server/api/dataloaders/table_fields.py +2 -2
  93. phoenix/server/api/dataloaders/token_prices_by_model.py +30 -0
  94. phoenix/server/api/dataloaders/trace_annotations_by_trace.py +27 -0
  95. phoenix/server/api/dataloaders/types.py +29 -0
  96. phoenix/server/api/exceptions.py +11 -1
  97. phoenix/server/api/helpers/dataset_helpers.py +5 -1
  98. phoenix/server/api/helpers/playground_clients.py +1243 -292
  99. phoenix/server/api/helpers/playground_registry.py +2 -2
  100. phoenix/server/api/helpers/playground_spans.py +8 -4
  101. phoenix/server/api/helpers/playground_users.py +26 -0
  102. phoenix/server/api/helpers/prompts/conversions/aws.py +83 -0
  103. phoenix/server/api/helpers/prompts/conversions/google.py +103 -0
  104. phoenix/server/api/helpers/prompts/models.py +205 -22
  105. phoenix/server/api/input_types/{SpanAnnotationFilter.py → AnnotationFilter.py} +22 -14
  106. phoenix/server/api/input_types/ChatCompletionInput.py +6 -2
  107. phoenix/server/api/input_types/CreateProjectInput.py +27 -0
  108. phoenix/server/api/input_types/CreateProjectSessionAnnotationInput.py +37 -0
  109. phoenix/server/api/input_types/DatasetFilter.py +17 -0
  110. phoenix/server/api/input_types/ExperimentRunSort.py +237 -0
  111. phoenix/server/api/input_types/GenerativeCredentialInput.py +9 -0
  112. phoenix/server/api/input_types/GenerativeModelInput.py +5 -0
  113. phoenix/server/api/input_types/ProjectSessionSort.py +161 -1
  114. phoenix/server/api/input_types/PromptFilter.py +14 -0
  115. phoenix/server/api/input_types/PromptVersionInput.py +52 -1
  116. phoenix/server/api/input_types/SpanSort.py +44 -7
  117. phoenix/server/api/input_types/TimeBinConfig.py +23 -0
  118. phoenix/server/api/input_types/UpdateAnnotationInput.py +34 -0
  119. phoenix/server/api/input_types/UserRoleInput.py +1 -0
  120. phoenix/server/api/mutations/__init__.py +10 -0
  121. phoenix/server/api/mutations/annotation_config_mutations.py +8 -8
  122. phoenix/server/api/mutations/api_key_mutations.py +19 -23
  123. phoenix/server/api/mutations/chat_mutations.py +154 -47
  124. phoenix/server/api/mutations/dataset_label_mutations.py +243 -0
  125. phoenix/server/api/mutations/dataset_mutations.py +21 -16
  126. phoenix/server/api/mutations/dataset_split_mutations.py +351 -0
  127. phoenix/server/api/mutations/experiment_mutations.py +2 -2
  128. phoenix/server/api/mutations/export_events_mutations.py +3 -3
  129. phoenix/server/api/mutations/model_mutations.py +210 -0
  130. phoenix/server/api/mutations/project_mutations.py +49 -10
  131. phoenix/server/api/mutations/project_session_annotations_mutations.py +158 -0
  132. phoenix/server/api/mutations/project_trace_retention_policy_mutations.py +8 -4
  133. phoenix/server/api/mutations/prompt_label_mutations.py +74 -65
  134. phoenix/server/api/mutations/prompt_mutations.py +65 -129
  135. phoenix/server/api/mutations/prompt_version_tag_mutations.py +11 -8
  136. phoenix/server/api/mutations/span_annotations_mutations.py +15 -10
  137. phoenix/server/api/mutations/trace_annotations_mutations.py +14 -10
  138. phoenix/server/api/mutations/trace_mutations.py +47 -3
  139. phoenix/server/api/mutations/user_mutations.py +66 -41
  140. phoenix/server/api/queries.py +768 -293
  141. phoenix/server/api/routers/__init__.py +2 -2
  142. phoenix/server/api/routers/auth.py +154 -88
  143. phoenix/server/api/routers/ldap.py +229 -0
  144. phoenix/server/api/routers/oauth2.py +369 -106
  145. phoenix/server/api/routers/v1/__init__.py +24 -4
  146. phoenix/server/api/routers/v1/annotation_configs.py +23 -31
  147. phoenix/server/api/routers/v1/annotations.py +481 -17
  148. phoenix/server/api/routers/v1/datasets.py +395 -81
  149. phoenix/server/api/routers/v1/documents.py +142 -0
  150. phoenix/server/api/routers/v1/evaluations.py +24 -31
  151. phoenix/server/api/routers/v1/experiment_evaluations.py +19 -8
  152. phoenix/server/api/routers/v1/experiment_runs.py +337 -59
  153. phoenix/server/api/routers/v1/experiments.py +479 -48
  154. phoenix/server/api/routers/v1/models.py +7 -0
  155. phoenix/server/api/routers/v1/projects.py +18 -49
  156. phoenix/server/api/routers/v1/prompts.py +54 -40
  157. phoenix/server/api/routers/v1/sessions.py +108 -0
  158. phoenix/server/api/routers/v1/spans.py +1091 -81
  159. phoenix/server/api/routers/v1/traces.py +132 -78
  160. phoenix/server/api/routers/v1/users.py +389 -0
  161. phoenix/server/api/routers/v1/utils.py +3 -7
  162. phoenix/server/api/subscriptions.py +305 -88
  163. phoenix/server/api/types/Annotation.py +90 -23
  164. phoenix/server/api/types/ApiKey.py +13 -17
  165. phoenix/server/api/types/AuthMethod.py +1 -0
  166. phoenix/server/api/types/ChatCompletionSubscriptionPayload.py +1 -0
  167. phoenix/server/api/types/CostBreakdown.py +12 -0
  168. phoenix/server/api/types/Dataset.py +226 -72
  169. phoenix/server/api/types/DatasetExample.py +88 -18
  170. phoenix/server/api/types/DatasetExperimentAnnotationSummary.py +10 -0
  171. phoenix/server/api/types/DatasetLabel.py +57 -0
  172. phoenix/server/api/types/DatasetSplit.py +98 -0
  173. phoenix/server/api/types/DatasetVersion.py +49 -4
  174. phoenix/server/api/types/DocumentAnnotation.py +212 -0
  175. phoenix/server/api/types/Experiment.py +264 -59
  176. phoenix/server/api/types/ExperimentComparison.py +5 -10
  177. phoenix/server/api/types/ExperimentRepeatedRunGroup.py +155 -0
  178. phoenix/server/api/types/ExperimentRepeatedRunGroupAnnotationSummary.py +9 -0
  179. phoenix/server/api/types/ExperimentRun.py +169 -65
  180. phoenix/server/api/types/ExperimentRunAnnotation.py +158 -39
  181. phoenix/server/api/types/GenerativeModel.py +245 -3
  182. phoenix/server/api/types/GenerativeProvider.py +70 -11
  183. phoenix/server/api/types/{Model.py → InferenceModel.py} +1 -1
  184. phoenix/server/api/types/ModelInterface.py +16 -0
  185. phoenix/server/api/types/PlaygroundModel.py +20 -0
  186. phoenix/server/api/types/Project.py +1278 -216
  187. phoenix/server/api/types/ProjectSession.py +188 -28
  188. phoenix/server/api/types/ProjectSessionAnnotation.py +187 -0
  189. phoenix/server/api/types/ProjectTraceRetentionPolicy.py +1 -1
  190. phoenix/server/api/types/Prompt.py +119 -39
  191. phoenix/server/api/types/PromptLabel.py +42 -25
  192. phoenix/server/api/types/PromptVersion.py +11 -8
  193. phoenix/server/api/types/PromptVersionTag.py +65 -25
  194. phoenix/server/api/types/ServerStatus.py +6 -0
  195. phoenix/server/api/types/Span.py +167 -123
  196. phoenix/server/api/types/SpanAnnotation.py +189 -42
  197. phoenix/server/api/types/SpanCostDetailSummaryEntry.py +10 -0
  198. phoenix/server/api/types/SpanCostSummary.py +10 -0
  199. phoenix/server/api/types/SystemApiKey.py +65 -1
  200. phoenix/server/api/types/TokenPrice.py +16 -0
  201. phoenix/server/api/types/TokenUsage.py +3 -3
  202. phoenix/server/api/types/Trace.py +223 -51
  203. phoenix/server/api/types/TraceAnnotation.py +149 -50
  204. phoenix/server/api/types/User.py +137 -32
  205. phoenix/server/api/types/UserApiKey.py +73 -26
  206. phoenix/server/api/types/node.py +10 -0
  207. phoenix/server/api/types/pagination.py +11 -2
  208. phoenix/server/app.py +290 -45
  209. phoenix/server/authorization.py +38 -3
  210. phoenix/server/bearer_auth.py +34 -24
  211. phoenix/server/cost_tracking/cost_details_calculator.py +196 -0
  212. phoenix/server/cost_tracking/cost_model_lookup.py +179 -0
  213. phoenix/server/cost_tracking/helpers.py +68 -0
  214. phoenix/server/cost_tracking/model_cost_manifest.json +3657 -830
  215. phoenix/server/cost_tracking/regex_specificity.py +397 -0
  216. phoenix/server/cost_tracking/token_cost_calculator.py +57 -0
  217. phoenix/server/daemons/__init__.py +0 -0
  218. phoenix/server/daemons/db_disk_usage_monitor.py +214 -0
  219. phoenix/server/daemons/generative_model_store.py +103 -0
  220. phoenix/server/daemons/span_cost_calculator.py +99 -0
  221. phoenix/server/dml_event.py +17 -0
  222. phoenix/server/dml_event_handler.py +5 -0
  223. phoenix/server/email/sender.py +56 -3
  224. phoenix/server/email/templates/db_disk_usage_notification.html +19 -0
  225. phoenix/server/email/types.py +11 -0
  226. phoenix/server/experiments/__init__.py +0 -0
  227. phoenix/server/experiments/utils.py +14 -0
  228. phoenix/server/grpc_server.py +11 -11
  229. phoenix/server/jwt_store.py +17 -15
  230. phoenix/server/ldap.py +1449 -0
  231. phoenix/server/main.py +26 -10
  232. phoenix/server/oauth2.py +330 -12
  233. phoenix/server/prometheus.py +66 -6
  234. phoenix/server/rate_limiters.py +4 -9
  235. phoenix/server/retention.py +33 -20
  236. phoenix/server/session_filters.py +49 -0
  237. phoenix/server/static/.vite/manifest.json +55 -51
  238. phoenix/server/static/assets/components-BreFUQQa.js +6702 -0
  239. phoenix/server/static/assets/{index-E0M82BdE.js → index-CTQoemZv.js} +140 -56
  240. phoenix/server/static/assets/pages-DBE5iYM3.js +9524 -0
  241. phoenix/server/static/assets/vendor-BGzfc4EU.css +1 -0
  242. phoenix/server/static/assets/vendor-DCE4v-Ot.js +920 -0
  243. phoenix/server/static/assets/vendor-codemirror-D5f205eT.js +25 -0
  244. phoenix/server/static/assets/vendor-recharts-V9cwpXsm.js +37 -0
  245. phoenix/server/static/assets/vendor-shiki-Do--csgv.js +5 -0
  246. phoenix/server/static/assets/vendor-three-CmB8bl_y.js +3840 -0
  247. phoenix/server/templates/index.html +40 -6
  248. phoenix/server/thread_server.py +1 -2
  249. phoenix/server/types.py +14 -4
  250. phoenix/server/utils.py +74 -0
  251. phoenix/session/client.py +56 -3
  252. phoenix/session/data_extractor.py +5 -0
  253. phoenix/session/evaluation.py +14 -5
  254. phoenix/session/session.py +45 -9
  255. phoenix/settings.py +5 -0
  256. phoenix/trace/attributes.py +80 -13
  257. phoenix/trace/dsl/helpers.py +90 -1
  258. phoenix/trace/dsl/query.py +8 -6
  259. phoenix/trace/projects.py +5 -0
  260. phoenix/utilities/template_formatters.py +1 -1
  261. phoenix/version.py +1 -1
  262. arize_phoenix-10.0.4.dist-info/RECORD +0 -405
  263. phoenix/server/api/types/Evaluation.py +0 -39
  264. phoenix/server/cost_tracking/cost_lookup.py +0 -255
  265. phoenix/server/static/assets/components-DULKeDfL.js +0 -4365
  266. phoenix/server/static/assets/pages-Cl0A-0U2.js +0 -7430
  267. phoenix/server/static/assets/vendor-WIZid84E.css +0 -1
  268. phoenix/server/static/assets/vendor-arizeai-Dy-0mSNw.js +0 -649
  269. phoenix/server/static/assets/vendor-codemirror-DBtifKNr.js +0 -33
  270. phoenix/server/static/assets/vendor-oB4u9zuV.js +0 -905
  271. phoenix/server/static/assets/vendor-recharts-D-T4KPz2.js +0 -59
  272. phoenix/server/static/assets/vendor-shiki-BMn4O_9F.js +0 -5
  273. phoenix/server/static/assets/vendor-three-C5WAXd5r.js +0 -2998
  274. phoenix/utilities/deprecation.py +0 -31
  275. {arize_phoenix-10.0.4.dist-info → arize_phoenix-12.28.1.dist-info}/entry_points.txt +0 -0
  276. {arize_phoenix-10.0.4.dist-info → arize_phoenix-12.28.1.dist-info}/licenses/LICENSE +0 -0
@@ -1,20 +1,22 @@
1
1
  from fastapi import APIRouter, Depends, HTTPException, Request
2
2
  from fastapi.security import APIKeyHeader
3
- from starlette.status import HTTP_403_FORBIDDEN
4
3
 
5
- from phoenix.server.bearer_auth import is_authenticated
4
+ from phoenix.server.bearer_auth import PhoenixUser, is_authenticated
6
5
 
7
6
  from .annotation_configs import router as annotation_configs_router
8
7
  from .annotations import router as annotations_router
9
8
  from .datasets import router as datasets_router
9
+ from .documents import router as documents_router
10
10
  from .evaluations import router as evaluations_router
11
11
  from .experiment_evaluations import router as experiment_evaluations_router
12
12
  from .experiment_runs import router as experiment_runs_router
13
13
  from .experiments import router as experiments_router
14
14
  from .projects import router as projects_router
15
15
  from .prompts import router as prompts_router
16
+ from .sessions import router as sessions_router
16
17
  from .spans import router as spans_router
17
18
  from .traces import router as traces_router
19
+ from .users import router as users_router
18
20
  from .utils import add_errors_to_responses
19
21
 
20
22
  REST_API_VERSION = "1.0"
@@ -27,7 +29,21 @@ async def prevent_access_in_read_only_mode(request: Request) -> None:
27
29
  if request.app.state.read_only:
28
30
  raise HTTPException(
29
31
  detail="The Phoenix REST API is disabled in read-only mode.",
30
- status_code=HTTP_403_FORBIDDEN,
32
+ status_code=403,
33
+ )
34
+
35
+
36
+ async def restrict_access_by_viewers(request: Request) -> None:
37
+ """
38
+ Prevents access to the REST API for viewers, except for GET requests
39
+ and specific allowed POST routes.
40
+ """
41
+ if request.method == "GET":
42
+ return
43
+ if isinstance(request.user, PhoenixUser) and request.user.is_viewer:
44
+ raise HTTPException(
45
+ status_code=403,
46
+ detail="Viewers cannot perform this action.",
31
47
  )
32
48
 
33
49
 
@@ -48,13 +64,14 @@ def create_v1_router(authentication_enabled: bool) -> APIRouter:
48
64
  )
49
65
  )
50
66
  dependencies.append(Depends(is_authenticated))
67
+ dependencies.append(Depends(restrict_access_by_viewers))
51
68
 
52
69
  router = APIRouter(
53
70
  prefix="/v1",
54
71
  dependencies=dependencies,
55
72
  responses=add_errors_to_responses(
56
73
  [
57
- HTTP_403_FORBIDDEN # adds a 403 response to routes in the generated OpenAPI schema
74
+ 403 # adds a 403 response to routes in the generated OpenAPI schema
58
75
  ]
59
76
  ),
60
77
  )
@@ -69,4 +86,7 @@ def create_v1_router(authentication_enabled: bool) -> APIRouter:
69
86
  router.include_router(evaluations_router)
70
87
  router.include_router(prompts_router)
71
88
  router.include_router(projects_router)
89
+ router.include_router(sessions_router)
90
+ router.include_router(documents_router)
91
+ router.include_router(users_router)
72
92
  return router
@@ -1,17 +1,12 @@
1
1
  import logging
2
2
  from typing import Annotated, List, Literal, Optional, Union
3
3
 
4
- from fastapi import APIRouter, HTTPException, Path, Query
4
+ from fastapi import APIRouter, Depends, HTTPException, Path, Query
5
5
  from pydantic import Field, RootModel
6
6
  from sqlalchemy import delete, 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 (
11
- HTTP_400_BAD_REQUEST,
12
- HTTP_404_NOT_FOUND,
13
- HTTP_409_CONFLICT,
14
- )
15
10
  from strawberry.relay import GlobalID
16
11
  from typing_extensions import TypeAlias, assert_never
17
12
 
@@ -44,6 +39,7 @@ from phoenix.server.api.types.AnnotationConfig import (
44
39
  from phoenix.server.api.types.AnnotationConfig import (
45
40
  FreeformAnnotationConfig as FreeformAnnotationConfigType,
46
41
  )
42
+ from phoenix.server.authorization import is_not_locked
47
43
 
48
44
  logger = logging.getLogger(__name__)
49
45
 
@@ -205,7 +201,7 @@ async def list_annotation_configs(
205
201
  except ValueError:
206
202
  raise HTTPException(
207
203
  detail=f"Invalid cursor: {cursor}",
208
- status_code=HTTP_400_BAD_REQUEST,
204
+ status_code=400,
209
205
  )
210
206
  if cursor_gid.type_name not in (
211
207
  CategoricalAnnotationConfigType.__name__,
@@ -214,7 +210,7 @@ async def list_annotation_configs(
214
210
  ):
215
211
  raise HTTPException(
216
212
  detail=f"Invalid cursor: {cursor}",
217
- status_code=HTTP_400_BAD_REQUEST,
213
+ status_code=400,
218
214
  )
219
215
  cursor_id = int(cursor_gid.node_id)
220
216
 
@@ -260,14 +256,13 @@ async def get_annotation_config_by_name_or_id(
260
256
  query = query.where(models.AnnotationConfig.name == config_identifier)
261
257
  config = await session.scalar(query)
262
258
  if not config:
263
- raise HTTPException(
264
- status_code=HTTP_404_NOT_FOUND, detail="Annotation configuration not found"
265
- )
259
+ raise HTTPException(status_code=404, detail="Annotation configuration not found")
266
260
  return GetAnnotationConfigResponseBody(data=db_to_api_annotation_config(config))
267
261
 
268
262
 
269
263
  @router.post(
270
264
  "/annotation_configs",
265
+ dependencies=[Depends(is_not_locked)],
271
266
  summary="Create an annotation configuration",
272
267
  )
273
268
  async def create_annotation_config(
@@ -280,7 +275,7 @@ async def create_annotation_config(
280
275
  try:
281
276
  db_config = _to_db_annotation_config(input_config)
282
277
  except ValueError as error:
283
- raise HTTPException(status_code=HTTP_400_BAD_REQUEST, detail=str(error))
278
+ raise HTTPException(status_code=400, detail=str(error))
284
279
 
285
280
  async with request.app.state.db() as session:
286
281
  annotation_config = models.AnnotationConfig(
@@ -292,7 +287,7 @@ async def create_annotation_config(
292
287
  await session.commit()
293
288
  except (PostgreSQLIntegrityError, SQLiteIntegrityError):
294
289
  raise HTTPException(
295
- status_code=HTTP_409_CONFLICT,
290
+ status_code=409,
296
291
  detail="The name of the annotation configuration is already taken",
297
292
  )
298
293
  return CreateAnnotationConfigResponseBody(
@@ -302,6 +297,7 @@ async def create_annotation_config(
302
297
 
303
298
  @router.put(
304
299
  "/annotation_configs/{config_id}",
300
+ dependencies=[Depends(is_not_locked)],
305
301
  summary="Update an annotation configuration",
306
302
  )
307
303
  async def update_annotation_config(
@@ -318,22 +314,18 @@ async def update_annotation_config(
318
314
  ContinuousAnnotationConfigType.__name__,
319
315
  FreeformAnnotationConfigType.__name__,
320
316
  ):
321
- raise HTTPException(
322
- status_code=HTTP_400_BAD_REQUEST, detail="Invalid annotation configuration ID"
323
- )
317
+ raise HTTPException(status_code=400, detail="Invalid annotation configuration ID")
324
318
  config_rowid = int(config_gid.node_id)
325
319
 
326
320
  try:
327
321
  db_config = _to_db_annotation_config(input_config)
328
322
  except ValueError as error:
329
- raise HTTPException(status_code=HTTP_400_BAD_REQUEST, detail=str(error))
323
+ raise HTTPException(status_code=400, detail=str(error))
330
324
 
331
325
  async with request.app.state.db() as session:
332
326
  existing_config = await session.get(models.AnnotationConfig, config_rowid)
333
327
  if not existing_config:
334
- raise HTTPException(
335
- status_code=HTTP_404_NOT_FOUND, detail="Annotation configuration not found"
336
- )
328
+ raise HTTPException(status_code=404, detail="Annotation configuration not found")
337
329
 
338
330
  existing_config.name = input_config.name
339
331
  existing_config.config = db_config
@@ -342,7 +334,7 @@ async def update_annotation_config(
342
334
  await session.commit()
343
335
  except (PostgreSQLIntegrityError, SQLiteIntegrityError):
344
336
  raise HTTPException(
345
- status_code=HTTP_409_CONFLICT,
337
+ status_code=409,
346
338
  detail="The name of the annotation configuration is already taken",
347
339
  )
348
340
 
@@ -357,15 +349,19 @@ async def delete_annotation_config(
357
349
  request: Request,
358
350
  config_id: str = Path(..., description="ID of the annotation configuration"),
359
351
  ) -> DeleteAnnotationConfigResponseBody:
360
- config_gid = GlobalID.from_id(config_id)
352
+ try:
353
+ config_gid = GlobalID.from_id(config_id)
354
+ except Exception:
355
+ raise HTTPException(
356
+ status_code=422,
357
+ detail=f"Invalid annotation configuration ID format: {config_id}",
358
+ )
361
359
  if config_gid.type_name not in (
362
360
  CategoricalAnnotationConfigType.__name__,
363
361
  ContinuousAnnotationConfigType.__name__,
364
362
  FreeformAnnotationConfigType.__name__,
365
363
  ):
366
- raise HTTPException(
367
- status_code=HTTP_400_BAD_REQUEST, detail="Invalid annotation configuration ID"
368
- )
364
+ raise HTTPException(status_code=400, detail="Invalid annotation configuration ID")
369
365
  config_rowid = int(config_gid.node_id)
370
366
  async with request.app.state.db() as session:
371
367
  stmt = (
@@ -375,9 +371,7 @@ async def delete_annotation_config(
375
371
  )
376
372
  annotation_config = await session.scalar(stmt)
377
373
  if annotation_config is None:
378
- raise HTTPException(
379
- status_code=HTTP_404_NOT_FOUND, detail="Annotation configuration not found"
380
- )
374
+ raise HTTPException(status_code=404, detail="Annotation configuration not found")
381
375
  await session.commit()
382
376
  return DeleteAnnotationConfigResponseBody(data=db_to_api_annotation_config(annotation_config))
383
377
 
@@ -397,9 +391,7 @@ def _get_annotation_config_db_id(config_gid: str) -> int:
397
391
  def _reserve_note_annotation_name(data: AnnotationConfigData) -> str:
398
392
  name = data.name
399
393
  if name == "note":
400
- raise HTTPException(
401
- status_code=HTTP_409_CONFLICT, detail="The name 'note' is reserved for span notes"
402
- )
394
+ raise HTTPException(status_code=409, detail="The name 'note' is reserved for span notes")
403
395
  return name
404
396
 
405
397