arize-phoenix 8.0.1__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 (38) hide show
  1. {arize_phoenix-8.0.1.dist-info → arize_phoenix-8.2.0.dist-info}/METADATA +1 -2
  2. {arize_phoenix-8.0.1.dist-info → arize_phoenix-8.2.0.dist-info}/RECORD +37 -36
  3. phoenix/config.py +32 -6
  4. phoenix/db/models.py +151 -11
  5. phoenix/server/api/context.py +4 -0
  6. phoenix/server/api/dataloaders/__init__.py +4 -0
  7. phoenix/server/api/dataloaders/span_by_id.py +29 -0
  8. phoenix/server/api/dataloaders/span_descendants.py +24 -15
  9. phoenix/server/api/dataloaders/span_fields.py +76 -0
  10. phoenix/server/api/dataloaders/trace_root_spans.py +9 -10
  11. phoenix/server/api/mutations/chat_mutations.py +10 -7
  12. phoenix/server/api/queries.py +2 -2
  13. phoenix/server/api/subscriptions.py +3 -3
  14. phoenix/server/api/types/Annotation.py +4 -1
  15. phoenix/server/api/types/DatasetExample.py +2 -2
  16. phoenix/server/api/types/Project.py +8 -10
  17. phoenix/server/api/types/ProjectSession.py +2 -2
  18. phoenix/server/api/types/Span.py +377 -120
  19. phoenix/server/api/types/SpanIOValue.py +39 -6
  20. phoenix/server/api/types/Trace.py +17 -15
  21. phoenix/server/app.py +4 -0
  22. phoenix/server/prometheus.py +113 -7
  23. phoenix/server/static/.vite/manifest.json +36 -36
  24. phoenix/server/static/assets/{components-B-qgPyHv.js → components-C48uRczp.js} +1 -1
  25. phoenix/server/static/assets/{index-D4KO1IcF.js → index-5klIR86Z.js} +2 -2
  26. phoenix/server/static/assets/{pages-DdcuL3Rh.js → pages-sERWyBWu.js} +326 -326
  27. phoenix/server/static/assets/{vendor-DQp7CrDA.js → vendor-Cqfydjep.js} +117 -117
  28. phoenix/server/static/assets/{vendor-arizeai-C1nEIEQq.js → vendor-arizeai-WnerlUPN.js} +1 -1
  29. phoenix/server/static/assets/{vendor-codemirror-BZXYUIkP.js → vendor-codemirror-D-ZZKLFq.js} +1 -1
  30. phoenix/server/static/assets/{vendor-recharts-BUFpwCVD.js → vendor-recharts-KY97ZPfK.js} +1 -1
  31. phoenix/server/static/assets/{vendor-shiki-C8L-c9jT.js → vendor-shiki-D5K9GnFn.js} +1 -1
  32. phoenix/trace/attributes.py +7 -2
  33. phoenix/version.py +1 -1
  34. phoenix/server/api/helpers/jsonschema.py +0 -135
  35. {arize_phoenix-8.0.1.dist-info → arize_phoenix-8.2.0.dist-info}/WHEEL +0 -0
  36. {arize_phoenix-8.0.1.dist-info → arize_phoenix-8.2.0.dist-info}/entry_points.txt +0 -0
  37. {arize_phoenix-8.0.1.dist-info → arize_phoenix-8.2.0.dist-info}/licenses/IP_NOTICE +0 -0
  38. {arize_phoenix-8.0.1.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.0.1
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
@@ -30,7 +30,6 @@ Requires-Dist: grpc-interceptor
30
30
  Requires-Dist: grpcio
31
31
  Requires-Dist: httpx
32
32
  Requires-Dist: jinja2
33
- Requires-Dist: jsonschema<=4.23.0,>=4.0.0
34
33
  Requires-Dist: numpy!=2.0.0
35
34
  Requires-Dist: openinference-instrumentation>=0.1.12
36
35
  Requires-Dist: openinference-semantic-conventions>=0.1.12
@@ -1,12 +1,12 @@
1
1
  phoenix/__init__.py,sha256=X3eUEwd2rG8KKWWYVNNDJoqo08ihfjgHhlP29dcdNJE,5481
2
2
  phoenix/auth.py,sha256=VVMHrWN31tln3Zo4z6ofecrV4daiqJjLd8r85mqlxek,10939
3
- phoenix/config.py,sha256=clBjtpvVN8VznxfYRBgmdG7MclXfCCvyham-Qyf0uWk,27107
3
+ phoenix/config.py,sha256=uGAIiD0dEdUybvCGNeO6TsL12BJl09vjGuXcWjnuPUA,28049
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=kpW1WL0kiB8XJsO6XycvZVJ-lBkNoenhQ7atCvBoSe8,5365
8
8
  phoenix/settings.py,sha256=ht-0oN-sMV6SPXrk7Tu1EZlngpAYkGNLYPhO8DyrdQI,661
9
- phoenix/version.py,sha256=qR-61NMOca8p2Rty8s6xwXQSXLDufw2os6i4zdyqfak,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
@@ -140,7 +142,6 @@ phoenix/server/api/dataloaders/cache/two_tier_cache.py,sha256=cmo8FUT3E91R139IEz
140
142
  phoenix/server/api/helpers/__init__.py,sha256=m2-xaSPqUiSs91k62JaRDjFNfl-1byxBfY-m_Vxw16U,272
141
143
  phoenix/server/api/helpers/dataset_helpers.py,sha256=DoMBTg-qXTnC_K4Evx1WKpCCYgRbITpVqyY-8efJRf0,8984
142
144
  phoenix/server/api/helpers/experiment_run_filters.py,sha256=DOnVwrmn39eAkk2mwuZP8kIcAnR5jrOgllEwWSjsw94,29893
143
- phoenix/server/api/helpers/jsonschema.py,sha256=J6C3Dlf7KTJ1rW6vvhnkB7NNPh2KonwE_0WMP9WKEV0,5470
144
145
  phoenix/server/api/helpers/playground_clients.py,sha256=NIRyKp61OLLZ-UOUUitM8zCh2Xg8bep4qXxSLwEb3XU,39804
145
146
  phoenix/server/api/helpers/playground_registry.py,sha256=CPLMziFB2wmr-dfbx7VbzO2f8YIG_k5RftzvGXYGQ1w,2570
146
147
  phoenix/server/api/helpers/playground_spans.py,sha256=PjGNDc7cpqn5lmRM6TO_J1eVRGlgsNdQ8IT--5JVz0o,16881
@@ -187,7 +188,7 @@ phoenix/server/api/input_types/UserRoleInput.py,sha256=xxhFe0ITZOgRVEJbVem_W6F1I
187
188
  phoenix/server/api/input_types/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
188
189
  phoenix/server/api/mutations/__init__.py,sha256=P28nrqaEjfwTI8ijBmVdWbCmtfQGhlG-_xYBqp7RvKc,1460
189
190
  phoenix/server/api/mutations/api_key_mutations.py,sha256=diNBL06zrFCmzR-leYbL-AxDqN05-YtbzsnsAdew_K8,6194
190
- phoenix/server/api/mutations/chat_mutations.py,sha256=WZoA9Y0IPt5Gi5PCfuPZtHBTz1nfuW6D_WHF0uM-9nU,22836
191
+ phoenix/server/api/mutations/chat_mutations.py,sha256=ChRh4YbNkBgFO05CNElzqJ1hIvo7lr5Zn0lvlRs51pE,22997
191
192
  phoenix/server/api/mutations/dataset_mutations.py,sha256=V52mL0vizxL9cvWWBkmd19rGu8xvu-B8LOosZkROPIk,27004
192
193
  phoenix/server/api/mutations/experiment_mutations.py,sha256=p3CoLAa8nFPa3D759Y2A7De_PVJNGOL98mA3HoZBrRQ,3188
193
194
  phoenix/server/api/mutations/export_events_mutations.py,sha256=xoDnVWC7eA_8wNQP0-oyiHojyUZ0EhVVSrsAnztetC0,3993
@@ -217,7 +218,7 @@ phoenix/server/api/routers/v1/prompts.py,sha256=kRsssgRulr-ys0ouLz4qG-TP-uG30UZc
217
218
  phoenix/server/api/routers/v1/spans.py,sha256=uoU_bwIgz86fuvPjP5sX8goDyuCcnsTig-x3f17p60U,9625
218
219
  phoenix/server/api/routers/v1/traces.py,sha256=hSv35QIB4mwFgp53rOpz3zWIiSwbZzQnjafD790QuJU,7908
219
220
  phoenix/server/api/routers/v1/utils.py,sha256=SoRl0Dc8By15ZckhNcXg2QRrqYjMvgTjVcqrZ6MwVmo,3065
220
- phoenix/server/api/types/Annotation.py,sha256=7Ym7iuVcbwHlw2yIRylz4nATAF_Cm-Z17qcjiooj1cc,751
221
+ phoenix/server/api/types/Annotation.py,sha256=r1JDvkWoFKeLRysos3l1exqKfs8zoklVv2tNSG9kFzQ,849
221
222
  phoenix/server/api/types/AnnotationSummary.py,sha256=Adg_tFla5lL2csDQeQwn58b8kTB_57hotAN6WLQCLzE,1512
222
223
  phoenix/server/api/types/AnnotatorKind.py,sha256=rPgGdbN1Gvc109sGQ_ZH-gfJbp93V9wlarzTEJNtUwI,236
223
224
  phoenix/server/api/types/ApiKey.py,sha256=76HIkWxyJqL3UjdMesX9l5y6XaD9QphwabXGQVY-MaQ,793
@@ -228,7 +229,7 @@ phoenix/server/api/types/Cluster.py,sha256=duX0pSC7P3YT1IztduuJtPsRj1x6oOOLfmWf2
228
229
  phoenix/server/api/types/CreateDatasetPayload.py,sha256=R-6zCmuD0f76RU9Giu78xwTHlASQs6Aq8yzvX1Kxc3g,140
229
230
  phoenix/server/api/types/DataQualityMetric.py,sha256=Aieg3bHeBFaAf4mqeRcH1zT04sXAtQD8ATSHJt7FaBQ,1538
230
231
  phoenix/server/api/types/Dataset.py,sha256=1NACY8_s-TZGgTwv6pkFlYPJDy14EVcQlrjgNd7dQ_U,11732
231
- phoenix/server/api/types/DatasetExample.py,sha256=5f7DO6jRec1G5jaJW5FrykFU9R07jDn8jxDkNbQX0Ek,3107
232
+ phoenix/server/api/types/DatasetExample.py,sha256=_9byxGpXfYb-hmFMUJeG7Bw1wsRKSJaHwF2IPAbFpFw,3115
232
233
  phoenix/server/api/types/DatasetExampleRevision.py,sha256=c-jWR6dTguEZTm54IMlFr0Ic84I3nefyDnZb7nF5hnI,874
233
234
  phoenix/server/api/types/DatasetValues.py,sha256=7VbCOLlzOXpZN80-zYF2UGuafRcPsZF-8WQNc0YsKFc,1119
234
235
  phoenix/server/api/types/DatasetVersion.py,sha256=NnDriqQUE20N_Jhyu3-IxHWJQE9wmdHEJIQAKyTVJpo,302
@@ -263,8 +264,8 @@ phoenix/server/api/types/MimeType.py,sha256=Zpi6zCalkSFgsvhzvOs-O1gYA04usAi9H__Q
263
264
  phoenix/server/api/types/Model.py,sha256=8UIFqMe1q-2ufBNg-gxHusV8wM1h-KbfLUeJjyVcMvQ,8081
264
265
  phoenix/server/api/types/NumericRange.py,sha256=afEjgF97Go_OvmjMggbPBt-zGM8IONewAyEiKEHRds0,192
265
266
  phoenix/server/api/types/PerformanceMetric.py,sha256=KFkmJDqP43eDUtARQOUqR7NYcxvL6Vh2uisHWU6H3ko,387
266
- phoenix/server/api/types/Project.py,sha256=hjHKaBtJk8rV-Nyu7MUNtT52wMO9fCM3nkfRXS54akM,18853
267
- 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
268
269
  phoenix/server/api/types/Prompt.py,sha256=ccP4eq1e38xbF0afclGWLOuDpBVpNbJ3AOSRClF8yFQ,4955
269
270
  phoenix/server/api/types/PromptLabel.py,sha256=g3IDSPYRZwb0qpMAk93R6J96jgYULUYGOciTnpeh3sI,1321
270
271
  phoenix/server/api/types/PromptResponse.py,sha256=Q8HKtpp8GpUOcxPCzZpkkokidDd6u0aZOv_SuPZZd5Q,630
@@ -276,14 +277,14 @@ phoenix/server/api/types/Retrieval.py,sha256=OhMK2ncjoyp5h1yjKhjlKpoTbQrMHuxmgSF
276
277
  phoenix/server/api/types/ScalarDriftMetricEnum.py,sha256=IUAcRPpgL41WdoIgK6cNk2Te38SspXGyEs-S1fY23_A,232
277
278
  phoenix/server/api/types/Segments.py,sha256=vT2v0efoa5cuBKxLtxTnsUP5YJJCZfTloM71Spu0tMI,2915
278
279
  phoenix/server/api/types/SortDir.py,sha256=OUpXhlCzCxPoXSDkJJygEs9Rw9pMymfaZUG5zPTrw4Y,152
279
- phoenix/server/api/types/Span.py,sha256=uTFIIlWuGgiXuw2cmJkUTJXM0fimogPJ7s7QAjctalE,15344
280
+ phoenix/server/api/types/Span.py,sha256=bUxpuaa1Z8OeyQtM38ARQOXdMEY9tTXUj8_PYDsevkQ,22924
280
281
  phoenix/server/api/types/SpanAnnotation.py,sha256=6b5G-b_OoRvDL2ayWk7MkbqarLK-F-pQMx21CpUuNGY,1168
281
- phoenix/server/api/types/SpanIOValue.py,sha256=u_g9QrX-E3fwneoucire73KY5gf3fE4V9IBN3qx2cvU,445
282
+ phoenix/server/api/types/SpanIOValue.py,sha256=c5TWdZZN3v0gHI5xWeY7gjD-sE9ugWlGGAio-gDS-Uo,1653
282
283
  phoenix/server/api/types/SystemApiKey.py,sha256=2ym8EgsTBIvxx1l9xZ-2YMovz58ZwYb_MaHBTJ9NH2E,166
283
284
  phoenix/server/api/types/TimeSeries.py,sha256=nuuZtfHmOhTjeB8_SvZ5PUQexAkTcPScwYeFC5RUlRU,5491
284
285
  phoenix/server/api/types/TokenUsage.py,sha256=g-PjAGVigpchQgkXAuC5sc53fn2YwAgfeXkGmFPi_TE,201
285
286
  phoenix/server/api/types/ToolDefinition.py,sha256=T6UH2vcbuPBDy7jKYOqMth2NdqxMPgDBf11Tpbt5Yb8,187
286
- phoenix/server/api/types/Trace.py,sha256=Nwof2FlcZtCFnKTS5UqvNZBlY-VcN5QMK7Zm9xEQUrU,5597
287
+ phoenix/server/api/types/Trace.py,sha256=6CqXcrI8rpEetGWAN8-lv6XoieLKqwobsKFV1qY4fsY,5645
287
288
  phoenix/server/api/types/TraceAnnotation.py,sha256=OW6A2zr1gomOuG0XQe55dk15XXX2DSM0DzatRbHWH5A,1256
288
289
  phoenix/server/api/types/UMAPPoints.py,sha256=49sWnxjcAJKRzqUY71Fa0tOPti5XjIIFT5cSg6oNu_U,1650
289
290
  phoenix/server/api/types/User.py,sha256=iTVUrI8U6-asOhBt1bOZNtoRAaRNC4WHgR1Uv2rHnWQ,1975
@@ -310,16 +311,16 @@ phoenix/server/static/apple-touch-icon-76x76.png,sha256=CT_xT12I0u2i0WU8JzBZBuOQ
310
311
  phoenix/server/static/apple-touch-icon.png,sha256=fOfpjqGpWYbJ0eAurKsyoZP1EAs6ZVooBJ_SGk2ZkDs,3801
311
312
  phoenix/server/static/favicon.ico,sha256=bY0vvCKRftemZfPShwZtE93DiiQdaYaozkPGwNFr6H8,34494
312
313
  phoenix/server/static/modernizr.js,sha256=mvK-XtkNqjOral-QvzoqsyOMECXIMu5BQwSVN_wcU9c,2564
313
- phoenix/server/static/.vite/manifest.json,sha256=nqF_R52ujYjPJIw5YIYSh8KeJLfrOz6jqYqi3hD2WKg,2165
314
- phoenix/server/static/assets/components-B-qgPyHv.js,sha256=LbHDZ8gvlTaJPyjDhyjRgiwFX1Ci01JnCIs9SxT0Yns,420034
315
- phoenix/server/static/assets/index-D4KO1IcF.js,sha256=y6HnAkkI28fsu0ReMb4VVxU0zORXDpu4K2GwMoQv3Jc,58963
316
- 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
317
318
  phoenix/server/static/assets/vendor-Cg6lcjUC.css,sha256=nZrkr0u6NNElFGvpWHk9GTHeGoibCXCli1bE7mXZGZg,1816
318
- phoenix/server/static/assets/vendor-DQp7CrDA.js,sha256=of8vHuO798FVjrUNrF52kg6MYuU8n9b810LurBo7YPA,2234991
319
- phoenix/server/static/assets/vendor-arizeai-C1nEIEQq.js,sha256=DtHpN1xsgJlnJzLe0Od9dPr4fAYgTz253y_5OQUfoOE,202331
320
- phoenix/server/static/assets/vendor-codemirror-BZXYUIkP.js,sha256=SNSmeyYixK2hCb6vnYw0kfzCp99M9tbTf4iwNPDPWrU,393496
321
- phoenix/server/static/assets/vendor-recharts-BUFpwCVD.js,sha256=MFNYYFLBb_kMw9jyAbQ90ggxCTbqz4vJJeNMyG2eyo4,282095
322
- 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
323
324
  phoenix/server/static/assets/vendor-three-C-AGeJYv.js,sha256=c9nLPH5YDRFCzTNuwWO8_5KqcnunPo53mQsb7y9JFW8,620972
324
325
  phoenix/server/templates/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
325
326
  phoenix/server/templates/index.html,sha256=e8_jdi7Eo19SK7DI_gglkTW094D17E0VAegoMmmmvIc,4330
@@ -329,7 +330,7 @@ phoenix/session/data_extractor.py,sha256=Y0RzYFaNy9fQj8PEIeQ76TBZ90_E1FW7bXu3K5x
329
330
  phoenix/session/evaluation.py,sha256=Q3fOMNELvqkk-b6a6PKc8pDJdsNQ0ZbTpseUSA2NKqs,5300
330
331
  phoenix/session/session.py,sha256=nD0v1VWQVOnKpviK2RjjFV3YLcNdrDkrclsK64Y3H6Y,27540
331
332
  phoenix/trace/__init__.py,sha256=ujk_uYjM8gmm-YqnyXxF-kekfwid0bcaPMTtNNcaw6U,407
332
- phoenix/trace/attributes.py,sha256=mHpOtdjGIs-VGSev_dwvxTeZTc9GILovi1Rdnj56HO0,12457
333
+ phoenix/trace/attributes.py,sha256=hyEKYZWPCP4NRmW7VmiC2voa3TH7FYKUBR9DYiVfXlw,12627
333
334
  phoenix/trace/errors.py,sha256=wB1z8qdPckngdfU-TORToekvg3344oNFAA83_hC2yFY,180
334
335
  phoenix/trace/evaluation_conventions.py,sha256=t8jydM3U0-T5YpiQKRJ3tWdWGlHtzKyttYdw-ddvPOk,1048
335
336
  phoenix/trace/exporter.py,sha256=bUXh8fjJIbHurrnt4bAm-cCWqUN5FqNsIc8DZzzklkQ,4695
@@ -360,9 +361,9 @@ phoenix/utilities/project.py,sha256=auVpARXkDb-JgeX5f2aStyFIkeKvGwN9l7qrFeJMVxI,
360
361
  phoenix/utilities/re.py,sha256=6YyUWIkv0zc2SigsxfOWIHzdpjKA_TZo2iqKq7zJKvw,2081
361
362
  phoenix/utilities/span_store.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
362
363
  phoenix/utilities/template_formatters.py,sha256=gh9PJD6WEGw7TEYXfSst1UR4pWWwmjxMLrDVQ_CkpkQ,2779
363
- arize_phoenix-8.0.1.dist-info/METADATA,sha256=gwS1U5U1XRKEaC_hNEKVfhjtm6sSsLJaKrX1irwioUg,23467
364
- arize_phoenix-8.0.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
365
- arize_phoenix-8.0.1.dist-info/entry_points.txt,sha256=Pgpn8Upxx9P8z8joPXZWl2LlnAlGc3gcQoVchb06X1Q,94
366
- arize_phoenix-8.0.1.dist-info/licenses/IP_NOTICE,sha256=JBqyyCYYxGDfzQ0TtsQgjts41IJoa-hiwDrBjCb9gHM,469
367
- arize_phoenix-8.0.1.dist-info/licenses/LICENSE,sha256=HFkW9REuMOkvKRACuwLPT0hRydHb3zNg-fdFt94td18,3794
368
- arize_phoenix-8.0.1.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/config.py CHANGED
@@ -8,7 +8,7 @@ from enum import Enum
8
8
  from importlib.metadata import version
9
9
  from pathlib import Path
10
10
  from typing import Optional, cast, overload
11
- from urllib.parse import urlparse
11
+ from urllib.parse import quote_plus, urlparse
12
12
 
13
13
  from phoenix.utilities.logging import log_a_list
14
14
 
@@ -624,11 +624,37 @@ def get_env_project_name() -> str:
624
624
 
625
625
 
626
626
  def get_env_database_connection_str() -> str:
627
- env_url = getenv(ENV_PHOENIX_SQL_DATABASE_URL)
628
- if env_url is None:
629
- working_dir = get_working_dir()
630
- return f"sqlite:///{working_dir}/phoenix.db"
631
- return env_url
627
+ if phoenix_url := os.getenv(ENV_PHOENIX_SQL_DATABASE_URL):
628
+ return phoenix_url
629
+
630
+ if postgres_url := get_env_postgres_connection_str():
631
+ return postgres_url
632
+
633
+ working_dir = get_working_dir()
634
+ return f"sqlite:///{working_dir}/phoenix.db"
635
+
636
+
637
+ def get_env_postgres_connection_str() -> Optional[str]:
638
+ pg_user = os.getenv("PHOENIX_POSTGRES_USER")
639
+ pg_password = os.getenv("PHOENIX_POSTGRES_PASSWORD")
640
+ pg_host = os.getenv("PHOENIX_POSTGRES_HOST")
641
+ pg_port = os.getenv("PHOENIX_POSTGRES_PORT")
642
+ pg_db = os.getenv("PHOENIX_POSTGRES_DB")
643
+
644
+ if pg_host and ":" in pg_host:
645
+ pg_host, parsed_port = pg_host.split(":")
646
+ pg_port = pg_port or parsed_port # use the explicitly set port if provided
647
+
648
+ if pg_host and pg_user and pg_password:
649
+ encoded_password = quote_plus(pg_password)
650
+ connection_str = f"postgresql://{pg_user}:{encoded_password}@{pg_host}"
651
+ if pg_port:
652
+ connection_str = f"{connection_str}:{pg_port}"
653
+ if pg_db:
654
+ connection_str = f"{connection_str}/{pg_db}"
655
+
656
+ return connection_str
657
+ return None
632
658
 
633
659
 
634
660
  def get_env_database_schema() -> Optional[str]:
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]