arize-phoenix 4.35.2__py3-none-any.whl → 4.36.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 (37) hide show
  1. {arize_phoenix-4.35.2.dist-info → arize_phoenix-4.36.0.dist-info}/METADATA +1 -1
  2. {arize_phoenix-4.35.2.dist-info → arize_phoenix-4.36.0.dist-info}/RECORD +37 -33
  3. phoenix/config.py +92 -2
  4. phoenix/db/alembic.ini +0 -34
  5. phoenix/db/engines.py +27 -10
  6. phoenix/db/insertion/dataset.py +0 -1
  7. phoenix/db/insertion/types.py +1 -1
  8. phoenix/db/migrate.py +3 -3
  9. phoenix/db/migrations/env.py +0 -7
  10. phoenix/inferences/fixtures.py +0 -1
  11. phoenix/inferences/inferences.py +0 -1
  12. phoenix/logging/__init__.py +3 -0
  13. phoenix/logging/_config.py +90 -0
  14. phoenix/logging/_filter.py +6 -0
  15. phoenix/logging/_formatter.py +69 -0
  16. phoenix/metrics/__init__.py +0 -1
  17. phoenix/otel/settings.py +4 -4
  18. phoenix/server/api/routers/v1/datasets.py +0 -1
  19. phoenix/server/app.py +2 -3
  20. phoenix/server/main.py +21 -21
  21. phoenix/server/telemetry.py +2 -2
  22. phoenix/services.py +0 -1
  23. phoenix/session/client.py +0 -1
  24. phoenix/session/evaluation.py +0 -1
  25. phoenix/session/session.py +0 -1
  26. phoenix/settings.py +9 -0
  27. phoenix/trace/exporter.py +0 -1
  28. phoenix/trace/fixtures.py +0 -2
  29. phoenix/trace/langchain/instrumentor.py +0 -1
  30. phoenix/trace/llama_index/callback.py +0 -1
  31. phoenix/trace/openai/instrumentor.py +0 -1
  32. phoenix/utilities/logging.py +9 -1
  33. phoenix/utilities/re.py +3 -3
  34. phoenix/version.py +1 -1
  35. {arize_phoenix-4.35.2.dist-info → arize_phoenix-4.36.0.dist-info}/WHEEL +0 -0
  36. {arize_phoenix-4.35.2.dist-info → arize_phoenix-4.36.0.dist-info}/licenses/IP_NOTICE +0 -0
  37. {arize_phoenix-4.35.2.dist-info → arize_phoenix-4.36.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.35.2
3
+ Version: 4.36.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
@@ -1,12 +1,12 @@
1
1
  phoenix/__init__.py,sha256=om_DSpDIu45_r_bdrataF5jIlQ2LcA1fMnno-ouePyQ,1798
2
2
  phoenix/auth.py,sha256=ugvGZlseYX9NkpWaSqb8D2kzUBlAPqT45Dx5_VUANqk,1621
3
- phoenix/config.py,sha256=3ZvppWHfrSdeIG8sFGl6kEygqhbS9KlEqLQTphM3iq0,11197
3
+ phoenix/config.py,sha256=U_jiiubbqji1ua8He46UGSsllmGCKU4M6f8kHlGErHw,14032
4
4
  phoenix/datetime_utils.py,sha256=yDKjwX2Vtqw9h5F_ProtP-TsXidM43uIvmJ_pOzYc9A,3405
5
5
  phoenix/exceptions.py,sha256=n2L2KKuecrdflB9MsCdAYCiSEvGJptIsfRkXMoJle7A,169
6
6
  phoenix/py.typed,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
7
- phoenix/services.py,sha256=OyML4t2XGnlqF0JXA9_uccL8HslTABxep9Ci7MViKEU,5216
8
- phoenix/settings.py,sha256=cO-qgis_S27nHirTobYI9hHPfZH18R--WMmxNdsVUwc,273
9
- phoenix/version.py,sha256=JgwtOJu3qkxZ92UyHg_h6MoGQFyFrvbtvfsPCW5FXyg,23
7
+ phoenix/services.py,sha256=aTxhcOA1pZHB6U-B3TEcp6fqDF5oT0xCUvEUNMZVTUQ,5175
8
+ phoenix/settings.py,sha256=ht-0oN-sMV6SPXrk7Tu1EZlngpAYkGNLYPhO8DyrdQI,661
9
+ phoenix/version.py,sha256=N8rRKvtYg5iq14cN-RICfSxhdihpUGicOmVloTAbxnk,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=km_a--PBHOuA337ClRw9xqhOHhrUT6Rl9pz_zV0JYkQ,4843
@@ -14,24 +14,24 @@ phoenix/core/model_schema.py,sha256=F2dbbVnkDLsPYoyZDv1q03uhvP8LcU1wXp0g-exiWs0,
14
14
  phoenix/core/model_schema_adapter.py,sha256=0Tm_Y_gV-WED8fKBCaFXAEFwE3CTEZS1dowqnTZ7x7g,8426
15
15
  phoenix/db/README.md,sha256=Mjrq0tG68uKQ81mN1Vmu3C795EEpZndmnE6CJXsPCXE,1909
16
16
  phoenix/db/__init__.py,sha256=pDjEFXukHmJBM-1D8RjmXkvLsz85YWNxMQczt81ec3A,118
17
- phoenix/db/alembic.ini,sha256=p8DjVqGUs_tTx8oU56JP7qj-rMUebNFizItUSv_hPhs,3763
17
+ phoenix/db/alembic.ini,sha256=GIS6HpHaKaJbbuahZg1Rc1D2_QqyCkV9r58wdARGf6w,3262
18
18
  phoenix/db/bulk_inserter.py,sha256=qgg8pt5k4VnHKOE0-KoReXVAfXRhLt-sMZihI-b4X9I,12761
19
- phoenix/db/engines.py,sha256=uWOZhIAspPCReFNqKtHsmZnY8H2DSi1kCOpQtmU3F1U,5793
19
+ phoenix/db/engines.py,sha256=GDko9_Xd41PR-lYYg1lgLGlByCFCUn8mtoKzM1Ax6f8,6481
20
20
  phoenix/db/helpers.py,sha256=2zSc4n5IJfu-CaOFoBfqTB35M1nTFcAc8tqLsNtF2Jw,3488
21
- phoenix/db/migrate.py,sha256=emiwHiFXijMw6P6gAvLcAw3qTDOuI6wmblD7sEnFLa8,2654
21
+ phoenix/db/migrate.py,sha256=lf6FFGzdyNjwtQ4TFi3mmHNGnnazMtG30PPB2qRF1Xc,2648
22
22
  phoenix/db/models.py,sha256=o97p2H4LeX8YHkTWFsbSGQANW9jHWVf6mJvE9KyFybU,23754
23
23
  phoenix/db/insertion/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
24
24
  phoenix/db/insertion/constants.py,sha256=8wifm7X-1XvroZ__R2Gc96NsgLhTDn0zXl4lehlXtcA,70
25
- phoenix/db/insertion/dataset.py,sha256=vBIroaSIWz5ZXE0Hul8osbebjS40gDCbsSP-YnazEwk,7212
25
+ phoenix/db/insertion/dataset.py,sha256=_vxy5e6W5jEuvO2fMKbbNCn9JvHkwI4LRKk_10eKFVg,7171
26
26
  phoenix/db/insertion/document_annotation.py,sha256=qp8E63LzlthAScQg6opqln5Qg1d7UdtP3rkYL4riTgU,5983
27
27
  phoenix/db/insertion/evaluation.py,sha256=SoI85N3MYUSeNgjKa5WzFw14OfNjNTjExv-2m3sxaR8,6371
28
28
  phoenix/db/insertion/helpers.py,sha256=z_Wnckhdf-F7xadWgaAV5ScXnLft8EtaYJCuIkma2Vw,3486
29
29
  phoenix/db/insertion/span.py,sha256=-CXn2LlEv2u7xbz7m8X5jALQ-BUNNzTxPJEzSmjhTpA,5958
30
30
  phoenix/db/insertion/span_annotation.py,sha256=eQK7fFjKjZGj25Qf_PTU9Q8DZiYJAw4lfcfdLKFsKe0,5259
31
31
  phoenix/db/insertion/trace_annotation.py,sha256=36CwcxSvDo2r-_y_GlmMXGnlH4BKVaMu5BWM9w-9l7A,5324
32
- phoenix/db/insertion/types.py,sha256=nQYYnpzcPxj2kdUoXfKE8ilOKlx1zpKLPc40OGuBlfk,8149
32
+ phoenix/db/insertion/types.py,sha256=4u5gvQnLQ1DE75PkWtmTp-J02m77669V9UBSibRn9P8,8147
33
33
  phoenix/db/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
34
- phoenix/db/migrations/env.py,sha256=QbzB5zrRs6XQQmrYeUpuzeilcMlM-MsbaAgHHYcIHTI,3626
34
+ phoenix/db/migrations/env.py,sha256=tFO3ceuCx9Es5_2w_BXclaQMmNQKNX21b1UEV5mYdeo,3387
35
35
  phoenix/db/migrations/script.py.mako,sha256=MEqL-2qATlST9TAOeYgscMn1uy6HUS9NFvDgl93dMj8,635
36
36
  phoenix/db/migrations/future_versions/README.md,sha256=3QtDx40SAD-IITjbdlKR2N_CBxT5y37C1OQs05EDt7o,184
37
37
  phoenix/db/migrations/future_versions/cd164e83824f_users_and_tokens.py,sha256=J2HD50V8GPuj6UVFOvUCuXtiTOLPU-MxrhRlMcpdBb0,8795
@@ -51,12 +51,16 @@ phoenix/experiments/evaluators/llm_evaluators.py,sha256=zyGhxXBDNi1qoj_8I95PRSwj
51
51
  phoenix/experiments/evaluators/utils.py,sha256=XYqB0bOljyR0GewmR_mm9Ndl_q95EkjjDqfXd7YVqTk,9303
52
52
  phoenix/inferences/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
53
53
  phoenix/inferences/errors.py,sha256=cGp9vxnw4SewFoWBV3ZGMkhE0Kh73lPIv3Ppz_H_RoA,8261
54
- phoenix/inferences/fixtures.py,sha256=oTtfjkI9ULxQ9bKtt91QGSDd3eyJ6T1ZylGiJf1iueo,20892
55
- phoenix/inferences/inferences.py,sha256=r-ByeW_AU6cu199iJMn_Td3XywqtRfrLS7cDuHaayUA,31147
54
+ phoenix/inferences/fixtures.py,sha256=X2RehNcNqkwNCgEh5JZsS37it_-rtrMppz-eQUEvefY,20851
55
+ phoenix/inferences/inferences.py,sha256=aH2uHRJR4_W7CVy3EMEioaEBM8cTRYBUyVuDDVjwVXw,31106
56
56
  phoenix/inferences/schema.py,sha256=UYej9IJ6pFeNW3fq721kJy16ONso_xVDm78Q68G4hl4,6643
57
57
  phoenix/inferences/validation.py,sha256=fdmbsjUBwtacRiVFdh9aem-QrgPfq_OlEmPdascWluc,8297
58
+ phoenix/logging/__init__.py,sha256=94Xsv4tcqRghZhpuyCn4kWV7yJ56X52aXjrWU8RM4hY,64
59
+ phoenix/logging/_config.py,sha256=7z4fReUOJ1h3dFDuVcgSF-pXpqeVqbnV5sxFcVQla1k,2881
60
+ phoenix/logging/_filter.py,sha256=jr95RFp0mgCU5tqTYXp5MSDC-Wgc3UHgPbA9J4URSjo,158
61
+ phoenix/logging/_formatter.py,sha256=2S65uTOhpamtxVFjD0Atk8zf_taZhbk6BpYtOm2JEVE,1777
58
62
  phoenix/metrics/README.md,sha256=5gekqTU-5gGdMwvcfNp2Wlu8p1ul9kGY_jq0XXQusoI,1964
59
- phoenix/metrics/__init__.py,sha256=OZ7Hc3iGsnGE6bgoDCy0BVEozcYTbE2Qu4VXpdu8KYk,2458
63
+ phoenix/metrics/__init__.py,sha256=W8lVORvjBo66pFgUmU9P8Fi8i4yI75wOPkhU42sfeQU,2417
60
64
  phoenix/metrics/binning.py,sha256=jDd7YcyEhptCp3zWcH6tfyq87vJ3c8L50ocSuxgPAoQ,12739
61
65
  phoenix/metrics/metrics.py,sha256=7SfkDmSnpzGATtBXlYHb42r-2BfV8ELMcMgjdw3c8yA,7907
62
66
  phoenix/metrics/mixins.py,sha256=moZ5hENIKzUQt2IRhWOd5EFXnoqQkVrpqEqMH7KQzyA,7440
@@ -65,20 +69,20 @@ phoenix/metrics/timeseries.py,sha256=Cib3E0njJzi0vZpmyADvbakFQA98rIkfDaYAOmsmBz8
65
69
  phoenix/metrics/wrappers.py,sha256=umZqa_5lf1wZSFe3FgzxF-qp1xbPdKD54W628GlGCUI,8392
66
70
  phoenix/otel/__init__.py,sha256=YvEiD-3aGZs9agwLNCXU34ofV3G-Q-dolfsiinOJuT0,407
67
71
  phoenix/otel/otel.py,sha256=Pe_M5eIKN3CQT6JS9FE6CnmWeiP1kG7zxkxzUgVJ_rE,19248
68
- phoenix/otel/settings.py,sha256=mVbWsZGww5evJlJJ7aownKPkHby_XOubr-ykGAZDBVY,3282
72
+ phoenix/otel/settings.py,sha256=JYGUeV87NFnhJbp34AjoNiSejn3QYMSdPIx4NpA97tM,3272
69
73
  phoenix/pointcloud/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
70
74
  phoenix/pointcloud/clustering.py,sha256=IzcG67kJ2hPP7pcqVmKPSL_6gKRonKdOT3bCtbTOqnk,820
71
75
  phoenix/pointcloud/pointcloud.py,sha256=4zAIkKs2xOUbchpj4XDAV-iPMXrfAJ15TG6rlIYGrao,2145
72
76
  phoenix/pointcloud/projectors.py,sha256=zO_RrtDYSv2rqVOfIP2_9Cv11Dc8EmcZR94xhFcBYPU,1057
73
77
  phoenix/pointcloud/umap_parameters.py,sha256=3UQSjrysVOvq2V4KNpTMqNqNiK0BsTZnPBHWZ4fyJtQ,1708
74
78
  phoenix/server/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
75
- phoenix/server/app.py,sha256=15zHrHy0dl-RuBDapU45t6pNSS4mXg21gHVhnRE4GM4,28469
79
+ phoenix/server/app.py,sha256=kQxDuHWLeDxfym3xvlvOzdw5BuzEW7F3fbPznqizMeQ,28502
76
80
  phoenix/server/dml_event.py,sha256=MpjCFqljxvgb9OB5Cez9vJesb3oHb3XxXictynBfcis,2851
77
81
  phoenix/server/dml_event_handler.py,sha256=yU23-DDwXcL35p5EPwFW0oZh6mxQxJrJAQPKcYZYJz4,8310
78
82
  phoenix/server/grpc_server.py,sha256=XWC2pjSefBBOhM6IXz0oQmjPtnonBvkG9ccUWEfHiiw,3446
79
- phoenix/server/main.py,sha256=169n7AIYKjcVw_Taw6dDRUBuVT3uqViehHOLSZGB4zE,14872
83
+ phoenix/server/main.py,sha256=b5g48hsLM6LQEwc9cgUru6Bosk0OJD4RsRTaIvvrwHM,14617
80
84
  phoenix/server/prometheus.py,sha256=j9DHB2fERuq_ZKmwVaqR-9wx5WcPPuU1Cm5Bhg5241Y,2996
81
- phoenix/server/telemetry.py,sha256=T_2OKrxNViAeaANlNspEekg_Y5uZIFWvKAnpz8Aoqvk,2762
85
+ phoenix/server/telemetry.py,sha256=4EluDDrhdDPxAjaW6lVSbi73xkB5XeUCZWOmZGdk0hg,2755
82
86
  phoenix/server/thread_server.py,sha256=RwXQGP_QhGD7le6WB7xEygEEuwBl5Ck_Zo8xGIYGi9M,2135
83
87
  phoenix/server/types.py,sha256=S2dReLNboR2nzjRK5j3MUyUDqu6AQFD7KRwJkeKj1q4,3609
84
88
  phoenix/server/api/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -161,7 +165,7 @@ phoenix/server/api/openapi/schema.py,sha256=oVZoflWMfzOrLKMIrjr3iLnJ13rmN-t_DOe9
161
165
  phoenix/server/api/routers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
162
166
  phoenix/server/api/routers/utils.py,sha256=M41BoH-fl37izhRuN2aX7lWm7jOC20A_3uClv9TVUUY,583
163
167
  phoenix/server/api/routers/v1/__init__.py,sha256=nb49zcOdAi3DSGuC9gUubN9Yri-o7-WFdlGak4jGuFw,1462
164
- phoenix/server/api/routers/v1/datasets.py,sha256=l3Hlc9AVyvX5GdT9iOXBsV-i4c_vtnCaXeSAWdNzcw8,37090
168
+ phoenix/server/api/routers/v1/datasets.py,sha256=pyLtVEGnjwxh1wJySBOUFrsjtawatfpaF8F3WijK8qU,37049
165
169
  phoenix/server/api/routers/v1/evaluations.py,sha256=FSfz9MTi8s65F07abDXlb9-y97fDZSYbqsCXpimwO7g,12628
166
170
  phoenix/server/api/routers/v1/experiment_evaluations.py,sha256=RTQnjupjmh07xowjq77ajbuAZhzIEfYxA4ZtECvGwOU,4844
167
171
  phoenix/server/api/routers/v1/experiment_runs.py,sha256=0G7GgGcZv9dzK47tsPp-p4k5O7W4F_aNRrsNuJN7mho,6393
@@ -255,16 +259,16 @@ phoenix/server/static/assets/vendor-three-DwGkEfCM.js,sha256=0D12ZgKzfKCTSdSTKJB
255
259
  phoenix/server/templates/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
256
260
  phoenix/server/templates/index.html,sha256=dAm0IClgJUdT5AOmjZvtgMg8F_xGrRGv95SAkUyx_kg,4325
257
261
  phoenix/session/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
258
- phoenix/session/client.py,sha256=ju-bjCbASZBK91uO-NvAc1NPCcPc-r_j01Ns2xV3jMA,33854
262
+ phoenix/session/client.py,sha256=cLKG3dM_MX4Lc3y-NlqnrxhWJSDDsMvLSwjxaTvcfeE,33813
259
263
  phoenix/session/data_extractor.py,sha256=gkEM3WWZAlWGMfRgQopAQlid4cSi6GNco-sdrGir0qc,2788
260
- phoenix/session/evaluation.py,sha256=3a33ilo-WU0F_Ze26lkCMMwni0GGceSXRRBLvP3CI1o,5352
261
- phoenix/session/session.py,sha256=3DSpXj_mlRAKnb9aNUkDjph19SLHx32IJSKYcR3r7cw,26994
264
+ phoenix/session/evaluation.py,sha256=aKeV8UVOyq3b7CYOwt3cWuLz0xzvMjX7vlEPILJ_fcs,5311
265
+ phoenix/session/session.py,sha256=p4l1EI_ynaiWlVx_TpjgIofM0hrVbsPq85ViSIlhIqQ,26953
262
266
  phoenix/trace/__init__.py,sha256=ujk_uYjM8gmm-YqnyXxF-kekfwid0bcaPMTtNNcaw6U,407
263
267
  phoenix/trace/attributes.py,sha256=B_OrzVaxZwFkrAFXZyicYoIti1UdUysURsvUS2GyW1U,12488
264
268
  phoenix/trace/errors.py,sha256=wB1z8qdPckngdfU-TORToekvg3344oNFAA83_hC2yFY,180
265
269
  phoenix/trace/evaluation_conventions.py,sha256=t8jydM3U0-T5YpiQKRJ3tWdWGlHtzKyttYdw-ddvPOk,1048
266
- phoenix/trace/exporter.py,sha256=eAYemdvDCHMugDJiaR29BFFMTQBdf3oerdkz34Cl3hE,4736
267
- phoenix/trace/fixtures.py,sha256=p9slFac24_gspfKSFCm-QxmvMA-ZOcqMcvbPKCUxOPw,18188
270
+ phoenix/trace/exporter.py,sha256=bUXh8fjJIbHurrnt4bAm-cCWqUN5FqNsIc8DZzzklkQ,4695
271
+ phoenix/trace/fixtures.py,sha256=4pibRxdvwZ4C7ni-2SN5O7_4meFV5cVucVKZrEmfk3w,18117
268
272
  phoenix/trace/otel.py,sha256=WA720jvRadiZBAKjsYoPyXzypHwbyEK2OZRVUwtbjB8,9976
269
273
  phoenix/trace/projects.py,sha256=2BwlNjFE-uwpqYtCu5YyBiYZk9wRPpM13vh3-Cv7GkA,2157
270
274
  phoenix/trace/schemas.py,sha256=HpWSyzec0yDHEQXEDuwyLbhpvKrqkGps8BJqGiIFj8Y,5978
@@ -279,11 +283,11 @@ phoenix/trace/dsl/filter.py,sha256=9NwATCUOgJ4Pms8XsEcinROUuxZ9UW-ISV09o65Ms70,3
279
283
  phoenix/trace/dsl/helpers.py,sha256=STQtbmF3yI97GM4yH_V--mrGe1JqldUJJc5LO1o4NWo,3919
280
284
  phoenix/trace/dsl/query.py,sha256=Mjk2ntKUmRZCCscpt0dMrzo26NhhwtdhC_s9itR3Ycc,30404
281
285
  phoenix/trace/langchain/__init__.py,sha256=F37GfD1pd5Kuw7R7iRUM1zXXpO8xEcycNZh5dwqBXNk,109
282
- phoenix/trace/langchain/instrumentor.py,sha256=zdh9uZfG7HWna6Wug_agS7MxSbUlfV-nhf3jWFZm61U,1412
286
+ phoenix/trace/langchain/instrumentor.py,sha256=a0NuyWqRCiAJNZyE_ymq5WF00I6VZVluuYnga4bdzuI,1371
283
287
  phoenix/trace/llama_index/__init__.py,sha256=4fpR5702Qh2t5TaXIx584EkA-BveCPftXPOKvI0Oi3I,105
284
- phoenix/trace/llama_index/callback.py,sha256=0EIyHorb0QjTn9kKW_dzrUfvl5d6re-tx_GesIAuDGY,4492
288
+ phoenix/trace/llama_index/callback.py,sha256=ROGgpbkXDc54Yr2S28WqoZoH4rJrUDsPTdEsP0F7AB4,4451
285
289
  phoenix/trace/openai/__init__.py,sha256=J3G0uqCxGdksUpaQVHds_Egv2drvh8UEqoLjiQAOveg,79
286
- phoenix/trace/openai/instrumentor.py,sha256=b_QT5KFgDdgfUV9X4MSwnqFgGxAo-jxduJk3KtWBzKo,1311
290
+ phoenix/trace/openai/instrumentor.py,sha256=sb3434Npe86VHsT_jyDHlfpXWZYER9H1k_IQ5-CFTvI,1270
287
291
  phoenix/trace/v1/__init__.py,sha256=-IbAD0ruESMjvQLvGAg9CTfjBUATFDx1OXseDPis6-0,88
288
292
  phoenix/trace/v1/evaluation_pb2.py,sha256=8sXvv2BW_vqD30MOMbmkeE2zpmm7ncik21kl3e-HzeQ,2254
289
293
  phoenix/trace/v1/evaluation_pb2.pyi,sha256=cCbbx06gwQmaH14s3J1X25TtaARh-k1abbxQdQCXGm8,4500
@@ -292,12 +296,12 @@ phoenix/utilities/client.py,sha256=6Swxe4TVA481eKnmkCox83WY52b2nA-DWIzkkGu6wwo,4
292
296
  phoenix/utilities/deprecation.py,sha256=cFuTVvjSYyRlrdxdJewjJVieIEHPk30BukSRGRydQ3k,1046
293
297
  phoenix/utilities/error_handling.py,sha256=7b5rpGFj9EWZ8yrZK1IHvxB89suWk3lggDayUQcvZds,1946
294
298
  phoenix/utilities/json.py,sha256=y_w-McDfvlTeGJT28sCtyjzVkwFicakxERG-sGRc8Ak,1948
295
- phoenix/utilities/logging.py,sha256=lDXd6EGaamBNcQxL4vP1au9-i_SXe0OraUDiJOcszSw,222
299
+ phoenix/utilities/logging.py,sha256=B8t2WPULOwVyuGLRLbwKsw5N41N26vtgF-lCAYgWTEk,508
296
300
  phoenix/utilities/project.py,sha256=8IJuMM4yUMoooPi37sictGj8Etu9rGmq6RFtc9848cQ,436
297
- phoenix/utilities/re.py,sha256=PDve_OLjRTM8yQQJHC8-n3HdIONi7aNils3ZKRZ5uBM,2045
301
+ phoenix/utilities/re.py,sha256=nr_B0txj_7CXc45953X6vr2KCRSWMuaXJSEkL8s8Sjc,2036
298
302
  phoenix/utilities/span_store.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
299
- arize_phoenix-4.35.2.dist-info/METADATA,sha256=tImL5zbN71ZgtUOnLmcBY3bK5dXV8dZ5EycHO9o1Y20,12003
300
- arize_phoenix-4.35.2.dist-info/WHEEL,sha256=1yFddiXMmvYK7QYTqtRNtX66WJ0Mz8PYEiEUoOUUxRY,87
301
- arize_phoenix-4.35.2.dist-info/licenses/IP_NOTICE,sha256=JBqyyCYYxGDfzQ0TtsQgjts41IJoa-hiwDrBjCb9gHM,469
302
- arize_phoenix-4.35.2.dist-info/licenses/LICENSE,sha256=HFkW9REuMOkvKRACuwLPT0hRydHb3zNg-fdFt94td18,3794
303
- arize_phoenix-4.35.2.dist-info/RECORD,,
303
+ arize_phoenix-4.36.0.dist-info/METADATA,sha256=NSFTsweJUI7kA6Z4eGG2Sz5qN-WmPKbTL2JHESzC79A,12003
304
+ arize_phoenix-4.36.0.dist-info/WHEEL,sha256=1yFddiXMmvYK7QYTqtRNtX66WJ0Mz8PYEiEUoOUUxRY,87
305
+ arize_phoenix-4.36.0.dist-info/licenses/IP_NOTICE,sha256=JBqyyCYYxGDfzQ0TtsQgjts41IJoa-hiwDrBjCb9gHM,469
306
+ arize_phoenix-4.36.0.dist-info/licenses/LICENSE,sha256=HFkW9REuMOkvKRACuwLPT0hRydHb3zNg-fdFt94td18,3794
307
+ arize_phoenix-4.36.0.dist-info/RECORD,,
phoenix/config.py CHANGED
@@ -1,13 +1,16 @@
1
+ import logging
1
2
  import os
2
3
  import re
3
4
  import tempfile
4
- from logging import getLogger
5
+ from enum import Enum
5
6
  from pathlib import Path
6
7
  from typing import Dict, List, Optional, Tuple
7
8
 
9
+ from phoenix.utilities.logging import log_a_list
10
+
8
11
  from .utilities.re import parse_env_headers
9
12
 
10
- logger = getLogger(__name__)
13
+ logger = logging.getLogger(__name__)
11
14
 
12
15
  # Phoenix environment variables
13
16
  ENV_PHOENIX_PORT = "PHOENIX_PORT"
@@ -56,6 +59,24 @@ ENV_PHOENIX_ENABLE_PROMETHEUS = "PHOENIX_ENABLE_PROMETHEUS"
56
59
  """
57
60
  Whether to enable Prometheus. Defaults to false.
58
61
  """
62
+ ENV_LOGGING_MODE = "PHOENIX_LOGGING_MODE"
63
+ """
64
+ The logging mode (either 'default' or 'structured').
65
+ """
66
+ ENV_LOGGING_LEVEL = "PHOENIX_LOGGING_LEVEL"
67
+ """
68
+ The logging level ('debug', 'info', 'warning', 'error', 'critical') for the Phoenix backend. For
69
+ database logging see ENV_DB_LOGGING_LEVEL. Defaults to info.
70
+ """
71
+ ENV_DB_LOGGING_LEVEL = "PHOENIX_DB_LOGGING_LEVEL"
72
+ """
73
+ The logging level ('debug', 'info', 'warning', 'error', 'critical') for the Phoenix ORM.
74
+ Defaults to warning.
75
+ """
76
+ ENV_LOG_MIGRATIONS = "PHOENIX_LOG_MIGRATIONS"
77
+ """
78
+ Whether or not to log migrations. Defaults to true.
79
+ """
59
80
 
60
81
  # Phoenix server OpenTelemetry instrumentation environment variables
61
82
  ENV_PHOENIX_SERVER_INSTRUMENTATION_OTLP_TRACE_COLLECTOR_HTTP_ENDPOINT = (
@@ -331,5 +352,74 @@ def get_web_base_url() -> str:
331
352
  return get_base_url()
332
353
 
333
354
 
355
+ class LoggingMode(Enum):
356
+ DEFAULT = "default"
357
+ STRUCTURED = "structured"
358
+
359
+
360
+ def get_env_logging_mode() -> LoggingMode:
361
+ if (logging_mode := os.getenv(ENV_LOGGING_MODE)) is None:
362
+ return LoggingMode.DEFAULT
363
+ try:
364
+ return LoggingMode(logging_mode.lower().strip())
365
+ except ValueError:
366
+ raise ValueError(
367
+ f"Invalid value `{logging_mode}` for env var `{ENV_LOGGING_MODE}`. "
368
+ f"Valid values are: {log_a_list([mode.value for mode in LoggingMode],'and')} "
369
+ "(case-insensitive)."
370
+ )
371
+
372
+
373
+ def get_env_logging_level() -> int:
374
+ return _get_logging_level(
375
+ env_var=ENV_LOGGING_LEVEL,
376
+ default_level=logging.INFO,
377
+ )
378
+
379
+
380
+ def get_env_db_logging_level() -> int:
381
+ return _get_logging_level(
382
+ env_var=ENV_DB_LOGGING_LEVEL,
383
+ default_level=logging.WARNING,
384
+ )
385
+
386
+
387
+ def _get_logging_level(env_var: str, default_level: int) -> int:
388
+ logging_level = os.getenv(env_var)
389
+ if not logging_level:
390
+ return default_level
391
+
392
+ # levelNamesMapping = logging.getLevelNamesMapping() is not supported in python 3.8
393
+ # but is supported in 3.12. Hence, we define the mapping ourselves and will remove
394
+ # this once we drop support for older python versions
395
+ levelNamesMapping = logging._nameToLevel.copy()
396
+
397
+ valid_values = [level for level in levelNamesMapping if level != "NOTSET"]
398
+
399
+ if logging_level.upper() not in valid_values:
400
+ raise ValueError(
401
+ f"Invalid value `{logging_level}` for env var `{env_var}`. "
402
+ f"Valid values are: {log_a_list(valid_values,'and')} (case-insensitive)."
403
+ )
404
+ return levelNamesMapping[logging_level.upper()]
405
+
406
+
407
+ def get_env_log_migrations() -> bool:
408
+ log_migrations = os.getenv(ENV_LOG_MIGRATIONS)
409
+ # Default to True
410
+ if log_migrations is None:
411
+ return True
412
+
413
+ if log_migrations.lower() == "true":
414
+ return True
415
+ elif log_migrations.lower() == "false":
416
+ return False
417
+ else:
418
+ raise ValueError(
419
+ f"Invalid value for environment variable {ENV_LOG_MIGRATIONS}: "
420
+ f"{log_migrations}. Value values are 'TRUE' and 'FALSE' (case-insensitive)."
421
+ )
422
+
423
+
334
424
  DEFAULT_PROJECT_NAME = "default"
335
425
  _KUBERNETES_PHOENIX_PORT_PATTERN = re.compile(r"^tcp://\d{1,3}[.]\d{1,3}[.]\d{1,3}[.]\d{1,3}:\d+$")
phoenix/db/alembic.ini CHANGED
@@ -83,37 +83,3 @@ version_path_separator = os # Use os.pathsep. Default configuration used for ne
83
83
  # ruff.executable = %(here)s/.venv/bin/ruff
84
84
  # ruff.options = --fix REVISION_SCRIPT_FILENAME
85
85
 
86
- # Logging configuration
87
- [loggers]
88
- keys = root,sqlalchemy,alembic
89
-
90
- [handlers]
91
- keys = console
92
-
93
- [formatters]
94
- keys = generic
95
-
96
- [logger_root]
97
- level = WARN
98
- handlers = console
99
- qualname =
100
-
101
- [logger_sqlalchemy]
102
- level = WARN
103
- handlers =
104
- qualname = sqlalchemy.engine
105
-
106
- [logger_alembic]
107
- level = WARN
108
- handlers =
109
- qualname = alembic
110
-
111
- [handler_console]
112
- class = StreamHandler
113
- args = (sys.stderr,)
114
- level = NOTSET
115
- formatter = generic
116
-
117
- [formatter_generic]
118
- format = %(levelname)-5.5s [%(name)s] %(message)s
119
- datefmt = %H:%M:%S
phoenix/db/engines.py CHANGED
@@ -13,7 +13,7 @@ from sqlalchemy import URL, StaticPool, event, make_url
13
13
  from sqlalchemy.ext.asyncio import AsyncEngine, create_async_engine
14
14
  from typing_extensions import assert_never
15
15
 
16
- from phoenix.config import get_env_database_schema
16
+ from phoenix.config import LoggingMode, get_env_database_schema
17
17
  from phoenix.db.helpers import SupportedSQLDialect
18
18
  from phoenix.db.migrate import migrate_in_thread
19
19
  from phoenix.db.models import init_models
@@ -64,7 +64,7 @@ def get_async_db_url(connection_str: str) -> URL:
64
64
  def create_engine(
65
65
  connection_str: str,
66
66
  migrate: bool = True,
67
- echo: bool = False,
67
+ log_to_stdout: bool = False,
68
68
  ) -> AsyncEngine:
69
69
  """
70
70
  Factory to create a SQLAlchemy engine from a URL string.
@@ -74,10 +74,25 @@ def create_engine(
74
74
  raise ValueError("Failed to parse database from connection string")
75
75
  backend = SupportedSQLDialect(url.get_backend_name())
76
76
  url = get_async_db_url(url.render_as_string(hide_password=False))
77
+ # If Phoenix is run as an application, we want to pass log_migrations_to_stdout=False
78
+ # and let the configured sqlalchemy logger handle the migration logs
79
+ log_migrations_to_stdout = (
80
+ Settings.log_migrations and Settings.logging_mode != LoggingMode.STRUCTURED
81
+ )
77
82
  if backend is SupportedSQLDialect.SQLITE:
78
- return aio_sqlite_engine(url=url, migrate=migrate, echo=echo)
83
+ return aio_sqlite_engine(
84
+ url=url,
85
+ migrate=migrate,
86
+ log_to_stdout=log_to_stdout,
87
+ log_migrations_to_stdout=log_migrations_to_stdout,
88
+ )
79
89
  elif backend is SupportedSQLDialect.POSTGRESQL:
80
- return aio_postgresql_engine(url=url, migrate=migrate, echo=echo)
90
+ return aio_postgresql_engine(
91
+ url=url,
92
+ migrate=migrate,
93
+ log_to_stdout=log_to_stdout,
94
+ log_migrations_to_stdout=log_migrations_to_stdout,
95
+ )
81
96
  else:
82
97
  assert_never(backend)
83
98
 
@@ -85,8 +100,9 @@ def create_engine(
85
100
  def aio_sqlite_engine(
86
101
  url: URL,
87
102
  migrate: bool = True,
88
- echo: bool = False,
89
103
  shared_cache: bool = True,
104
+ log_to_stdout: bool = False,
105
+ log_migrations_to_stdout: bool = True,
90
106
  ) -> AsyncEngine:
91
107
  database = url.database or ":memory:"
92
108
  if database.startswith("file:"):
@@ -105,7 +121,7 @@ def aio_sqlite_engine(
105
121
 
106
122
  engine = create_async_engine(
107
123
  url=url,
108
- echo=echo,
124
+ echo=log_to_stdout,
109
125
  json_serializer=_dumps,
110
126
  async_creator=async_creator,
111
127
  poolclass=StaticPool,
@@ -123,7 +139,7 @@ def aio_sqlite_engine(
123
139
  else:
124
140
  sync_engine = sqlalchemy.create_engine(
125
141
  url=url.set(drivername="sqlite"),
126
- echo=Settings.log_migrations,
142
+ echo=log_migrations_to_stdout,
127
143
  json_serializer=_dumps,
128
144
  creator=lambda: sqlean.connect(f"file:{database}", uri=True),
129
145
  )
@@ -143,14 +159,15 @@ def set_postgresql_search_path(schema: str) -> Callable[[Connection, Any], None]
143
159
  def aio_postgresql_engine(
144
160
  url: URL,
145
161
  migrate: bool = True,
146
- echo: bool = False,
162
+ log_to_stdout: bool = False,
163
+ log_migrations_to_stdout: bool = True,
147
164
  ) -> AsyncEngine:
148
- engine = create_async_engine(url=url, echo=echo, json_serializer=_dumps)
165
+ engine = create_async_engine(url=url, echo=log_to_stdout, json_serializer=_dumps)
149
166
  if not migrate:
150
167
  return engine
151
168
  sync_engine = sqlalchemy.create_engine(
152
169
  url=url.set(drivername="postgresql+psycopg"),
153
- echo=Settings.log_migrations,
170
+ echo=log_migrations_to_stdout,
154
171
  json_serializer=_dumps,
155
172
  )
156
173
  if schema := get_env_database_schema():
@@ -24,7 +24,6 @@ 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())
28
27
 
29
28
  DatasetId: TypeAlias = int
30
29
  DatasetVersionId: TypeAlias = int
@@ -29,7 +29,7 @@ from phoenix.db.insertion.helpers import insert_on_conflict
29
29
  from phoenix.server.dml_event import DmlEvent
30
30
  from phoenix.server.types import DbSessionFactory
31
31
 
32
- logger = logging.getLogger("__name__")
32
+ logger = logging.getLogger(__name__)
33
33
 
34
34
 
35
35
  class Insertable(Protocol):
phoenix/db/migrate.py CHANGED
@@ -14,7 +14,6 @@ from phoenix.exceptions import PhoenixMigrationError
14
14
  from phoenix.settings import Settings
15
15
 
16
16
  logger = logging.getLogger(__name__)
17
- logger.addHandler(logging.NullHandler())
18
17
 
19
18
 
20
19
  def printif(condition: bool, text: str) -> None:
@@ -48,8 +47,9 @@ def migrate(
48
47
  alembic_cfg.set_main_option("script_location", scripts_location)
49
48
  url = str(engine.url).replace("%", "%%")
50
49
  alembic_cfg.set_main_option("sqlalchemy.url", url)
51
- alembic_cfg.attributes["connection"] = engine.connect()
52
- command.upgrade(alembic_cfg, "head")
50
+ with engine.connect() as conn:
51
+ alembic_cfg.attributes["connection"] = conn
52
+ command.upgrade(alembic_cfg, "head")
53
53
  engine.dispose()
54
54
  printif(log_migrations, "---------------------------")
55
55
  printif(log_migrations, "✅ Migrations complete.")
@@ -1,5 +1,4 @@
1
1
  import asyncio
2
- from logging.config import fileConfig
3
2
 
4
3
  from alembic import context
5
4
  from sqlalchemy import Connection, engine_from_config, pool
@@ -14,14 +13,8 @@ from phoenix.settings import Settings
14
13
  # access to the values within the .ini file in use.
15
14
  config = context.config
16
15
 
17
- # Interpret the config file for Python logging.
18
- # This line sets up loggers basically.
19
- if config.config_file_name is not None:
20
- fileConfig(config.config_file_name, disable_existing_loggers=False)
21
-
22
16
  # add your model's MetaData object here
23
17
  # for 'autogenerate' support
24
-
25
18
  target_metadata = Base.metadata
26
19
 
27
20
  # other values from the config, defined by the needs of env.py,
@@ -18,7 +18,6 @@ from phoenix.inferences.schema import (
18
18
  )
19
19
 
20
20
  logger = logging.getLogger(__name__)
21
- logger.addHandler(logging.NullHandler())
22
21
 
23
22
 
24
23
  class InferencesRole(Enum):
@@ -34,7 +34,6 @@ from .schema import (
34
34
  from .validation import validate_inferences_inputs
35
35
 
36
36
  logger = logging.getLogger(__name__)
37
- logger.addHandler(logging.NullHandler())
38
37
 
39
38
  # A schema like object. Not recommended to use this directly
40
39
  SchemaLike: TypeAlias = Any
@@ -0,0 +1,3 @@
1
+ from ._config import setup_logging
2
+
3
+ __all__ = ["setup_logging"]
@@ -0,0 +1,90 @@
1
+ import atexit
2
+ import logging
3
+ import logging.config
4
+ import logging.handlers
5
+ import queue
6
+ from sys import stderr, stdout
7
+
8
+ from typing_extensions import assert_never
9
+
10
+ from phoenix.config import LoggingMode
11
+ from phoenix.logging._filter import NonErrorFilter
12
+ from phoenix.settings import Settings
13
+
14
+ from ._formatter import PhoenixJSONFormatter
15
+
16
+
17
+ def setup_logging() -> None:
18
+ """
19
+ Configures logging for the specified logging mode.
20
+ """
21
+ logging_mode = Settings.logging_mode
22
+ if logging_mode is LoggingMode.DEFAULT:
23
+ _setup_library_logging()
24
+ elif logging_mode is LoggingMode.STRUCTURED:
25
+ _setup_application_logging()
26
+ else:
27
+ assert_never(logging_mode)
28
+
29
+
30
+ def _setup_library_logging() -> None:
31
+ """
32
+ Configures logging if Phoenix is used as a library
33
+ """
34
+ logger = logging.getLogger("phoenix")
35
+ logger.setLevel(Settings.logging_level)
36
+ db_logger = logging.getLogger("sqlalchemy")
37
+ db_logger.setLevel(Settings.db_logging_level)
38
+ logger.info("Default logging ready")
39
+
40
+
41
+ def _setup_application_logging() -> None:
42
+ """
43
+ Configures logging if Phoenix is used as an application
44
+ """
45
+ sql_engine_logger = logging.getLogger("sqlalchemy.engine.Engine")
46
+ # Remove all existing handlers
47
+ for handler in sql_engine_logger.handlers[:]:
48
+ sql_engine_logger.removeHandler(handler)
49
+ handler.close()
50
+
51
+ phoenix_logger = logging.getLogger("phoenix")
52
+ phoenix_logger.setLevel(Settings.logging_level)
53
+ phoenix_logger.propagate = False # Do not pass records to the root logger
54
+ sql_logger = logging.getLogger("sqlalchemy")
55
+ sql_logger.setLevel(Settings.db_logging_level)
56
+ sql_logger.propagate = False # Do not pass records to the root logger
57
+
58
+ log_queue = queue.Queue() # type:ignore
59
+ queue_handler = logging.handlers.QueueHandler(log_queue)
60
+ phoenix_logger.addHandler(queue_handler)
61
+ sql_logger.addHandler(queue_handler)
62
+
63
+ fmt_keys = {
64
+ "level": "levelname",
65
+ "message": "message",
66
+ "timestamp": "timestamp",
67
+ "logger": "name",
68
+ "module": "module",
69
+ "function": "funcName",
70
+ "line": "lineno",
71
+ "thread_name": "threadName",
72
+ }
73
+ formatter = PhoenixJSONFormatter(fmt_keys=fmt_keys)
74
+
75
+ # stdout handler
76
+ stdout_handler = logging.StreamHandler(stdout)
77
+ stdout_handler.setFormatter(formatter)
78
+ stdout_handler.setLevel(Settings.logging_level)
79
+ stdout_handler.addFilter(NonErrorFilter())
80
+
81
+ # stderr handler
82
+ stderr_handler = logging.StreamHandler(stderr)
83
+ stderr_handler.setFormatter(formatter)
84
+ stderr_handler.setLevel(logging.WARNING)
85
+
86
+ queue_listener = logging.handlers.QueueListener(log_queue, stdout_handler, stderr_handler)
87
+ if queue_listener is not None:
88
+ queue_listener.start()
89
+ atexit.register(queue_listener.stop)
90
+ phoenix_logger.info("Structured logging ready")
@@ -0,0 +1,6 @@
1
+ import logging
2
+
3
+
4
+ class NonErrorFilter(logging.Filter):
5
+ def filter(self, record: logging.LogRecord) -> bool:
6
+ return record.levelno <= logging.INFO
@@ -0,0 +1,69 @@
1
+ import datetime as dt
2
+ import json
3
+ import logging
4
+ from typing import Dict, Optional
5
+
6
+ LOG_RECORD_BUILTIN_ATTRS = {
7
+ "args",
8
+ "asctime",
9
+ "created",
10
+ "exc_info",
11
+ "exc_text",
12
+ "filename",
13
+ "funcName",
14
+ "levelname",
15
+ "levelno",
16
+ "lineno",
17
+ "module",
18
+ "msecs",
19
+ "message",
20
+ "msg",
21
+ "name",
22
+ "pathname",
23
+ "process",
24
+ "processName",
25
+ "relativeCreated",
26
+ "stack_info",
27
+ "thread",
28
+ "threadName",
29
+ "taskName",
30
+ }
31
+
32
+
33
+ class PhoenixJSONFormatter(logging.Formatter):
34
+ def __init__(
35
+ self,
36
+ *,
37
+ fmt_keys: Optional[Dict[str, str]] = None,
38
+ ):
39
+ super().__init__()
40
+ self.fmt_keys = fmt_keys if fmt_keys is not None else {}
41
+
42
+ def format(self, record: logging.LogRecord) -> str:
43
+ message = self._prepare_log_dict(record)
44
+ return json.dumps(message, default=str)
45
+
46
+ def _prepare_log_dict(self, record: logging.LogRecord) -> Dict[str, str]:
47
+ always_fields = {
48
+ "message": record.getMessage(),
49
+ "timestamp": dt.datetime.fromtimestamp(record.created, tz=dt.timezone.utc).isoformat(),
50
+ }
51
+ if record.exc_info is not None:
52
+ always_fields["exc_info"] = self.formatException(record.exc_info)
53
+
54
+ if record.stack_info is not None:
55
+ always_fields["stack_info"] = self.formatStack(record.stack_info)
56
+
57
+ message = {
58
+ key: msg_val
59
+ if (msg_val := always_fields.pop(val, None)) is not None
60
+ else getattr(record, val)
61
+ for key, val in self.fmt_keys.items()
62
+ }
63
+ message.update(always_fields)
64
+
65
+ for key, val in record.__dict__.items():
66
+ if key not in LOG_RECORD_BUILTIN_ATTRS:
67
+ message[key] = val
68
+
69
+ return message
@@ -10,7 +10,6 @@ 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())
14
13
 
15
14
 
16
15
  @dataclass(frozen=True)
phoenix/otel/settings.py CHANGED
@@ -1,10 +1,10 @@
1
+ import logging
1
2
  import os
2
3
  import urllib
3
- from logging import getLogger
4
4
  from re import compile
5
5
  from typing import Dict, List, Optional
6
6
 
7
- _logger = getLogger(__name__)
7
+ logger = logging.getLogger(__name__)
8
8
 
9
9
  # Environment variables specific to the subpackage
10
10
  ENV_PHOENIX_COLLECTOR_ENDPOINT = "PHOENIX_COLLECTOR_ENDPOINT"
@@ -72,13 +72,13 @@ def parse_env_headers(s: str) -> Dict[str, str]:
72
72
  encoded_header = f"{urllib.parse.quote(name)}={urllib.parse.quote(value)}"
73
73
  match = _HEADER_PATTERN.fullmatch(encoded_header.strip())
74
74
  if not match:
75
- _logger.warning(
75
+ logger.warning(
76
76
  "Header format invalid! Header values in environment variables must be "
77
77
  "URL encoded: %s",
78
78
  f"{name}: ****",
79
79
  )
80
80
  continue
81
- _logger.warning(
81
+ logger.warning(
82
82
  "Header values in environment variables should be URL encoded, attempting to "
83
83
  "URL encode header: {name}: ****"
84
84
  )
@@ -71,7 +71,6 @@ from .utils import (
71
71
  )
72
72
 
73
73
  logger = logging.getLogger(__name__)
74
- logger.addHandler(logging.NullHandler())
75
74
 
76
75
  DATASET_NODE_NAME = DatasetNodeType.__name__
77
76
  DATASET_VERSION_NODE_NAME = DatasetVersionNodeType.__name__
phoenix/server/app.py CHANGED
@@ -118,8 +118,6 @@ if TYPE_CHECKING:
118
118
  from opentelemetry.trace import TracerProvider
119
119
 
120
120
  logger = logging.getLogger(__name__)
121
- logger.setLevel(logging.INFO)
122
- logger.addHandler(logging.NullHandler())
123
121
 
124
122
  router = APIRouter(include_in_schema=False)
125
123
 
@@ -542,7 +540,7 @@ def create_engine_and_run_migrations(
542
540
  database_url: str,
543
541
  ) -> AsyncEngine:
544
542
  try:
545
- return create_engine(database_url)
543
+ return create_engine(connection_str=database_url, migrate=True, log_to_stdout=False)
546
544
  except PhoenixMigrationError as e:
547
545
  msg = (
548
546
  "\n\n⚠️⚠️ Phoenix failed to migrate the database to the latest version. ⚠️⚠️\n\n"
@@ -604,6 +602,7 @@ def create_app(
604
602
  secret: Optional[str] = None,
605
603
  scaffolder_config: Optional[ScaffolderConfig] = None,
606
604
  ) -> FastAPI:
605
+ logger.info(f"Server umap params: {umap_params}")
607
606
  startup_callbacks_list: List[_Callback] = list(startup_callbacks)
608
607
  shutdown_callbacks_list: List[_Callback] = list(shutdown_callbacks)
609
608
  initial_batch_of_spans: Iterable[Tuple[Span, str]] = (
phoenix/server/main.py CHANGED
@@ -1,6 +1,5 @@
1
1
  import atexit
2
2
  import codecs
3
- import logging
4
3
  import os
5
4
  import sys
6
5
  from argparse import ArgumentParser
@@ -20,18 +19,22 @@ from phoenix.config import (
20
19
  get_auth_settings,
21
20
  get_env_database_connection_str,
22
21
  get_env_database_schema,
22
+ get_env_db_logging_level,
23
23
  get_env_enable_prometheus,
24
24
  get_env_grpc_port,
25
25
  get_env_host,
26
26
  get_env_host_root_path,
27
+ get_env_log_migrations,
28
+ get_env_logging_level,
29
+ get_env_logging_mode,
27
30
  get_env_port,
28
31
  get_pids_path,
29
- get_working_dir,
30
32
  )
31
33
  from phoenix.core.model_schema_adapter import create_model_from_inferences
32
34
  from phoenix.db import get_printable_db_url
33
35
  from phoenix.inferences.fixtures import FIXTURES, get_inferences
34
36
  from phoenix.inferences.inferences import EMPTY_INFERENCES, Inferences
37
+ from phoenix.logging import setup_logging
35
38
  from phoenix.pointcloud.umap_parameters import (
36
39
  DEFAULT_MIN_DIST,
37
40
  DEFAULT_N_NEIGHBORS,
@@ -59,9 +62,6 @@ from phoenix.trace.fixtures import (
59
62
  from phoenix.trace.otel import decode_otlp_span, encode_span_to_otlp
60
63
  from phoenix.trace.schemas import Span
61
64
 
62
- logger = logging.getLogger(__name__)
63
- logger.addHandler(logging.NullHandler())
64
-
65
65
  _WELCOME_MESSAGE = Environment(loader=BaseLoader()).from_string("""
66
66
 
67
67
  ██████╗ ██╗ ██╗ ██████╗ ███████╗███╗ ██╗██╗██╗ ██╗
@@ -121,19 +121,16 @@ def _get_pid_file() -> Path:
121
121
 
122
122
  DEFAULT_UMAP_PARAMS_STR = f"{DEFAULT_MIN_DIST},{DEFAULT_N_NEIGHBORS},{DEFAULT_N_SAMPLES}"
123
123
 
124
- if __name__ == "__main__":
124
+
125
+ def main() -> None:
125
126
  primary_inferences_name: str
126
127
  reference_inferences_name: Optional[str]
127
128
  trace_dataset_name: Optional[str] = None
128
- simulate_streaming: Optional[bool] = None
129
129
 
130
130
  primary_inferences: Inferences = EMPTY_INFERENCES
131
131
  reference_inferences: Optional[Inferences] = None
132
132
  corpus_inferences: Optional[Inferences] = None
133
133
 
134
- # Initialize the settings for the Server
135
- Settings.log_migrations = True
136
-
137
134
  # automatically remove the pid file when the process is being gracefully terminated
138
135
  atexit.register(_remove_pid_file)
139
136
 
@@ -230,7 +227,7 @@ if __name__ == "__main__":
230
227
  db_connection_str = (
231
228
  args.database_url if args.database_url else get_env_database_connection_str()
232
229
  )
233
- export_path = Path(args.export_path) if args.export_path else EXPORT_DIR
230
+ export_path = Path(args.export_path) if args.export_path else Path(EXPORT_DIR)
234
231
 
235
232
  force_fixture_ingestion = False
236
233
  scaffold_datasets = False
@@ -260,7 +257,6 @@ if __name__ == "__main__":
260
257
  reference_inferences = None
261
258
  elif args.command == "trace-fixture":
262
259
  trace_dataset_name = args.fixture
263
- simulate_streaming = args.simulate_streaming
264
260
  elif args.command == "demo":
265
261
  fixture_name = args.fixture
266
262
  primary_inferences, reference_inferences, corpus_inferences = get_inferences(
@@ -268,7 +264,6 @@ if __name__ == "__main__":
268
264
  args.no_internet,
269
265
  )
270
266
  trace_dataset_name = args.trace_fixture
271
- simulate_streaming = args.simulate_streaming
272
267
  elif args.command == "serve":
273
268
  # We use sets to avoid duplicates
274
269
  if args.with_fixture:
@@ -290,12 +285,6 @@ if __name__ == "__main__":
290
285
  force_fixture_ingestion = args.force_fixture_ingestion
291
286
  scaffold_datasets = args.scaffold_datasets
292
287
  host: Optional[str] = args.host or get_env_host()
293
- display_host = host or "localhost"
294
- # If the host is "::", the convention is to bind to all interfaces. However, uvicorn
295
- # does not support this directly unless the host is set to None.
296
- if host and ":" in host:
297
- # format IPv6 hosts in brackets
298
- display_host = f"[{host}]"
299
288
  if host == "::":
300
289
  # TODO(dustin): why is this necessary? it's not type compliant
301
290
  host = None
@@ -336,13 +325,11 @@ if __name__ == "__main__":
336
325
  n_samples=int(umap_params_list[2]),
337
326
  )
338
327
 
339
- logger.info(f"Server umap params: {umap_params}")
340
328
  if enable_prometheus := get_env_enable_prometheus():
341
329
  from phoenix.server.prometheus import start_prometheus
342
330
 
343
331
  start_prometheus()
344
332
 
345
- working_dir = get_working_dir().resolve()
346
333
  engine = create_engine_and_run_migrations(db_connection_str)
347
334
  instrumentation_cleanups = instrument_engine_if_enabled(engine)
348
335
  factory = DbSessionFactory(db=_db(engine), dialect=engine.dialect.name)
@@ -394,3 +381,16 @@ if __name__ == "__main__":
394
381
 
395
382
  # Start the server
396
383
  server.run()
384
+
385
+
386
+ def initialize_settings() -> None:
387
+ Settings.logging_mode = get_env_logging_mode()
388
+ Settings.logging_level = get_env_logging_level()
389
+ Settings.db_logging_level = get_env_db_logging_level()
390
+ Settings.log_migrations = get_env_log_migrations()
391
+
392
+
393
+ if __name__ == "__main__":
394
+ initialize_settings()
395
+ setup_logging()
396
+ main()
@@ -1,3 +1,4 @@
1
+ import logging
1
2
  import os
2
3
  from typing import TYPE_CHECKING
3
4
 
@@ -8,9 +9,8 @@ from phoenix.config import (
8
9
 
9
10
  if TYPE_CHECKING:
10
11
  from opentelemetry.trace import TracerProvider
11
- from logging import getLogger
12
12
 
13
- logger = getLogger(__name__)
13
+ logger = logging.getLogger(__name__)
14
14
 
15
15
 
16
16
  def normalize_http_collector_endpoint(endpoint: str) -> str:
phoenix/services.py CHANGED
@@ -12,7 +12,6 @@ import psutil
12
12
  from phoenix.config import SERVER_DIR, get_pids_path, get_running_pid
13
13
 
14
14
  logger = logging.getLogger(__name__)
15
- logger.addHandler(logging.NullHandler())
16
15
 
17
16
 
18
17
  class Service:
phoenix/session/client.py CHANGED
@@ -51,7 +51,6 @@ from phoenix.trace.otel import encode_span_to_otlp
51
51
  from phoenix.utilities.client import VersionedClient
52
52
 
53
53
  logger = logging.getLogger(__name__)
54
- logger.addHandler(logging.NullHandler())
55
54
 
56
55
  DEFAULT_TIMEOUT_IN_SECONDS = 5
57
56
 
@@ -35,7 +35,6 @@ __all__ = [
35
35
  ]
36
36
 
37
37
  logger = logging.getLogger(__name__)
38
- logger.addHandler(logging.NullHandler())
39
38
 
40
39
 
41
40
  def encode_evaluations(evaluations: Evaluations) -> Iterator[pb.Evaluation]:
@@ -63,7 +63,6 @@ except: # noqa
63
63
  pass
64
64
 
65
65
  logger = logging.getLogger(__name__)
66
- logger.addHandler(logging.NullHandler())
67
66
 
68
67
  # type workaround
69
68
  # https://github.com/python/mypy/issues/5264#issuecomment-399407428
phoenix/settings.py CHANGED
@@ -1,5 +1,8 @@
1
+ import logging
1
2
  from dataclasses import dataclass, field
2
3
 
4
+ from phoenix.config import LoggingMode
5
+
3
6
 
4
7
  @dataclass
5
8
  class _Settings:
@@ -7,6 +10,12 @@ class _Settings:
7
10
 
8
11
  # By default, don't log migrations
9
12
  log_migrations: bool = field(default=False)
13
+ # By default, Phoenix does not configure its loggers and acts as a library
14
+ logging_mode: LoggingMode = field(default=LoggingMode.DEFAULT)
15
+ # By default, log level is INFO
16
+ logging_level: int = field(default=logging.INFO)
17
+ # By default, log level is WARNING
18
+ db_logging_level: int = field(default=logging.WARNING)
10
19
 
11
20
 
12
21
  # Singleton instance of the settings
phoenix/trace/exporter.py CHANGED
@@ -20,7 +20,6 @@ from phoenix.config import (
20
20
  )
21
21
 
22
22
  logger = logging.getLogger(__name__)
23
- logger.addHandler(logging.NullHandler())
24
23
 
25
24
  END_OF_QUEUE = None # sentinel value for queue termination
26
25
 
phoenix/trace/fixtures.py CHANGED
@@ -38,8 +38,6 @@ from phoenix.trace.utils import (
38
38
  )
39
39
 
40
40
  logger = logging.getLogger(__name__)
41
- logger.setLevel(logging.INFO)
42
- logger.addHandler(logging.NullHandler())
43
41
 
44
42
 
45
43
  class EvaluationResultSchema(NamedTuple):
@@ -13,7 +13,6 @@ from phoenix.config import get_env_project_name
13
13
  from phoenix.trace.exporter import _OpenInferenceExporter
14
14
 
15
15
  logger = logging.getLogger(__name__)
16
- logger.addHandler(logging.NullHandler())
17
16
 
18
17
  __all__ = ("LangChainInstrumentor",)
19
18
 
@@ -13,7 +13,6 @@ from phoenix.trace.errors import IncompatibleLibraryVersionError
13
13
  from phoenix.trace.exporter import _OpenInferenceExporter
14
14
 
15
15
  logger = logging.getLogger(__name__)
16
- logger.addHandler(logging.NullHandler())
17
16
 
18
17
  LLAMA_INDEX_MODERN_VERSION = (0, 10, 0)
19
18
  INSTRUMENTATION_MODERN_VERSION = (1, 0, 0)
@@ -13,7 +13,6 @@ from phoenix.config import get_env_project_name
13
13
  from phoenix.trace.exporter import _OpenInferenceExporter
14
14
 
15
15
  logger = logging.getLogger(__name__)
16
- logger.addHandler(logging.NullHandler())
17
16
 
18
17
 
19
18
  class OpenAIInstrumentor(Instrumentor):
@@ -1,6 +1,6 @@
1
1
  # A collection of printing and logging utilities
2
2
 
3
- from typing import Any
3
+ from typing import Any, List
4
4
 
5
5
  from tqdm.auto import tqdm
6
6
 
@@ -8,3 +8,11 @@ from tqdm.auto import tqdm
8
8
  def printif(condition: bool, *args: Any, **kwargs: Any) -> None:
9
9
  if condition:
10
10
  tqdm.write(*args, **kwargs)
11
+
12
+
13
+ def log_a_list(list_of_str: List[str], join_word: str) -> str:
14
+ if list_of_str is None or len(list_of_str) == 0:
15
+ return ""
16
+ if len(list_of_str) == 1:
17
+ return list_of_str[0]
18
+ return f"{', '.join(map(str, list_of_str[:-1]))} {join_word} {list_of_str[-1]}"
phoenix/utilities/re.py CHANGED
@@ -1,9 +1,9 @@
1
- from logging import getLogger
1
+ import logging
2
2
  from re import compile, split
3
3
  from typing import Dict, List
4
4
  from urllib.parse import unquote
5
5
 
6
- _logger = getLogger(__name__)
6
+ logger = logging.getLogger(__name__)
7
7
 
8
8
  # Optional whitespace
9
9
  _OWS = r"[ \t]*"
@@ -35,7 +35,7 @@ def parse_env_headers(s: str) -> Dict[str, str]:
35
35
  continue
36
36
  match = _HEADER_PATTERN.fullmatch(header.strip())
37
37
  if not match:
38
- _logger.warning(
38
+ logger.warning(
39
39
  "Header format invalid! Header values in environment variables must be "
40
40
  "URL encoded: %s",
41
41
  header,
phoenix/version.py CHANGED
@@ -1 +1 @@
1
- __version__ = "4.35.2"
1
+ __version__ = "4.36.0"