arize-phoenix 10.2.2__py3-none-any.whl → 10.3.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.

Potentially problematic release.


This version of arize-phoenix might be problematic. Click here for more details.

Files changed (31) hide show
  1. {arize_phoenix-10.2.2.dist-info → arize_phoenix-10.3.1.dist-info}/METADATA +5 -2
  2. {arize_phoenix-10.2.2.dist-info → arize_phoenix-10.3.1.dist-info}/RECORD +31 -30
  3. phoenix/config.py +1 -1
  4. phoenix/db/enums.py +3 -12
  5. phoenix/db/facilitator.py +25 -33
  6. phoenix/db/models.py +6 -1
  7. phoenix/server/api/mutations/api_key_mutations.py +5 -4
  8. phoenix/server/api/mutations/user_mutations.py +3 -11
  9. phoenix/server/api/queries.py +5 -5
  10. phoenix/server/api/routers/oauth2.py +1 -4
  11. phoenix/server/api/routers/v1/__init__.py +2 -0
  12. phoenix/server/api/routers/v1/models.py +7 -0
  13. phoenix/server/api/routers/v1/projects.py +2 -3
  14. phoenix/server/api/routers/v1/users.py +346 -0
  15. phoenix/server/bearer_auth.py +3 -4
  16. phoenix/server/jwt_store.py +9 -9
  17. phoenix/server/static/.vite/manifest.json +44 -44
  18. phoenix/server/static/assets/{components-ClD3sHta.js → components-CivAVDaz.js} +108 -108
  19. phoenix/server/static/assets/{index-CXawXHw0.js → index-Czk1v3pw.js} +2 -2
  20. phoenix/server/static/assets/{pages-BFtNRfTL.js → pages-Gj3J1sqc.js} +393 -393
  21. phoenix/server/static/assets/{vendor-arizeai-DHqMQzfV.js → vendor-arizeai-BD5gvaef.js} +1 -1
  22. phoenix/server/static/assets/{vendor-codemirror-DWdZV1Is.js → vendor-codemirror-DjHzu2rA.js} +1 -1
  23. phoenix/server/static/assets/{vendor-DOUbLVp5.js → vendor-dV-IJBHw.js} +1 -1
  24. phoenix/server/static/assets/{vendor-recharts-BfHdRd1Y.js → vendor-recharts-CuDlk54J.js} +1 -1
  25. phoenix/server/static/assets/{vendor-shiki-CHu75YVL.js → vendor-shiki-CZcHetjR.js} +1 -1
  26. phoenix/server/types.py +3 -2
  27. phoenix/version.py +1 -1
  28. {arize_phoenix-10.2.2.dist-info → arize_phoenix-10.3.1.dist-info}/WHEEL +0 -0
  29. {arize_phoenix-10.2.2.dist-info → arize_phoenix-10.3.1.dist-info}/entry_points.txt +0 -0
  30. {arize_phoenix-10.2.2.dist-info → arize_phoenix-10.3.1.dist-info}/licenses/IP_NOTICE +0 -0
  31. {arize_phoenix-10.2.2.dist-info → arize_phoenix-10.3.1.dist-info}/licenses/LICENSE +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: arize-phoenix
3
- Version: 10.2.2
3
+ Version: 10.3.1
4
4
  Summary: AI Observability and Evaluation
5
5
  Project-URL: Documentation, https://docs.arize.com/phoenix/
6
6
  Project-URL: Issues, https://github.com/Arize-ai/phoenix/issues
@@ -158,6 +158,9 @@ Description-Content-Type: text/markdown
158
158
  <a target="_blank" href="https://hub.docker.com/r/arizephoenix/phoenix/tags">
159
159
  <img src="https://img.shields.io/docker/v/arizephoenix/phoenix?sort=semver&logo=docker&label=image&color=blue">
160
160
  </a>
161
+ <a target="_blank" href="https://hub.docker.com/r/arizephoenix/phoenix-helm">
162
+ <img src="https://img.shields.io/badge/Helm-blue?style=flat&logo=helm&labelColor=grey"/>
163
+ </a>
161
164
  <a target="_blank" href="https://github.com/Arize-ai/phoenix/tree/main/js/packages/phoenix-mcp">
162
165
  <img src="https://badge.mcpx.dev?status=on" title="MCP Enabled"/>
163
166
  </a>
@@ -238,7 +241,7 @@ Phoenix is built on top of OpenTelemetry and is vendor, language, and framework
238
241
  | [LangChain.js](https://docs.arize.com/phoenix/tracing/integrations-tracing/langchain.js) | `@arizeai/openinference-instrumentation-langchain` | [![NPM Version](https://img.shields.io/npm/v/@arizeai/openinference-instrumentation-langchain.svg)](https://www.npmjs.com/package/@arizeai/openinference-instrumentation-langchain) |
239
242
  | [Vercel AI SDK](https://docs.arize.com/phoenix/tracing/integrations-tracing/vercel-ai-sdk) | `@arizeai/openinference-vercel` | [![NPM Version](https://img.shields.io/npm/v/@arizeai/openinference-vercel)](https://www.npmjs.com/package/@arizeai/openinference-vercel) |
240
243
  | [BeeAI](https://docs.arize.com/phoenix/tracing/integrations-tracing/beeai) | `@arizeai/openinference-instrumentation-beeai` | [![NPM Version](https://img.shields.io/npm/v/@arizeai/openinference-vercel)](https://www.npmjs.com/package/@arizeai/openinference-instrumentation-beeai) |
241
- | [Mastra](https://docs.arize.com/phoenix/integrations/mastra) | `@arizeai/openinference-instrumentation-mastra` | [![NPM Version](https://img.shields.io/npm/v/@arizeai/openinference-instrumentation-mastra.svg)](https://www.npmjs.com/package/@arizeai/openinference-mastra) |
244
+ | [Mastra](https://docs.arize.com/phoenix/integrations/mastra) | `@arizeai/openinference-mastra` | [![NPM Version](https://img.shields.io/npm/v/@arizeai/openinference-mastra.svg)](https://www.npmjs.com/package/@arizeai/openinference-mastra) |
242
245
 
243
246
  ### Platforms
244
247
 
@@ -1,12 +1,12 @@
1
1
  phoenix/__init__.py,sha256=X3eUEwd2rG8KKWWYVNNDJoqo08ihfjgHhlP29dcdNJE,5481
2
2
  phoenix/auth.py,sha256=yW78f1xWNjTE30ACGUM14nOd5BzkukhlzA9B45kSUkM,11053
3
- phoenix/config.py,sha256=J8fyMSM1sHegfIsUVPCUH58uew-CSSH52w9kyAIN0PE,57002
3
+ phoenix/config.py,sha256=zgir90pfb2aAqPm-scG73B30Z-1UppSrxKCPAuGtLbM,57004
4
4
  phoenix/datetime_utils.py,sha256=iJzNG6YJ6V7_u8B2iA7P2Z26FyxYbOPtx0dhJ7kNDHA,3398
5
5
  phoenix/exceptions.py,sha256=n2L2KKuecrdflB9MsCdAYCiSEvGJptIsfRkXMoJle7A,169
6
6
  phoenix/py.typed,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
7
7
  phoenix/services.py,sha256=ngkyKGVatX3cO2WJdo2hKdaVKP-xJCMvqthvga6kJss,5196
8
8
  phoenix/settings.py,sha256=x87BX7hWGQQZbrW_vrYqFR_izCGfO9gFc--JXUG4Tdk,754
9
- phoenix/version.py,sha256=ABrnvMD1B1q5-JnF_zTnewrPlIqJicGO3er4zwRQWpU,23
9
+ phoenix/version.py,sha256=ZgsYsdcG4VZPGFkEdGV8vDiBkuiX5YDF1heoxEtbzrY,23
10
10
  phoenix/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
11
  phoenix/core/embedding_dimension.py,sha256=zKGbcvwOXgLf-yrJBpQyKtd-LEOPRKHnUToyAU8Owis,87
12
12
  phoenix/core/model.py,sha256=qBFraOtmwCCnWJltKNP18DDG0mULXigytlFsa6YOz6k,4837
@@ -18,11 +18,11 @@ phoenix/db/alembic.ini,sha256=GIS6HpHaKaJbbuahZg1Rc1D2_QqyCkV9r58wdARGf6w,3262
18
18
  phoenix/db/bulk_inserter.py,sha256=faNjuwLqqsw4ky8sa4D0h9u5TvEDTOjrccUW89L008E,12725
19
19
  phoenix/db/constants.py,sha256=-YE2rkzcROG06_rerfnX5hC7fLzOHx1Gjw4nXhX_um4,46
20
20
  phoenix/db/engines.py,sha256=tB_8iWMDz0folryVvw29sbBUxJOB2XZ-Xx0Uexj3uns,6889
21
- phoenix/db/enums.py,sha256=k8zkgDvJT-Fk4P8apvPjRdS2xlJWH8_YGN_W-wYm5tk,373
22
- phoenix/db/facilitator.py,sha256=FBwGSRoxv_ZVIzC7HRVPSNy3c3q2Dj-vW_QmYYENSIE,9670
21
+ phoenix/db/enums.py,sha256=w3O5YuJEEzVTwVDZb8b2UUFhU8yK_GosF081VVrrno0,188
22
+ phoenix/db/facilitator.py,sha256=E25egiPMhuDVjH_kl1yMSO1bNWsP8Kn0IK-hXJXDD_4,9574
23
23
  phoenix/db/helpers.py,sha256=rbbHcl-STzcEpcXCYx6jbKzko7r3ggrWHHsXjZ48HsM,5352
24
24
  phoenix/db/migrate.py,sha256=oUrXH8yEbcpL4eh09aSCuUiSrhFli0eT5D_j4ZmYChY,2797
25
- phoenix/db/models.py,sha256=UELkdw132bsF9d3g2Jh-lM2UCX2sA6A8osMRTo7wZAg,51939
25
+ phoenix/db/models.py,sha256=UXUyYD4f6Cz4Zmg76jhjJmUbpTf1evC5mTc__u1l3FY,52195
26
26
  phoenix/db/pg_config.py,sha256=h6mB7qF7t4Zk6VGvAiyefHGVu74o-yJynaWzeE39k9Y,6001
27
27
  phoenix/db/insertion/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
28
28
  phoenix/db/insertion/constants.py,sha256=8wifm7X-1XvroZ__R2Gc96NsgLhTDn0zXl4lehlXtcA,70
@@ -91,11 +91,11 @@ phoenix/pointcloud/umap_parameters.py,sha256=db_WEPoamuWtopZx7tQfAXPnoE0MS8FkAV0
91
91
  phoenix/server/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
92
92
  phoenix/server/app.py,sha256=1N7OWYQ3obWhdb_Qbrg8mSuexvNk_oT1Taq8smfmMiU,41265
93
93
  phoenix/server/authorization.py,sha256=fofeRwuoodCUB3DQYPCuAgIyeiwopXW0tkH_KZvU0Rg,1848
94
- phoenix/server/bearer_auth.py,sha256=gh_DncYFcOcDzNO7V6zYAGDidSb_UKMGdcDUqV7gdhQ,6748
94
+ phoenix/server/bearer_auth.py,sha256=b2iHV2nwvWlZJ2O_nWOzHctJ0aUrEIOygIDrO7VOCSw,6700
95
95
  phoenix/server/dml_event.py,sha256=MjJmVEKytq75chBOSyvYDusUnEbg1pHpIjR3pZkUaJA,2838
96
96
  phoenix/server/dml_event_handler.py,sha256=EZLXmCvx4pJrCkz29gxwKwmvmUkTtPCHw6klR-XM8qE,8258
97
97
  phoenix/server/grpc_server.py,sha256=dod29zE_Zlir7NyLcdVM8GH3P8sy-9ykzfaBfVifyE4,4656
98
- phoenix/server/jwt_store.py,sha256=ZMNfRwFebRI2UDO3vmMxZF8qpZDT6LR8q-n0uoi1Y4g,16891
98
+ phoenix/server/jwt_store.py,sha256=B6uVildN_dQDTG_-aHHvuVSI7wIVK1yvED-_y6se2GU,16905
99
99
  phoenix/server/main.py,sha256=FI7wZW8SYPvVLC6dUVpweFcHmKcLTaUxfGE0i_4lZNw,18252
100
100
  phoenix/server/oauth2.py,sha256=GvUqZBoZ5dG-l2G1RMl1SUcN10jNAjaMXFznMSWz2Zs,3336
101
101
  phoenix/server/prometheus.py,sha256=1KjvSfjSa2-BPjDybVMM_Kag316CsN-Zwt64YNr_snc,7825
@@ -103,14 +103,14 @@ phoenix/server/rate_limiters.py,sha256=cFc73D2NaxqNZZDbwfIDw4So-fRVOJPBtqxOZ8Qky
103
103
  phoenix/server/retention.py,sha256=MQe1FWuc_NxhqgIq5q2hfFhWT8ddAmpppgI74xYEQ6c,3064
104
104
  phoenix/server/telemetry.py,sha256=4EluDDrhdDPxAjaW6lVSbi73xkB5XeUCZWOmZGdk0hg,2755
105
105
  phoenix/server/thread_server.py,sha256=Ea2AWreN1lwJsT2wYvGaRaiXrzBqH4kgkZpx0FO5Ocw,2144
106
- phoenix/server/types.py,sha256=gJJPBcDRkQ9VHZIt_aLqG_OBbGt1oWp4e3W3Jp61oKs,7409
106
+ phoenix/server/types.py,sha256=b17xahdt6uwDdUYul0xctu7TbBC65AjarlhUzOiXFNE,7443
107
107
  phoenix/server/api/README.md,sha256=Pyq1PLPgTzXAswrfIhGXrjI3Skq8it2jTVnanT6Ba4Q,1162
108
108
  phoenix/server/api/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
109
109
  phoenix/server/api/auth.py,sha256=cvKH2FQLL7PasiqZMHgu9P4qchhEG7a7P9Nxy35FoIQ,1551
110
110
  phoenix/server/api/context.py,sha256=oxNmVIIyycl22iQZjv59lU1inwlo-Povxe2Ok7t54mw,6954
111
111
  phoenix/server/api/exceptions.py,sha256=TA0JuY2YRnj35qGuMSQ8d0ToHum9gWm9W--3fSKHrX0,1171
112
112
  phoenix/server/api/interceptor.py,sha256=ykDnoC_apUd-llVli3m1CW18kNSIgjz2qZ6m5JmPDu8,1294
113
- phoenix/server/api/queries.py,sha256=LhJdTVYZX8xo0vuSzlKz5x5-I0le4byfUdCBMSSsgtw,40793
113
+ phoenix/server/api/queries.py,sha256=MzfCwdR2oLlZn8p0t0VMBp2-1ZFDwleKiYvtnAU9bFc,40710
114
114
  phoenix/server/api/schema.py,sha256=fcs36xQwFF_Qe41_5cWR8wYpDvOrnbcyTeo5WNMbDsA,1702
115
115
  phoenix/server/api/subscriptions.py,sha256=TnZhdoNHMXp1NkUVLA-eB54woll7FvxtsB2pLt1dO0w,23001
116
116
  phoenix/server/api/utils.py,sha256=quCBRcusc6PUq9tJq7M8PgwFZp7nXgVAxtbw8feribY,833
@@ -207,7 +207,7 @@ phoenix/server/api/input_types/UserRoleInput.py,sha256=xxhFe0ITZOgRVEJbVem_W6F1I
207
207
  phoenix/server/api/input_types/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
208
208
  phoenix/server/api/mutations/__init__.py,sha256=fGFokUtX4J1q9yYzRtPOwRyfJNyL_8z60gaWX9IPvtM,1872
209
209
  phoenix/server/api/mutations/annotation_config_mutations.py,sha256=i7NsQhYICcQ-I-tnFjGtVAYc8WVmMBacmRaqHWJ25t4,15433
210
- phoenix/server/api/mutations/api_key_mutations.py,sha256=diNBL06zrFCmzR-leYbL-AxDqN05-YtbzsnsAdew_K8,6194
210
+ phoenix/server/api/mutations/api_key_mutations.py,sha256=nfnRjALCaQMi_jIbEPW4G3Dn3tPnmZVU11tpBbBijGA,6242
211
211
  phoenix/server/api/mutations/chat_mutations.py,sha256=ChRh4YbNkBgFO05CNElzqJ1hIvo7lr5Zn0lvlRs51pE,22997
212
212
  phoenix/server/api/mutations/dataset_mutations.py,sha256=KRlF-Ag3twqaBpLR_6WYxf57DffaGuFBm-soaBPStbI,27787
213
213
  phoenix/server/api/mutations/experiment_mutations.py,sha256=p3CoLAa8nFPa3D759Y2A7De_PVJNGOL98mA3HoZBrRQ,3188
@@ -220,16 +220,16 @@ phoenix/server/api/mutations/prompt_version_tag_mutations.py,sha256=t77osYb5he2A
220
220
  phoenix/server/api/mutations/span_annotations_mutations.py,sha256=LQPcODp7-ZobXspjmtLaamyQa8UkTONC_va-ST9r-k8,15015
221
221
  phoenix/server/api/mutations/trace_annotations_mutations.py,sha256=PLNNzSlk3fyJHkAVaMGR8pbWB63nOos-cStUWbTi7f8,11995
222
222
  phoenix/server/api/mutations/trace_mutations.py,sha256=D5h2HYdlTo6yYZNq-O-PjaS9GeiZHxxVaOxDdh7fwjw,2957
223
- phoenix/server/api/mutations/user_mutations.py,sha256=wrYhO6s-pFQvH_1dTBLZafcIWnxdE6HU6EqxdUZDSPI,15568
223
+ phoenix/server/api/mutations/user_mutations.py,sha256=pNQ7nyOnqBsm5pAQALdvsEaGM889JQTKg0kBv_-YyYs,15393
224
224
  phoenix/server/api/openapi/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
225
225
  phoenix/server/api/openapi/main.py,sha256=yKdzJYI4cxy_1mFcK4_7YObIcuRviBIfwNjB23RG14k,461
226
226
  phoenix/server/api/openapi/schema.py,sha256=WGmHWSIyJhtc5EIh_M3vlXU-EgHkFuTlyVofgS0kj1I,529
227
227
  phoenix/server/api/routers/__init__.py,sha256=YIzHsIFOOXuCRbDkMUHx-McrANFJK5UfUn6a4BNIzmo,277
228
228
  phoenix/server/api/routers/auth.py,sha256=bP2ptHe53BxlG10w-8ul6fYd1ZpYW3iscV3mlMlFGp8,11479
229
229
  phoenix/server/api/routers/embeddings.py,sha256=BpZGJee0pdL0W5Rp1L0b30dEtZTgJeVqXky8LgZ0ZXw,898
230
- phoenix/server/api/routers/oauth2.py,sha256=ByE3QImKb7FWQKK2RJTzQ1J7WiLlSw_5WnksRtSstIw,24120
230
+ phoenix/server/api/routers/oauth2.py,sha256=EUoBeh4Ix-Uwt_q_RD75xbMcdVdd0xJLDYjELdVHBTw,24051
231
231
  phoenix/server/api/routers/utils.py,sha256=M41BoH-fl37izhRuN2aX7lWm7jOC20A_3uClv9TVUUY,583
232
- phoenix/server/api/routers/v1/__init__.py,sha256=0oOcsKJkQtBXAjZAo3AMtfjyW3OGCU4MI4TGW5nV6lo,2614
232
+ phoenix/server/api/routers/v1/__init__.py,sha256=ngLMPjC7lgZxgKy_Is33KxTRnMzSqy25qTTChCVx_Mo,2696
233
233
  phoenix/server/api/routers/v1/annotation_configs.py,sha256=rZ3yJm7m75BVegSjSHqsdqf7n26roGg7vYYiiKfWA3A,15898
234
234
  phoenix/server/api/routers/v1/annotations.py,sha256=oeafR2tCLu-uIwM9J72gN3MX5WDhrOMU3Jqd1uIiFqg,5921
235
235
  phoenix/server/api/routers/v1/datasets.py,sha256=Xh-M8bnCmjflmPcgv8dAG8Cek88sApuqQlvNBuSnrYc,37534
@@ -237,11 +237,12 @@ phoenix/server/api/routers/v1/evaluations.py,sha256=GFTo42aIEX0Htn0EjjoE1JZDYlvr
237
237
  phoenix/server/api/routers/v1/experiment_evaluations.py,sha256=xSs004jNYsOl3eg-6Zjo2tt9TefTd7WR3twWYrsNQNk,4828
238
238
  phoenix/server/api/routers/v1/experiment_runs.py,sha256=FreGzzprPpJ_DBHUkdUckca6EGCrnvgVpqk3CLT7wRc,7000
239
239
  phoenix/server/api/routers/v1/experiments.py,sha256=V9_sxqLTE1MKGFu9H3FEdGKr70lYMbGZx813MGaavfQ,20430
240
- phoenix/server/api/routers/v1/models.py,sha256=r0nM2kFJ3mxDqgc5vFr1cjNuyOPs3RIKE_DS2VMdF48,1749
241
- phoenix/server/api/routers/v1/projects.py,sha256=RVOAWW8RQIqaebQFcIbk1OGxO7B1BITtUewlcSUhasg,12615
240
+ phoenix/server/api/routers/v1/models.py,sha256=p3gJN-9SWiUYTUTft4bZMsZVCBNTb4nN1Foy68eRZzQ,1997
241
+ phoenix/server/api/routers/v1/projects.py,sha256=LFWxHYPRZy-1EvroNylL635vU1UuDbcuRo1oD26yBnw,12551
242
242
  phoenix/server/api/routers/v1/prompts.py,sha256=aBOUBwLDzZDIzJQkxJcR8ZKnakNJOLMwzsLKINSs1mA,26545
243
243
  phoenix/server/api/routers/v1/spans.py,sha256=qJVN0pVgZM5cMXQoNrCwmFjKDm_7-JHKdt_KU9IDFsA,32121
244
244
  phoenix/server/api/routers/v1/traces.py,sha256=DfzeszQvtlrVxvurJLaWJJAhkCZ4BodLwpFuBYPwN5Q,8206
245
+ phoenix/server/api/routers/v1/users.py,sha256=ZcW3if0L8-lUusTzET7fhlhvnmiCICDrGimzB7i3irc,11947
245
246
  phoenix/server/api/routers/v1/utils.py,sha256=oXIOGPzPTkE0ZWUTRCoRIQQ7wTzoSwtWFaUSjlGBqts,4960
246
247
  phoenix/server/api/types/Annotation.py,sha256=gsl8CwjIbDUbZRj4d9USwZ_w_Tkz4i7zuZh9ftV80jA,1132
247
248
  phoenix/server/api/types/AnnotationConfig.py,sha256=TPukZUgvFC17W93Vnme21EhswasBMR-ZiuSWteiWZOU,3891
@@ -347,16 +348,16 @@ phoenix/server/static/apple-touch-icon-76x76.png,sha256=CT_xT12I0u2i0WU8JzBZBuOQ
347
348
  phoenix/server/static/apple-touch-icon.png,sha256=fOfpjqGpWYbJ0eAurKsyoZP1EAs6ZVooBJ_SGk2ZkDs,3801
348
349
  phoenix/server/static/favicon.ico,sha256=bY0vvCKRftemZfPShwZtE93DiiQdaYaozkPGwNFr6H8,34494
349
350
  phoenix/server/static/modernizr.js,sha256=mvK-XtkNqjOral-QvzoqsyOMECXIMu5BQwSVN_wcU9c,2564
350
- phoenix/server/static/.vite/manifest.json,sha256=nusT1ZL6d9VYlA98soTaFzNOLUQcCwHV7LFJSD1itG4,2165
351
- phoenix/server/static/assets/components-ClD3sHta.js,sha256=bpA1H7yxmHnfPpXom80A_7ZRTHI8dYRF1EmK5CIymIM,546485
352
- phoenix/server/static/assets/index-CXawXHw0.js,sha256=ShQnyX324rK1NMlHg-XAEfRU42wpleJ4yApCdbvswI8,60432
353
- phoenix/server/static/assets/pages-BFtNRfTL.js,sha256=-7Yl5uH2NZms_-g5unnOlOCgnFsIdDs-RprWj1lueSY,1033529
354
- phoenix/server/static/assets/vendor-DOUbLVp5.js,sha256=AN12odor8w2dKOG4LokzFITR1h52TP1qnBDQl1u67Dc,2744392
351
+ phoenix/server/static/.vite/manifest.json,sha256=mLKhRgNe0Eg8cTyY34dLtU-YAHT0FFnjJ7dECENiB6k,2165
352
+ phoenix/server/static/assets/components-CivAVDaz.js,sha256=baHfjvKJYQnbQPTgY_XHXTI0wft8SYwGJPa_LahAXVE,546532
353
+ phoenix/server/static/assets/index-Czk1v3pw.js,sha256=9ek0aGlGW1tIBPssclZTQBLda4hRZmqYQNQkerAxM5w,60432
354
+ phoenix/server/static/assets/pages-Gj3J1sqc.js,sha256=L381PAqX9buyPGTmBMDJBWezqkJtIJ-H99a0V0di2bw,1033868
355
355
  phoenix/server/static/assets/vendor-WIZid84E.css,sha256=spZD2r7XL5GfLO13ln-IuXfnjAref8l6g_n_AvxxOlI,5517
356
- phoenix/server/static/assets/vendor-arizeai-DHqMQzfV.js,sha256=zaVJbapZYf9-reZp549G232mEi7MY7yZR2cBbchRISs,193248
357
- phoenix/server/static/assets/vendor-codemirror-DWdZV1Is.js,sha256=S0-OzJX5aZayqOQS3KcC-ztH5EITxeCHnOA2nk5li8A,781264
358
- phoenix/server/static/assets/vendor-recharts-BfHdRd1Y.js,sha256=YdjHHSsRjbhTkgi_NdynIGnwqX1YibrXZhGgbewsOfo,282150
359
- phoenix/server/static/assets/vendor-shiki-CHu75YVL.js,sha256=FaxGIHyaAJFTau_Jt8GlW41aKphdTaEur_nSd6UrbpU,8980312
356
+ phoenix/server/static/assets/vendor-arizeai-BD5gvaef.js,sha256=FnrGblO2Yf2AsjKo8uvcYdmWdRs19t_j8hE9xTV6Nzc,193248
357
+ phoenix/server/static/assets/vendor-codemirror-DjHzu2rA.js,sha256=rExnSZuhZqwYVj4vDUDL5CBsjr1q5UiG1cdbYYLBO54,781264
358
+ phoenix/server/static/assets/vendor-dV-IJBHw.js,sha256=GNMZtd8vBm5bOIz2LyhvpJqLcOtIcqBedO5wPHkQPn0,2744392
359
+ phoenix/server/static/assets/vendor-recharts-CuDlk54J.js,sha256=DZFWTvpEE75lwBjwWlJS3TIzoSXRIKwTMvSP9X9RruE,282150
360
+ phoenix/server/static/assets/vendor-shiki-CZcHetjR.js,sha256=9CEX6GxHPOvUxCF9jp9puN1uYULyLGm-PrVOUoK9118,8980312
360
361
  phoenix/server/static/assets/vendor-three-C5WAXd5r.js,sha256=ELkg06u70N7h8oFmvqdoHyPuUf9VgGEWeT4LKFx4VWo,620975
361
362
  phoenix/server/templates/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
362
363
  phoenix/server/templates/index.html,sha256=NpJ83DULqcStXFbShNamX4_NPDtnnucuBxppvUYjJa8,4409
@@ -397,9 +398,9 @@ phoenix/utilities/project.py,sha256=auVpARXkDb-JgeX5f2aStyFIkeKvGwN9l7qrFeJMVxI,
397
398
  phoenix/utilities/re.py,sha256=6YyUWIkv0zc2SigsxfOWIHzdpjKA_TZo2iqKq7zJKvw,2081
398
399
  phoenix/utilities/span_store.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
399
400
  phoenix/utilities/template_formatters.py,sha256=gh9PJD6WEGw7TEYXfSst1UR4pWWwmjxMLrDVQ_CkpkQ,2779
400
- arize_phoenix-10.2.2.dist-info/METADATA,sha256=IDFKRGlqJiS1SwJHhj310VMmYvG2H_GDCdOnSdYkUGg,26816
401
- arize_phoenix-10.2.2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
402
- arize_phoenix-10.2.2.dist-info/entry_points.txt,sha256=Pgpn8Upxx9P8z8joPXZWl2LlnAlGc3gcQoVchb06X1Q,94
403
- arize_phoenix-10.2.2.dist-info/licenses/IP_NOTICE,sha256=JBqyyCYYxGDfzQ0TtsQgjts41IJoa-hiwDrBjCb9gHM,469
404
- arize_phoenix-10.2.2.dist-info/licenses/LICENSE,sha256=HFkW9REuMOkvKRACuwLPT0hRydHb3zNg-fdFt94td18,3794
405
- arize_phoenix-10.2.2.dist-info/RECORD,,
401
+ arize_phoenix-10.3.1.dist-info/METADATA,sha256=IhITrAH1Gmw_7fvQxJJDtjJsy2wXZZgGdNS7s3hIQwk,27004
402
+ arize_phoenix-10.3.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
403
+ arize_phoenix-10.3.1.dist-info/entry_points.txt,sha256=Pgpn8Upxx9P8z8joPXZWl2LlnAlGc3gcQoVchb06X1Q,94
404
+ arize_phoenix-10.3.1.dist-info/licenses/IP_NOTICE,sha256=JBqyyCYYxGDfzQ0TtsQgjts41IJoa-hiwDrBjCb9gHM,469
405
+ arize_phoenix-10.3.1.dist-info/licenses/LICENSE,sha256=HFkW9REuMOkvKRACuwLPT0hRydHb3zNg-fdFt94td18,3794
406
+ arize_phoenix-10.3.1.dist-info/RECORD,,
phoenix/config.py CHANGED
@@ -1177,7 +1177,7 @@ def ensure_working_dir_if_needed() -> None:
1177
1177
  This is bypassed if a postgres database is configured and a working directory is not set.
1178
1178
  """
1179
1179
  if _no_local_storage():
1180
- pass
1180
+ return
1181
1181
 
1182
1182
  logger.info(f"📋 Ensuring phoenix working directory: {WORKING_DIR}")
1183
1183
  try:
phoenix/db/enums.py CHANGED
@@ -1,19 +1,10 @@
1
- from collections.abc import Mapping
2
- from enum import Enum
3
-
4
1
  from sqlalchemy.orm import InstrumentedAttribute
5
2
 
6
3
  from phoenix.db import models
7
4
 
8
- __all__ = ["UserRole", "COLUMN_ENUMS"]
9
-
10
-
11
- class UserRole(Enum):
12
- SYSTEM = "SYSTEM"
13
- ADMIN = "ADMIN"
14
- MEMBER = "MEMBER"
5
+ __all__ = ["ENUM_COLUMNS"]
15
6
 
16
7
 
17
- COLUMN_ENUMS: Mapping[InstrumentedAttribute[str], type[Enum]] = {
18
- models.UserRole.name: UserRole,
8
+ ENUM_COLUMNS: set[InstrumentedAttribute[str]] = {
9
+ models.UserRole.name,
19
10
  }
phoenix/db/facilitator.py CHANGED
@@ -7,12 +7,7 @@ from asyncio import gather
7
7
  from functools import partial
8
8
  from typing import Optional
9
9
 
10
- from sqlalchemy import (
11
- distinct,
12
- exists,
13
- insert,
14
- select,
15
- )
10
+ import sqlalchemy as sa
16
11
 
17
12
  from phoenix import config
18
13
  from phoenix.auth import (
@@ -30,7 +25,8 @@ from phoenix.config import (
30
25
  )
31
26
  from phoenix.db import models
32
27
  from phoenix.db.constants import DEFAULT_PROJECT_TRACE_RETENTION_POLICY_ID
33
- from phoenix.db.enums import COLUMN_ENUMS, UserRole
28
+ from phoenix.db.enums import ENUM_COLUMNS
29
+ from phoenix.db.models import UserRoleName
34
30
  from phoenix.db.types.trace_retention import (
35
31
  MaxDaysRule,
36
32
  TraceRetentionCronExpression,
@@ -76,18 +72,17 @@ async def _ensure_enums(db: DbSessionFactory) -> None:
76
72
  they will be added. If any values are present in the database but not in the enum, an error will
77
73
  be raised. This function is idempotent: it will not add duplicate values to the database.
78
74
  """
79
- for column, enum in COLUMN_ENUMS.items():
75
+ for column in ENUM_COLUMNS:
80
76
  table = column.class_
77
+ assert isinstance(column.type, sa.Enum)
81
78
  async with db() as session:
82
- existing = set(
83
- [_ async for _ in await session.stream_scalars(select(distinct(column)))]
84
- )
85
- expected = set(e.value for e in enum)
79
+ existing = set(await session.scalars(sa.select(column)))
80
+ expected = set(column.type.enums)
86
81
  if unexpected := existing - expected:
87
82
  raise ValueError(f"Unexpected values in {table.name}.{column.key}: {unexpected}")
88
83
  if not (missing := expected - existing):
89
84
  continue
90
- await session.execute(insert(table), [{column.key: v} for v in missing])
85
+ await session.execute(sa.insert(table), [{column.key: v} for v in missing])
91
86
 
92
87
 
93
88
  async def _ensure_user_roles(db: DbSessionFactory) -> None:
@@ -97,21 +92,22 @@ async def _ensure_user_roles(db: DbSessionFactory) -> None:
97
92
  the email "admin@localhost".
98
93
  """
99
94
  async with db() as session:
100
- role_ids = {
95
+ role_ids: dict[UserRoleName, int] = {
101
96
  name: id_
102
97
  async for name, id_ in await session.stream(
103
- select(models.UserRole.name, models.UserRole.id)
98
+ sa.select(models.UserRole.name, models.UserRole.id)
104
99
  )
105
100
  }
106
- existing_roles = [
101
+ existing_roles: list[UserRoleName] = [
107
102
  name
108
103
  async for name in await session.stream_scalars(
109
- select(distinct(models.UserRole.name)).join_from(models.User, models.UserRole)
104
+ sa.select(sa.distinct(models.UserRole.name)).join_from(models.User, models.UserRole)
110
105
  )
111
106
  ]
112
- if (system_role := UserRole.SYSTEM.value) not in existing_roles and (
113
- system_role_id := role_ids.get(system_role)
114
- ) is not None:
107
+ if (
108
+ "SYSTEM" not in existing_roles
109
+ and (system_role_id := role_ids.get("SYSTEM")) is not None
110
+ ):
115
111
  system_user = models.LocalUser(
116
112
  user_role_id=system_role_id,
117
113
  username=DEFAULT_SYSTEM_USERNAME,
@@ -121,9 +117,7 @@ async def _ensure_user_roles(db: DbSessionFactory) -> None:
121
117
  password_hash=secrets.token_bytes(DEFAULT_SECRET_LENGTH),
122
118
  )
123
119
  session.add(system_user)
124
- if (admin_role := UserRole.ADMIN.value) not in existing_roles and (
125
- admin_role_id := role_ids.get(admin_role)
126
- ) is not None:
120
+ if "ADMIN" not in existing_roles and (admin_role_id := role_ids.get("ADMIN")) is not None:
127
121
  salt = secrets.token_bytes(DEFAULT_SECRET_LENGTH)
128
122
  password = get_env_default_admin_initial_password()
129
123
  compute = partial(compute_password_hash, password=password, salt=salt)
@@ -147,9 +141,9 @@ async def _get_system_user_id(db: DbSessionFactory) -> None:
147
141
  """
148
142
  async with db() as session:
149
143
  system_user_id = await session.scalar(
150
- select(models.User.id)
144
+ sa.select(models.User.id)
151
145
  .join(models.UserRole)
152
- .where(models.UserRole.name == UserRole.SYSTEM.value)
146
+ .where(models.UserRole.name == "SYSTEM")
153
147
  .order_by(models.User.id)
154
148
  .limit(1)
155
149
  )
@@ -173,7 +167,7 @@ async def _ensure_admins(
173
167
  async with db() as session:
174
168
  existing_emails = set(
175
169
  await session.scalars(
176
- select(models.User.email).where(models.User.email.in_(admins.keys()))
170
+ sa.select(models.User.email).where(models.User.email.in_(admins.keys()))
177
171
  )
178
172
  )
179
173
  admins = {
@@ -183,7 +177,7 @@ async def _ensure_admins(
183
177
  return
184
178
  existing_usernames = set(
185
179
  await session.scalars(
186
- select(models.User.username).where(models.User.username.in_(admins.values()))
180
+ sa.select(models.User.username).where(models.User.username.in_(admins.values()))
187
181
  )
188
182
  )
189
183
  admins = {
@@ -193,9 +187,7 @@ async def _ensure_admins(
193
187
  }
194
188
  if not admins:
195
189
  return
196
- admin_role_id = await session.scalar(
197
- select(models.UserRole.id).filter_by(name=UserRole.ADMIN.value)
198
- )
190
+ admin_role_id = await session.scalar(sa.select(models.UserRole.id).filter_by(name="ADMIN"))
199
191
  assert admin_role_id is not None, "Admin role not found in database"
200
192
  user: models.User
201
193
  for email, username in admins.items():
@@ -248,8 +240,8 @@ async def _ensure_default_project_trace_retention_policy(db: DbSessionFactory) -
248
240
  assert DEFAULT_PROJECT_TRACE_RETENTION_POLICY_ID == 0
249
241
  async with db() as session:
250
242
  if await session.scalar(
251
- select(
252
- exists().where(
243
+ sa.select(
244
+ sa.exists().where(
253
245
  models.ProjectTraceRetentionPolicy.id
254
246
  == DEFAULT_PROJECT_TRACE_RETENTION_POLICY_ID
255
247
  )
@@ -259,7 +251,7 @@ async def _ensure_default_project_trace_retention_policy(db: DbSessionFactory) -
259
251
  cron_expression = TraceRetentionCronExpression(root="0 0 * * 0")
260
252
  rule = TraceRetentionRule(root=MaxDaysRule(max_days=0))
261
253
  await session.execute(
262
- insert(models.ProjectTraceRetentionPolicy),
254
+ sa.insert(models.ProjectTraceRetentionPolicy),
263
255
  [
264
256
  {
265
257
  "id": DEFAULT_PROJECT_TRACE_RETENTION_POLICY_ID,
phoenix/db/models.py CHANGED
@@ -146,6 +146,7 @@ def render_values_w_union(
146
146
  return compiler.process(subquery, from_linter=from_linter, **kw)
147
147
 
148
148
 
149
+ UserRoleName: TypeAlias = Literal["SYSTEM", "ADMIN", "MEMBER"]
149
150
  AuthMethod: TypeAlias = Literal["LOCAL", "OAUTH2"]
150
151
 
151
152
 
@@ -1132,7 +1133,7 @@ class ExperimentRunAnnotation(Base):
1132
1133
 
1133
1134
  class UserRole(Base):
1134
1135
  __tablename__ = "user_roles"
1135
- name: Mapped[str] = mapped_column(unique=True, index=True)
1136
+ name: Mapped[UserRoleName] = mapped_column(unique=True, index=True)
1136
1137
  users: Mapped[list["User"]] = relationship("User", back_populates="role")
1137
1138
 
1138
1139
 
@@ -1231,6 +1232,8 @@ class OAuth2User(User):
1231
1232
  *,
1232
1233
  email: str,
1233
1234
  username: str,
1235
+ oauth2_client_id: Optional[str] = None,
1236
+ oauth2_user_id: Optional[str] = None,
1234
1237
  user_role_id: Optional[int] = None,
1235
1238
  ) -> None:
1236
1239
  super().__init__(
@@ -1239,6 +1242,8 @@ class OAuth2User(User):
1239
1242
  user_role_id=user_role_id,
1240
1243
  reset_password=False,
1241
1244
  auth_method="OAUTH2",
1245
+ oauth2_client_id=oauth2_client_id,
1246
+ oauth2_user_id=oauth2_user_id,
1242
1247
  )
1243
1248
 
1244
1249
 
@@ -7,7 +7,8 @@ from strawberry import UNSET
7
7
  from strawberry.relay import GlobalID
8
8
  from strawberry.types import Info
9
9
 
10
- from phoenix.db import enums, models
10
+ from phoenix.db import models
11
+ from phoenix.db.models import UserRoleName
11
12
  from phoenix.server.api.auth import IsAdmin, IsLocked, IsNotReadOnly
12
13
  from phoenix.server.api.context import Context
13
14
  from phoenix.server.api.exceptions import Unauthorized
@@ -65,13 +66,13 @@ class ApiKeyMutationMixin:
65
66
  self, info: Info[Context, None], input: CreateApiKeyInput
66
67
  ) -> CreateSystemApiKeyMutationPayload:
67
68
  assert (token_store := info.context.token_store) is not None
68
- user_role = enums.UserRole.SYSTEM
69
+ user_role: UserRoleName = "SYSTEM"
69
70
  async with info.context.db() as session:
70
71
  # Get the system user - note this could be pushed into a dataloader
71
72
  system_user = await session.scalar(
72
73
  select(models.User)
73
74
  .join(models.UserRole) # Join User with UserRole
74
- .where(models.UserRole.name == user_role.value) # Filter where role is SYSTEM
75
+ .where(models.UserRole.name == user_role) # Filter where role is SYSTEM
75
76
  .order_by(models.User.id)
76
77
  .limit(1)
77
78
  )
@@ -117,7 +118,7 @@ class ApiKeyMutationMixin:
117
118
  issued_at=issued_at,
118
119
  expiration_time=input.expires_at or None,
119
120
  attributes=ApiKeyAttributes(
120
- user_role=enums.UserRole.MEMBER,
121
+ user_role="ADMIN" if user.is_admin else "MEMBER",
121
122
  name=input.name,
122
123
  description=input.description,
123
124
  ),
@@ -25,7 +25,7 @@ from phoenix.auth import (
25
25
  validate_password_format,
26
26
  )
27
27
  from phoenix.config import get_env_disable_basic_auth
28
- from phoenix.db import enums, models
28
+ from phoenix.db import models
29
29
  from phoenix.server.api.auth import IsAdmin, IsLocked, IsNotReadOnly
30
30
  from phoenix.server.api.context import Context
31
31
  from phoenix.server.api.exceptions import BadRequest, Conflict, NotFound, Unauthorized
@@ -256,16 +256,8 @@ class UserMutationMixin:
256
256
  set(input.user_ids),
257
257
  )
258
258
  )
259
- system_user_role_id = (
260
- select(models.UserRole.id)
261
- .where(models.UserRole.name == enums.UserRole.SYSTEM.value)
262
- .scalar_subquery()
263
- )
264
- admin_user_role_id = (
265
- select(models.UserRole.id)
266
- .where(models.UserRole.name == enums.UserRole.ADMIN.value)
267
- .scalar_subquery()
268
- )
259
+ system_user_role_id = select(models.UserRole.id).filter_by(name="SYSTEM").scalar_subquery()
260
+ admin_user_role_id = select(models.UserRole.id).filter_by(name="ADMIN").scalar_subquery()
269
261
  default_admin_user_id = (
270
262
  select(models.User.id)
271
263
  .where(
@@ -18,7 +18,7 @@ from phoenix.config import (
18
18
  get_env_database_allocated_storage_capacity_gibibytes,
19
19
  getenv,
20
20
  )
21
- from phoenix.db import enums, models
21
+ from phoenix.db import models
22
22
  from phoenix.db.constants import DEFAULT_PROJECT_TRACE_RETENTION_POLICY_ID
23
23
  from phoenix.db.helpers import SupportedSQLDialect, exclude_experiment_projects
24
24
  from phoenix.db.models import DatasetExample as OrmExample
@@ -165,7 +165,7 @@ class Query:
165
165
  stmt = (
166
166
  select(models.User)
167
167
  .join(models.UserRole)
168
- .where(models.UserRole.name != enums.UserRole.SYSTEM.value)
168
+ .where(models.UserRole.name != "SYSTEM")
169
169
  .order_by(models.User.email)
170
170
  .options(joinedload(models.User.role))
171
171
  )
@@ -181,7 +181,7 @@ class Query:
181
181
  ) -> list[UserRole]:
182
182
  async with info.context.db() as session:
183
183
  roles = await session.scalars(
184
- select(models.UserRole).where(models.UserRole.name != enums.UserRole.SYSTEM.value)
184
+ select(models.UserRole).where(models.UserRole.name != "SYSTEM")
185
185
  )
186
186
  return [
187
187
  UserRole(
@@ -197,7 +197,7 @@ class Query:
197
197
  select(models.ApiKey)
198
198
  .join(models.User)
199
199
  .join(models.UserRole)
200
- .where(models.UserRole.name != enums.UserRole.SYSTEM.value)
200
+ .where(models.UserRole.name != "SYSTEM")
201
201
  )
202
202
  async with info.context.db() as session:
203
203
  api_keys = await session.scalars(stmt)
@@ -209,7 +209,7 @@ class Query:
209
209
  select(models.ApiKey)
210
210
  .join(models.User)
211
211
  .join(models.UserRole)
212
- .where(models.UserRole.name == enums.UserRole.SYSTEM.value)
212
+ .where(models.UserRole.name == "SYSTEM")
213
213
  )
214
214
  async with info.context.db() as session:
215
215
  api_keys = await session.scalars(stmt)
@@ -37,7 +37,6 @@ from phoenix.config import (
37
37
  get_env_disable_rate_limit,
38
38
  )
39
39
  from phoenix.db import models
40
- from phoenix.db.enums import UserRole
41
40
  from phoenix.server.bearer_auth import create_access_and_refresh_tokens
42
41
  from phoenix.server.oauth2 import OAuth2Client
43
42
  from phoenix.server.rate_limiters import (
@@ -457,9 +456,7 @@ async def _create_user(
457
456
  if email_exists:
458
457
  raise EmailAlreadyInUse(f"An account for {email} is already in use.")
459
458
  member_role_id = (
460
- select(models.UserRole.id)
461
- .where(models.UserRole.name == UserRole.MEMBER.value)
462
- .scalar_subquery()
459
+ select(models.UserRole.id).where(models.UserRole.name == "MEMBER").scalar_subquery()
463
460
  )
464
461
  user_id = await session.scalar(
465
462
  insert(models.User)
@@ -15,6 +15,7 @@ from .projects import router as projects_router
15
15
  from .prompts import router as prompts_router
16
16
  from .spans import router as spans_router
17
17
  from .traces import router as traces_router
18
+ from .users import router as users_router
18
19
  from .utils import add_errors_to_responses
19
20
 
20
21
  REST_API_VERSION = "1.0"
@@ -69,4 +70,5 @@ def create_v1_router(authentication_enabled: bool) -> APIRouter:
69
70
  router.include_router(evaluations_router)
70
71
  router.include_router(prompts_router)
71
72
  router.include_router(projects_router)
73
+ router.include_router(users_router)
72
74
  return router
@@ -1,7 +1,10 @@
1
1
  from datetime import datetime
2
+ from typing import Any
2
3
 
3
4
  from pydantic import BaseModel, ConfigDict
4
5
 
6
+ from phoenix.db.types.db_models import UNDEFINED
7
+
5
8
 
6
9
  def datetime_encoder(dt: datetime) -> str:
7
10
  """
@@ -43,3 +46,7 @@ class V1RoutesBaseModel(BaseModel):
43
46
  []
44
47
  ), # suppress warnings about protected namespaces starting with `model_` on pydantic 2.9
45
48
  )
49
+
50
+ def __init__(self, *args: Any, **kwargs: Any) -> None:
51
+ kwargs = {k: v for k, v in kwargs.items() if v is not UNDEFINED}
52
+ super().__init__(*args, **kwargs)
@@ -14,7 +14,6 @@ from strawberry.relay import GlobalID
14
14
 
15
15
  from phoenix.config import DEFAULT_PROJECT_NAME
16
16
  from phoenix.db import models
17
- from phoenix.db.enums import UserRole
18
17
  from phoenix.db.helpers import exclude_experiment_projects
19
18
  from phoenix.server.api.routers.v1.models import V1RoutesBaseModel
20
19
  from phoenix.server.api.routers.v1.utils import (
@@ -257,7 +256,7 @@ async def update_project(
257
256
  .where(models.User.id == int(request.user.identity))
258
257
  )
259
258
  role_name = await session.scalar(stmt)
260
- if role_name != UserRole.ADMIN.value:
259
+ if role_name != "ADMIN":
261
260
  raise HTTPException(
262
261
  status_code=HTTP_403_FORBIDDEN,
263
262
  detail="Only admins can update projects",
@@ -317,7 +316,7 @@ async def delete_project(
317
316
  .where(models.User.id == int(request.user.identity))
318
317
  )
319
318
  role_name = await session.scalar(stmt)
320
- if role_name != UserRole.ADMIN.value:
319
+ if role_name != "ADMIN":
321
320
  raise HTTPException(
322
321
  status_code=HTTP_403_FORBIDDEN,
323
322
  detail="Only admins can delete projects",