arize-phoenix 8.1.0__py3-none-any.whl → 8.2.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 (36) hide show
  1. {arize_phoenix-8.1.0.dist-info → arize_phoenix-8.2.0.dist-info}/METADATA +1 -1
  2. {arize_phoenix-8.1.0.dist-info → arize_phoenix-8.2.0.dist-info}/RECORD +36 -34
  3. phoenix/db/models.py +151 -11
  4. phoenix/server/api/context.py +4 -0
  5. phoenix/server/api/dataloaders/__init__.py +4 -0
  6. phoenix/server/api/dataloaders/span_by_id.py +29 -0
  7. phoenix/server/api/dataloaders/span_descendants.py +24 -15
  8. phoenix/server/api/dataloaders/span_fields.py +76 -0
  9. phoenix/server/api/dataloaders/trace_root_spans.py +9 -10
  10. phoenix/server/api/mutations/chat_mutations.py +10 -7
  11. phoenix/server/api/queries.py +2 -2
  12. phoenix/server/api/subscriptions.py +3 -3
  13. phoenix/server/api/types/Annotation.py +4 -1
  14. phoenix/server/api/types/DatasetExample.py +2 -2
  15. phoenix/server/api/types/Project.py +8 -10
  16. phoenix/server/api/types/ProjectSession.py +2 -2
  17. phoenix/server/api/types/Span.py +377 -120
  18. phoenix/server/api/types/SpanIOValue.py +39 -6
  19. phoenix/server/api/types/Trace.py +17 -15
  20. phoenix/server/app.py +4 -0
  21. phoenix/server/prometheus.py +113 -7
  22. phoenix/server/static/.vite/manifest.json +36 -36
  23. phoenix/server/static/assets/{components-B-qgPyHv.js → components-C48uRczp.js} +1 -1
  24. phoenix/server/static/assets/{index-D4KO1IcF.js → index-5klIR86Z.js} +2 -2
  25. phoenix/server/static/assets/{pages-DdcuL3Rh.js → pages-sERWyBWu.js} +326 -326
  26. phoenix/server/static/assets/{vendor-DQp7CrDA.js → vendor-Cqfydjep.js} +117 -117
  27. phoenix/server/static/assets/{vendor-arizeai-C1nEIEQq.js → vendor-arizeai-WnerlUPN.js} +1 -1
  28. phoenix/server/static/assets/{vendor-codemirror-BZXYUIkP.js → vendor-codemirror-D-ZZKLFq.js} +1 -1
  29. phoenix/server/static/assets/{vendor-recharts-BUFpwCVD.js → vendor-recharts-KY97ZPfK.js} +1 -1
  30. phoenix/server/static/assets/{vendor-shiki-C8L-c9jT.js → vendor-shiki-D5K9GnFn.js} +1 -1
  31. phoenix/trace/attributes.py +7 -2
  32. phoenix/version.py +1 -1
  33. {arize_phoenix-8.1.0.dist-info → arize_phoenix-8.2.0.dist-info}/WHEEL +0 -0
  34. {arize_phoenix-8.1.0.dist-info → arize_phoenix-8.2.0.dist-info}/entry_points.txt +0 -0
  35. {arize_phoenix-8.1.0.dist-info → arize_phoenix-8.2.0.dist-info}/licenses/IP_NOTICE +0 -0
  36. {arize_phoenix-8.1.0.dist-info → arize_phoenix-8.2.0.dist-info}/licenses/LICENSE +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: arize-phoenix
3
- Version: 8.1.0
3
+ Version: 8.2.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
@@ -6,7 +6,7 @@ phoenix/exceptions.py,sha256=n2L2KKuecrdflB9MsCdAYCiSEvGJptIsfRkXMoJle7A,169
6
6
  phoenix/py.typed,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
7
7
  phoenix/services.py,sha256=kpW1WL0kiB8XJsO6XycvZVJ-lBkNoenhQ7atCvBoSe8,5365
8
8
  phoenix/settings.py,sha256=ht-0oN-sMV6SPXrk7Tu1EZlngpAYkGNLYPhO8DyrdQI,661
9
- phoenix/version.py,sha256=c04nFsyfS0zYoDvZjLO-uEi12TFB5EWSD6fiWiI7OLQ,22
9
+ phoenix/version.py,sha256=Xb24P8T6pG5QQaEw-UTO0iybyDoxvRGJaUR_qFoVAJQ,22
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
@@ -21,7 +21,7 @@ phoenix/db/enums.py,sha256=tt7iovXLhVTLZ3_LbHNGgcI44SnNjXfkKtLAZG57T54,428
21
21
  phoenix/db/facilitator.py,sha256=sAYqzBXYSVBKPTQVYrd7ZmtqMAr1zP9dVJwjfNGW7hc,4207
22
22
  phoenix/db/helpers.py,sha256=daKbpY2QhTPo9a_T1xNHKI4WzWHkMmmrGIws7Hw-RZ4,4884
23
23
  phoenix/db/migrate.py,sha256=oUrXH8yEbcpL4eh09aSCuUiSrhFli0eT5D_j4ZmYChY,2797
24
- phoenix/db/models.py,sha256=oIndWsURbLAaOsnYpmT9iiWcbnVdPC2pfUfMrsq07Sk,38802
24
+ phoenix/db/models.py,sha256=MpG6E3vGP5_kcc2p-4_CmLwPQgHs2-ht_Iwt6oJgljs,43888
25
25
  phoenix/db/insertion/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
26
26
  phoenix/db/insertion/constants.py,sha256=8wifm7X-1XvroZ__R2Gc96NsgLhTDn0zXl4lehlXtcA,70
27
27
  phoenix/db/insertion/dataset.py,sha256=I9OC1ouVx7m6BH_c8hvcxW1dWGRAtpvXee29yBTuFkg,7136
@@ -80,7 +80,7 @@ phoenix/pointcloud/pointcloud.py,sha256=SN_1wXZcwKrtSnHGZLDZGx71orqE1WyVF7E-D58d
80
80
  phoenix/pointcloud/projectors.py,sha256=TQgwc9cJDjJkin1WZyZzgl3HsYrLLiyWD7Czy4jNW3U,1088
81
81
  phoenix/pointcloud/umap_parameters.py,sha256=db_WEPoamuWtopZx7tQfAXPnoE0MS8FkAV0_ThjEx_Q,1735
82
82
  phoenix/server/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
83
- phoenix/server/app.py,sha256=aQa0XbToiHNFPWyi6xPXYN76GJDxJXAeEySl7Lz8TBs,38886
83
+ phoenix/server/app.py,sha256=9wYjV0Nmc5oascyqAhQ_Cx5dIH_ofEsdn_L_77mtG6w,39041
84
84
  phoenix/server/bearer_auth.py,sha256=0UudvkAS_dxna5JEJJhGUYwB6Ny-e22ssX5Mm79QwCk,5907
85
85
  phoenix/server/dml_event.py,sha256=MjJmVEKytq75chBOSyvYDusUnEbg1pHpIjR3pZkUaJA,2838
86
86
  phoenix/server/dml_event_handler.py,sha256=EZLXmCvx4pJrCkz29gxwKwmvmUkTtPCHw6klR-XM8qE,8258
@@ -88,7 +88,7 @@ phoenix/server/grpc_server.py,sha256=SknR-iLIUqU9swiAyLhbCUREA1obOM6w49xgdK1AQDs
88
88
  phoenix/server/jwt_store.py,sha256=asxzY4_ZBM2FWAMstHvhvnKUP_0AA3v3xPTL2IOgNqY,16831
89
89
  phoenix/server/main.py,sha256=khpRUAF6wo6rrFrhFbWIoZi43dQL-uY8QtdEucGT1y8,16311
90
90
  phoenix/server/oauth2.py,sha256=EV4wcCwG0N7cJRcfGNURdP5rZgRVCeRDvXyle19A27Y,2064
91
- phoenix/server/prometheus.py,sha256=x9zV3M8VeZ9GS32SLr5OxNdi1VaxvdHCZPx2zI8Hoa4,3549
91
+ phoenix/server/prometheus.py,sha256=1KjvSfjSa2-BPjDybVMM_Kag316CsN-Zwt64YNr_snc,7825
92
92
  phoenix/server/rate_limiters.py,sha256=cFc73D2NaxqNZZDbwfIDw4So-fRVOJPBtqxOZ8Qky_s,7155
93
93
  phoenix/server/telemetry.py,sha256=4EluDDrhdDPxAjaW6lVSbi73xkB5XeUCZWOmZGdk0hg,2755
94
94
  phoenix/server/thread_server.py,sha256=Ea2AWreN1lwJsT2wYvGaRaiXrzBqH4kgkZpx0FO5Ocw,2144
@@ -96,14 +96,14 @@ phoenix/server/types.py,sha256=gJJPBcDRkQ9VHZIt_aLqG_OBbGt1oWp4e3W3Jp61oKs,7409
96
96
  phoenix/server/api/README.md,sha256=Pyq1PLPgTzXAswrfIhGXrjI3Skq8it2jTVnanT6Ba4Q,1162
97
97
  phoenix/server/api/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
98
98
  phoenix/server/api/auth.py,sha256=nywpmfMI1trZTbZRD3oBj4kFjzg_vnxDljcM431T1eY,1246
99
- phoenix/server/api/context.py,sha256=AtmQ_j68oxUqVNn1m2ycDQGgzU46pPlw63tSXvUUHVM,6094
99
+ phoenix/server/api/context.py,sha256=6dty-UK5ARXG8wVbyBEUPD9aARVjlkCbqkeSHL8ZJIw,6217
100
100
  phoenix/server/api/exceptions.py,sha256=TA0JuY2YRnj35qGuMSQ8d0ToHum9gWm9W--3fSKHrX0,1171
101
101
  phoenix/server/api/interceptor.py,sha256=ykDnoC_apUd-llVli3m1CW18kNSIgjz2qZ6m5JmPDu8,1294
102
- phoenix/server/api/queries.py,sha256=4ICBJz45jd9N36Mk6KmpDojfihnVkYFv4_RSFO6X6W4,32320
102
+ phoenix/server/api/queries.py,sha256=33FaHsmiVWRT21rJwMjDwF6UnJyknF97vto24zwfJWQ,32328
103
103
  phoenix/server/api/schema.py,sha256=fcs36xQwFF_Qe41_5cWR8wYpDvOrnbcyTeo5WNMbDsA,1702
104
- phoenix/server/api/subscriptions.py,sha256=VXN7BPVR0WaI2RawXYOsa7orHv5341gWpUH0pU1Mnzg,22951
104
+ phoenix/server/api/subscriptions.py,sha256=DSIgQF6lQqkbc7D0AaI5R4g3hIHbU04H5Y2UIpwmpy0,22989
105
105
  phoenix/server/api/utils.py,sha256=quCBRcusc6PUq9tJq7M8PgwFZp7nXgVAxtbw8feribY,833
106
- phoenix/server/api/dataloaders/__init__.py,sha256=lVJitwc8lP1HoP5dzzDuEt2TyTdWt6THBCBob1IWgjw,4036
106
+ phoenix/server/api/dataloaders/__init__.py,sha256=32yLWx_Hzeh5F1zVHm6ZqRoN6d43ThMU9d47iuQH3BY,4179
107
107
  phoenix/server/api/dataloaders/annotation_summaries.py,sha256=2sHmIDX7n8tuPeBTs9bMKtlMKWn_Ph9awTZqmwn2Owc,5505
108
108
  phoenix/server/api/dataloaders/average_experiment_run_latency.py,sha256=GLFoFAbztOH-0FVzzZ77mATIO63UcjB50j3qXiNi-tE,1811
109
109
  phoenix/server/api/dataloaders/dataset_example_revisions.py,sha256=xF7M2dg3UmjhdCrscnztCIBBI0cg3RF48IIqvilpc18,4623
@@ -127,12 +127,14 @@ phoenix/server/api/dataloaders/session_num_traces_with_error.py,sha256=Y5xmr4Nme
127
127
  phoenix/server/api/dataloaders/session_token_usages.py,sha256=9RXZUyzDmzp5WDp_BpmvvDhk5gIIaV97x0xxUM1H9bA,1586
128
128
  phoenix/server/api/dataloaders/session_trace_latency_ms_quantile.py,sha256=s_Ce_BSad1Wvabq9hajdq-i-9rLbZ0esU2gwg9h7Vwg,2157
129
129
  phoenix/server/api/dataloaders/span_annotations.py,sha256=y5TvxnljS2P3hm_NiQd24qy313AG76lyjcZFNWGls1A,1028
130
+ phoenix/server/api/dataloaders/span_by_id.py,sha256=gaqsMqMjJfzgsAuC--lZoRvgHk_1o78_0HnpXejg9Bc,1001
130
131
  phoenix/server/api/dataloaders/span_dataset_examples.py,sha256=rpBStVP7ZMIH11Cpq4-zCJ4amNC5ZzX72NYv4vAQTdc,1255
131
- phoenix/server/api/dataloaders/span_descendants.py,sha256=1NhD59micO9ozKTF85bFKVCqqsrfDzrK0e0nlg6QyvI,2051
132
+ phoenix/server/api/dataloaders/span_descendants.py,sha256=lEUpPkQtM5twbBrwzBbrJN7wZpG0K2rdL7hG2PAs5pk,2308
133
+ phoenix/server/api/dataloaders/span_fields.py,sha256=HNggF00gbajMEUh5pwIpUPGENytRSVL304Lp4p9L7yk,2965
132
134
  phoenix/server/api/dataloaders/span_projects.py,sha256=JTfuKn2BBn72QdAP53ZGP2OUCgZJ7AzlzQAx8WivBog,1234
133
135
  phoenix/server/api/dataloaders/token_counts.py,sha256=Sr_sBfLgsKYCIjgzTFV-fuat7s7DATM2u6ZftLaOnoQ,4629
134
136
  phoenix/server/api/dataloaders/trace_by_trace_ids.py,sha256=xqYJOjCgXlTzour4vY72kO-2gJFgWMjD3759o78cq44,874
135
- phoenix/server/api/dataloaders/trace_root_spans.py,sha256=IpSsoBRF0NHtbFhQasA_mux6v-m6b0yFRINygjCBOtM,1106
137
+ phoenix/server/api/dataloaders/trace_root_spans.py,sha256=0Y2ImTPmDHpz_quRMIjuxED2M0dAm2M0StZUmSTiTiw,1000
136
138
  phoenix/server/api/dataloaders/user_roles.py,sha256=TvPWve38TlGFvaFkMeFvnnHBn1OMHUJTErRLeYrXR8I,1029
137
139
  phoenix/server/api/dataloaders/users.py,sha256=Uh86Kny_xpqDdEvEklcTBUA_MELgu4Z0jgh45ZqTzXs,1075
138
140
  phoenix/server/api/dataloaders/cache/__init__.py,sha256=SYoOM9n8FJaMdQarma5d1blu-jIg2GB8Shqg5ezSzZ8,106
@@ -186,7 +188,7 @@ phoenix/server/api/input_types/UserRoleInput.py,sha256=xxhFe0ITZOgRVEJbVem_W6F1I
186
188
  phoenix/server/api/input_types/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
187
189
  phoenix/server/api/mutations/__init__.py,sha256=P28nrqaEjfwTI8ijBmVdWbCmtfQGhlG-_xYBqp7RvKc,1460
188
190
  phoenix/server/api/mutations/api_key_mutations.py,sha256=diNBL06zrFCmzR-leYbL-AxDqN05-YtbzsnsAdew_K8,6194
189
- phoenix/server/api/mutations/chat_mutations.py,sha256=WZoA9Y0IPt5Gi5PCfuPZtHBTz1nfuW6D_WHF0uM-9nU,22836
191
+ phoenix/server/api/mutations/chat_mutations.py,sha256=ChRh4YbNkBgFO05CNElzqJ1hIvo7lr5Zn0lvlRs51pE,22997
190
192
  phoenix/server/api/mutations/dataset_mutations.py,sha256=V52mL0vizxL9cvWWBkmd19rGu8xvu-B8LOosZkROPIk,27004
191
193
  phoenix/server/api/mutations/experiment_mutations.py,sha256=p3CoLAa8nFPa3D759Y2A7De_PVJNGOL98mA3HoZBrRQ,3188
192
194
  phoenix/server/api/mutations/export_events_mutations.py,sha256=xoDnVWC7eA_8wNQP0-oyiHojyUZ0EhVVSrsAnztetC0,3993
@@ -216,7 +218,7 @@ phoenix/server/api/routers/v1/prompts.py,sha256=kRsssgRulr-ys0ouLz4qG-TP-uG30UZc
216
218
  phoenix/server/api/routers/v1/spans.py,sha256=uoU_bwIgz86fuvPjP5sX8goDyuCcnsTig-x3f17p60U,9625
217
219
  phoenix/server/api/routers/v1/traces.py,sha256=hSv35QIB4mwFgp53rOpz3zWIiSwbZzQnjafD790QuJU,7908
218
220
  phoenix/server/api/routers/v1/utils.py,sha256=SoRl0Dc8By15ZckhNcXg2QRrqYjMvgTjVcqrZ6MwVmo,3065
219
- phoenix/server/api/types/Annotation.py,sha256=7Ym7iuVcbwHlw2yIRylz4nATAF_Cm-Z17qcjiooj1cc,751
221
+ phoenix/server/api/types/Annotation.py,sha256=r1JDvkWoFKeLRysos3l1exqKfs8zoklVv2tNSG9kFzQ,849
220
222
  phoenix/server/api/types/AnnotationSummary.py,sha256=Adg_tFla5lL2csDQeQwn58b8kTB_57hotAN6WLQCLzE,1512
221
223
  phoenix/server/api/types/AnnotatorKind.py,sha256=rPgGdbN1Gvc109sGQ_ZH-gfJbp93V9wlarzTEJNtUwI,236
222
224
  phoenix/server/api/types/ApiKey.py,sha256=76HIkWxyJqL3UjdMesX9l5y6XaD9QphwabXGQVY-MaQ,793
@@ -227,7 +229,7 @@ phoenix/server/api/types/Cluster.py,sha256=duX0pSC7P3YT1IztduuJtPsRj1x6oOOLfmWf2
227
229
  phoenix/server/api/types/CreateDatasetPayload.py,sha256=R-6zCmuD0f76RU9Giu78xwTHlASQs6Aq8yzvX1Kxc3g,140
228
230
  phoenix/server/api/types/DataQualityMetric.py,sha256=Aieg3bHeBFaAf4mqeRcH1zT04sXAtQD8ATSHJt7FaBQ,1538
229
231
  phoenix/server/api/types/Dataset.py,sha256=1NACY8_s-TZGgTwv6pkFlYPJDy14EVcQlrjgNd7dQ_U,11732
230
- phoenix/server/api/types/DatasetExample.py,sha256=5f7DO6jRec1G5jaJW5FrykFU9R07jDn8jxDkNbQX0Ek,3107
232
+ phoenix/server/api/types/DatasetExample.py,sha256=_9byxGpXfYb-hmFMUJeG7Bw1wsRKSJaHwF2IPAbFpFw,3115
231
233
  phoenix/server/api/types/DatasetExampleRevision.py,sha256=c-jWR6dTguEZTm54IMlFr0Ic84I3nefyDnZb7nF5hnI,874
232
234
  phoenix/server/api/types/DatasetValues.py,sha256=7VbCOLlzOXpZN80-zYF2UGuafRcPsZF-8WQNc0YsKFc,1119
233
235
  phoenix/server/api/types/DatasetVersion.py,sha256=NnDriqQUE20N_Jhyu3-IxHWJQE9wmdHEJIQAKyTVJpo,302
@@ -262,8 +264,8 @@ phoenix/server/api/types/MimeType.py,sha256=Zpi6zCalkSFgsvhzvOs-O1gYA04usAi9H__Q
262
264
  phoenix/server/api/types/Model.py,sha256=8UIFqMe1q-2ufBNg-gxHusV8wM1h-KbfLUeJjyVcMvQ,8081
263
265
  phoenix/server/api/types/NumericRange.py,sha256=afEjgF97Go_OvmjMggbPBt-zGM8IONewAyEiKEHRds0,192
264
266
  phoenix/server/api/types/PerformanceMetric.py,sha256=KFkmJDqP43eDUtARQOUqR7NYcxvL6Vh2uisHWU6H3ko,387
265
- phoenix/server/api/types/Project.py,sha256=hjHKaBtJk8rV-Nyu7MUNtT52wMO9fCM3nkfRXS54akM,18853
266
- phoenix/server/api/types/ProjectSession.py,sha256=j3RJrTG9umruav7d81AqnQVz-Zpi60nXmnsr2Wplpac,4646
267
+ phoenix/server/api/types/Project.py,sha256=abX-1eefV8tdmv9gEBz0nZaKKtvFPBrJJYcdaCr7vP4,18752
268
+ phoenix/server/api/types/ProjectSession.py,sha256=LVsFSoWt9U1dykWVtyOXF_mqp0Re8fj4oaogsOKKO1Y,4660
267
269
  phoenix/server/api/types/Prompt.py,sha256=ccP4eq1e38xbF0afclGWLOuDpBVpNbJ3AOSRClF8yFQ,4955
268
270
  phoenix/server/api/types/PromptLabel.py,sha256=g3IDSPYRZwb0qpMAk93R6J96jgYULUYGOciTnpeh3sI,1321
269
271
  phoenix/server/api/types/PromptResponse.py,sha256=Q8HKtpp8GpUOcxPCzZpkkokidDd6u0aZOv_SuPZZd5Q,630
@@ -275,14 +277,14 @@ phoenix/server/api/types/Retrieval.py,sha256=OhMK2ncjoyp5h1yjKhjlKpoTbQrMHuxmgSF
275
277
  phoenix/server/api/types/ScalarDriftMetricEnum.py,sha256=IUAcRPpgL41WdoIgK6cNk2Te38SspXGyEs-S1fY23_A,232
276
278
  phoenix/server/api/types/Segments.py,sha256=vT2v0efoa5cuBKxLtxTnsUP5YJJCZfTloM71Spu0tMI,2915
277
279
  phoenix/server/api/types/SortDir.py,sha256=OUpXhlCzCxPoXSDkJJygEs9Rw9pMymfaZUG5zPTrw4Y,152
278
- phoenix/server/api/types/Span.py,sha256=uTFIIlWuGgiXuw2cmJkUTJXM0fimogPJ7s7QAjctalE,15344
280
+ phoenix/server/api/types/Span.py,sha256=bUxpuaa1Z8OeyQtM38ARQOXdMEY9tTXUj8_PYDsevkQ,22924
279
281
  phoenix/server/api/types/SpanAnnotation.py,sha256=6b5G-b_OoRvDL2ayWk7MkbqarLK-F-pQMx21CpUuNGY,1168
280
- phoenix/server/api/types/SpanIOValue.py,sha256=u_g9QrX-E3fwneoucire73KY5gf3fE4V9IBN3qx2cvU,445
282
+ phoenix/server/api/types/SpanIOValue.py,sha256=c5TWdZZN3v0gHI5xWeY7gjD-sE9ugWlGGAio-gDS-Uo,1653
281
283
  phoenix/server/api/types/SystemApiKey.py,sha256=2ym8EgsTBIvxx1l9xZ-2YMovz58ZwYb_MaHBTJ9NH2E,166
282
284
  phoenix/server/api/types/TimeSeries.py,sha256=nuuZtfHmOhTjeB8_SvZ5PUQexAkTcPScwYeFC5RUlRU,5491
283
285
  phoenix/server/api/types/TokenUsage.py,sha256=g-PjAGVigpchQgkXAuC5sc53fn2YwAgfeXkGmFPi_TE,201
284
286
  phoenix/server/api/types/ToolDefinition.py,sha256=T6UH2vcbuPBDy7jKYOqMth2NdqxMPgDBf11Tpbt5Yb8,187
285
- phoenix/server/api/types/Trace.py,sha256=Nwof2FlcZtCFnKTS5UqvNZBlY-VcN5QMK7Zm9xEQUrU,5597
287
+ phoenix/server/api/types/Trace.py,sha256=6CqXcrI8rpEetGWAN8-lv6XoieLKqwobsKFV1qY4fsY,5645
286
288
  phoenix/server/api/types/TraceAnnotation.py,sha256=OW6A2zr1gomOuG0XQe55dk15XXX2DSM0DzatRbHWH5A,1256
287
289
  phoenix/server/api/types/UMAPPoints.py,sha256=49sWnxjcAJKRzqUY71Fa0tOPti5XjIIFT5cSg6oNu_U,1650
288
290
  phoenix/server/api/types/User.py,sha256=iTVUrI8U6-asOhBt1bOZNtoRAaRNC4WHgR1Uv2rHnWQ,1975
@@ -309,16 +311,16 @@ phoenix/server/static/apple-touch-icon-76x76.png,sha256=CT_xT12I0u2i0WU8JzBZBuOQ
309
311
  phoenix/server/static/apple-touch-icon.png,sha256=fOfpjqGpWYbJ0eAurKsyoZP1EAs6ZVooBJ_SGk2ZkDs,3801
310
312
  phoenix/server/static/favicon.ico,sha256=bY0vvCKRftemZfPShwZtE93DiiQdaYaozkPGwNFr6H8,34494
311
313
  phoenix/server/static/modernizr.js,sha256=mvK-XtkNqjOral-QvzoqsyOMECXIMu5BQwSVN_wcU9c,2564
312
- phoenix/server/static/.vite/manifest.json,sha256=nqF_R52ujYjPJIw5YIYSh8KeJLfrOz6jqYqi3hD2WKg,2165
313
- phoenix/server/static/assets/components-B-qgPyHv.js,sha256=LbHDZ8gvlTaJPyjDhyjRgiwFX1Ci01JnCIs9SxT0Yns,420034
314
- phoenix/server/static/assets/index-D4KO1IcF.js,sha256=y6HnAkkI28fsu0ReMb4VVxU0zORXDpu4K2GwMoQv3Jc,58963
315
- phoenix/server/static/assets/pages-DdcuL3Rh.js,sha256=VYg3giHtIIYTtsuOAexOLJ4o1TUNF_Srcd3q3XQasTo,821509
314
+ phoenix/server/static/.vite/manifest.json,sha256=n2Kx62btxpzHwBfc7RBKnNxX1ux-QCNTazb-9jPETvo,2165
315
+ phoenix/server/static/assets/components-C48uRczp.js,sha256=v3gfQhyERlmWNtOssc-cBz6i4P7GmTqIrCjf7emrIME,420034
316
+ phoenix/server/static/assets/index-5klIR86Z.js,sha256=kUXGzV2YpVw0mWthayX8Tl-47l16Dne7xJOifLmJRmM,58963
317
+ phoenix/server/static/assets/pages-sERWyBWu.js,sha256=jPJiX3xpn211FVcvzlD-Dp165Dx3lXX_RtmEFZtwvWc,821609
316
318
  phoenix/server/static/assets/vendor-Cg6lcjUC.css,sha256=nZrkr0u6NNElFGvpWHk9GTHeGoibCXCli1bE7mXZGZg,1816
317
- phoenix/server/static/assets/vendor-DQp7CrDA.js,sha256=of8vHuO798FVjrUNrF52kg6MYuU8n9b810LurBo7YPA,2234991
318
- phoenix/server/static/assets/vendor-arizeai-C1nEIEQq.js,sha256=DtHpN1xsgJlnJzLe0Od9dPr4fAYgTz253y_5OQUfoOE,202331
319
- phoenix/server/static/assets/vendor-codemirror-BZXYUIkP.js,sha256=SNSmeyYixK2hCb6vnYw0kfzCp99M9tbTf4iwNPDPWrU,393496
320
- phoenix/server/static/assets/vendor-recharts-BUFpwCVD.js,sha256=MFNYYFLBb_kMw9jyAbQ90ggxCTbqz4vJJeNMyG2eyo4,282095
321
- phoenix/server/static/assets/vendor-shiki-C8L-c9jT.js,sha256=53LSVOulG6hgSohJZgN6ItJsgkvkNydLQbC_7Dm03jg,8980312
319
+ phoenix/server/static/assets/vendor-Cqfydjep.js,sha256=3sepoB8zViIDMxKuc4lpMVfCmATxlvc9vCEPI2B2uVQ,2235092
320
+ phoenix/server/static/assets/vendor-arizeai-WnerlUPN.js,sha256=MahCU9MTwcGSh9tsu-gsF-SWqCISSFIYq9ZnMoKLPkA,202331
321
+ phoenix/server/static/assets/vendor-codemirror-D-ZZKLFq.js,sha256=DJbYiCh2ng3IV5yyktfweoMAKiLYdhz0EFElIpbzsco,393496
322
+ phoenix/server/static/assets/vendor-recharts-KY97ZPfK.js,sha256=5LKQPOTBvQfISnmmzDoWqK-AaVtRQTKTYXVTEvyG1KU,282095
323
+ phoenix/server/static/assets/vendor-shiki-D5K9GnFn.js,sha256=mi8NXvOV1AbRuOTVp3wlon_CBUs4RtzqbPjqTD2OnbU,8980312
322
324
  phoenix/server/static/assets/vendor-three-C-AGeJYv.js,sha256=c9nLPH5YDRFCzTNuwWO8_5KqcnunPo53mQsb7y9JFW8,620972
323
325
  phoenix/server/templates/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
324
326
  phoenix/server/templates/index.html,sha256=e8_jdi7Eo19SK7DI_gglkTW094D17E0VAegoMmmmvIc,4330
@@ -328,7 +330,7 @@ phoenix/session/data_extractor.py,sha256=Y0RzYFaNy9fQj8PEIeQ76TBZ90_E1FW7bXu3K5x
328
330
  phoenix/session/evaluation.py,sha256=Q3fOMNELvqkk-b6a6PKc8pDJdsNQ0ZbTpseUSA2NKqs,5300
329
331
  phoenix/session/session.py,sha256=nD0v1VWQVOnKpviK2RjjFV3YLcNdrDkrclsK64Y3H6Y,27540
330
332
  phoenix/trace/__init__.py,sha256=ujk_uYjM8gmm-YqnyXxF-kekfwid0bcaPMTtNNcaw6U,407
331
- phoenix/trace/attributes.py,sha256=mHpOtdjGIs-VGSev_dwvxTeZTc9GILovi1Rdnj56HO0,12457
333
+ phoenix/trace/attributes.py,sha256=hyEKYZWPCP4NRmW7VmiC2voa3TH7FYKUBR9DYiVfXlw,12627
332
334
  phoenix/trace/errors.py,sha256=wB1z8qdPckngdfU-TORToekvg3344oNFAA83_hC2yFY,180
333
335
  phoenix/trace/evaluation_conventions.py,sha256=t8jydM3U0-T5YpiQKRJ3tWdWGlHtzKyttYdw-ddvPOk,1048
334
336
  phoenix/trace/exporter.py,sha256=bUXh8fjJIbHurrnt4bAm-cCWqUN5FqNsIc8DZzzklkQ,4695
@@ -359,9 +361,9 @@ phoenix/utilities/project.py,sha256=auVpARXkDb-JgeX5f2aStyFIkeKvGwN9l7qrFeJMVxI,
359
361
  phoenix/utilities/re.py,sha256=6YyUWIkv0zc2SigsxfOWIHzdpjKA_TZo2iqKq7zJKvw,2081
360
362
  phoenix/utilities/span_store.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
361
363
  phoenix/utilities/template_formatters.py,sha256=gh9PJD6WEGw7TEYXfSst1UR4pWWwmjxMLrDVQ_CkpkQ,2779
362
- arize_phoenix-8.1.0.dist-info/METADATA,sha256=kX65Ch5ML6Yfm2nHtpVG6tpNXLN7M6Lcyq_-Fo2BSek,23425
363
- arize_phoenix-8.1.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
364
- arize_phoenix-8.1.0.dist-info/entry_points.txt,sha256=Pgpn8Upxx9P8z8joPXZWl2LlnAlGc3gcQoVchb06X1Q,94
365
- arize_phoenix-8.1.0.dist-info/licenses/IP_NOTICE,sha256=JBqyyCYYxGDfzQ0TtsQgjts41IJoa-hiwDrBjCb9gHM,469
366
- arize_phoenix-8.1.0.dist-info/licenses/LICENSE,sha256=HFkW9REuMOkvKRACuwLPT0hRydHb3zNg-fdFt94td18,3794
367
- arize_phoenix-8.1.0.dist-info/RECORD,,
364
+ arize_phoenix-8.2.0.dist-info/METADATA,sha256=Jbtjw1VWOqupiGoCwLAMg9h53_MQu0OKHS3wWkIBlqs,23425
365
+ arize_phoenix-8.2.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
366
+ arize_phoenix-8.2.0.dist-info/entry_points.txt,sha256=Pgpn8Upxx9P8z8joPXZWl2LlnAlGc3gcQoVchb06X1Q,94
367
+ arize_phoenix-8.2.0.dist-info/licenses/IP_NOTICE,sha256=JBqyyCYYxGDfzQ0TtsQgjts41IJoa-hiwDrBjCb9gHM,469
368
+ arize_phoenix-8.2.0.dist-info/licenses/LICENSE,sha256=HFkW9REuMOkvKRACuwLPT0hRydHb3zNg-fdFt94td18,3794
369
+ arize_phoenix-8.2.0.dist-info/RECORD,,
phoenix/db/models.py CHANGED
@@ -1,7 +1,9 @@
1
1
  from datetime import datetime, timezone
2
2
  from enum import Enum
3
- from typing import Any, Optional, TypedDict
3
+ from typing import Any, Optional, Sequence, TypedDict
4
4
 
5
+ import sqlalchemy.sql as sql
6
+ from openinference.semconv.trace import RerankerAttributes, SpanAttributes
5
7
  from sqlalchemy import (
6
8
  JSON,
7
9
  NUMERIC,
@@ -12,6 +14,7 @@ from sqlalchemy import (
12
14
  Float,
13
15
  ForeignKey,
14
16
  Index,
17
+ Integer,
15
18
  MetaData,
16
19
  Null,
17
20
  String,
@@ -25,6 +28,7 @@ from sqlalchemy import (
25
28
  text,
26
29
  )
27
30
  from sqlalchemy.dialects import postgresql
31
+ from sqlalchemy.dialects.sqlite.base import SQLiteCompiler
28
32
  from sqlalchemy.ext.asyncio import AsyncEngine, AsyncSession
29
33
  from sqlalchemy.ext.compiler import compiles
30
34
  from sqlalchemy.ext.hybrid import hybrid_property
@@ -36,6 +40,8 @@ from sqlalchemy.orm import (
36
40
  relationship,
37
41
  )
38
42
  from sqlalchemy.sql import expression
43
+ from sqlalchemy.sql.compiler import SQLCompiler
44
+ from sqlalchemy.sql.functions import coalesce
39
45
 
40
46
  from phoenix.config import get_env_database_schema
41
47
  from phoenix.datetime_utils import normalize_datetime
@@ -54,6 +60,18 @@ from phoenix.server.api.helpers.prompts.models import (
54
60
  is_prompt_invocation_parameters,
55
61
  is_prompt_template,
56
62
  )
63
+ from phoenix.trace.attributes import get_attribute_value
64
+
65
+ INPUT_MIME_TYPE = SpanAttributes.INPUT_MIME_TYPE.split(".")
66
+ INPUT_VALUE = SpanAttributes.INPUT_VALUE.split(".")
67
+ LLM_TOKEN_COUNT_TOTAL = SpanAttributes.LLM_TOKEN_COUNT_TOTAL.split(".")
68
+ LLM_TOKEN_COUNT_PROMPT = SpanAttributes.LLM_TOKEN_COUNT_PROMPT.split(".")
69
+ LLM_TOKEN_COUNT_COMPLETION = SpanAttributes.LLM_TOKEN_COUNT_COMPLETION.split(".")
70
+ METADATA = SpanAttributes.METADATA.split(".")
71
+ OUTPUT_MIME_TYPE = SpanAttributes.OUTPUT_MIME_TYPE.split(".")
72
+ OUTPUT_VALUE = SpanAttributes.OUTPUT_VALUE.split(".")
73
+ RERANKER_OUTPUT_DOCUMENTS = RerankerAttributes.RERANKER_OUTPUT_DOCUMENTS.split(".")
74
+ RETRIEVAL_DOCUMENTS = SpanAttributes.RETRIEVAL_DOCUMENTS.split(".")
57
75
 
58
76
 
59
77
  class AuthMethod(Enum):
@@ -341,13 +359,11 @@ class Trace(Base):
341
359
 
342
360
  @hybrid_property
343
361
  def latency_ms(self) -> float:
344
- # See https://docs.sqlalchemy.org/en/20/orm/extensions/hybrid.html
345
362
  return (self.end_time - self.start_time).total_seconds() * 1000
346
363
 
347
364
  @latency_ms.inplace.expression
348
365
  @classmethod
349
366
  def _latency_ms_expression(cls) -> ColumnElement[float]:
350
- # See https://docs.sqlalchemy.org/en/20/orm/extensions/hybrid.html
351
367
  return LatencyMs(cls.start_time, cls.end_time)
352
368
 
353
369
  project: Mapped["Project"] = relationship(
@@ -404,25 +420,124 @@ class Span(Base):
404
420
 
405
421
  @hybrid_property
406
422
  def latency_ms(self) -> float:
407
- # See https://docs.sqlalchemy.org/en/20/orm/extensions/hybrid.html
408
- return (self.end_time - self.start_time).total_seconds() * 1000
423
+ return round((self.end_time - self.start_time).total_seconds() * 1000, 1)
409
424
 
410
425
  @latency_ms.inplace.expression
411
426
  @classmethod
412
427
  def _latency_ms_expression(cls) -> ColumnElement[float]:
413
- # See https://docs.sqlalchemy.org/en/20/orm/extensions/hybrid.html
414
428
  return LatencyMs(cls.start_time, cls.end_time)
415
429
 
430
+ @hybrid_property
431
+ def input_value(self) -> Any:
432
+ return get_attribute_value(self.attributes, INPUT_VALUE)
433
+
434
+ @input_value.inplace.expression
435
+ @classmethod
436
+ def _input_value_expression(cls) -> ColumnElement[Any]:
437
+ return cls.attributes[INPUT_VALUE]
438
+
439
+ @hybrid_property
440
+ def input_value_first_101_chars(self) -> Any:
441
+ if (v := get_attribute_value(self.attributes, INPUT_VALUE)) is None:
442
+ return None
443
+ return str(v)[:101]
444
+
445
+ @input_value_first_101_chars.inplace.expression
446
+ @classmethod
447
+ def _input_value_first_101_chars_expression(cls) -> ColumnElement[Any]:
448
+ return case(
449
+ (
450
+ cls.attributes[INPUT_VALUE] != sql.null(),
451
+ func.substr(cls.attributes[INPUT_VALUE].as_string(), 1, 101),
452
+ ),
453
+ )
454
+
455
+ @hybrid_property
456
+ def input_mime_type(self) -> Any:
457
+ return get_attribute_value(self.attributes, INPUT_MIME_TYPE)
458
+
459
+ @input_mime_type.inplace.expression
460
+ @classmethod
461
+ def _input_mime_type_expression(cls) -> ColumnElement[Any]:
462
+ return cls.attributes[INPUT_MIME_TYPE]
463
+
464
+ @hybrid_property
465
+ def output_value(self) -> Any:
466
+ return get_attribute_value(self.attributes, OUTPUT_VALUE)
467
+
468
+ @output_value.inplace.expression
469
+ @classmethod
470
+ def _output_value_expression(cls) -> ColumnElement[Any]:
471
+ return cls.attributes[OUTPUT_VALUE]
472
+
473
+ @hybrid_property
474
+ def output_value_first_101_chars(self) -> Any:
475
+ if (v := get_attribute_value(self.attributes, OUTPUT_VALUE)) is None:
476
+ return None
477
+ return str(v)[:101]
478
+
479
+ @output_value_first_101_chars.inplace.expression
480
+ @classmethod
481
+ def _output_value_first_101_chars_expression(cls) -> ColumnElement[Any]:
482
+ return case(
483
+ (
484
+ cls.attributes[OUTPUT_VALUE] != sql.null(),
485
+ func.substr(cls.attributes[OUTPUT_VALUE].as_string(), 1, 101),
486
+ ),
487
+ )
488
+
489
+ @hybrid_property
490
+ def output_mime_type(self) -> Any:
491
+ return get_attribute_value(self.attributes, OUTPUT_MIME_TYPE)
492
+
493
+ @output_mime_type.inplace.expression
494
+ @classmethod
495
+ def _output_mime_type_expression(cls) -> ColumnElement[Any]:
496
+ return cls.attributes[OUTPUT_MIME_TYPE]
497
+
498
+ @hybrid_property
499
+ def metadata_(self) -> Any:
500
+ return get_attribute_value(self.attributes, METADATA)
501
+
502
+ @metadata_.inplace.expression
503
+ @classmethod
504
+ def _metadata_expression(cls) -> ColumnElement[Any]:
505
+ return cls.attributes[METADATA]
506
+
507
+ @hybrid_property
508
+ def num_documents(self) -> int:
509
+ if self.span_kind.upper() == "RERANKER":
510
+ reranker_documents = get_attribute_value(self.attributes, RERANKER_OUTPUT_DOCUMENTS)
511
+ return len(reranker_documents) if isinstance(reranker_documents, Sequence) else 0
512
+ retrieval_documents = get_attribute_value(self.attributes, RETRIEVAL_DOCUMENTS)
513
+ return len(retrieval_documents) if isinstance(retrieval_documents, Sequence) else 0
514
+
515
+ @num_documents.inplace.expression
516
+ @classmethod
517
+ def _num_documents_expression(cls) -> ColumnElement[int]:
518
+ return NumDocuments(cls.attributes, cls.span_kind)
519
+
416
520
  @hybrid_property
417
521
  def cumulative_llm_token_count_total(self) -> int:
418
522
  return self.cumulative_llm_token_count_prompt + self.cumulative_llm_token_count_completion
419
523
 
524
+ @cumulative_llm_token_count_total.inplace.expression
525
+ @classmethod
526
+ def _cumulative_llm_token_count_total_expression(cls) -> ColumnElement[int]:
527
+ return cls.cumulative_llm_token_count_prompt + cls.cumulative_llm_token_count_completion
528
+
420
529
  @hybrid_property
421
- def llm_token_count_total(self) -> Optional[int]:
422
- if self.llm_token_count_prompt is None and self.llm_token_count_completion is None:
423
- return None
530
+ def llm_token_count_total(self) -> int:
424
531
  return (self.llm_token_count_prompt or 0) + (self.llm_token_count_completion or 0)
425
532
 
533
+ @llm_token_count_total.inplace.expression
534
+ @classmethod
535
+ def _llm_token_count_total_expression(cls) -> ColumnElement[int]:
536
+ return coalesce(
537
+ coalesce(cls.llm_token_count_prompt, 0) + coalesce(cls.llm_token_count_completion, 0),
538
+ 0,
539
+ )
540
+
426
541
  trace: Mapped["Trace"] = relationship("Trace", back_populates="spans")
427
542
  document_annotations: Mapped[list["DocumentAnnotation"]] = relationship(back_populates="span")
428
543
  dataset_examples: Mapped[list["DatasetExample"]] = relationship(back_populates="span")
@@ -477,6 +592,33 @@ def _(element: Any, compiler: Any, **kw: Any) -> Any:
477
592
  )
478
593
 
479
594
 
595
+ class NumDocuments(expression.FunctionElement[int]):
596
+ # See https://docs.sqlalchemy.org/en/20/core/compiler.html
597
+ inherit_cache = True
598
+ type = Integer()
599
+ name = "num_documents"
600
+
601
+
602
+ @compiles(NumDocuments)
603
+ def _(element: Any, compiler: SQLCompiler, **kw: Any) -> Any:
604
+ # See https://docs.sqlalchemy.org/en/20/core/compiler.html
605
+ array_length = (
606
+ func.json_array_length if isinstance(compiler, SQLiteCompiler) else func.jsonb_array_length
607
+ )
608
+ attributes, span_kind = list(element.clauses)
609
+ retrieval_docs = attributes[RETRIEVAL_DOCUMENTS]
610
+ num_retrieval_docs = coalesce(array_length(retrieval_docs), 0)
611
+ reranker_docs = attributes[RERANKER_OUTPUT_DOCUMENTS]
612
+ num_reranker_docs = coalesce(array_length(reranker_docs), 0)
613
+ return compiler.process(
614
+ sql.case(
615
+ (func.upper(span_kind) == "RERANKER", num_reranker_docs),
616
+ else_=num_retrieval_docs,
617
+ ),
618
+ **kw,
619
+ )
620
+
621
+
480
622
  class TextContains(expression.FunctionElement[str]):
481
623
  # See https://docs.sqlalchemy.org/en/20/core/compiler.html
482
624
  inherit_cache = True
@@ -763,13 +905,11 @@ class ExperimentRun(Base):
763
905
 
764
906
  @hybrid_property
765
907
  def latency_ms(self) -> float:
766
- # See https://docs.sqlalchemy.org/en/20/orm/extensions/hybrid.html
767
908
  return (self.end_time - self.start_time).total_seconds() * 1000
768
909
 
769
910
  @latency_ms.inplace.expression
770
911
  @classmethod
771
912
  def _latency_expression(cls) -> ColumnElement[float]:
772
- # See https://docs.sqlalchemy.org/en/20/orm/extensions/hybrid.html
773
913
  return LatencyMs(cls.start_time, cls.end_time)
774
914
 
775
915
  trace: Mapped["Trace"] = relationship(
@@ -38,8 +38,10 @@ from phoenix.server.api.dataloaders import (
38
38
  SessionTokenUsagesDataLoader,
39
39
  SessionTraceLatencyMsQuantileDataLoader,
40
40
  SpanAnnotationsDataLoader,
41
+ SpanByIdDataLoader,
41
42
  SpanDatasetExamplesDataLoader,
42
43
  SpanDescendantsDataLoader,
44
+ SpanFieldsDataLoader,
43
45
  SpanProjectsDataLoader,
44
46
  TokenCountDataLoader,
45
47
  TraceByTraceIdsDataLoader,
@@ -83,8 +85,10 @@ class DataLoaders:
83
85
  session_token_usages: SessionTokenUsagesDataLoader
84
86
  session_trace_latency_ms_quantile: SessionTraceLatencyMsQuantileDataLoader
85
87
  span_annotations: SpanAnnotationsDataLoader
88
+ span_by_id: SpanByIdDataLoader
86
89
  span_dataset_examples: SpanDatasetExamplesDataLoader
87
90
  span_descendants: SpanDescendantsDataLoader
91
+ span_fields: SpanFieldsDataLoader
88
92
  span_projects: SpanProjectsDataLoader
89
93
  token_counts: TokenCountDataLoader
90
94
  trace_by_trace_ids: TraceByTraceIdsDataLoader
@@ -26,8 +26,10 @@ from .session_num_traces_with_error import SessionNumTracesWithErrorDataLoader
26
26
  from .session_token_usages import SessionTokenUsagesDataLoader
27
27
  from .session_trace_latency_ms_quantile import SessionTraceLatencyMsQuantileDataLoader
28
28
  from .span_annotations import SpanAnnotationsDataLoader
29
+ from .span_by_id import SpanByIdDataLoader
29
30
  from .span_dataset_examples import SpanDatasetExamplesDataLoader
30
31
  from .span_descendants import SpanDescendantsDataLoader
32
+ from .span_fields import SpanFieldsDataLoader
31
33
  from .span_projects import SpanProjectsDataLoader
32
34
  from .token_counts import TokenCountCache, TokenCountDataLoader
33
35
  from .trace_by_trace_ids import TraceByTraceIdsDataLoader
@@ -58,6 +60,8 @@ __all__ = [
58
60
  "SessionNumTracesWithErrorDataLoader",
59
61
  "SessionTokenUsagesDataLoader",
60
62
  "SessionTraceLatencyMsQuantileDataLoader",
63
+ "SpanFieldsDataLoader",
64
+ "SpanByIdDataLoader",
61
65
  "SpanDatasetExamplesDataLoader",
62
66
  "SpanDescendantsDataLoader",
63
67
  "SpanProjectsDataLoader",
@@ -0,0 +1,29 @@
1
+ from typing import Iterable, Union
2
+
3
+ from sqlalchemy import select
4
+ from strawberry.dataloader import DataLoader
5
+ from typing_extensions import TypeAlias
6
+
7
+ from phoenix.db import models
8
+ from phoenix.server.types import DbSessionFactory
9
+
10
+ SpanRowId: TypeAlias = int
11
+
12
+ Key: TypeAlias = SpanRowId
13
+ Result: TypeAlias = models.Span
14
+
15
+
16
+ class SpanByIdDataLoader(DataLoader[Key, Result]):
17
+ def __init__(self, db: DbSessionFactory) -> None:
18
+ super().__init__(load_fn=self._load_fn)
19
+ self._db = db
20
+
21
+ async def _load_fn(self, keys: Iterable[Key]) -> list[Union[Result, ValueError]]:
22
+ span_rowids = list(set(keys))
23
+ spans: dict[Key, Result] = {}
24
+ stmt = select(models.Span).where(models.Span.id.in_(span_rowids))
25
+ async with self._db() as session:
26
+ data = await session.stream_scalars(stmt)
27
+ async for span in data:
28
+ spans[span.id] = span
29
+ return [spans.get(span_rowid, ValueError("Invalid span row id")) for span_rowid in keys]
@@ -1,18 +1,18 @@
1
- from random import randint
1
+ from secrets import token_hex
2
+ from typing import Iterable
2
3
 
3
4
  from aioitertools.itertools import groupby
4
5
  from sqlalchemy import select
5
- from sqlalchemy.orm import joinedload
6
6
  from strawberry.dataloader import DataLoader
7
7
  from typing_extensions import TypeAlias
8
8
 
9
9
  from phoenix.db import models
10
10
  from phoenix.server.types import DbSessionFactory
11
11
 
12
- SpanId: TypeAlias = str
12
+ SpanRowId: TypeAlias = int
13
13
 
14
- Key: TypeAlias = SpanId
15
- Result: TypeAlias = list[models.Span]
14
+ Key: TypeAlias = SpanRowId
15
+ Result: TypeAlias = list[SpanRowId]
16
16
 
17
17
 
18
18
  class SpanDescendantsDataLoader(DataLoader[Key, Result]):
@@ -20,16 +20,25 @@ class SpanDescendantsDataLoader(DataLoader[Key, Result]):
20
20
  super().__init__(load_fn=self._load_fn)
21
21
  self._db = db
22
22
 
23
- async def _load_fn(self, keys: list[Key]) -> list[Result]:
24
- root_ids = set(keys)
25
- root_id_label = f"root_id_{randint(0, 10**6):06}"
23
+ async def _load_fn(self, keys: Iterable[Key]) -> list[Result]:
24
+ root_ids = (
25
+ select(
26
+ models.Span.span_id,
27
+ models.Span.id,
28
+ )
29
+ .where(models.Span.id.in_(set(keys)))
30
+ .subquery()
31
+ )
32
+ root_id_label = f"root_id_{token_hex(8)}"
33
+ root_rowid_label = f"root_rowid_{token_hex(8)}"
26
34
  descendant_ids = (
27
35
  select(
28
36
  models.Span.id,
29
37
  models.Span.span_id,
30
38
  models.Span.parent_id.label(root_id_label),
39
+ root_ids.c.id.label(root_rowid_label),
31
40
  )
32
- .where(models.Span.parent_id.in_(root_ids))
41
+ .join(root_ids, models.Span.parent_id == root_ids.c.span_id)
33
42
  .cte(recursive=True)
34
43
  )
35
44
  parent_ids = descendant_ids.alias()
@@ -38,20 +47,20 @@ class SpanDescendantsDataLoader(DataLoader[Key, Result]):
38
47
  models.Span.id,
39
48
  models.Span.span_id,
40
49
  parent_ids.c[root_id_label],
50
+ parent_ids.c[root_rowid_label],
41
51
  ).join(
42
52
  parent_ids,
43
53
  models.Span.parent_id == parent_ids.c.span_id,
44
54
  )
45
55
  )
46
56
  stmt = (
47
- select(descendant_ids.c[root_id_label], models.Span)
57
+ select(descendant_ids.c[root_rowid_label], models.Span.id)
48
58
  .join(descendant_ids, models.Span.id == descendant_ids.c.id)
49
- .options(joinedload(models.Span.trace, innerjoin=True).load_only(models.Trace.trace_id))
50
- .order_by(descendant_ids.c[root_id_label])
59
+ .order_by(descendant_ids.c[root_rowid_label])
51
60
  )
52
- results: dict[SpanId, Result] = {key: [] for key in keys}
61
+ results: dict[Key, Result] = {key: [] for key in keys}
53
62
  async with self._db() as session:
54
63
  data = await session.stream(stmt)
55
- async for root_id, group in groupby(data, key=lambda d: d[0]):
56
- results[root_id].extend(span for _, span in group)
64
+ async for key, group in groupby(data, key=lambda d: d[0]):
65
+ results[key].extend(span_rowid for _, span_rowid in group)
57
66
  return [results[key].copy() for key in keys]