arize-phoenix 10.11.0__py3-none-any.whl → 10.13.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 (25) hide show
  1. {arize_phoenix-10.11.0.dist-info → arize_phoenix-10.13.0.dist-info}/METADATA +1 -1
  2. {arize_phoenix-10.11.0.dist-info → arize_phoenix-10.13.0.dist-info}/RECORD +25 -25
  3. phoenix/config.py +15 -0
  4. phoenix/server/api/helpers/playground_clients.py +0 -1
  5. phoenix/server/api/queries.py +10 -0
  6. phoenix/server/api/routers/v1/spans.py +164 -3
  7. phoenix/server/api/types/Project.py +19 -0
  8. phoenix/server/app.py +4 -0
  9. phoenix/server/main.py +3 -0
  10. phoenix/server/static/.vite/manifest.json +36 -36
  11. phoenix/server/static/assets/{components-DGavuwFF.js → components-37rV35eH.js} +256 -256
  12. phoenix/server/static/assets/{index-DNmPxkEL.js → index-CdNW7TcY.js} +2 -2
  13. phoenix/server/static/assets/{pages-Bns7xROJ.js → pages-BWWAYqfd.js} +545 -478
  14. phoenix/server/static/assets/{vendor-ARQZvmz5.js → vendor-BKYy4SMr.js} +1 -1
  15. phoenix/server/static/assets/{vendor-arizeai-Ct6kvW4e.js → vendor-arizeai-hGVPFFRq.js} +22 -22
  16. phoenix/server/static/assets/{vendor-codemirror-BxoXtD6f.js → vendor-codemirror-BlmFw5CA.js} +1 -1
  17. phoenix/server/static/assets/{vendor-recharts-Cl8AO7Np.js → vendor-recharts-Bz7zqjbW.js} +1 -1
  18. phoenix/server/static/assets/{vendor-shiki-BpqODMgR.js → vendor-shiki-BitvudxD.js} +1 -1
  19. phoenix/server/templates/index.html +24 -0
  20. phoenix/settings.py +3 -0
  21. phoenix/version.py +1 -1
  22. {arize_phoenix-10.11.0.dist-info → arize_phoenix-10.13.0.dist-info}/WHEEL +0 -0
  23. {arize_phoenix-10.11.0.dist-info → arize_phoenix-10.13.0.dist-info}/entry_points.txt +0 -0
  24. {arize_phoenix-10.11.0.dist-info → arize_phoenix-10.13.0.dist-info}/licenses/IP_NOTICE +0 -0
  25. {arize_phoenix-10.11.0.dist-info → arize_phoenix-10.13.0.dist-info}/licenses/LICENSE +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: arize-phoenix
3
- Version: 10.11.0
3
+ Version: 10.13.0
4
4
  Summary: AI Observability and Evaluation
5
5
  Project-URL: Documentation, https://arize.com/docs/phoenix/
6
6
  Project-URL: Issues, https://github.com/Arize-ai/phoenix/issues
@@ -1,12 +1,12 @@
1
1
  phoenix/__init__.py,sha256=xkpXH76HFbEDCq8IhiFp-2GnEHx39xPMdOpV5Skew1w,5481
2
2
  phoenix/auth.py,sha256=yW78f1xWNjTE30ACGUM14nOd5BzkukhlzA9B45kSUkM,11053
3
- phoenix/config.py,sha256=zgir90pfb2aAqPm-scG73B30Z-1UppSrxKCPAuGtLbM,57004
3
+ phoenix/config.py,sha256=1K086wVZDsu8GC1qwk3EBe2Uuw0ZSAhiZhI62PlspaU,57463
4
4
  phoenix/datetime_utils.py,sha256=iJzNG6YJ6V7_u8B2iA7P2Z26FyxYbOPtx0dhJ7kNDHA,3398
5
5
  phoenix/exceptions.py,sha256=n2L2KKuecrdflB9MsCdAYCiSEvGJptIsfRkXMoJle7A,169
6
6
  phoenix/py.typed,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
7
7
  phoenix/services.py,sha256=ngkyKGVatX3cO2WJdo2hKdaVKP-xJCMvqthvga6kJss,5196
8
- phoenix/settings.py,sha256=x87BX7hWGQQZbrW_vrYqFR_izCGfO9gFc--JXUG4Tdk,754
9
- phoenix/version.py,sha256=QlkUWf1vZWZO1B4-0PjOW339CFFq5tlWz3teIz9l6-E,24
8
+ phoenix/settings.py,sha256=2kHfT3BNOVd4dAO1bq-syEQbHSG8oX2-7NhOwK2QREk,896
9
+ phoenix/version.py,sha256=ZCckrFpi-O_8Vaq7PPbDoo6WhScFhLb8iXD_3Rb_SpY,24
10
10
  phoenix/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
11
  phoenix/core/embedding_dimension.py,sha256=zKGbcvwOXgLf-yrJBpQyKtd-LEOPRKHnUToyAU8Owis,87
12
12
  phoenix/core/model.py,sha256=qBFraOtmwCCnWJltKNP18DDG0mULXigytlFsa6YOz6k,4837
@@ -89,14 +89,14 @@ phoenix/pointcloud/pointcloud.py,sha256=SN_1wXZcwKrtSnHGZLDZGx71orqE1WyVF7E-D58d
89
89
  phoenix/pointcloud/projectors.py,sha256=TQgwc9cJDjJkin1WZyZzgl3HsYrLLiyWD7Czy4jNW3U,1088
90
90
  phoenix/pointcloud/umap_parameters.py,sha256=db_WEPoamuWtopZx7tQfAXPnoE0MS8FkAV0_ThjEx_Q,1735
91
91
  phoenix/server/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
92
- phoenix/server/app.py,sha256=1N7OWYQ3obWhdb_Qbrg8mSuexvNk_oT1Taq8smfmMiU,41265
92
+ phoenix/server/app.py,sha256=PFXCs_lGUr_LxLktje4dYI3TZmw8VC3KgXzZexfVQ8Q,41497
93
93
  phoenix/server/authorization.py,sha256=fofeRwuoodCUB3DQYPCuAgIyeiwopXW0tkH_KZvU0Rg,1848
94
94
  phoenix/server/bearer_auth.py,sha256=b2iHV2nwvWlZJ2O_nWOzHctJ0aUrEIOygIDrO7VOCSw,6700
95
95
  phoenix/server/dml_event.py,sha256=MjJmVEKytq75chBOSyvYDusUnEbg1pHpIjR3pZkUaJA,2838
96
96
  phoenix/server/dml_event_handler.py,sha256=EZLXmCvx4pJrCkz29gxwKwmvmUkTtPCHw6klR-XM8qE,8258
97
97
  phoenix/server/grpc_server.py,sha256=dod29zE_Zlir7NyLcdVM8GH3P8sy-9ykzfaBfVifyE4,4656
98
98
  phoenix/server/jwt_store.py,sha256=B6uVildN_dQDTG_-aHHvuVSI7wIVK1yvED-_y6se2GU,16905
99
- phoenix/server/main.py,sha256=ZL9cCqhaawTWOr6DIs6x09QjZ2xDk-jdoiyJcrjJFMc,18294
99
+ phoenix/server/main.py,sha256=j00TIU7QYOIXaJW9EpqsjEACKtwtSy70s0zWdwfuPw0,18436
100
100
  phoenix/server/oauth2.py,sha256=GvUqZBoZ5dG-l2G1RMl1SUcN10jNAjaMXFznMSWz2Zs,3336
101
101
  phoenix/server/prometheus.py,sha256=1KjvSfjSa2-BPjDybVMM_Kag316CsN-Zwt64YNr_snc,7825
102
102
  phoenix/server/rate_limiters.py,sha256=cFc73D2NaxqNZZDbwfIDw4So-fRVOJPBtqxOZ8Qky_s,7155
@@ -110,7 +110,7 @@ phoenix/server/api/auth.py,sha256=cvKH2FQLL7PasiqZMHgu9P4qchhEG7a7P9Nxy35FoIQ,15
110
110
  phoenix/server/api/context.py,sha256=oxNmVIIyycl22iQZjv59lU1inwlo-Povxe2Ok7t54mw,6954
111
111
  phoenix/server/api/exceptions.py,sha256=TA0JuY2YRnj35qGuMSQ8d0ToHum9gWm9W--3fSKHrX0,1171
112
112
  phoenix/server/api/interceptor.py,sha256=ykDnoC_apUd-llVli3m1CW18kNSIgjz2qZ6m5JmPDu8,1294
113
- phoenix/server/api/queries.py,sha256=_Ae8W6Y9O3_6gFdWjwVXIiX2kVvN8Ev_VcIQgO4x_88,41278
113
+ phoenix/server/api/queries.py,sha256=J2OU59r6Jq5zs4fFl_nawj56dcBXH8iz5Ze_02YjVog,41606
114
114
  phoenix/server/api/schema.py,sha256=fcs36xQwFF_Qe41_5cWR8wYpDvOrnbcyTeo5WNMbDsA,1702
115
115
  phoenix/server/api/subscriptions.py,sha256=73s6TzwI2M_bjIZDYwgohdI_13iv7pgpLCvZYNuExnw,23777
116
116
  phoenix/server/api/utils.py,sha256=quCBRcusc6PUq9tJq7M8PgwFZp7nXgVAxtbw8feribY,833
@@ -158,7 +158,7 @@ phoenix/server/api/helpers/__init__.py,sha256=m2-xaSPqUiSs91k62JaRDjFNfl-1byxBfY
158
158
  phoenix/server/api/helpers/annotations.py,sha256=9gMXKpMTfWEChoSCnvdWYuyB0hlSnNOp-qUdar9Vono,262
159
159
  phoenix/server/api/helpers/dataset_helpers.py,sha256=DoMBTg-qXTnC_K4Evx1WKpCCYgRbITpVqyY-8efJRf0,8984
160
160
  phoenix/server/api/helpers/experiment_run_filters.py,sha256=DOnVwrmn39eAkk2mwuZP8kIcAnR5jrOgllEwWSjsw94,29893
161
- phoenix/server/api/helpers/playground_clients.py,sha256=ycvccG8bzwtIeU0LQpR6IMOn3B3DftmCwqfuYYZ7YYM,47957
161
+ phoenix/server/api/helpers/playground_clients.py,sha256=laFDXVnj-n5-nze5OeJSLT5nwdz2IWFTVccKNcLqK7U,47932
162
162
  phoenix/server/api/helpers/playground_registry.py,sha256=CPLMziFB2wmr-dfbx7VbzO2f8YIG_k5RftzvGXYGQ1w,2570
163
163
  phoenix/server/api/helpers/playground_spans.py,sha256=QpXwPl_fFNwm_iA1A77XApUyXMl1aDmonw8aXuNZ_4k,17132
164
164
  phoenix/server/api/helpers/prompts/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -242,7 +242,7 @@ phoenix/server/api/routers/v1/experiments.py,sha256=V9_sxqLTE1MKGFu9H3FEdGKr70lY
242
242
  phoenix/server/api/routers/v1/models.py,sha256=p3gJN-9SWiUYTUTft4bZMsZVCBNTb4nN1Foy68eRZzQ,1997
243
243
  phoenix/server/api/routers/v1/projects.py,sha256=LFWxHYPRZy-1EvroNylL635vU1UuDbcuRo1oD26yBnw,12551
244
244
  phoenix/server/api/routers/v1/prompts.py,sha256=aBOUBwLDzZDIzJQkxJcR8ZKnakNJOLMwzsLKINSs1mA,26545
245
- phoenix/server/api/routers/v1/spans.py,sha256=MxZ9RvZqc8JGgcDAgH_S9rTc9YfXOEF_j9kqBf3z4qY,38379
245
+ phoenix/server/api/routers/v1/spans.py,sha256=HhZtXsNTdOmMXWtMkz3VUb3aqpGsVAU5XOYi2KfvGMc,44106
246
246
  phoenix/server/api/routers/v1/traces.py,sha256=DfzeszQvtlrVxvurJLaWJJAhkCZ4BodLwpFuBYPwN5Q,8206
247
247
  phoenix/server/api/routers/v1/users.py,sha256=ZcW3if0L8-lUusTzET7fhlhvnmiCICDrGimzB7i3irc,11947
248
248
  phoenix/server/api/routers/v1/utils.py,sha256=oXIOGPzPTkE0ZWUTRCoRIQQ7wTzoSwtWFaUSjlGBqts,4960
@@ -295,7 +295,7 @@ phoenix/server/api/types/MimeType.py,sha256=Zpi6zCalkSFgsvhzvOs-O1gYA04usAi9H__Q
295
295
  phoenix/server/api/types/Model.py,sha256=8UIFqMe1q-2ufBNg-gxHusV8wM1h-KbfLUeJjyVcMvQ,8081
296
296
  phoenix/server/api/types/NumericRange.py,sha256=afEjgF97Go_OvmjMggbPBt-zGM8IONewAyEiKEHRds0,192
297
297
  phoenix/server/api/types/PerformanceMetric.py,sha256=KFkmJDqP43eDUtARQOUqR7NYcxvL6Vh2uisHWU6H3ko,387
298
- phoenix/server/api/types/Project.py,sha256=akRLgsKHboF1bBLByopizfr1hqErvUIjG205iOVK1bg,29155
298
+ phoenix/server/api/types/Project.py,sha256=FulRsgTWcvZEyiV3lzJ505HMa9Eq7aoQr3OkbPWH2Ws,29864
299
299
  phoenix/server/api/types/ProjectSession.py,sha256=fyfVtpUpFOTnBx8DFnH3dr7WXAssN0ooAgrQSSi7kEI,4677
300
300
  phoenix/server/api/types/ProjectTraceRetentionPolicy.py,sha256=tYy2kgalPDyuaYZr0VUHjH0YpXaiF_QOzg5yfaV_c7c,3782
301
301
  phoenix/server/api/types/Prompt.py,sha256=ccP4eq1e38xbF0afclGWLOuDpBVpNbJ3AOSRClF8yFQ,4955
@@ -350,19 +350,19 @@ phoenix/server/static/apple-touch-icon-76x76.png,sha256=CT_xT12I0u2i0WU8JzBZBuOQ
350
350
  phoenix/server/static/apple-touch-icon.png,sha256=fOfpjqGpWYbJ0eAurKsyoZP1EAs6ZVooBJ_SGk2ZkDs,3801
351
351
  phoenix/server/static/favicon.ico,sha256=bY0vvCKRftemZfPShwZtE93DiiQdaYaozkPGwNFr6H8,34494
352
352
  phoenix/server/static/modernizr.js,sha256=mvK-XtkNqjOral-QvzoqsyOMECXIMu5BQwSVN_wcU9c,2564
353
- phoenix/server/static/.vite/manifest.json,sha256=837OSh-IivU2fHSnxu_J0lAxevm63ybzAVP1FWmfznk,2165
354
- phoenix/server/static/assets/components-DGavuwFF.js,sha256=tdDtY9b8Ea0izLG8C_lurQVeumIHzgk4zGb-RlkInTU,565838
355
- phoenix/server/static/assets/index-DNmPxkEL.js,sha256=0TQaN2Tox98yiKfS6N6VkbJO0XpbnFUcpfZPRp5aolU,61125
356
- phoenix/server/static/assets/pages-Bns7xROJ.js,sha256=6q_I_rjaJi27n58102xzSN16fL7DWWZTMhxAaMQPCnQ,1054513
357
- phoenix/server/static/assets/vendor-ARQZvmz5.js,sha256=U_VQSuHOpuX7DxntJS2ckfxW2a4hV-E3MCqeJSxwdCY,2735976
353
+ phoenix/server/static/.vite/manifest.json,sha256=FX0jH9ulkLFAOYFQ_fY2V_eLblREsX4bRMst-NAYskU,2165
354
+ phoenix/server/static/assets/components-37rV35eH.js,sha256=bkm_c7mVyxDrVczT2PTykcER8YdFKEhJOWilzyoZj7c,566746
355
+ phoenix/server/static/assets/index-CdNW7TcY.js,sha256=VUdSy0fh-bRVlI9kH5irs9iJknpeEJvE4V7C8xgq-IM,61125
356
+ phoenix/server/static/assets/pages-BWWAYqfd.js,sha256=dVpjKI0o-nl6MgAi4HZvQwixBPWSQimxYNrKjSkkTDI,1061332
357
+ phoenix/server/static/assets/vendor-BKYy4SMr.js,sha256=lUYFHNxWckrqWult9uTiNwc6-Qv5Bfr1IcjrHoos1oU,2735976
358
358
  phoenix/server/static/assets/vendor-WIZid84E.css,sha256=spZD2r7XL5GfLO13ln-IuXfnjAref8l6g_n_AvxxOlI,5517
359
- phoenix/server/static/assets/vendor-arizeai-Ct6kvW4e.js,sha256=n_v4BAgm9-OaJMlQTf0OtLfPmPBdagnu85gVuds43sM,181771
360
- phoenix/server/static/assets/vendor-codemirror-BxoXtD6f.js,sha256=Gcja1za7lbo8aPTmdl_GxXszEVLxofqj8QpL6Y99RIo,781264
361
- phoenix/server/static/assets/vendor-recharts-Cl8AO7Np.js,sha256=CGhGfasxVd1a2RK1hDy3ePvfFXGL_pnm4eCPmvjXsio,282150
362
- phoenix/server/static/assets/vendor-shiki-BpqODMgR.js,sha256=A-kILnWoORSTVGyjSr71swRchewgMYGmkKigXoKJ5_g,8980312
359
+ phoenix/server/static/assets/vendor-arizeai-hGVPFFRq.js,sha256=pcKvIP_UPEeg-UJpM4DKz0bQQ1X1eIfmwz16Uu7pTY4,178469
360
+ phoenix/server/static/assets/vendor-codemirror-BlmFw5CA.js,sha256=QODyQdDaw_c_n-qaHcjfGxzU2ECerJcAVtnaF-8L6O4,781264
361
+ phoenix/server/static/assets/vendor-recharts-Bz7zqjbW.js,sha256=c7rl5xi-366k-e16Udyr6M_ZADAZGELnUOJrBR-1PBc,282150
362
+ phoenix/server/static/assets/vendor-shiki-BitvudxD.js,sha256=cVscU0RuS8MzpAVyJc4N28iswI9hNRUM_9rUO0RR0ug,8980312
363
363
  phoenix/server/static/assets/vendor-three-C5WAXd5r.js,sha256=ELkg06u70N7h8oFmvqdoHyPuUf9VgGEWeT4LKFx4VWo,620975
364
364
  phoenix/server/templates/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
365
- phoenix/server/templates/index.html,sha256=NpJ83DULqcStXFbShNamX4_NPDtnnucuBxppvUYjJa8,4409
365
+ phoenix/server/templates/index.html,sha256=TxaZTZKUz7-xQ3XlPO3DAPMj6S1rMEr5v6g1UmgaW70,6761
366
366
  phoenix/session/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
367
367
  phoenix/session/client.py,sha256=uw5WlCuFcN_eEj7Ko2bhJVcaihEIp7Evy50KnL6Sq-k,35602
368
368
  phoenix/session/data_extractor.py,sha256=Y0RzYFaNy9fQj8PEIeQ76TBZ90_E1FW7bXu3K5x0EZY,2782
@@ -400,9 +400,9 @@ phoenix/utilities/project.py,sha256=auVpARXkDb-JgeX5f2aStyFIkeKvGwN9l7qrFeJMVxI,
400
400
  phoenix/utilities/re.py,sha256=6YyUWIkv0zc2SigsxfOWIHzdpjKA_TZo2iqKq7zJKvw,2081
401
401
  phoenix/utilities/span_store.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
402
402
  phoenix/utilities/template_formatters.py,sha256=gh9PJD6WEGw7TEYXfSst1UR4pWWwmjxMLrDVQ_CkpkQ,2779
403
- arize_phoenix-10.11.0.dist-info/METADATA,sha256=CMrCkCw3Kjk4rgC8KbjvXGUU-WMgURw4cbfXBI-f_qk,27006
404
- arize_phoenix-10.11.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
405
- arize_phoenix-10.11.0.dist-info/entry_points.txt,sha256=Pgpn8Upxx9P8z8joPXZWl2LlnAlGc3gcQoVchb06X1Q,94
406
- arize_phoenix-10.11.0.dist-info/licenses/IP_NOTICE,sha256=JBqyyCYYxGDfzQ0TtsQgjts41IJoa-hiwDrBjCb9gHM,469
407
- arize_phoenix-10.11.0.dist-info/licenses/LICENSE,sha256=HFkW9REuMOkvKRACuwLPT0hRydHb3zNg-fdFt94td18,3794
408
- arize_phoenix-10.11.0.dist-info/RECORD,,
403
+ arize_phoenix-10.13.0.dist-info/METADATA,sha256=-62CQ8KNaGaN5BH9v0396cVRQ0hiBNE1ZYq6YUJ5jFM,27006
404
+ arize_phoenix-10.13.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
405
+ arize_phoenix-10.13.0.dist-info/entry_points.txt,sha256=Pgpn8Upxx9P8z8joPXZWl2LlnAlGc3gcQoVchb06X1Q,94
406
+ arize_phoenix-10.13.0.dist-info/licenses/IP_NOTICE,sha256=JBqyyCYYxGDfzQ0TtsQgjts41IJoa-hiwDrBjCb9gHM,469
407
+ arize_phoenix-10.13.0.dist-info/licenses/LICENSE,sha256=HFkW9REuMOkvKRACuwLPT0hRydHb3zNg-fdFt94td18,3794
408
+ arize_phoenix-10.13.0.dist-info/RECORD,,
phoenix/config.py CHANGED
@@ -50,6 +50,11 @@ ENV_PHOENIX_PROJECT_NAME = "PHOENIX_PROJECT_NAME"
50
50
  """
51
51
  The project name to use when logging traces and evals. defaults to 'default'.
52
52
  """
53
+ ENV_PHOENIX_FULLSTORY_ORG = "PHOENIX_FULLSTORY_ORG"
54
+ """
55
+ The FullStory organization ID for web analytics tracking. When set, FullStory tracking
56
+ will be enabled in the Phoenix web interface.
57
+ """
53
58
  ENV_PHOENIX_SQL_DATABASE_URL = "PHOENIX_SQL_DATABASE_URL"
54
59
  """
55
60
  The SQL database URL to use when logging traces and evals.
@@ -1520,6 +1525,16 @@ def get_env_allowed_origins() -> Optional[list[str]]:
1520
1525
  return allowed_origins.split(",")
1521
1526
 
1522
1527
 
1528
+ def get_env_fullstory_org() -> Optional[str]:
1529
+ """
1530
+ Get the FullStory organization ID from environment variables.
1531
+
1532
+ Returns:
1533
+ Optional[str]: The FullStory organization ID if set, None otherwise.
1534
+ """
1535
+ return getenv(ENV_PHOENIX_FULLSTORY_ORG)
1536
+
1537
+
1523
1538
  def verify_server_environment_variables() -> None:
1524
1539
  """Verify that the environment variables are set correctly. Raises an error otherwise."""
1525
1540
  get_env_root_url()
@@ -359,7 +359,6 @@ class OpenAIBaseStreamingClient(PlaygroundStreamingClient):
359
359
  ):
360
360
  if (usage := chunk.usage) is not None:
361
361
  token_usage = usage
362
- continue
363
362
  if not chunk.choices:
364
363
  # for Azure, initial chunk contains the content filter
365
364
  continue
@@ -319,6 +319,16 @@ class Query:
319
319
  after: Optional[CursorString] = UNSET,
320
320
  filter_condition: Optional[str] = UNSET,
321
321
  ) -> Connection[ExperimentComparison]:
322
+ # Handle empty experiment_ids gracefully
323
+ if not experiment_ids:
324
+ return connection_from_list(
325
+ data=[],
326
+ args=ConnectionArgs(
327
+ first=first,
328
+ after=after if isinstance(after, CursorString) else None,
329
+ ),
330
+ )
331
+
322
332
  experiment_ids_ = [
323
333
  from_global_id_with_expected_type(experiment_id, OrmExperiment.__name__)
324
334
  for experiment_id in experiment_ids
@@ -1,3 +1,4 @@
1
+ import json
1
2
  import warnings
2
3
  from asyncio import get_running_loop
3
4
  from collections.abc import AsyncIterator
@@ -12,7 +13,12 @@ from pydantic import BaseModel, Field
12
13
  from sqlalchemy import select
13
14
  from starlette.requests import Request
14
15
  from starlette.responses import Response, StreamingResponse
15
- from starlette.status import HTTP_404_NOT_FOUND, HTTP_422_UNPROCESSABLE_ENTITY
16
+ from starlette.status import (
17
+ HTTP_202_ACCEPTED,
18
+ HTTP_400_BAD_REQUEST,
19
+ HTTP_404_NOT_FOUND,
20
+ HTTP_422_UNPROCESSABLE_ENTITY,
21
+ )
16
22
  from strawberry.relay import GlobalID
17
23
 
18
24
  from phoenix.config import DEFAULT_PROJECT_NAME
@@ -26,6 +32,19 @@ from phoenix.server.bearer_auth import PhoenixUser
26
32
  from phoenix.server.dml_event import SpanAnnotationInsertEvent
27
33
  from phoenix.trace.attributes import flatten
28
34
  from phoenix.trace.dsl import SpanQuery as SpanQuery_
35
+ from phoenix.trace.schemas import (
36
+ Span as SpanForInsertion,
37
+ )
38
+ from phoenix.trace.schemas import (
39
+ SpanContext as InsertionSpanContext,
40
+ )
41
+ from phoenix.trace.schemas import (
42
+ SpanEvent as InternalSpanEvent,
43
+ )
44
+ from phoenix.trace.schemas import (
45
+ SpanKind,
46
+ SpanStatusCode,
47
+ )
29
48
  from phoenix.utilities.json import encode_df_as_json_string
30
49
 
31
50
  from .models import V1RoutesBaseModel
@@ -393,7 +412,9 @@ class SpanEvent(V1RoutesBaseModel):
393
412
 
394
413
 
395
414
  class Span(V1RoutesBaseModel):
396
- id: str = Field(description="Span Global ID, distinct from the OpenTelemetry span ID")
415
+ id: str = Field(
416
+ default="", description="Span Global ID, distinct from the OpenTelemetry span ID"
417
+ )
397
418
  name: str = Field(description="Name of the span operation")
398
419
  context: SpanContext = Field(description="Span context containing trace_id and span_id")
399
420
  span_kind: str = Field(description="Type of work that the span encapsulates")
@@ -440,7 +461,7 @@ async def query_spans_handler(
440
461
  )
441
462
  end_time = request_body.end_time or request_body.stop_time
442
463
  try:
443
- span_queries = [SpanQuery_.from_dict(query.dict()) for query in queries]
464
+ span_queries = [SpanQuery_.from_dict(query.model_dump()) for query in queries]
444
465
  except Exception as e:
445
466
  raise HTTPException(
446
467
  detail=f"Invalid query: {e}",
@@ -956,3 +977,143 @@ async def annotate_spans(
956
977
  for id_ in inserted_ids
957
978
  ]
958
979
  )
980
+
981
+
982
+ class CreateSpansRequestBody(RequestBody[list[Span]]):
983
+ data: list[Span]
984
+
985
+
986
+ class CreateSpansResponseBody(V1RoutesBaseModel):
987
+ total_received: int = Field(description="Total number of spans received")
988
+ total_queued: int = Field(description="Number of spans successfully queued for insertion")
989
+
990
+
991
+ @router.post(
992
+ "/projects/{project_identifier}/spans",
993
+ operation_id="createSpans",
994
+ summary="Create spans",
995
+ description=(
996
+ "Submit spans to be inserted into a project. If any spans are invalid or "
997
+ "duplicates, no spans will be inserted."
998
+ ),
999
+ responses=add_errors_to_responses([HTTP_404_NOT_FOUND, HTTP_400_BAD_REQUEST]),
1000
+ status_code=HTTP_202_ACCEPTED,
1001
+ )
1002
+ async def create_spans(
1003
+ request: Request,
1004
+ request_body: CreateSpansRequestBody,
1005
+ project_identifier: str = Path(
1006
+ description=(
1007
+ "The project identifier: either project ID or project name. If using a project name, "
1008
+ "it cannot contain slash (/), question mark (?), or pound sign (#) characters."
1009
+ )
1010
+ ),
1011
+ ) -> CreateSpansResponseBody:
1012
+ def convert_api_span_for_insertion(api_span: Span) -> SpanForInsertion:
1013
+ """
1014
+ Convert from API Span to phoenix.trace.schemas.Span
1015
+ Note: The 'id' field has a default empty string and is ignored during insertion.
1016
+ """
1017
+ try:
1018
+ span_kind = SpanKind(api_span.span_kind.upper())
1019
+ except ValueError:
1020
+ span_kind = SpanKind.UNKNOWN
1021
+
1022
+ try:
1023
+ status_code = SpanStatusCode(api_span.status_code.upper())
1024
+ except ValueError:
1025
+ status_code = SpanStatusCode.UNSET
1026
+
1027
+ internal_events: list[InternalSpanEvent] = []
1028
+ for event in api_span.events:
1029
+ if event.timestamp:
1030
+ internal_events.append(
1031
+ InternalSpanEvent(
1032
+ name=event.name, timestamp=event.timestamp, attributes=event.attributes
1033
+ )
1034
+ )
1035
+
1036
+ # Add back the openinference.span.kind attribute since it's stored separately in the API
1037
+ attributes = dict(api_span.attributes)
1038
+ attributes["openinference.span.kind"] = api_span.span_kind
1039
+
1040
+ # Create span for insertion - note we ignore the 'id' field as it's server-generated
1041
+ return SpanForInsertion(
1042
+ name=api_span.name,
1043
+ context=InsertionSpanContext(
1044
+ trace_id=api_span.context.trace_id, span_id=api_span.context.span_id
1045
+ ),
1046
+ span_kind=span_kind,
1047
+ parent_id=api_span.parent_id,
1048
+ start_time=api_span.start_time,
1049
+ end_time=api_span.end_time,
1050
+ status_code=status_code,
1051
+ status_message=api_span.status_message,
1052
+ attributes=attributes,
1053
+ events=internal_events,
1054
+ conversation=None, # Unused
1055
+ )
1056
+
1057
+ async with request.app.state.db() as session:
1058
+ project = await _get_project_by_identifier(session, project_identifier)
1059
+
1060
+ total_received = len(request_body.data)
1061
+ duplicate_spans: list[dict[str, str]] = []
1062
+ invalid_spans: list[dict[str, str]] = []
1063
+ spans_to_queue: list[tuple[SpanForInsertion, str]] = []
1064
+
1065
+ existing_span_ids: set[str] = set()
1066
+ span_ids = [span.context.span_id for span in request_body.data]
1067
+ async with request.app.state.db() as session:
1068
+ existing_result = await session.execute(
1069
+ select(models.Span.span_id).where(models.Span.span_id.in_(span_ids))
1070
+ )
1071
+ existing_span_ids = {row[0] for row in existing_result}
1072
+
1073
+ for api_span in request_body.data:
1074
+ # Check if it's a duplicate
1075
+ if api_span.context.span_id in existing_span_ids:
1076
+ duplicate_spans.append(
1077
+ {
1078
+ "span_id": api_span.context.span_id,
1079
+ "trace_id": api_span.context.trace_id,
1080
+ }
1081
+ )
1082
+ continue
1083
+
1084
+ try:
1085
+ span_for_insertion = convert_api_span_for_insertion(api_span)
1086
+ spans_to_queue.append((span_for_insertion, project.name))
1087
+ except Exception as e:
1088
+ invalid_spans.append(
1089
+ {
1090
+ "span_id": api_span.context.span_id,
1091
+ "trace_id": api_span.context.trace_id,
1092
+ "error": str(e),
1093
+ }
1094
+ )
1095
+
1096
+ # If there are any duplicates or invalid spans, reject the entire request
1097
+ if duplicate_spans or invalid_spans:
1098
+ error_detail = {
1099
+ "error": "Request contains invalid or duplicate spans",
1100
+ "total_received": total_received,
1101
+ "total_queued": 0, # No spans are queued when there are validation errors
1102
+ "total_duplicates": len(duplicate_spans),
1103
+ "total_invalid": len(invalid_spans),
1104
+ "duplicate_spans": duplicate_spans,
1105
+ "invalid_spans": invalid_spans,
1106
+ }
1107
+ raise HTTPException(
1108
+ status_code=HTTP_400_BAD_REQUEST,
1109
+ detail=json.dumps(error_detail),
1110
+ )
1111
+
1112
+ # All spans are valid, queue them all
1113
+ for span_for_insertion, project_name in spans_to_queue:
1114
+ await request.state.queue_span_for_bulk_insert(span_for_insertion, project_name)
1115
+
1116
+ return CreateSpansResponseBody(
1117
+ total_received=total_received,
1118
+ total_queued=len(spans_to_queue),
1119
+ )
@@ -326,8 +326,27 @@ class Project(Node):
326
326
  after: Optional[CursorString] = UNSET,
327
327
  sort: Optional[ProjectSessionSort] = UNSET,
328
328
  filter_io_substring: Optional[str] = UNSET,
329
+ session_id: Optional[str] = UNSET,
329
330
  ) -> Connection[ProjectSession]:
330
331
  table = models.ProjectSession
332
+ if session_id:
333
+ async with info.context.db() as session:
334
+ ans = await session.scalar(
335
+ select(table).filter_by(
336
+ session_id=session_id,
337
+ project_id=self.project_rowid,
338
+ )
339
+ )
340
+ if ans:
341
+ return connection_from_list(
342
+ data=[to_gql_project_session(ans)],
343
+ args=ConnectionArgs(),
344
+ )
345
+ elif not filter_io_substring:
346
+ return connection_from_list(
347
+ data=[],
348
+ args=ConnectionArgs(),
349
+ )
331
350
  stmt = select(table).filter_by(project_id=self.project_rowid)
332
351
  if time_range:
333
352
  if time_range.start:
phoenix/server/app.py CHANGED
@@ -214,6 +214,8 @@ class AppConfig(NamedTuple):
214
214
  oauth2_idps: Sequence[OAuth2Idp]
215
215
  basic_auth_disabled: bool = False
216
216
  auto_login_idp_name: Optional[str] = None
217
+ fullstory_org: Optional[str] = None
218
+ """ FullStory organization ID for web analytics tracking """
217
219
 
218
220
 
219
221
  class Static(StaticFiles):
@@ -279,6 +281,7 @@ class Static(StaticFiles):
279
281
  "oauth2_idps": self._app_config.oauth2_idps,
280
282
  "basic_auth_disabled": self._app_config.basic_auth_disabled,
281
283
  "auto_login_idp_name": self._app_config.auto_login_idp_name,
284
+ "fullstory_org": self._app_config.fullstory_org,
282
285
  },
283
286
  )
284
287
  except Exception as e:
@@ -964,6 +967,7 @@ def create_app(
964
967
  oauth2_idps=oauth2_idps,
965
968
  basic_auth_disabled=basic_auth_disabled,
966
969
  auto_login_idp_name=auto_login_idp_name,
970
+ fullstory_org=Settings.fullstory_org,
967
971
  ),
968
972
  ),
969
973
  name="static",
phoenix/server/main.py CHANGED
@@ -25,6 +25,7 @@ from phoenix.config import (
25
25
  get_env_db_logging_level,
26
26
  get_env_disable_migrations,
27
27
  get_env_enable_prometheus,
28
+ get_env_fullstory_org,
28
29
  get_env_grpc_port,
29
30
  get_env_host,
30
31
  get_env_host_root_path,
@@ -484,11 +485,13 @@ def main() -> None:
484
485
 
485
486
 
486
487
  def initialize_settings() -> None:
488
+ """Initialize the settings from environment variables."""
487
489
  Settings.logging_mode = get_env_logging_mode()
488
490
  Settings.logging_level = get_env_logging_level()
489
491
  Settings.db_logging_level = get_env_db_logging_level()
490
492
  Settings.log_migrations = get_env_log_migrations()
491
493
  Settings.disable_migrations = get_env_disable_migrations()
494
+ Settings.fullstory_org = get_env_fullstory_org()
492
495
 
493
496
 
494
497
  if __name__ == "__main__":
@@ -1,28 +1,28 @@
1
1
  {
2
- "_components-DGavuwFF.js": {
3
- "file": "assets/components-DGavuwFF.js",
2
+ "_components-37rV35eH.js": {
3
+ "file": "assets/components-37rV35eH.js",
4
4
  "name": "components",
5
5
  "imports": [
6
- "_vendor-ARQZvmz5.js",
7
- "_pages-Bns7xROJ.js",
8
- "_vendor-arizeai-Ct6kvW4e.js",
9
- "_vendor-codemirror-BxoXtD6f.js",
6
+ "_vendor-BKYy4SMr.js",
7
+ "_pages-BWWAYqfd.js",
8
+ "_vendor-arizeai-hGVPFFRq.js",
9
+ "_vendor-codemirror-BlmFw5CA.js",
10
10
  "_vendor-three-C5WAXd5r.js"
11
11
  ]
12
12
  },
13
- "_pages-Bns7xROJ.js": {
14
- "file": "assets/pages-Bns7xROJ.js",
13
+ "_pages-BWWAYqfd.js": {
14
+ "file": "assets/pages-BWWAYqfd.js",
15
15
  "name": "pages",
16
16
  "imports": [
17
- "_vendor-ARQZvmz5.js",
18
- "_vendor-arizeai-Ct6kvW4e.js",
19
- "_components-DGavuwFF.js",
20
- "_vendor-codemirror-BxoXtD6f.js",
21
- "_vendor-recharts-Cl8AO7Np.js"
17
+ "_vendor-BKYy4SMr.js",
18
+ "_vendor-arizeai-hGVPFFRq.js",
19
+ "_components-37rV35eH.js",
20
+ "_vendor-codemirror-BlmFw5CA.js",
21
+ "_vendor-recharts-Bz7zqjbW.js"
22
22
  ]
23
23
  },
24
- "_vendor-ARQZvmz5.js": {
25
- "file": "assets/vendor-ARQZvmz5.js",
24
+ "_vendor-BKYy4SMr.js": {
25
+ "file": "assets/vendor-BKYy4SMr.js",
26
26
  "name": "vendor",
27
27
  "imports": [
28
28
  "_vendor-three-C5WAXd5r.js"
@@ -35,33 +35,33 @@
35
35
  "file": "assets/vendor-WIZid84E.css",
36
36
  "src": "_vendor-WIZid84E.css"
37
37
  },
38
- "_vendor-arizeai-Ct6kvW4e.js": {
39
- "file": "assets/vendor-arizeai-Ct6kvW4e.js",
38
+ "_vendor-arizeai-hGVPFFRq.js": {
39
+ "file": "assets/vendor-arizeai-hGVPFFRq.js",
40
40
  "name": "vendor-arizeai",
41
41
  "imports": [
42
- "_vendor-ARQZvmz5.js"
42
+ "_vendor-BKYy4SMr.js"
43
43
  ]
44
44
  },
45
- "_vendor-codemirror-BxoXtD6f.js": {
46
- "file": "assets/vendor-codemirror-BxoXtD6f.js",
45
+ "_vendor-codemirror-BlmFw5CA.js": {
46
+ "file": "assets/vendor-codemirror-BlmFw5CA.js",
47
47
  "name": "vendor-codemirror",
48
48
  "imports": [
49
- "_vendor-ARQZvmz5.js",
50
- "_vendor-shiki-BpqODMgR.js"
49
+ "_vendor-BKYy4SMr.js",
50
+ "_vendor-shiki-BitvudxD.js"
51
51
  ]
52
52
  },
53
- "_vendor-recharts-Cl8AO7Np.js": {
54
- "file": "assets/vendor-recharts-Cl8AO7Np.js",
53
+ "_vendor-recharts-Bz7zqjbW.js": {
54
+ "file": "assets/vendor-recharts-Bz7zqjbW.js",
55
55
  "name": "vendor-recharts",
56
56
  "imports": [
57
- "_vendor-ARQZvmz5.js"
57
+ "_vendor-BKYy4SMr.js"
58
58
  ]
59
59
  },
60
- "_vendor-shiki-BpqODMgR.js": {
61
- "file": "assets/vendor-shiki-BpqODMgR.js",
60
+ "_vendor-shiki-BitvudxD.js": {
61
+ "file": "assets/vendor-shiki-BitvudxD.js",
62
62
  "name": "vendor-shiki",
63
63
  "imports": [
64
- "_vendor-ARQZvmz5.js"
64
+ "_vendor-BKYy4SMr.js"
65
65
  ]
66
66
  },
67
67
  "_vendor-three-C5WAXd5r.js": {
@@ -69,19 +69,19 @@
69
69
  "name": "vendor-three"
70
70
  },
71
71
  "index.tsx": {
72
- "file": "assets/index-DNmPxkEL.js",
72
+ "file": "assets/index-CdNW7TcY.js",
73
73
  "name": "index",
74
74
  "src": "index.tsx",
75
75
  "isEntry": true,
76
76
  "imports": [
77
- "_vendor-ARQZvmz5.js",
78
- "_vendor-arizeai-Ct6kvW4e.js",
79
- "_pages-Bns7xROJ.js",
80
- "_components-DGavuwFF.js",
77
+ "_vendor-BKYy4SMr.js",
78
+ "_vendor-arizeai-hGVPFFRq.js",
79
+ "_pages-BWWAYqfd.js",
80
+ "_components-37rV35eH.js",
81
81
  "_vendor-three-C5WAXd5r.js",
82
- "_vendor-codemirror-BxoXtD6f.js",
83
- "_vendor-shiki-BpqODMgR.js",
84
- "_vendor-recharts-Cl8AO7Np.js"
82
+ "_vendor-codemirror-BlmFw5CA.js",
83
+ "_vendor-shiki-BitvudxD.js",
84
+ "_vendor-recharts-Bz7zqjbW.js"
85
85
  ]
86
86
  }
87
87
  }