arize-phoenix 4.25.0__py3-none-any.whl → 4.27.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of arize-phoenix might be problematic. Click here for more details.
- {arize_phoenix-4.25.0.dist-info → arize_phoenix-4.27.0.dist-info}/METADATA +4 -4
- {arize_phoenix-4.25.0.dist-info → arize_phoenix-4.27.0.dist-info}/RECORD +24 -22
- phoenix/auth.py +45 -0
- phoenix/db/engines.py +1 -1
- phoenix/inferences/fixtures.py +14 -8
- phoenix/server/api/input_types/UserRoleInput.py +9 -0
- phoenix/server/api/mutations/__init__.py +2 -0
- phoenix/server/api/mutations/user_mutations.py +89 -0
- phoenix/server/api/routers/auth.py +52 -0
- phoenix/server/api/routers/v1/experiments.py +55 -1
- phoenix/server/api/routers/v1/utils.py +2 -2
- phoenix/server/app.py +174 -28
- phoenix/server/main.py +71 -7
- phoenix/server/static/.vite/manifest.json +9 -9
- phoenix/server/static/assets/{components-1Ahruijo.js → components-1MfQimGx.js} +3 -3
- phoenix/server/static/assets/index-B263sE2x.js +100 -0
- phoenix/server/static/assets/{pages-CFS6mPnW.js → pages-CqZDVx20.js} +180 -171
- phoenix/session/client.py +17 -1
- phoenix/trace/dsl/helpers.py +3 -1
- phoenix/trace/fixtures.py +47 -4
- phoenix/version.py +1 -1
- phoenix/auth/__init__.py +0 -0
- phoenix/auth/utils.py +0 -15
- phoenix/server/static/assets/index-BEE_RWJx.js +0 -100
- {arize_phoenix-4.25.0.dist-info → arize_phoenix-4.27.0.dist-info}/WHEEL +0 -0
- {arize_phoenix-4.25.0.dist-info → arize_phoenix-4.27.0.dist-info}/licenses/IP_NOTICE +0 -0
- {arize_phoenix-4.25.0.dist-info → arize_phoenix-4.27.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.
|
|
3
|
+
Version: 4.27.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
|
|
@@ -45,8 +45,7 @@ Requires-Dist: pydantic!=2.0.*,<3,>=1.0
|
|
|
45
45
|
Requires-Dist: pyjwt
|
|
46
46
|
Requires-Dist: python-multipart
|
|
47
47
|
Requires-Dist: scikit-learn
|
|
48
|
-
Requires-Dist: scipy
|
|
49
|
-
Requires-Dist: scipy; platform_system != 'Darwin'
|
|
48
|
+
Requires-Dist: scipy
|
|
50
49
|
Requires-Dist: sqlalchemy[asyncio]<3,>=2.0.4
|
|
51
50
|
Requires-Dist: sqlean-py>=3.45.1
|
|
52
51
|
Requires-Dist: starlette
|
|
@@ -90,7 +89,7 @@ Requires-Dist: pandas>=1.0; extra == 'dev'
|
|
|
90
89
|
Requires-Dist: portpicker; extra == 'dev'
|
|
91
90
|
Requires-Dist: pre-commit; extra == 'dev'
|
|
92
91
|
Requires-Dist: prometheus-client; extra == 'dev'
|
|
93
|
-
Requires-Dist: psycopg[binary]; extra == 'dev'
|
|
92
|
+
Requires-Dist: psycopg[binary,pool]; extra == 'dev'
|
|
94
93
|
Requires-Dist: pytest-asyncio; extra == 'dev'
|
|
95
94
|
Requires-Dist: pytest-cov; extra == 'dev'
|
|
96
95
|
Requires-Dist: pytest-postgresql; extra == 'dev'
|
|
@@ -111,6 +110,7 @@ Requires-Dist: llama-index-readers-file==0.1.25; extra == 'llama-index'
|
|
|
111
110
|
Requires-Dist: llama-index==0.10.51; extra == 'llama-index'
|
|
112
111
|
Provides-Extra: pg
|
|
113
112
|
Requires-Dist: asyncpg; extra == 'pg'
|
|
113
|
+
Requires-Dist: psycopg[binary,pool]; extra == 'pg'
|
|
114
114
|
Provides-Extra: test
|
|
115
115
|
Description-Content-Type: text/markdown
|
|
116
116
|
|
|
@@ -1,13 +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
7
|
phoenix/services.py,sha256=OyML4t2XGnlqF0JXA9_uccL8HslTABxep9Ci7MViKEU,5216
|
|
7
8
|
phoenix/settings.py,sha256=cO-qgis_S27nHirTobYI9hHPfZH18R--WMmxNdsVUwc,273
|
|
8
|
-
phoenix/version.py,sha256=
|
|
9
|
-
phoenix/auth/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
10
|
-
phoenix/auth/utils.py,sha256=g-97oiJR219P6JvDUU5likHN4CdWtWo7bDJRmfVb0qk,450
|
|
9
|
+
phoenix/version.py,sha256=3tdrXCYXhzGl0HhTFxiRhMv5mTezDVgvqXVYnKeIJeo,23
|
|
11
10
|
phoenix/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
12
11
|
phoenix/core/embedding_dimension.py,sha256=zKGbcvwOXgLf-yrJBpQyKtd-LEOPRKHnUToyAU8Owis,87
|
|
13
12
|
phoenix/core/model.py,sha256=km_a--PBHOuA337ClRw9xqhOHhrUT6Rl9pz_zV0JYkQ,4843
|
|
@@ -17,7 +16,7 @@ phoenix/db/README.md,sha256=IvKaZyf9ECbGBYYePaRhBveKZwDbxAc-c7BMxJYZh6Q,595
|
|
|
17
16
|
phoenix/db/__init__.py,sha256=pDjEFXukHmJBM-1D8RjmXkvLsz85YWNxMQczt81ec3A,118
|
|
18
17
|
phoenix/db/alembic.ini,sha256=p8DjVqGUs_tTx8oU56JP7qj-rMUebNFizItUSv_hPhs,3763
|
|
19
18
|
phoenix/db/bulk_inserter.py,sha256=qgg8pt5k4VnHKOE0-KoReXVAfXRhLt-sMZihI-b4X9I,12761
|
|
20
|
-
phoenix/db/engines.py,sha256=
|
|
19
|
+
phoenix/db/engines.py,sha256=l9Zl7mPd1q4RTXpThzYzc4Lo7TuuBwaGrC-zK0SMnn4,5300
|
|
21
20
|
phoenix/db/helpers.py,sha256=2zSc4n5IJfu-CaOFoBfqTB35M1nTFcAc8tqLsNtF2Jw,3488
|
|
22
21
|
phoenix/db/migrate.py,sha256=NNcci4LHw0wFR7U6quWrA-sw_A4h2lAA1_LePMLkb4w,2629
|
|
23
22
|
phoenix/db/models.py,sha256=lYWJYtD2asQwKU1B8JKyteWpHVYjhr1j0tmZdf9CQ5Y,23686
|
|
@@ -52,7 +51,7 @@ phoenix/experiments/evaluators/llm_evaluators.py,sha256=zyGhxXBDNi1qoj_8I95PRSwj
|
|
|
52
51
|
phoenix/experiments/evaluators/utils.py,sha256=XYqB0bOljyR0GewmR_mm9Ndl_q95EkjjDqfXd7YVqTk,9303
|
|
53
52
|
phoenix/inferences/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
54
53
|
phoenix/inferences/errors.py,sha256=cGp9vxnw4SewFoWBV3ZGMkhE0Kh73lPIv3Ppz_H_RoA,8261
|
|
55
|
-
phoenix/inferences/fixtures.py,sha256=
|
|
54
|
+
phoenix/inferences/fixtures.py,sha256=oTtfjkI9ULxQ9bKtt91QGSDd3eyJ6T1ZylGiJf1iueo,20892
|
|
56
55
|
phoenix/inferences/inferences.py,sha256=r-ByeW_AU6cu199iJMn_Td3XywqtRfrLS7cDuHaayUA,31147
|
|
57
56
|
phoenix/inferences/schema.py,sha256=UYej9IJ6pFeNW3fq721kJy16ONso_xVDm78Q68G4hl4,6643
|
|
58
57
|
phoenix/inferences/validation.py,sha256=fdmbsjUBwtacRiVFdh9aem-QrgPfq_OlEmPdascWluc,8297
|
|
@@ -70,11 +69,11 @@ phoenix/pointcloud/pointcloud.py,sha256=4zAIkKs2xOUbchpj4XDAV-iPMXrfAJ15TG6rlIYG
|
|
|
70
69
|
phoenix/pointcloud/projectors.py,sha256=zO_RrtDYSv2rqVOfIP2_9Cv11Dc8EmcZR94xhFcBYPU,1057
|
|
71
70
|
phoenix/pointcloud/umap_parameters.py,sha256=3UQSjrysVOvq2V4KNpTMqNqNiK0BsTZnPBHWZ4fyJtQ,1708
|
|
72
71
|
phoenix/server/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
73
|
-
phoenix/server/app.py,sha256=
|
|
72
|
+
phoenix/server/app.py,sha256=rPzpaEpTSViIP-RLbHzYfxAf7zplKOEbFHxCb40YXFc,26954
|
|
74
73
|
phoenix/server/dml_event.py,sha256=MpjCFqljxvgb9OB5Cez9vJesb3oHb3XxXictynBfcis,2851
|
|
75
74
|
phoenix/server/dml_event_handler.py,sha256=6p-PucctivelVHfO-_9zNxWZYPr_eGjDF3bKjLtc5co,8251
|
|
76
75
|
phoenix/server/grpc_server.py,sha256=jllxDNkpLQxDkvej4RhTokobowbvydF-SU8gSw1MTCc,3378
|
|
77
|
-
phoenix/server/main.py,sha256=
|
|
76
|
+
phoenix/server/main.py,sha256=KcyiOtU7pJrWASTih4huF53WizXUdjCpWSqY6glk-mA,14037
|
|
78
77
|
phoenix/server/prometheus.py,sha256=j9DHB2fERuq_ZKmwVaqR-9wx5WcPPuU1Cm5Bhg5241Y,2996
|
|
79
78
|
phoenix/server/telemetry.py,sha256=T_2OKrxNViAeaANlNspEekg_Y5uZIFWvKAnpz8Aoqvk,2762
|
|
80
79
|
phoenix/server/thread_server.py,sha256=RwXQGP_QhGD7le6WB7xEygEEuwBl5Ck_Zo8xGIYGi9M,2135
|
|
@@ -138,8 +137,9 @@ phoenix/server/api/input_types/SpanAnnotationSort.py,sha256=T5pAGzmh4MiJp9JMAzND
|
|
|
138
137
|
phoenix/server/api/input_types/SpanSort.py,sha256=Dhvl8BIoV52yHoqntfOax_gUc15uH8ITI_00Ha7PvYc,5959
|
|
139
138
|
phoenix/server/api/input_types/TimeRange.py,sha256=yzx-gxj8mDeGLft1FzU_x1MVEgIG5Pt6-f8PUVDgipQ,522
|
|
140
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
|
|
141
141
|
phoenix/server/api/input_types/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
142
|
-
phoenix/server/api/mutations/__init__.py,sha256=
|
|
142
|
+
phoenix/server/api/mutations/__init__.py,sha256=Cu4lPgUFRAGzKO528jKepwKtfre9lkLTN059S2Shmnw,977
|
|
143
143
|
phoenix/server/api/mutations/api_key_mutations.py,sha256=cv6AT6UAL55lTC_UqMdcN-1TjWAgqqZi__S9Tw12t6I,3688
|
|
144
144
|
phoenix/server/api/mutations/auth.py,sha256=8o4tTfGCPkpUauuB9ijPH84Od77UX_UrQWfmUsnujI4,524
|
|
145
145
|
phoenix/server/api/mutations/dataset_mutations.py,sha256=0feBUW_07FEIx6uzepjxfRVhk5lAck0AkrqS1GVdoF4,27041
|
|
@@ -148,21 +148,23 @@ phoenix/server/api/mutations/export_events_mutations.py,sha256=t_wYBxaqvBJYRoHsl
|
|
|
148
148
|
phoenix/server/api/mutations/project_mutations.py,sha256=MLm7I97lJ85hTuc1tq8sdYA8Ps5WKMV-bGqeeN-Ey90,2279
|
|
149
149
|
phoenix/server/api/mutations/span_annotations_mutations.py,sha256=DM9gzxrMSAcxwXQ6jNaNGDVgl8oP50LZsBWRYQwLaSo,5955
|
|
150
150
|
phoenix/server/api/mutations/trace_annotations_mutations.py,sha256=VDiNzX63Agci7WeMbiK-C770JedlC5R7TZVe1UaRhDE,5930
|
|
151
|
+
phoenix/server/api/mutations/user_mutations.py,sha256=uUZ9LEPQAWRxGA4CVHFClHSGpyMlFHwgi6blu3pkuVA,2998
|
|
151
152
|
phoenix/server/api/openapi/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
152
153
|
phoenix/server/api/openapi/main.py,sha256=KNutA_7AvV_WlGX8cOkvvDujcJKQ7AD1HT6rTpCpR8A,616
|
|
153
154
|
phoenix/server/api/openapi/schema.py,sha256=oVZoflWMfzOrLKMIrjr3iLnJ13rmN-t_DOe9g6KoN5s,471
|
|
154
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
|
|
155
157
|
phoenix/server/api/routers/utils.py,sha256=M41BoH-fl37izhRuN2aX7lWm7jOC20A_3uClv9TVUUY,583
|
|
156
158
|
phoenix/server/api/routers/v1/__init__.py,sha256=nb49zcOdAi3DSGuC9gUubN9Yri-o7-WFdlGak4jGuFw,1462
|
|
157
159
|
phoenix/server/api/routers/v1/datasets.py,sha256=l3Hlc9AVyvX5GdT9iOXBsV-i4c_vtnCaXeSAWdNzcw8,37090
|
|
158
160
|
phoenix/server/api/routers/v1/evaluations.py,sha256=FSfz9MTi8s65F07abDXlb9-y97fDZSYbqsCXpimwO7g,12628
|
|
159
161
|
phoenix/server/api/routers/v1/experiment_evaluations.py,sha256=RTQnjupjmh07xowjq77ajbuAZhzIEfYxA4ZtECvGwOU,4844
|
|
160
162
|
phoenix/server/api/routers/v1/experiment_runs.py,sha256=0G7GgGcZv9dzK47tsPp-p4k5O7W4F_aNRrsNuJN7mho,6393
|
|
161
|
-
phoenix/server/api/routers/v1/experiments.py,sha256=
|
|
163
|
+
phoenix/server/api/routers/v1/experiments.py,sha256=3u275sGuYSiMyzC_obbjK3mf6aYb7SkY2c_wOg3z4xg,11751
|
|
162
164
|
phoenix/server/api/routers/v1/pydantic_compat.py,sha256=FeK8oe2brqu-djsoqRxiKL4tw5cHmi89OHVfCFxYsAo,2890
|
|
163
165
|
phoenix/server/api/routers/v1/spans.py,sha256=MAkMLrONFtItQxkHJde_Wpvz0jsgydegxVZOkZkRUsU,8781
|
|
164
166
|
phoenix/server/api/routers/v1/traces.py,sha256=HJDmYKMATL40dZEJro6uQ3imbCZBzk3nUun9d21jcDs,7799
|
|
165
|
-
phoenix/server/api/routers/v1/utils.py,sha256=
|
|
167
|
+
phoenix/server/api/routers/v1/utils.py,sha256=ph2tC3crWewKhzM2JnX-gAelEHfGLxZeFKXHVWrddmI,3086
|
|
166
168
|
phoenix/server/api/types/Annotation.py,sha256=7Ym7iuVcbwHlw2yIRylz4nATAF_Cm-Z17qcjiooj1cc,751
|
|
167
169
|
phoenix/server/api/types/AnnotationSummary.py,sha256=8B2LIROqcrPOi8hvYygsblKvSEBfSrysnKOV7F36hgA,1518
|
|
168
170
|
phoenix/server/api/types/AnnotatorKind.py,sha256=rPgGdbN1Gvc109sGQ_ZH-gfJbp93V9wlarzTEJNtUwI,236
|
|
@@ -235,10 +237,10 @@ phoenix/server/static/apple-touch-icon-76x76.png,sha256=CT_xT12I0u2i0WU8JzBZBuOQ
|
|
|
235
237
|
phoenix/server/static/apple-touch-icon.png,sha256=fOfpjqGpWYbJ0eAurKsyoZP1EAs6ZVooBJ_SGk2ZkDs,3801
|
|
236
238
|
phoenix/server/static/favicon.ico,sha256=bY0vvCKRftemZfPShwZtE93DiiQdaYaozkPGwNFr6H8,34494
|
|
237
239
|
phoenix/server/static/modernizr.js,sha256=mvK-XtkNqjOral-QvzoqsyOMECXIMu5BQwSVN_wcU9c,2564
|
|
238
|
-
phoenix/server/static/.vite/manifest.json,sha256=
|
|
239
|
-
phoenix/server/static/assets/components-
|
|
240
|
-
phoenix/server/static/assets/index-
|
|
241
|
-
phoenix/server/static/assets/pages-
|
|
240
|
+
phoenix/server/static/.vite/manifest.json,sha256=NCacyzu0qbu92qY-iwPA7JHvzK56ZJnp_usNyMD3fZw,1929
|
|
241
|
+
phoenix/server/static/assets/components-1MfQimGx.js,sha256=NfJgri_ChJVeYEExpGOvfr5SLFuX_bZcvjfEVGH3HWI,187209
|
|
242
|
+
phoenix/server/static/assets/index-B263sE2x.js,sha256=PZG-hlU6oncPYh7r6tfdNm5pjuL94SFLHg4fCyr5Oe8,7515
|
|
243
|
+
phoenix/server/static/assets/pages-CqZDVx20.js,sha256=_Lor33vSFj6dZBGvOK4fPt1vXj9pndZFpyyi5BG_8AY,467596
|
|
242
244
|
phoenix/server/static/assets/vendor-DxkFTwjz.css,sha256=nZrkr0u6NNElFGvpWHk9GTHeGoibCXCli1bE7mXZGZg,1816
|
|
243
245
|
phoenix/server/static/assets/vendor-aSQri0vz.js,sha256=x_07SENutKMhtJ9HgFqkQHvwsDTfPkMmzQznY3HY7Zo,1359197
|
|
244
246
|
phoenix/server/static/assets/vendor-arizeai-CsdcB1NH.js,sha256=VEn7hFJXcHV_DODmeDi9pEpF_D2NQ1bZYewbPe3BhIw,304008
|
|
@@ -248,7 +250,7 @@ phoenix/server/static/assets/vendor-three-DwGkEfCM.js,sha256=0D12ZgKzfKCTSdSTKJB
|
|
|
248
250
|
phoenix/server/templates/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
249
251
|
phoenix/server/templates/index.html,sha256=dAm0IClgJUdT5AOmjZvtgMg8F_xGrRGv95SAkUyx_kg,4325
|
|
250
252
|
phoenix/session/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
251
|
-
phoenix/session/client.py,sha256=
|
|
253
|
+
phoenix/session/client.py,sha256=SqnGTinAIiPGhAXFKu95MTiCHJKn4MMgfk2od2wW9s4,33291
|
|
252
254
|
phoenix/session/data_extractor.py,sha256=gkEM3WWZAlWGMfRgQopAQlid4cSi6GNco-sdrGir0qc,2788
|
|
253
255
|
phoenix/session/evaluation.py,sha256=3a33ilo-WU0F_Ze26lkCMMwni0GGceSXRRBLvP3CI1o,5352
|
|
254
256
|
phoenix/session/session.py,sha256=3DSpXj_mlRAKnb9aNUkDjph19SLHx32IJSKYcR3r7cw,26994
|
|
@@ -257,7 +259,7 @@ phoenix/trace/attributes.py,sha256=B_OrzVaxZwFkrAFXZyicYoIti1UdUysURsvUS2GyW1U,1
|
|
|
257
259
|
phoenix/trace/errors.py,sha256=wB1z8qdPckngdfU-TORToekvg3344oNFAA83_hC2yFY,180
|
|
258
260
|
phoenix/trace/evaluation_conventions.py,sha256=t8jydM3U0-T5YpiQKRJ3tWdWGlHtzKyttYdw-ddvPOk,1048
|
|
259
261
|
phoenix/trace/exporter.py,sha256=eAYemdvDCHMugDJiaR29BFFMTQBdf3oerdkz34Cl3hE,4736
|
|
260
|
-
phoenix/trace/fixtures.py,sha256=
|
|
262
|
+
phoenix/trace/fixtures.py,sha256=EHfqgvPoux6KkckX00WeG2Vhas8H5vqqFBMTztwgV-s,16857
|
|
261
263
|
phoenix/trace/otel.py,sha256=WA720jvRadiZBAKjsYoPyXzypHwbyEK2OZRVUwtbjB8,9976
|
|
262
264
|
phoenix/trace/projects.py,sha256=2BwlNjFE-uwpqYtCu5YyBiYZk9wRPpM13vh3-Cv7GkA,2157
|
|
263
265
|
phoenix/trace/schemas.py,sha256=HpWSyzec0yDHEQXEDuwyLbhpvKrqkGps8BJqGiIFj8Y,5978
|
|
@@ -269,7 +271,7 @@ phoenix/trace/utils.py,sha256=1SEQr37cdHOM0P3BdL1dszArj3Zm-VJQyb1BcJs_qO8,1833
|
|
|
269
271
|
phoenix/trace/dsl/README.md,sha256=ihmP9zGUC5V-TDbzKla76LuyDqPDQIBUH2BORwxNI68,2902
|
|
270
272
|
phoenix/trace/dsl/__init__.py,sha256=WIQIjJg362XD3s50OsPJJ0xbDsGp41bSv7vDllLrPuA,144
|
|
271
273
|
phoenix/trace/dsl/filter.py,sha256=9NwATCUOgJ4Pms8XsEcinROUuxZ9UW-ISV09o65Ms70,32600
|
|
272
|
-
phoenix/trace/dsl/helpers.py,sha256=
|
|
274
|
+
phoenix/trace/dsl/helpers.py,sha256=STQtbmF3yI97GM4yH_V--mrGe1JqldUJJc5LO1o4NWo,3919
|
|
273
275
|
phoenix/trace/dsl/query.py,sha256=W0t-tiXh2WIVb96lzFAGQOQ-U46uKux78d4KL3rW-PE,30316
|
|
274
276
|
phoenix/trace/langchain/__init__.py,sha256=F37GfD1pd5Kuw7R7iRUM1zXXpO8xEcycNZh5dwqBXNk,109
|
|
275
277
|
phoenix/trace/langchain/instrumentor.py,sha256=zdh9uZfG7HWna6Wug_agS7MxSbUlfV-nhf3jWFZm61U,1412
|
|
@@ -289,8 +291,8 @@ phoenix/utilities/logging.py,sha256=lDXd6EGaamBNcQxL4vP1au9-i_SXe0OraUDiJOcszSw,
|
|
|
289
291
|
phoenix/utilities/project.py,sha256=8IJuMM4yUMoooPi37sictGj8Etu9rGmq6RFtc9848cQ,436
|
|
290
292
|
phoenix/utilities/re.py,sha256=PDve_OLjRTM8yQQJHC8-n3HdIONi7aNils3ZKRZ5uBM,2045
|
|
291
293
|
phoenix/utilities/span_store.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
292
|
-
arize_phoenix-4.
|
|
293
|
-
arize_phoenix-4.
|
|
294
|
-
arize_phoenix-4.
|
|
295
|
-
arize_phoenix-4.
|
|
296
|
-
arize_phoenix-4.
|
|
294
|
+
arize_phoenix-4.27.0.dist-info/METADATA,sha256=P6671H1bFkfBHk7FF-1pEwq0_9AoN8zR0RRwVJRqZ2w,11936
|
|
295
|
+
arize_phoenix-4.27.0.dist-info/WHEEL,sha256=1yFddiXMmvYK7QYTqtRNtX66WJ0Mz8PYEiEUoOUUxRY,87
|
|
296
|
+
arize_phoenix-4.27.0.dist-info/licenses/IP_NOTICE,sha256=JBqyyCYYxGDfzQ0TtsQgjts41IJoa-hiwDrBjCb9gHM,469
|
|
297
|
+
arize_phoenix-4.27.0.dist-info/licenses/LICENSE,sha256=HFkW9REuMOkvKRACuwLPT0hRydHb3zNg-fdFt94td18,3794
|
|
298
|
+
arize_phoenix-4.27.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
|
@@ -139,7 +139,7 @@ def aio_postgresql_engine(
|
|
|
139
139
|
if not migrate:
|
|
140
140
|
return engine
|
|
141
141
|
sync_engine = sqlalchemy.create_engine(
|
|
142
|
-
url=url.set(drivername="postgresql"),
|
|
142
|
+
url=url.set(drivername="postgresql+psycopg"),
|
|
143
143
|
echo=Settings.log_migrations,
|
|
144
144
|
json_serializer=_dumps,
|
|
145
145
|
)
|
phoenix/inferences/fixtures.py
CHANGED
|
@@ -422,7 +422,7 @@ def get_inferences(
|
|
|
422
422
|
Downloads primary and reference inferences for a fixture if they are not found
|
|
423
423
|
locally.
|
|
424
424
|
"""
|
|
425
|
-
fixture =
|
|
425
|
+
fixture = get_fixture_by_name(fixture_name=fixture_name)
|
|
426
426
|
if no_internet:
|
|
427
427
|
paths = {role: INFERENCES_DIR / path for role, path in fixture.paths()}
|
|
428
428
|
else:
|
|
@@ -436,9 +436,11 @@ def get_inferences(
|
|
|
436
436
|
if fixture.reference_file_name is not None:
|
|
437
437
|
reference_inferences = Inferences(
|
|
438
438
|
read_parquet(paths[InferencesRole.REFERENCE]),
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
439
|
+
(
|
|
440
|
+
fixture.reference_schema
|
|
441
|
+
if fixture.reference_schema is not None
|
|
442
|
+
else fixture.primary_schema
|
|
443
|
+
),
|
|
442
444
|
"training",
|
|
443
445
|
)
|
|
444
446
|
corpus_inferences = None
|
|
@@ -451,10 +453,14 @@ def get_inferences(
|
|
|
451
453
|
return primary_inferences, reference_inferences, corpus_inferences
|
|
452
454
|
|
|
453
455
|
|
|
454
|
-
def
|
|
456
|
+
def get_fixture_by_name(fixture_name: str) -> Fixture:
|
|
455
457
|
"""
|
|
456
|
-
Returns the fixture whose name matches the input name.
|
|
457
|
-
|
|
458
|
+
Returns the fixture whose name matches the input name.
|
|
459
|
+
|
|
460
|
+
Raises
|
|
461
|
+
------
|
|
462
|
+
ValueError
|
|
463
|
+
if the input fixture name does not match any known fixture names.
|
|
458
464
|
"""
|
|
459
465
|
if fixture_name not in NAME_TO_FIXTURE:
|
|
460
466
|
valid_fixture_names = ", ".join(NAME_TO_FIXTURE.keys())
|
|
@@ -496,7 +502,7 @@ def load_example(use_case: str) -> ExampleInferences:
|
|
|
496
502
|
reference).
|
|
497
503
|
|
|
498
504
|
"""
|
|
499
|
-
fixture =
|
|
505
|
+
fixture = get_fixture_by_name(use_case)
|
|
500
506
|
primary_inferences, reference_inferences, corpus_inferences = get_inferences(use_case)
|
|
501
507
|
print(f"📥 Loaded {use_case} example datasets.")
|
|
502
508
|
print("ℹ️ About this use-case:")
|
|
@@ -7,6 +7,7 @@ from phoenix.server.api.mutations.export_events_mutations import ExportEventsMut
|
|
|
7
7
|
from phoenix.server.api.mutations.project_mutations import ProjectMutationMixin
|
|
8
8
|
from phoenix.server.api.mutations.span_annotations_mutations import SpanAnnotationMutationMixin
|
|
9
9
|
from phoenix.server.api.mutations.trace_annotations_mutations import TraceAnnotationMutationMixin
|
|
10
|
+
from phoenix.server.api.mutations.user_mutations import UserMutationMixin
|
|
10
11
|
|
|
11
12
|
|
|
12
13
|
@strawberry.type
|
|
@@ -18,5 +19,6 @@ class Mutation(
|
|
|
18
19
|
SpanAnnotationMutationMixin,
|
|
19
20
|
TraceAnnotationMutationMixin,
|
|
20
21
|
ApiKeyMutationMixin,
|
|
22
|
+
UserMutationMixin,
|
|
21
23
|
):
|
|
22
24
|
pass
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
from typing import Optional
|
|
3
|
+
|
|
4
|
+
import strawberry
|
|
5
|
+
from sqlalchemy import insert, select
|
|
6
|
+
from sqlean.dbapi2 import IntegrityError # type: ignore[import-untyped]
|
|
7
|
+
from strawberry.types import Info
|
|
8
|
+
|
|
9
|
+
from phoenix.auth import compute_password_hash, validate_email_format, validate_password_format
|
|
10
|
+
from phoenix.db import models
|
|
11
|
+
from phoenix.server.api.context import Context
|
|
12
|
+
from phoenix.server.api.input_types.UserRoleInput import UserRoleInput
|
|
13
|
+
from phoenix.server.api.types.User import User
|
|
14
|
+
from phoenix.server.api.types.UserRole import UserRole
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@strawberry.input
|
|
18
|
+
class CreateUserInput:
|
|
19
|
+
email: str
|
|
20
|
+
username: Optional[str]
|
|
21
|
+
password: str
|
|
22
|
+
role: UserRoleInput
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@strawberry.type
|
|
26
|
+
class UserMutationPayload:
|
|
27
|
+
user: User
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@strawberry.type
|
|
31
|
+
class UserMutationMixin:
|
|
32
|
+
@strawberry.mutation
|
|
33
|
+
async def create_user(
|
|
34
|
+
self,
|
|
35
|
+
info: Info[Context, None],
|
|
36
|
+
input: CreateUserInput,
|
|
37
|
+
) -> UserMutationPayload:
|
|
38
|
+
validate_email_format(email := input.email)
|
|
39
|
+
validate_password_format(password := input.password)
|
|
40
|
+
role_name = input.role.value
|
|
41
|
+
user_role_id = (
|
|
42
|
+
select(models.UserRole.id).where(models.UserRole.name == role_name).scalar_subquery()
|
|
43
|
+
)
|
|
44
|
+
secret = info.context.get_secret()
|
|
45
|
+
loop = asyncio.get_running_loop()
|
|
46
|
+
password_hash = await loop.run_in_executor(
|
|
47
|
+
executor=None,
|
|
48
|
+
func=lambda: compute_password_hash(password=password, salt=secret),
|
|
49
|
+
)
|
|
50
|
+
try:
|
|
51
|
+
async with info.context.db() as session:
|
|
52
|
+
user = await session.scalar(
|
|
53
|
+
insert(models.User)
|
|
54
|
+
.values(
|
|
55
|
+
user_role_id=user_role_id,
|
|
56
|
+
username=input.username,
|
|
57
|
+
email=email,
|
|
58
|
+
auth_method="LOCAL",
|
|
59
|
+
password_hash=password_hash,
|
|
60
|
+
reset_password=True,
|
|
61
|
+
)
|
|
62
|
+
.returning(models.User)
|
|
63
|
+
)
|
|
64
|
+
assert user is not None
|
|
65
|
+
except IntegrityError as error:
|
|
66
|
+
raise ValueError(_get_user_create_error_message(error))
|
|
67
|
+
return UserMutationPayload(
|
|
68
|
+
user=User(
|
|
69
|
+
id_attr=user.id,
|
|
70
|
+
email=user.email,
|
|
71
|
+
username=user.username,
|
|
72
|
+
created_at=user.created_at,
|
|
73
|
+
role=UserRole(id_attr=user.user_role_id, name=role_name),
|
|
74
|
+
)
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def _get_user_create_error_message(error: IntegrityError) -> str:
|
|
79
|
+
"""
|
|
80
|
+
Gets a user-facing error message to explain why user creation failed.
|
|
81
|
+
"""
|
|
82
|
+
original_error_message = str(error)
|
|
83
|
+
username_already_exists = "users.username" in original_error_message
|
|
84
|
+
email_already_exists = "users.email" in original_error_message
|
|
85
|
+
if username_already_exists:
|
|
86
|
+
return "Username already exists"
|
|
87
|
+
elif email_already_exists:
|
|
88
|
+
return "Email already exists"
|
|
89
|
+
return "Failed to create user"
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
from datetime import timedelta
|
|
3
|
+
|
|
4
|
+
from fastapi import APIRouter, Form, Request, Response
|
|
5
|
+
from sqlalchemy import select
|
|
6
|
+
from starlette.status import HTTP_204_NO_CONTENT, HTTP_401_UNAUTHORIZED
|
|
7
|
+
from typing_extensions import Annotated
|
|
8
|
+
|
|
9
|
+
from phoenix.auth import is_valid_password
|
|
10
|
+
from phoenix.db import models
|
|
11
|
+
|
|
12
|
+
router = APIRouter(include_in_schema=False)
|
|
13
|
+
|
|
14
|
+
PHOENIX_ACCESS_TOKEN_COOKIE_NAME = "phoenix-access-token"
|
|
15
|
+
PHOENIX_ACCESS_TOKEN_COOKIE_MAX_AGE_IN_SECONDS = int(timedelta(days=31).total_seconds())
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@router.post("/login")
|
|
19
|
+
async def login(
|
|
20
|
+
request: Request,
|
|
21
|
+
email: Annotated[str, Form()],
|
|
22
|
+
password: Annotated[str, Form()],
|
|
23
|
+
) -> Response:
|
|
24
|
+
async with request.app.state.db() as session:
|
|
25
|
+
if (
|
|
26
|
+
user := await session.scalar(select(models.User).where(models.User.email == email))
|
|
27
|
+
) is None or (password_hash := user.password_hash) is None:
|
|
28
|
+
return Response(status_code=HTTP_401_UNAUTHORIZED)
|
|
29
|
+
secret = request.app.state.get_secret()
|
|
30
|
+
loop = asyncio.get_running_loop()
|
|
31
|
+
if not await loop.run_in_executor(
|
|
32
|
+
executor=None,
|
|
33
|
+
func=lambda: is_valid_password(password=password, salt=secret, password_hash=password_hash),
|
|
34
|
+
):
|
|
35
|
+
return Response(status_code=HTTP_401_UNAUTHORIZED)
|
|
36
|
+
response = Response(status_code=HTTP_204_NO_CONTENT)
|
|
37
|
+
response.set_cookie(
|
|
38
|
+
key=PHOENIX_ACCESS_TOKEN_COOKIE_NAME,
|
|
39
|
+
value="token", # todo: compute access token
|
|
40
|
+
secure=True,
|
|
41
|
+
httponly=True,
|
|
42
|
+
samesite="strict",
|
|
43
|
+
max_age=PHOENIX_ACCESS_TOKEN_COOKIE_MAX_AGE_IN_SECONDS,
|
|
44
|
+
)
|
|
45
|
+
return response
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
@router.post("/logout")
|
|
49
|
+
async def logout() -> Response:
|
|
50
|
+
response = Response(status_code=HTTP_204_NO_CONTENT)
|
|
51
|
+
response.delete_cookie(key=PHOENIX_ACCESS_TOKEN_COOKIE_NAME)
|
|
52
|
+
return response
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
from datetime import datetime
|
|
2
2
|
from random import getrandbits
|
|
3
|
-
from typing import Any, Dict, Optional
|
|
3
|
+
from typing import Any, Dict, List, Optional
|
|
4
4
|
|
|
5
5
|
from fastapi import APIRouter, HTTPException
|
|
6
6
|
from pydantic import Field
|
|
@@ -252,3 +252,57 @@ async def get_experiment(request: Request, experiment_id: str) -> GetExperimentR
|
|
|
252
252
|
updated_at=experiment.updated_at,
|
|
253
253
|
)
|
|
254
254
|
)
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
class ListExperimentsResponseBody(ResponseBody[List[Experiment]]):
|
|
258
|
+
pass
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
@router.get(
|
|
262
|
+
"/datasets/{dataset_id}/experiments",
|
|
263
|
+
operation_id="listExperiments",
|
|
264
|
+
summary="List experiments by dataset",
|
|
265
|
+
response_description="Experiments retrieved successfully",
|
|
266
|
+
)
|
|
267
|
+
async def list_experiments(
|
|
268
|
+
request: Request,
|
|
269
|
+
dataset_id: str,
|
|
270
|
+
) -> ListExperimentsResponseBody:
|
|
271
|
+
dataset_gid = GlobalID.from_id(dataset_id)
|
|
272
|
+
try:
|
|
273
|
+
dataset_rowid = from_global_id_with_expected_type(dataset_gid, "Dataset")
|
|
274
|
+
except ValueError:
|
|
275
|
+
raise HTTPException(
|
|
276
|
+
detail=f"Dataset with ID {dataset_gid} does not exist",
|
|
277
|
+
status_code=HTTP_404_NOT_FOUND,
|
|
278
|
+
)
|
|
279
|
+
async with request.app.state.db() as session:
|
|
280
|
+
query = (
|
|
281
|
+
select(models.Experiment)
|
|
282
|
+
.where(models.Experiment.dataset_id == dataset_rowid)
|
|
283
|
+
.order_by(models.Experiment.id.desc())
|
|
284
|
+
)
|
|
285
|
+
|
|
286
|
+
result = await session.execute(query)
|
|
287
|
+
experiments = result.scalars().all()
|
|
288
|
+
|
|
289
|
+
if not experiments:
|
|
290
|
+
return ListExperimentsResponseBody(data=[])
|
|
291
|
+
|
|
292
|
+
data = [
|
|
293
|
+
Experiment(
|
|
294
|
+
id=str(GlobalID("Experiment", str(experiment.id))),
|
|
295
|
+
dataset_id=str(GlobalID("Dataset", str(experiment.dataset_id))),
|
|
296
|
+
dataset_version_id=str(
|
|
297
|
+
GlobalID("DatasetVersion", str(experiment.dataset_version_id))
|
|
298
|
+
),
|
|
299
|
+
repetitions=experiment.repetitions,
|
|
300
|
+
metadata=experiment.metadata_,
|
|
301
|
+
project_name=None,
|
|
302
|
+
created_at=experiment.created_at,
|
|
303
|
+
updated_at=experiment.updated_at,
|
|
304
|
+
)
|
|
305
|
+
for experiment in experiments
|
|
306
|
+
]
|
|
307
|
+
|
|
308
|
+
return ListExperimentsResponseBody(data=data)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
from typing import Any, Dict, Generic, List, Optional, TypedDict, Union
|
|
1
|
+
from typing import Any, Dict, Generic, List, Optional, TypedDict, TypeVar, Union
|
|
2
2
|
|
|
3
|
-
from typing_extensions import TypeAlias,
|
|
3
|
+
from typing_extensions import TypeAlias, assert_never
|
|
4
4
|
|
|
5
5
|
from .pydantic_compat import V1RoutesBaseModel
|
|
6
6
|
|