arize-phoenix 4.24.0__py3-none-any.whl → 4.26.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-4.24.0.dist-info → arize_phoenix-4.26.0.dist-info}/METADATA +12 -7
  2. {arize_phoenix-4.24.0.dist-info → arize_phoenix-4.26.0.dist-info}/RECORD +46 -41
  3. phoenix/auth.py +45 -0
  4. phoenix/db/engines.py +15 -2
  5. phoenix/db/insertion/dataset.py +1 -0
  6. phoenix/db/migrate.py +21 -10
  7. phoenix/db/migrations/future_versions/cd164e83824f_users_and_tokens.py +7 -6
  8. phoenix/db/migrations/versions/3be8647b87d8_add_token_columns_to_spans_table.py +4 -12
  9. phoenix/db/models.py +1 -1
  10. phoenix/inferences/fixtures.py +1 -0
  11. phoenix/inferences/inferences.py +1 -0
  12. phoenix/metrics/__init__.py +1 -0
  13. phoenix/server/api/context.py +14 -0
  14. phoenix/server/api/input_types/UserRoleInput.py +9 -0
  15. phoenix/server/api/mutations/__init__.py +4 -0
  16. phoenix/server/api/mutations/api_key_mutations.py +119 -0
  17. phoenix/server/api/mutations/auth.py +7 -0
  18. phoenix/server/api/mutations/user_mutations.py +89 -0
  19. phoenix/server/api/queries.py +7 -6
  20. phoenix/server/api/routers/auth.py +52 -0
  21. phoenix/server/api/routers/v1/datasets.py +1 -0
  22. phoenix/server/api/routers/v1/spans.py +1 -1
  23. phoenix/server/api/types/UserRole.py +1 -1
  24. phoenix/server/app.py +61 -9
  25. phoenix/server/main.py +24 -19
  26. phoenix/server/static/.vite/manifest.json +31 -31
  27. phoenix/server/static/assets/{components-DzA9gIHT.js → components-1Ahruijo.js} +4 -4
  28. phoenix/server/static/assets/{index-BuTlV4Gk.js → index-BEE_RWJx.js} +2 -2
  29. phoenix/server/static/assets/{pages-DzkUGFGV.js → pages-CFS6mPnW.js} +263 -220
  30. phoenix/server/static/assets/{vendor-CIqy43_9.js → vendor-aSQri0vz.js} +58 -58
  31. phoenix/server/static/assets/{vendor-arizeai-B1YgcWL8.js → vendor-arizeai-CsdcB1NH.js} +1 -1
  32. phoenix/server/static/assets/{vendor-codemirror-_bcwCA1C.js → vendor-codemirror-CYHkhs7D.js} +1 -1
  33. phoenix/server/static/assets/{vendor-recharts-C3pM_Wlg.js → vendor-recharts-B0sannek.js} +1 -1
  34. phoenix/server/types.py +12 -4
  35. phoenix/services.py +1 -0
  36. phoenix/session/client.py +1 -1
  37. phoenix/session/evaluation.py +1 -0
  38. phoenix/session/session.py +2 -1
  39. phoenix/trace/fixtures.py +37 -0
  40. phoenix/trace/langchain/instrumentor.py +1 -1
  41. phoenix/trace/llama_index/callback.py +1 -0
  42. phoenix/trace/openai/instrumentor.py +1 -0
  43. phoenix/version.py +1 -1
  44. {arize_phoenix-4.24.0.dist-info → arize_phoenix-4.26.0.dist-info}/WHEEL +0 -0
  45. {arize_phoenix-4.24.0.dist-info → arize_phoenix-4.26.0.dist-info}/licenses/IP_NOTICE +0 -0
  46. {arize_phoenix-4.24.0.dist-info → arize_phoenix-4.26.0.dist-info}/licenses/LICENSE +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: arize-phoenix
3
- Version: 4.24.0
3
+ Version: 4.26.0
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
@@ -42,9 +42,11 @@ Requires-Dist: protobuf<6.0,>=3.20
42
42
  Requires-Dist: psutil
43
43
  Requires-Dist: pyarrow
44
44
  Requires-Dist: pydantic!=2.0.*,<3,>=1.0
45
+ Requires-Dist: pyjwt
45
46
  Requires-Dist: python-multipart
46
47
  Requires-Dist: scikit-learn
47
- Requires-Dist: scipy
48
+ Requires-Dist: scipy!=1.14.1; platform_system == 'Darwin'
49
+ Requires-Dist: scipy; platform_system != 'Darwin'
48
50
  Requires-Dist: sqlalchemy[asyncio]<3,>=2.0.4
49
51
  Requires-Dist: sqlean-py>=3.45.1
50
52
  Requires-Dist: starlette
@@ -85,14 +87,16 @@ Requires-Dist: nbqa; extra == 'dev'
85
87
  Requires-Dist: pandas-stubs==2.0.3.230814; (python_version < '3.9') and extra == 'dev'
86
88
  Requires-Dist: pandas-stubs==2.2.2.240603; (python_version >= '3.9') and extra == 'dev'
87
89
  Requires-Dist: pandas>=1.0; extra == 'dev'
90
+ Requires-Dist: portpicker; extra == 'dev'
88
91
  Requires-Dist: pre-commit; extra == 'dev'
89
92
  Requires-Dist: prometheus-client; extra == 'dev'
90
- Requires-Dist: psycopg[binary]; extra == 'dev'
93
+ Requires-Dist: psycopg[binary,pool]; extra == 'dev'
91
94
  Requires-Dist: pytest-asyncio; extra == 'dev'
92
95
  Requires-Dist: pytest-cov; extra == 'dev'
93
96
  Requires-Dist: pytest-postgresql; extra == 'dev'
94
- Requires-Dist: pytest==8.3.1; extra == 'dev'
95
- Requires-Dist: ruff==0.5.4; extra == 'dev'
97
+ Requires-Dist: pytest-xdist; extra == 'dev'
98
+ Requires-Dist: pytest==8.3.2; extra == 'dev'
99
+ Requires-Dist: ruff==0.6.1; extra == 'dev'
96
100
  Requires-Dist: strawberry-graphql[debug-server,opentelemetry]==0.236.0; extra == 'dev'
97
101
  Requires-Dist: tabulate; extra == 'dev'
98
102
  Requires-Dist: types-tabulate; extra == 'dev'
@@ -107,6 +111,8 @@ Requires-Dist: llama-index-readers-file==0.1.25; extra == 'llama-index'
107
111
  Requires-Dist: llama-index==0.10.51; extra == 'llama-index'
108
112
  Provides-Extra: pg
109
113
  Requires-Dist: asyncpg; extra == 'pg'
114
+ Requires-Dist: psycopg[binary,pool]; extra == 'pg'
115
+ Provides-Extra: test
110
116
  Description-Content-Type: text/markdown
111
117
 
112
118
  <p align="center">
@@ -144,9 +150,8 @@ Phoenix is an open-source AI observability platform designed for experimentation
144
150
  - **_Evaluation_** - Leverage LLMs to benchmark your application's performance using response and retrieval evals.
145
151
  - **_Datasets_** - Create versioned datasets of examples for experimentation, evaluation, and fine-tuning.
146
152
  - **_Experiments_** - Track and evaluate changes to prompts, LLMs, and retrieval.
147
- - **_Inference Analysis_** - Visualize inferences and embeddings using dimensionality reduction and clustering to identify drift and performance degradation.
148
153
 
149
- Phoenix is vendor and language agnostic with out-of-the-box support for popular frameworks (🦙LlamaIndex, 🦜⛓LangChain, 🧩DSPy) and LLM providers (OpenAI, Bedrock, and more). For details on auto-instrumentation, check out the [OpenInference](https://github.com/Arize-ai/openinference) project.
154
+ Phoenix is vendor and language agnostic with out-of-the-box support for popular frameworks (🦙LlamaIndex, 🦜⛓LangChain, Haystack, 🧩DSPy) and LLM providers (OpenAI, Bedrock, and more). For details on auto-instrumentation, check out the [OpenInference](https://github.com/Arize-ai/openinference) project.
150
155
 
151
156
  Phoenix runs practically anywhere, including your Jupyter notebook, local machine, containerized deployment, or in the cloud.
152
157
 
@@ -1,11 +1,12 @@
1
1
  phoenix/__init__.py,sha256=TGNWqm2UW-l67yIRpOtmqGHVAmdoobSNqUsiTtip7uQ,1542
2
+ phoenix/auth.py,sha256=N8vTFmc5BEsdX4xr6Bmh6OwBrNUQykr74LuCIkC28jA,1455
2
3
  phoenix/config.py,sha256=wYA_8GSSz5rnpfIWDjeBL9ehKuTy9jqXaMZnxUqRYEU,10131
3
4
  phoenix/datetime_utils.py,sha256=yDKjwX2Vtqw9h5F_ProtP-TsXidM43uIvmJ_pOzYc9A,3405
4
5
  phoenix/exceptions.py,sha256=n2L2KKuecrdflB9MsCdAYCiSEvGJptIsfRkXMoJle7A,169
5
6
  phoenix/py.typed,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
6
- phoenix/services.py,sha256=aTxhcOA1pZHB6U-B3TEcp6fqDF5oT0xCUvEUNMZVTUQ,5175
7
+ phoenix/services.py,sha256=OyML4t2XGnlqF0JXA9_uccL8HslTABxep9Ci7MViKEU,5216
7
8
  phoenix/settings.py,sha256=cO-qgis_S27nHirTobYI9hHPfZH18R--WMmxNdsVUwc,273
8
- phoenix/version.py,sha256=l7f0EI2kXBLXXp82SILFQvXoX83QSluu_cmoTReAP9g,23
9
+ phoenix/version.py,sha256=4h0uTKk4f4HhC84z3Ggthi61qR_IkvFgq-cnwxm5tCU,23
9
10
  phoenix/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
11
  phoenix/core/embedding_dimension.py,sha256=zKGbcvwOXgLf-yrJBpQyKtd-LEOPRKHnUToyAU8Owis,87
11
12
  phoenix/core/model.py,sha256=km_a--PBHOuA337ClRw9xqhOHhrUT6Rl9pz_zV0JYkQ,4843
@@ -15,13 +16,13 @@ phoenix/db/README.md,sha256=IvKaZyf9ECbGBYYePaRhBveKZwDbxAc-c7BMxJYZh6Q,595
15
16
  phoenix/db/__init__.py,sha256=pDjEFXukHmJBM-1D8RjmXkvLsz85YWNxMQczt81ec3A,118
16
17
  phoenix/db/alembic.ini,sha256=p8DjVqGUs_tTx8oU56JP7qj-rMUebNFizItUSv_hPhs,3763
17
18
  phoenix/db/bulk_inserter.py,sha256=qgg8pt5k4VnHKOE0-KoReXVAfXRhLt-sMZihI-b4X9I,12761
18
- phoenix/db/engines.py,sha256=R3btYTSOSd6BwRA59EmhhojL0HCQ7NnzFIXQrPYS0iU,4812
19
+ phoenix/db/engines.py,sha256=l9Zl7mPd1q4RTXpThzYzc4Lo7TuuBwaGrC-zK0SMnn4,5300
19
20
  phoenix/db/helpers.py,sha256=2zSc4n5IJfu-CaOFoBfqTB35M1nTFcAc8tqLsNtF2Jw,3488
20
- phoenix/db/migrate.py,sha256=MuhtNWnR24riROvarvKfbRb4_D5xuQi6P760vBUKl1E,2270
21
- phoenix/db/models.py,sha256=96XA9m3vV-HpQDggHqGWPNvAOEMANediIbvvqAUaY7U,23686
21
+ phoenix/db/migrate.py,sha256=NNcci4LHw0wFR7U6quWrA-sw_A4h2lAA1_LePMLkb4w,2629
22
+ phoenix/db/models.py,sha256=lYWJYtD2asQwKU1B8JKyteWpHVYjhr1j0tmZdf9CQ5Y,23686
22
23
  phoenix/db/insertion/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
23
24
  phoenix/db/insertion/constants.py,sha256=8wifm7X-1XvroZ__R2Gc96NsgLhTDn0zXl4lehlXtcA,70
24
- phoenix/db/insertion/dataset.py,sha256=_vxy5e6W5jEuvO2fMKbbNCn9JvHkwI4LRKk_10eKFVg,7171
25
+ phoenix/db/insertion/dataset.py,sha256=vBIroaSIWz5ZXE0Hul8osbebjS40gDCbsSP-YnazEwk,7212
25
26
  phoenix/db/insertion/document_annotation.py,sha256=qp8E63LzlthAScQg6opqln5Qg1d7UdtP3rkYL4riTgU,5983
26
27
  phoenix/db/insertion/evaluation.py,sha256=SoI85N3MYUSeNgjKa5WzFw14OfNjNTjExv-2m3sxaR8,6371
27
28
  phoenix/db/insertion/helpers.py,sha256=z_Wnckhdf-F7xadWgaAV5ScXnLft8EtaYJCuIkma2Vw,3486
@@ -33,10 +34,10 @@ phoenix/db/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3h
33
34
  phoenix/db/migrations/env.py,sha256=QbzB5zrRs6XQQmrYeUpuzeilcMlM-MsbaAgHHYcIHTI,3626
34
35
  phoenix/db/migrations/script.py.mako,sha256=MEqL-2qATlST9TAOeYgscMn1uy6HUS9NFvDgl93dMj8,635
35
36
  phoenix/db/migrations/future_versions/README.md,sha256=3QtDx40SAD-IITjbdlKR2N_CBxT5y37C1OQs05EDt7o,184
36
- phoenix/db/migrations/future_versions/cd164e83824f_users_and_tokens.py,sha256=6iSewwJCLbGWG1qojJHDiwM-6GIddgsLlGosVdSo6s8,8794
37
+ phoenix/db/migrations/future_versions/cd164e83824f_users_and_tokens.py,sha256=J2HD50V8GPuj6UVFOvUCuXtiTOLPU-MxrhRlMcpdBb0,8795
37
38
  phoenix/db/migrations/versions/.gitignore,sha256=chLdMrfkICZvLY7lCEcuqF32sVp61Jml4PodFryEU94,33
38
39
  phoenix/db/migrations/versions/10460e46d750_datasets.py,sha256=eZAyz720DmpOd7RnuxDN2dVNXVuMrdlCA7eAOxyMtfs,8695
39
- phoenix/db/migrations/versions/3be8647b87d8_add_token_columns_to_spans_table.py,sha256=x6oKFwn7Zmite4G0trDQPpMCn0I7jejuBcN3-ivEuDg,3938
40
+ phoenix/db/migrations/versions/3be8647b87d8_add_token_columns_to_spans_table.py,sha256=bV7urfCPkrXhkyg8oaUfLYN093UoRecLQXPkXJ8SIOs,3666
40
41
  phoenix/db/migrations/versions/cf03bd6bae1d_init.py,sha256=09cpofqje8zi4eQFfUn-i21x7VcsUYOfLKKUlrtKrGc,8662
41
42
  phoenix/experiments/__init__.py,sha256=6JGwgUd7xCbGpuHqYZlsmErmYvVgv7N_j43bn3dUqsk,123
42
43
  phoenix/experiments/functions.py,sha256=B60_JT0jmLFQbkweZc2Px1q1vzE3Q_w2EI5jRgT19Rk,32589
@@ -50,12 +51,12 @@ phoenix/experiments/evaluators/llm_evaluators.py,sha256=zyGhxXBDNi1qoj_8I95PRSwj
50
51
  phoenix/experiments/evaluators/utils.py,sha256=XYqB0bOljyR0GewmR_mm9Ndl_q95EkjjDqfXd7YVqTk,9303
51
52
  phoenix/inferences/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
52
53
  phoenix/inferences/errors.py,sha256=cGp9vxnw4SewFoWBV3ZGMkhE0Kh73lPIv3Ppz_H_RoA,8261
53
- phoenix/inferences/fixtures.py,sha256=FC2eRL4dpobKQHYOilFtDexUWFkMZ_w6jun_4WkbMk0,20792
54
- phoenix/inferences/inferences.py,sha256=aH2uHRJR4_W7CVy3EMEioaEBM8cTRYBUyVuDDVjwVXw,31106
54
+ phoenix/inferences/fixtures.py,sha256=r5mN8I58rirO2YqHr_88G2aRC0ADYLSKozgxEG89MTM,20833
55
+ phoenix/inferences/inferences.py,sha256=r-ByeW_AU6cu199iJMn_Td3XywqtRfrLS7cDuHaayUA,31147
55
56
  phoenix/inferences/schema.py,sha256=UYej9IJ6pFeNW3fq721kJy16ONso_xVDm78Q68G4hl4,6643
56
57
  phoenix/inferences/validation.py,sha256=fdmbsjUBwtacRiVFdh9aem-QrgPfq_OlEmPdascWluc,8297
57
58
  phoenix/metrics/README.md,sha256=5gekqTU-5gGdMwvcfNp2Wlu8p1ul9kGY_jq0XXQusoI,1964
58
- phoenix/metrics/__init__.py,sha256=W8lVORvjBo66pFgUmU9P8Fi8i4yI75wOPkhU42sfeQU,2417
59
+ phoenix/metrics/__init__.py,sha256=OZ7Hc3iGsnGE6bgoDCy0BVEozcYTbE2Qu4VXpdu8KYk,2458
59
60
  phoenix/metrics/binning.py,sha256=jDd7YcyEhptCp3zWcH6tfyq87vJ3c8L50ocSuxgPAoQ,12739
60
61
  phoenix/metrics/metrics.py,sha256=7SfkDmSnpzGATtBXlYHb42r-2BfV8ELMcMgjdw3c8yA,7907
61
62
  phoenix/metrics/mixins.py,sha256=moZ5hENIKzUQt2IRhWOd5EFXnoqQkVrpqEqMH7KQzyA,7440
@@ -68,19 +69,19 @@ phoenix/pointcloud/pointcloud.py,sha256=4zAIkKs2xOUbchpj4XDAV-iPMXrfAJ15TG6rlIYG
68
69
  phoenix/pointcloud/projectors.py,sha256=zO_RrtDYSv2rqVOfIP2_9Cv11Dc8EmcZR94xhFcBYPU,1057
69
70
  phoenix/pointcloud/umap_parameters.py,sha256=3UQSjrysVOvq2V4KNpTMqNqNiK0BsTZnPBHWZ4fyJtQ,1708
70
71
  phoenix/server/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
71
- phoenix/server/app.py,sha256=A0IIC7ONjBlRWqfAjoCqfoVS0xcnPfWk3gmxphgAjcM,19674
72
+ phoenix/server/app.py,sha256=KcxSXDR_awZlIcJg4YkUTZIXlje85_dpdMB2OM-M7SQ,22059
72
73
  phoenix/server/dml_event.py,sha256=MpjCFqljxvgb9OB5Cez9vJesb3oHb3XxXictynBfcis,2851
73
74
  phoenix/server/dml_event_handler.py,sha256=6p-PucctivelVHfO-_9zNxWZYPr_eGjDF3bKjLtc5co,8251
74
75
  phoenix/server/grpc_server.py,sha256=jllxDNkpLQxDkvej4RhTokobowbvydF-SU8gSw1MTCc,3378
75
- phoenix/server/main.py,sha256=xxG650qi2bIX6HCcfSiLd1QyImiBoWbsVJ55m21GyNg,11471
76
+ phoenix/server/main.py,sha256=rcXBEcSpnwFHwTZcMIu3s8Y7ABCMEZYJ0xb8wKtBfL0,11731
76
77
  phoenix/server/prometheus.py,sha256=j9DHB2fERuq_ZKmwVaqR-9wx5WcPPuU1Cm5Bhg5241Y,2996
77
78
  phoenix/server/telemetry.py,sha256=T_2OKrxNViAeaANlNspEekg_Y5uZIFWvKAnpz8Aoqvk,2762
78
79
  phoenix/server/thread_server.py,sha256=RwXQGP_QhGD7le6WB7xEygEEuwBl5Ck_Zo8xGIYGi9M,2135
79
- phoenix/server/types.py,sha256=UCCkwEzUAbRdu-hZpG7A2hdPM09onBezaXNtWX4A7og,3431
80
+ phoenix/server/types.py,sha256=S2dReLNboR2nzjRK5j3MUyUDqu6AQFD7KRwJkeKj1q4,3609
80
81
  phoenix/server/api/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
81
- phoenix/server/api/context.py,sha256=2-kJpoix-OISxyAhoI5FFEnQMt9ad-3HQ3VOFCjdbxU,2799
82
+ phoenix/server/api/context.py,sha256=N_kqhQIlRyPS6JpN-fgCZ2U4_ZbhaknH-23g5JoRU8g,3282
82
83
  phoenix/server/api/interceptor.py,sha256=ykDnoC_apUd-llVli3m1CW18kNSIgjz2qZ6m5JmPDu8,1294
83
- phoenix/server/api/queries.py,sha256=cO47wUByLDTjrt4N28kVkQbxbJXusZ8TTCkXiIcEuaU,23804
84
+ phoenix/server/api/queries.py,sha256=GXQzz1KmRqZCOENemdgoqBlTVA_4V2AZE-g0hBcO0Qk,23839
84
85
  phoenix/server/api/schema.py,sha256=BcxdqO5CSGqpKd-AAJHMjFlzaK9oJA8GJuxmMfcdjn4,434
85
86
  phoenix/server/api/utils.py,sha256=Kl47G-1A7QKTDrc75BU2QK6HupsG6MWuXxy351FOfKQ,858
86
87
  phoenix/server/api/dataloaders/__init__.py,sha256=TrOGnU_SD_vEIxOE_dm8HrD5C2ScLFQ4xQ7f8r-E76s,3064
@@ -136,28 +137,32 @@ phoenix/server/api/input_types/SpanAnnotationSort.py,sha256=T5pAGzmh4MiJp9JMAzND
136
137
  phoenix/server/api/input_types/SpanSort.py,sha256=Dhvl8BIoV52yHoqntfOax_gUc15uH8ITI_00Ha7PvYc,5959
137
138
  phoenix/server/api/input_types/TimeRange.py,sha256=yzx-gxj8mDeGLft1FzU_x1MVEgIG5Pt6-f8PUVDgipQ,522
138
139
  phoenix/server/api/input_types/TraceAnnotationSort.py,sha256=BzwiUnMh2VsgQYnhDlbJ6ljHugqIS4YDUlYzvq_tl3o,365
140
+ phoenix/server/api/input_types/UserRoleInput.py,sha256=xxhFe0ITZOgRVEJbVem_W6F1Ip_H6xDENdQqMMx-kKE,129
139
141
  phoenix/server/api/input_types/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
140
- phoenix/server/api/mutations/__init__.py,sha256=UKUAhD5NY-ZI7XONnRRkaHoFuuU3idmE4fk6Sjgy18M,776
141
- phoenix/server/api/mutations/auth.py,sha256=vPRFoj7J6PV6QeODewG4K0PhoOebS5AfMRpbi_wuhyQ,311
142
+ phoenix/server/api/mutations/__init__.py,sha256=Cu4lPgUFRAGzKO528jKepwKtfre9lkLTN059S2Shmnw,977
143
+ phoenix/server/api/mutations/api_key_mutations.py,sha256=cv6AT6UAL55lTC_UqMdcN-1TjWAgqqZi__S9Tw12t6I,3688
144
+ phoenix/server/api/mutations/auth.py,sha256=8o4tTfGCPkpUauuB9ijPH84Od77UX_UrQWfmUsnujI4,524
142
145
  phoenix/server/api/mutations/dataset_mutations.py,sha256=0feBUW_07FEIx6uzepjxfRVhk5lAck0AkrqS1GVdoF4,27041
143
146
  phoenix/server/api/mutations/experiment_mutations.py,sha256=OXtLYdLA33RGy1MFctfv6ug2sODcDElhJph_J9vkIjk,3157
144
147
  phoenix/server/api/mutations/export_events_mutations.py,sha256=t_wYBxaqvBJYRoHslh3Bmoxmwlzoy0u8SsBKWIKN5hE,4028
145
148
  phoenix/server/api/mutations/project_mutations.py,sha256=MLm7I97lJ85hTuc1tq8sdYA8Ps5WKMV-bGqeeN-Ey90,2279
146
149
  phoenix/server/api/mutations/span_annotations_mutations.py,sha256=DM9gzxrMSAcxwXQ6jNaNGDVgl8oP50LZsBWRYQwLaSo,5955
147
150
  phoenix/server/api/mutations/trace_annotations_mutations.py,sha256=VDiNzX63Agci7WeMbiK-C770JedlC5R7TZVe1UaRhDE,5930
151
+ phoenix/server/api/mutations/user_mutations.py,sha256=uUZ9LEPQAWRxGA4CVHFClHSGpyMlFHwgi6blu3pkuVA,2998
148
152
  phoenix/server/api/openapi/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
149
153
  phoenix/server/api/openapi/main.py,sha256=KNutA_7AvV_WlGX8cOkvvDujcJKQ7AD1HT6rTpCpR8A,616
150
154
  phoenix/server/api/openapi/schema.py,sha256=oVZoflWMfzOrLKMIrjr3iLnJ13rmN-t_DOe9g6KoN5s,471
151
155
  phoenix/server/api/routers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
156
+ phoenix/server/api/routers/auth.py,sha256=dGug0NjOjW1mmIghmheAgHutG7_0-RjL-FcEReWzTHc,1806
152
157
  phoenix/server/api/routers/utils.py,sha256=M41BoH-fl37izhRuN2aX7lWm7jOC20A_3uClv9TVUUY,583
153
158
  phoenix/server/api/routers/v1/__init__.py,sha256=nb49zcOdAi3DSGuC9gUubN9Yri-o7-WFdlGak4jGuFw,1462
154
- phoenix/server/api/routers/v1/datasets.py,sha256=pyLtVEGnjwxh1wJySBOUFrsjtawatfpaF8F3WijK8qU,37049
159
+ phoenix/server/api/routers/v1/datasets.py,sha256=l3Hlc9AVyvX5GdT9iOXBsV-i4c_vtnCaXeSAWdNzcw8,37090
155
160
  phoenix/server/api/routers/v1/evaluations.py,sha256=FSfz9MTi8s65F07abDXlb9-y97fDZSYbqsCXpimwO7g,12628
156
161
  phoenix/server/api/routers/v1/experiment_evaluations.py,sha256=RTQnjupjmh07xowjq77ajbuAZhzIEfYxA4ZtECvGwOU,4844
157
162
  phoenix/server/api/routers/v1/experiment_runs.py,sha256=0G7GgGcZv9dzK47tsPp-p4k5O7W4F_aNRrsNuJN7mho,6393
158
163
  phoenix/server/api/routers/v1/experiments.py,sha256=GeT3Rya4bdaCr6sCf2Vx6fQ_gfMX5XyFHmODCSJiCfU,9951
159
164
  phoenix/server/api/routers/v1/pydantic_compat.py,sha256=FeK8oe2brqu-djsoqRxiKL4tw5cHmi89OHVfCFxYsAo,2890
160
- phoenix/server/api/routers/v1/spans.py,sha256=Hd63VHQUqSy0VHsYQ5DPLf5WwKALMXZOIh_ig_5MStc,8780
165
+ phoenix/server/api/routers/v1/spans.py,sha256=MAkMLrONFtItQxkHJde_Wpvz0jsgydegxVZOkZkRUsU,8781
161
166
  phoenix/server/api/routers/v1/traces.py,sha256=HJDmYKMATL40dZEJro6uQ3imbCZBzk3nUun9d21jcDs,7799
162
167
  phoenix/server/api/routers/v1/utils.py,sha256=xvl2v-BKUkqmFVMmgmmWGFKuRBTrUdoiAeT3mCYEE68,3086
163
168
  phoenix/server/api/types/Annotation.py,sha256=7Ym7iuVcbwHlw2yIRylz4nATAF_Cm-Z17qcjiooj1cc,751
@@ -215,7 +220,7 @@ phoenix/server/api/types/TraceAnnotation.py,sha256=OW6A2zr1gomOuG0XQe55dk15XXX2D
215
220
  phoenix/server/api/types/UMAPPoints.py,sha256=5sOuruzM8saXa8C2XiyUfk2XPrkVGmhqKpclMYRw1dk,1656
216
221
  phoenix/server/api/types/User.py,sha256=j04JLq51GBqjcAuZfErUn05bB3nSpy-DlNL2jm4AaHA,300
217
222
  phoenix/server/api/types/UserApiKey.py,sha256=ZHfCHHxPUE5CSKiQuCqzlY7GkSZ5OzvcORoBMRPA3gg,227
218
- phoenix/server/api/types/UserRole.py,sha256=VydiFaNIjcuYtvrkBykhWc2nXvHWWhAUsZ9D1Tyqdng,140
223
+ phoenix/server/api/types/UserRole.py,sha256=I6QuYMZRuJT0kKs6KQQYroCZ9D60MQWdzNqdlvOYmRk,140
219
224
  phoenix/server/api/types/ValidationResult.py,sha256=pHwdYk4J7SJ5xhlWWHg_6qWkfk4rjOx-bSkGHvkDE3Q,142
220
225
  phoenix/server/api/types/VectorDriftMetricEnum.py,sha256=etiJM5ZjQuD-oE7sY-FbdIKY050jk3IU49IMwmfJbEc,188
221
226
  phoenix/server/api/types/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -232,29 +237,29 @@ phoenix/server/static/apple-touch-icon-76x76.png,sha256=CT_xT12I0u2i0WU8JzBZBuOQ
232
237
  phoenix/server/static/apple-touch-icon.png,sha256=fOfpjqGpWYbJ0eAurKsyoZP1EAs6ZVooBJ_SGk2ZkDs,3801
233
238
  phoenix/server/static/favicon.ico,sha256=bY0vvCKRftemZfPShwZtE93DiiQdaYaozkPGwNFr6H8,34494
234
239
  phoenix/server/static/modernizr.js,sha256=mvK-XtkNqjOral-QvzoqsyOMECXIMu5BQwSVN_wcU9c,2564
235
- phoenix/server/static/.vite/manifest.json,sha256=Sc2AOYCyZmm4BdNQ7itmmI9AFoBxJzfMoZC8YoWh1gA,1929
236
- phoenix/server/static/assets/components-DzA9gIHT.js,sha256=miEpsrzpn_BM4W1HFe2dvNJJILaO8sq677NhjhiL8w4,187160
237
- phoenix/server/static/assets/index-BuTlV4Gk.js,sha256=JsAPpelKgf1a9JtrnyKLgJn4w3vUzYLCk5bsCXnCnkM,7466
238
- phoenix/server/static/assets/pages-DzkUGFGV.js,sha256=qX2Q1wyfONVCfbx7uPd2W33MIZ4B561SIl0TO2LDK1Y,457253
239
- phoenix/server/static/assets/vendor-CIqy43_9.js,sha256=YclKUblIZ8JB6hhl36BtG_7FEsrNYbgnsW0d0w6zIxk,1355423
240
+ phoenix/server/static/.vite/manifest.json,sha256=5TDpyzP0p7LFbVpIP1n6hAq2Y9BIMRY07AZTk7p3_pA,1929
241
+ phoenix/server/static/assets/components-1Ahruijo.js,sha256=wZdgukvLzeGCBdNCLuU-yOra8gsjFIuC9hSEwa7LuDw,187182
242
+ phoenix/server/static/assets/index-BEE_RWJx.js,sha256=WrJXGQvMa2pSjnH_xjCwyRmDfm9MVD6FXq0YytraYEI,7461
243
+ phoenix/server/static/assets/pages-CFS6mPnW.js,sha256=OLH9XUE7sS_MRSpD7x-CW4PJt981P-C2biAvQ0dO88g,466231
240
244
  phoenix/server/static/assets/vendor-DxkFTwjz.css,sha256=nZrkr0u6NNElFGvpWHk9GTHeGoibCXCli1bE7mXZGZg,1816
241
- phoenix/server/static/assets/vendor-arizeai-B1YgcWL8.js,sha256=b-Q3bHiEkWo29daqYevZItyECmgFu0LMjYgpbDK3mSc,304008
242
- phoenix/server/static/assets/vendor-codemirror-_bcwCA1C.js,sha256=oqAOjM0IviW-Si9fscZsSTR3_aj2vdp8H6acoyIFzek,503031
243
- phoenix/server/static/assets/vendor-recharts-C3pM_Wlg.js,sha256=gnMIx0IktJjLzrgDehKdRebp3EkFAGli_067pIMVXFY,282859
245
+ phoenix/server/static/assets/vendor-aSQri0vz.js,sha256=x_07SENutKMhtJ9HgFqkQHvwsDTfPkMmzQznY3HY7Zo,1359197
246
+ phoenix/server/static/assets/vendor-arizeai-CsdcB1NH.js,sha256=VEn7hFJXcHV_DODmeDi9pEpF_D2NQ1bZYewbPe3BhIw,304008
247
+ phoenix/server/static/assets/vendor-codemirror-CYHkhs7D.js,sha256=FmRfBdG2nCiQBDRc5bleOXwtUlepZjabWDnYhRq5rsc,503031
248
+ phoenix/server/static/assets/vendor-recharts-B0sannek.js,sha256=Fi6Z_dwtI67mHHAdHISUHWzZQYsOwV1NGtomvOLv9xA,282859
244
249
  phoenix/server/static/assets/vendor-three-DwGkEfCM.js,sha256=0D12ZgKzfKCTSdSTKJBFR2RZO_xxeMXrqDp0AszZqHY,620972
245
250
  phoenix/server/templates/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
246
251
  phoenix/server/templates/index.html,sha256=dAm0IClgJUdT5AOmjZvtgMg8F_xGrRGv95SAkUyx_kg,4325
247
252
  phoenix/session/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
248
- phoenix/session/client.py,sha256=C-NpmDkcyGZHR0vv5gWtteUi01cIorjWnqie9WuZb9s,32663
253
+ phoenix/session/client.py,sha256=GzeSHbNQOh5dyzHV90t_cHJpn5VL0vkNmSnaMq-2ug4,32703
249
254
  phoenix/session/data_extractor.py,sha256=gkEM3WWZAlWGMfRgQopAQlid4cSi6GNco-sdrGir0qc,2788
250
- phoenix/session/evaluation.py,sha256=aKeV8UVOyq3b7CYOwt3cWuLz0xzvMjX7vlEPILJ_fcs,5311
251
- phoenix/session/session.py,sha256=LOdcO7_XMArUMnyRlikLMZq41jgZ0Vl_BxDEpYxF4UU,26953
255
+ phoenix/session/evaluation.py,sha256=3a33ilo-WU0F_Ze26lkCMMwni0GGceSXRRBLvP3CI1o,5352
256
+ phoenix/session/session.py,sha256=3DSpXj_mlRAKnb9aNUkDjph19SLHx32IJSKYcR3r7cw,26994
252
257
  phoenix/trace/__init__.py,sha256=ujk_uYjM8gmm-YqnyXxF-kekfwid0bcaPMTtNNcaw6U,407
253
258
  phoenix/trace/attributes.py,sha256=B_OrzVaxZwFkrAFXZyicYoIti1UdUysURsvUS2GyW1U,12488
254
259
  phoenix/trace/errors.py,sha256=wB1z8qdPckngdfU-TORToekvg3344oNFAA83_hC2yFY,180
255
260
  phoenix/trace/evaluation_conventions.py,sha256=t8jydM3U0-T5YpiQKRJ3tWdWGlHtzKyttYdw-ddvPOk,1048
256
261
  phoenix/trace/exporter.py,sha256=eAYemdvDCHMugDJiaR29BFFMTQBdf3oerdkz34Cl3hE,4736
257
- phoenix/trace/fixtures.py,sha256=LABY-H6BdDA-fS9dZ13uTovXCPTdfWcvU8HsJ_qPspc,14106
262
+ phoenix/trace/fixtures.py,sha256=PgawxpyxXz2596gJEj0yU27t-zcphz_ORGsxeAMvjKY,15345
258
263
  phoenix/trace/otel.py,sha256=WA720jvRadiZBAKjsYoPyXzypHwbyEK2OZRVUwtbjB8,9976
259
264
  phoenix/trace/projects.py,sha256=2BwlNjFE-uwpqYtCu5YyBiYZk9wRPpM13vh3-Cv7GkA,2157
260
265
  phoenix/trace/schemas.py,sha256=HpWSyzec0yDHEQXEDuwyLbhpvKrqkGps8BJqGiIFj8Y,5978
@@ -269,11 +274,11 @@ phoenix/trace/dsl/filter.py,sha256=9NwATCUOgJ4Pms8XsEcinROUuxZ9UW-ISV09o65Ms70,3
269
274
  phoenix/trace/dsl/helpers.py,sha256=ULAhqWULPqYWCSNX7y50DVKIqfySx86nqb6hDvZPnVk,3896
270
275
  phoenix/trace/dsl/query.py,sha256=W0t-tiXh2WIVb96lzFAGQOQ-U46uKux78d4KL3rW-PE,30316
271
276
  phoenix/trace/langchain/__init__.py,sha256=F37GfD1pd5Kuw7R7iRUM1zXXpO8xEcycNZh5dwqBXNk,109
272
- phoenix/trace/langchain/instrumentor.py,sha256=isW3KRRP55t3JeaipAJ_aS7N_3HtwScPCO67ymSLr0Y,1372
277
+ phoenix/trace/langchain/instrumentor.py,sha256=zdh9uZfG7HWna6Wug_agS7MxSbUlfV-nhf3jWFZm61U,1412
273
278
  phoenix/trace/llama_index/__init__.py,sha256=4fpR5702Qh2t5TaXIx584EkA-BveCPftXPOKvI0Oi3I,105
274
- phoenix/trace/llama_index/callback.py,sha256=ROGgpbkXDc54Yr2S28WqoZoH4rJrUDsPTdEsP0F7AB4,4451
279
+ phoenix/trace/llama_index/callback.py,sha256=0EIyHorb0QjTn9kKW_dzrUfvl5d6re-tx_GesIAuDGY,4492
275
280
  phoenix/trace/openai/__init__.py,sha256=J3G0uqCxGdksUpaQVHds_Egv2drvh8UEqoLjiQAOveg,79
276
- phoenix/trace/openai/instrumentor.py,sha256=sb3434Npe86VHsT_jyDHlfpXWZYER9H1k_IQ5-CFTvI,1270
281
+ phoenix/trace/openai/instrumentor.py,sha256=b_QT5KFgDdgfUV9X4MSwnqFgGxAo-jxduJk3KtWBzKo,1311
277
282
  phoenix/trace/v1/__init__.py,sha256=-IbAD0ruESMjvQLvGAg9CTfjBUATFDx1OXseDPis6-0,88
278
283
  phoenix/trace/v1/evaluation_pb2.py,sha256=8sXvv2BW_vqD30MOMbmkeE2zpmm7ncik21kl3e-HzeQ,2254
279
284
  phoenix/trace/v1/evaluation_pb2.pyi,sha256=cCbbx06gwQmaH14s3J1X25TtaARh-k1abbxQdQCXGm8,4500
@@ -286,8 +291,8 @@ phoenix/utilities/logging.py,sha256=lDXd6EGaamBNcQxL4vP1au9-i_SXe0OraUDiJOcszSw,
286
291
  phoenix/utilities/project.py,sha256=8IJuMM4yUMoooPi37sictGj8Etu9rGmq6RFtc9848cQ,436
287
292
  phoenix/utilities/re.py,sha256=PDve_OLjRTM8yQQJHC8-n3HdIONi7aNils3ZKRZ5uBM,2045
288
293
  phoenix/utilities/span_store.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
289
- arize_phoenix-4.24.0.dist-info/METADATA,sha256=EPfELiqfJFn2mwDGfD5En4FTu66BEa_cmlyblNqkvBI,11902
290
- arize_phoenix-4.24.0.dist-info/WHEEL,sha256=1yFddiXMmvYK7QYTqtRNtX66WJ0Mz8PYEiEUoOUUxRY,87
291
- arize_phoenix-4.24.0.dist-info/licenses/IP_NOTICE,sha256=JBqyyCYYxGDfzQ0TtsQgjts41IJoa-hiwDrBjCb9gHM,469
292
- arize_phoenix-4.24.0.dist-info/licenses/LICENSE,sha256=HFkW9REuMOkvKRACuwLPT0hRydHb3zNg-fdFt94td18,3794
293
- arize_phoenix-4.24.0.dist-info/RECORD,,
294
+ arize_phoenix-4.26.0.dist-info/METADATA,sha256=tf_mydvCrJ0GBxa58iEnat5FdCDItFKPN88hnZj7Dqs,12023
295
+ arize_phoenix-4.26.0.dist-info/WHEEL,sha256=1yFddiXMmvYK7QYTqtRNtX66WJ0Mz8PYEiEUoOUUxRY,87
296
+ arize_phoenix-4.26.0.dist-info/licenses/IP_NOTICE,sha256=JBqyyCYYxGDfzQ0TtsQgjts41IJoa-hiwDrBjCb9gHM,469
297
+ arize_phoenix-4.26.0.dist-info/licenses/LICENSE,sha256=HFkW9REuMOkvKRACuwLPT0hRydHb3zNg-fdFt94td18,3794
298
+ arize_phoenix-4.26.0.dist-info/RECORD,,
phoenix/auth.py ADDED
@@ -0,0 +1,45 @@
1
+ import re
2
+ from hashlib import pbkdf2_hmac
3
+
4
+
5
+ def compute_password_hash(*, password: str, salt: str) -> str:
6
+ """
7
+ Salts and hashes a password using PBKDF2, HMAC, and SHA256.
8
+ """
9
+ password_bytes = password.encode("utf-8")
10
+ salt_bytes = salt.encode("utf-8")
11
+ password_hash_bytes = pbkdf2_hmac("sha256", password_bytes, salt_bytes, NUM_ITERATIONS)
12
+ password_hash = password_hash_bytes.hex()
13
+ return password_hash
14
+
15
+
16
+ def is_valid_password(*, password: str, salt: str, password_hash: str) -> bool:
17
+ """
18
+ Determines whether the password is valid by salting and hashing the password
19
+ and comparing against the existing hash value.
20
+ """
21
+ return password_hash == compute_password_hash(password=password, salt=salt)
22
+
23
+
24
+ def validate_email_format(email: str) -> None:
25
+ """
26
+ Checks that the email has a valid format.
27
+ """
28
+ if EMAIL_PATTERN.match(email) is None:
29
+ raise ValueError("Invalid email address")
30
+
31
+
32
+ def validate_password_format(password: str) -> None:
33
+ """
34
+ Checks that the password has a valid format.
35
+ """
36
+ if not password:
37
+ raise ValueError("Password must be non-empty")
38
+ if any(char.isspace() for char in password):
39
+ raise ValueError("Password cannot contain whitespace characters")
40
+ if not password.isascii():
41
+ raise ValueError("Password can contain only ASCII characters")
42
+
43
+
44
+ EMAIL_PATTERN = re.compile(r"^[^@\s]+@[^@\s]+[.][^@\s]+\Z")
45
+ NUM_ITERATIONS = 10_000
phoenix/db/engines.py CHANGED
@@ -7,6 +7,7 @@ from typing import Any
7
7
 
8
8
  import aiosqlite
9
9
  import numpy as np
10
+ import sqlalchemy
10
11
  import sqlean
11
12
  from sqlalchemy import URL, StaticPool, event, make_url
12
13
  from sqlalchemy.ext.asyncio import AsyncEngine, create_async_engine
@@ -15,6 +16,7 @@ from typing_extensions import assert_never
15
16
  from phoenix.db.helpers import SupportedSQLDialect
16
17
  from phoenix.db.migrate import migrate_in_thread
17
18
  from phoenix.db.models import init_models
19
+ from phoenix.settings import Settings
18
20
 
19
21
  sqlean.extensions.enable("text", "stats")
20
22
 
@@ -118,7 +120,13 @@ def aio_sqlite_engine(
118
120
  else:
119
121
  asyncio.create_task(init_models(engine))
120
122
  else:
121
- migrate_in_thread(engine.url)
123
+ sync_engine = sqlalchemy.create_engine(
124
+ url=url.set(drivername="sqlite"),
125
+ echo=Settings.log_migrations,
126
+ json_serializer=_dumps,
127
+ creator=lambda: sqlean.connect(f"file:{database}", uri=True),
128
+ )
129
+ migrate_in_thread(sync_engine)
122
130
  return engine
123
131
 
124
132
 
@@ -130,7 +138,12 @@ def aio_postgresql_engine(
130
138
  engine = create_async_engine(url=url, echo=echo, json_serializer=_dumps)
131
139
  if not migrate:
132
140
  return engine
133
- migrate_in_thread(engine.url)
141
+ sync_engine = sqlalchemy.create_engine(
142
+ url=url.set(drivername="postgresql+psycopg"),
143
+ echo=Settings.log_migrations,
144
+ json_serializer=_dumps,
145
+ )
146
+ migrate_in_thread(sync_engine)
134
147
  return engine
135
148
 
136
149
 
@@ -24,6 +24,7 @@ from phoenix.db import models
24
24
  from phoenix.db.insertion.helpers import DataManipulationEvent
25
25
 
26
26
  logger = logging.getLogger(__name__)
27
+ logger.addHandler(logging.NullHandler())
27
28
 
28
29
  DatasetId: TypeAlias = int
29
30
  DatasetVersionId: TypeAlias = int
phoenix/db/migrate.py CHANGED
@@ -1,25 +1,34 @@
1
+ import codecs
1
2
  import logging
3
+ import sys
2
4
  from pathlib import Path
3
- from queue import Empty, Queue
5
+ from queue import Empty, SimpleQueue
4
6
  from threading import Thread
5
7
  from typing import Optional
6
8
 
7
9
  from alembic import command
8
10
  from alembic.config import Config
9
- from sqlalchemy import URL
11
+ from sqlalchemy import Engine
10
12
 
11
13
  from phoenix.exceptions import PhoenixMigrationError
12
14
  from phoenix.settings import Settings
13
15
 
14
16
  logger = logging.getLogger(__name__)
17
+ logger.addHandler(logging.NullHandler())
15
18
 
16
19
 
17
20
  def printif(condition: bool, text: str) -> None:
18
- if condition:
19
- print(text)
21
+ if not condition:
22
+ return
23
+ if sys.platform.startswith("win"):
24
+ text = codecs.encode(text, "ascii", errors="ignore").decode("ascii").strip()
25
+ print(text)
20
26
 
21
27
 
22
- def migrate(url: URL, error_queue: Optional["Queue[Exception]"] = None) -> None:
28
+ def migrate(
29
+ engine: Engine,
30
+ error_queue: Optional["SimpleQueue[BaseException]"] = None,
31
+ ) -> None:
23
32
  """
24
33
  Runs migrations on the database.
25
34
  NB: Migrate only works on non-memory databases.
@@ -37,24 +46,26 @@ def migrate(url: URL, error_queue: Optional["Queue[Exception]"] = None) -> None:
37
46
  # Explicitly set the migration directory
38
47
  scripts_location = str(Path(__file__).parent.resolve() / "migrations")
39
48
  alembic_cfg.set_main_option("script_location", scripts_location)
40
- alembic_cfg.set_main_option("sqlalchemy.url", str(url).replace("%", "%%"))
49
+ url = str(engine.url).replace("%", "%%")
50
+ alembic_cfg.set_main_option("sqlalchemy.url", url)
51
+ alembic_cfg.attributes["connection"] = engine.connect()
41
52
  command.upgrade(alembic_cfg, "head")
42
53
  printif(log_migrations, "---------------------------")
43
54
  printif(log_migrations, "✅ Migrations complete.")
44
- except Exception as e:
55
+ except BaseException as e:
45
56
  if error_queue:
46
57
  error_queue.put(e)
47
58
  raise e
48
59
 
49
60
 
50
- def migrate_in_thread(url: URL) -> None:
61
+ def migrate_in_thread(engine: Engine) -> None:
51
62
  """
52
63
  Runs migrations on the database in a separate thread.
53
64
  This is needed because depending on the context (notebook)
54
65
  the migration process can fail to execute in the main thread.
55
66
  """
56
- error_queue: Queue[Exception] = Queue()
57
- t = Thread(target=migrate, args=(url, error_queue))
67
+ error_queue: SimpleQueue[BaseException] = SimpleQueue()
68
+ t = Thread(target=migrate, args=(engine, error_queue))
58
69
  t.start()
59
70
  t.join()
60
71
 
@@ -11,7 +11,6 @@ from typing import Any, Dict, List, Optional, Sequence, TypedDict, Union
11
11
 
12
12
  import sqlalchemy as sa
13
13
  from alembic import op
14
- from phoenix.datetime_utils import normalize_datetime
15
14
  from sqlalchemy import (
16
15
  JSON,
17
16
  TIMESTAMP,
@@ -32,6 +31,8 @@ from sqlalchemy.orm import (
32
31
  mapped_column,
33
32
  )
34
33
 
34
+ from phoenix.datetime_utils import normalize_datetime
35
+
35
36
 
36
37
  class JSONB(JSON):
37
38
  # See https://docs.sqlalchemy.org/en/20/core/custom_types.html
@@ -113,7 +114,7 @@ class Base(DeclarativeBase):
113
114
  class UserRole(Base):
114
115
  __tablename__ = "user_roles"
115
116
  id: Mapped[int] = mapped_column(primary_key=True)
116
- role: Mapped[str] = mapped_column(unique=True)
117
+ name: Mapped[str] = mapped_column(unique=True)
117
118
 
118
119
 
119
120
  class User(Base):
@@ -150,10 +151,10 @@ async def insert_roles_and_users(connection: AsyncConnection) -> None:
150
151
  user to the `users` table.
151
152
  """
152
153
  await connection.execute(
153
- insert(UserRole).values([{"role": "SYSTEM"}, {"role": "ADMIN"}, {"role": "MEMBER"}])
154
+ insert(UserRole).values([{"name": "SYSTEM"}, {"name": "ADMIN"}, {"name": "MEMBER"}])
154
155
  )
155
- system_user_role_id = sa.select(UserRole.id).where(UserRole.role == "SYSTEM").scalar_subquery()
156
- admin_user_role_id = sa.select(UserRole.id).where(UserRole.role == "ADMIN").scalar_subquery()
156
+ system_user_role_id = sa.select(UserRole.id).where(UserRole.name == "SYSTEM").scalar_subquery()
157
+ admin_user_role_id = sa.select(UserRole.id).where(UserRole.name == "ADMIN").scalar_subquery()
157
158
  await connection.execute(
158
159
  insert(User).values(
159
160
  [
@@ -183,7 +184,7 @@ def upgrade() -> None:
183
184
  "user_roles",
184
185
  sa.Column("id", sa.Integer, primary_key=True),
185
186
  sa.Column(
186
- "role",
187
+ "name",
187
188
  sa.String,
188
189
  nullable=False,
189
190
  unique=True,
@@ -19,7 +19,6 @@ from sqlalchemy import (
19
19
  update,
20
20
  )
21
21
  from sqlalchemy.dialects import postgresql
22
- from sqlalchemy.ext.asyncio.engine import AsyncConnection
23
22
  from sqlalchemy.ext.compiler import compiles
24
23
  from sqlalchemy.orm import (
25
24
  DeclarativeBase,
@@ -111,11 +110,10 @@ LLM_TOKEN_COUNT_PROMPT = SpanAttributes.LLM_TOKEN_COUNT_PROMPT.split(".")
111
110
  LLM_TOKEN_COUNT_COMPLETION = SpanAttributes.LLM_TOKEN_COUNT_COMPLETION.split(".")
112
111
 
113
112
 
114
- async def get_token_counts_from_attributes(connection: AsyncConnection) -> None:
115
- """
116
- Gets token counts from attributes if present.
117
- """
118
- await connection.execute(
113
+ def upgrade() -> None:
114
+ op.add_column("spans", sa.Column("llm_token_count_prompt", sa.Integer, nullable=True))
115
+ op.add_column("spans", sa.Column("llm_token_count_completion", sa.Integer, nullable=True))
116
+ op.execute(
119
117
  update(Span).values(
120
118
  llm_token_count_prompt=Span.attributes[LLM_TOKEN_COUNT_PROMPT].as_float(),
121
119
  llm_token_count_completion=Span.attributes[LLM_TOKEN_COUNT_COMPLETION].as_float(),
@@ -123,12 +121,6 @@ async def get_token_counts_from_attributes(connection: AsyncConnection) -> None:
123
121
  )
124
122
 
125
123
 
126
- def upgrade() -> None:
127
- op.add_column("spans", sa.Column("llm_token_count_prompt", sa.Integer, nullable=True))
128
- op.add_column("spans", sa.Column("llm_token_count_completion", sa.Integer, nullable=True))
129
- op.run_async(get_token_counts_from_attributes)
130
-
131
-
132
124
  def downgrade() -> None:
133
125
  op.drop_column("spans", "llm_token_count_completion")
134
126
  op.drop_column("spans", "llm_token_count_prompt")
phoenix/db/models.py CHANGED
@@ -625,7 +625,7 @@ if ENABLE_AUTH:
625
625
  class UserRole(Base):
626
626
  __tablename__ = "user_roles"
627
627
  id: Mapped[int] = mapped_column(primary_key=True)
628
- role: Mapped[str] = mapped_column(unique=True)
628
+ name: Mapped[str] = mapped_column(unique=True)
629
629
  users: Mapped[List["User"]] = relationship("User", back_populates="role")
630
630
 
631
631
  class User(Base):
@@ -18,6 +18,7 @@ from phoenix.inferences.schema import (
18
18
  )
19
19
 
20
20
  logger = logging.getLogger(__name__)
21
+ logger.addHandler(logging.NullHandler())
21
22
 
22
23
 
23
24
  class InferencesRole(Enum):
@@ -34,6 +34,7 @@ from .schema import (
34
34
  from .validation import validate_inferences_inputs
35
35
 
36
36
  logger = logging.getLogger(__name__)
37
+ logger.addHandler(logging.NullHandler())
37
38
 
38
39
  # A schema like object. Not recommended to use this directly
39
40
  SchemaLike: TypeAlias = Any
@@ -10,6 +10,7 @@ import pandas as pd
10
10
  from phoenix.core.model_schema import Column
11
11
 
12
12
  logger = logging.getLogger(__name__)
13
+ logger.addHandler(logging.NullHandler())
13
14
 
14
15
 
15
16
  @dataclass(frozen=True)
@@ -74,3 +74,17 @@ class Context(BaseContext):
74
74
  event_queue: CanPutItem[DmlEvent] = _NoOp()
75
75
  corpus: Optional[Model] = None
76
76
  read_only: bool = False
77
+ secret: Optional[str] = None
78
+
79
+ def get_secret(self) -> str:
80
+ """A type safe way to get the application secret. Throws an error if the secret is not set.
81
+
82
+ Returns:
83
+ str: the phoenix secret
84
+ """
85
+ if self.secret is None:
86
+ raise ValueError(
87
+ "Application secret not set."
88
+ " Please set the PHOENIX_SECRET environment variable and re-deploy the application."
89
+ )
90
+ return self.secret
@@ -0,0 +1,9 @@
1
+ from enum import Enum
2
+
3
+ import strawberry
4
+
5
+
6
+ @strawberry.enum
7
+ class UserRoleInput(Enum):
8
+ ADMIN = "ADMIN"
9
+ MEMBER = "MEMBER"
@@ -1,11 +1,13 @@
1
1
  import strawberry
2
2
 
3
+ from phoenix.server.api.mutations.api_key_mutations import ApiKeyMutationMixin
3
4
  from phoenix.server.api.mutations.dataset_mutations import DatasetMutationMixin
4
5
  from phoenix.server.api.mutations.experiment_mutations import ExperimentMutationMixin
5
6
  from phoenix.server.api.mutations.export_events_mutations import ExportEventsMutationMixin
6
7
  from phoenix.server.api.mutations.project_mutations import ProjectMutationMixin
7
8
  from phoenix.server.api.mutations.span_annotations_mutations import SpanAnnotationMutationMixin
8
9
  from phoenix.server.api.mutations.trace_annotations_mutations import TraceAnnotationMutationMixin
10
+ from phoenix.server.api.mutations.user_mutations import UserMutationMixin
9
11
 
10
12
 
11
13
  @strawberry.type
@@ -16,5 +18,7 @@ class Mutation(
16
18
  ExportEventsMutationMixin,
17
19
  SpanAnnotationMutationMixin,
18
20
  TraceAnnotationMutationMixin,
21
+ ApiKeyMutationMixin,
22
+ UserMutationMixin,
19
23
  ):
20
24
  pass