arize-phoenix 11.27.0__py3-none-any.whl → 11.29.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 (23) hide show
  1. {arize_phoenix-11.27.0.dist-info → arize_phoenix-11.29.0.dist-info}/METADATA +3 -2
  2. {arize_phoenix-11.27.0.dist-info → arize_phoenix-11.29.0.dist-info}/RECORD +23 -23
  3. phoenix/db/insertion/document_annotation.py +2 -2
  4. phoenix/db/insertion/span_annotation.py +2 -2
  5. phoenix/db/insertion/trace_annotation.py +2 -2
  6. phoenix/db/insertion/types.py +12 -0
  7. phoenix/server/api/dataloaders/average_experiment_run_latency.py +17 -21
  8. phoenix/server/api/dataloaders/experiment_annotation_summaries.py +88 -34
  9. phoenix/server/api/dataloaders/experiment_error_rates.py +21 -28
  10. phoenix/server/api/routers/v1/evaluations.py +4 -0
  11. phoenix/server/api/routers/v1/spans.py +1 -0
  12. phoenix/server/api/routers/v1/traces.py +2 -0
  13. phoenix/server/api/types/Dataset.py +69 -21
  14. phoenix/server/api/types/Experiment.py +2 -3
  15. phoenix/server/static/.vite/manifest.json +9 -9
  16. phoenix/server/static/assets/{components-BC-SDWO9.js → components-dCdVienD.js} +5 -4
  17. phoenix/server/static/assets/{index-CjTh13rR.js → index-Bp44T8N2.js} +1 -1
  18. phoenix/server/static/assets/{pages-D19Jzk-2.js → pages-CA4bKhm9.js} +517 -544
  19. phoenix/version.py +1 -1
  20. {arize_phoenix-11.27.0.dist-info → arize_phoenix-11.29.0.dist-info}/WHEEL +0 -0
  21. {arize_phoenix-11.27.0.dist-info → arize_phoenix-11.29.0.dist-info}/entry_points.txt +0 -0
  22. {arize_phoenix-11.27.0.dist-info → arize_phoenix-11.29.0.dist-info}/licenses/IP_NOTICE +0 -0
  23. {arize_phoenix-11.27.0.dist-info → arize_phoenix-11.29.0.dist-info}/licenses/LICENSE +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: arize-phoenix
3
- Version: 11.27.0
3
+ Version: 11.29.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
@@ -50,7 +50,8 @@ Requires-Dist: python-multipart
50
50
  Requires-Dist: scikit-learn
51
51
  Requires-Dist: scipy
52
52
  Requires-Dist: sqlalchemy[asyncio]<3,>=2.0.4
53
- Requires-Dist: sqlean-py>=3.45.1
53
+ Requires-Dist: sqlean-py<3.50,>=3.45.1; platform_system == 'Windows'
54
+ Requires-Dist: sqlean-py>=3.45.1; platform_system != 'Windows'
54
55
  Requires-Dist: starlette
55
56
  Requires-Dist: strawberry-graphql==0.270.1
56
57
  Requires-Dist: tqdm
@@ -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=ngkyKGVatX3cO2WJdo2hKdaVKP-xJCMvqthvga6kJss,5196
8
8
  phoenix/settings.py,sha256=2kHfT3BNOVd4dAO1bq-syEQbHSG8oX2-7NhOwK2QREk,896
9
- phoenix/version.py,sha256=ofjFoTgUco9LdD4reUgJGjEcFX2RzUAJpVKpDorgoms,24
9
+ phoenix/version.py,sha256=dmpBux9ABrJi8fLg-MbszS9TOYZ3loPQG7TMhFmf_iE,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
@@ -27,13 +27,13 @@ phoenix/db/pg_config.py,sha256=h6mB7qF7t4Zk6VGvAiyefHGVu74o-yJynaWzeE39k9Y,6001
27
27
  phoenix/db/insertion/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
28
28
  phoenix/db/insertion/constants.py,sha256=8wifm7X-1XvroZ__R2Gc96NsgLhTDn0zXl4lehlXtcA,70
29
29
  phoenix/db/insertion/dataset.py,sha256=KeUtfSnznmkDDlGs6KM5URduZtIo8I951tybamwoLsc,7216
30
- phoenix/db/insertion/document_annotation.py,sha256=rLL35nxcBXh2O1RaY-R_nucER6wVPZBVJ3XkQQnmiQE,6498
30
+ phoenix/db/insertion/document_annotation.py,sha256=Lo6pPEUNNBYFjN_PPKqX9aZdchyIOgX3wPZe7vPVwHA,6540
31
31
  phoenix/db/insertion/evaluation.py,sha256=XIlDQMx9FFu_hO1TEao8_ScgKETw61AoeGezsmngXrY,6876
32
32
  phoenix/db/insertion/helpers.py,sha256=wA4f_B8DI1pfNvX7R5luG1MklwX6rtCrvv_SOcjtJGo,4106
33
33
  phoenix/db/insertion/span.py,sha256=UB4UR8QlkJdVzbJ7_-S6FXIHYjyKZSOEVzCOvwGw7cY,8414
34
- phoenix/db/insertion/span_annotation.py,sha256=EqjT7OcCUO69PAw3shkOSQunxWtLNpToO7gtqlK8dbY,5682
35
- phoenix/db/insertion/trace_annotation.py,sha256=0wIrAEDMYDvwRbd7zYAGIcnZtdjoPEINP51m28Wk1VI,5722
36
- phoenix/db/insertion/types.py,sha256=hOwtuYeKA7rjRXT2bfsD_SwYnkrIpxdVirQ50jq1fws,8253
34
+ phoenix/db/insertion/span_annotation.py,sha256=WA9538nPNIgRucRkd2S1zCC2TUMlJPvgMm1cCnH50Y4,5724
35
+ phoenix/db/insertion/trace_annotation.py,sha256=UKmAsSRd1mAwdGWu-i2aEGC3Yu16tqfbpzqMGtJnkVU,5764
36
+ phoenix/db/insertion/types.py,sha256=nIdqq-9vqciHMP4UrX0pmKnVUSfxfnO8l0ECPz0kiSs,8694
37
37
  phoenix/db/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
38
38
  phoenix/db/migrations/env.py,sha256=tFO3ceuCx9Es5_2w_BXclaQMmNQKNX21b1UEV5mYdeo,3387
39
39
  phoenix/db/migrations/script.py.mako,sha256=MEqL-2qATlST9TAOeYgscMn1uy6HUS9NFvDgl93dMj8,635
@@ -120,14 +120,14 @@ phoenix/server/api/utils.py,sha256=quCBRcusc6PUq9tJq7M8PgwFZp7nXgVAxtbw8feribY,8
120
120
  phoenix/server/api/dataloaders/__init__.py,sha256=ddiX1BdbyGkPTzMZNo-hkF_2kqIquelBUFvQejnAJYk,6834
121
121
  phoenix/server/api/dataloaders/annotation_configs_by_project.py,sha256=_Nfiug9o01JimU3Z0LpZJ0uaMCjchXomyt_dYAxPFRY,1178
122
122
  phoenix/server/api/dataloaders/annotation_summaries.py,sha256=0b23-bucBKyL25RWb2QzCNJjQzrq403qMmHKUVD5W4M,14377
123
- phoenix/server/api/dataloaders/average_experiment_run_latency.py,sha256=GLFoFAbztOH-0FVzzZ77mATIO63UcjB50j3qXiNi-tE,1811
123
+ phoenix/server/api/dataloaders/average_experiment_run_latency.py,sha256=EgRkx_2QIzdOZe4D-0kd3B9HMoAibU9DZDotUh2CWFE,1945
124
124
  phoenix/server/api/dataloaders/dataset_example_revisions.py,sha256=xF7M2dg3UmjhdCrscnztCIBBI0cg3RF48IIqvilpc18,4623
125
125
  phoenix/server/api/dataloaders/dataset_example_spans.py,sha256=z_MFquqAcJ9wat7BBp7MVeJ9BYuu2EZEdaog52iWDno,1390
126
126
  phoenix/server/api/dataloaders/document_evaluation_summaries.py,sha256=9fdROnzp-mymggHwNvpRkCk93LUFxxLy55-j3HP_2HY,5565
127
127
  phoenix/server/api/dataloaders/document_evaluations.py,sha256=KCnCItJ2DQOCHvxFp1KK2AStPN1akGlAGOEzEAu6c6I,1246
128
128
  phoenix/server/api/dataloaders/document_retrieval_metrics.py,sha256=37EcAW7oYQuWYHMDHb0wcqbWj9lhSskvzDO7NJbT5Js,4136
129
- phoenix/server/api/dataloaders/experiment_annotation_summaries.py,sha256=fFEjpJzUOhu_cJKQ-YnwEvtn8NDl6bPs-moERe-Bp04,2767
130
- phoenix/server/api/dataloaders/experiment_error_rates.py,sha256=V4U_y16LwfBDksTZ4QP1dDALGSsQAQ1KcttKRE6lXto,1920
129
+ phoenix/server/api/dataloaders/experiment_annotation_summaries.py,sha256=Jfurk2bBqt3RTq1ydqZ4B4H_0mTyW6XMeIImShT1aQs,5643
130
+ phoenix/server/api/dataloaders/experiment_error_rates.py,sha256=06IZF07qt2y167DBM49QkSNdnphPArhcsgYFcunaL-U,1992
131
131
  phoenix/server/api/dataloaders/experiment_run_annotations.py,sha256=uJ--9Ue4tnmZYH9Zy-Cj2Y2t3xzs2xUqN8Y6EGe1MCo,1296
132
132
  phoenix/server/api/dataloaders/experiment_run_counts.py,sha256=j_7229IL705p_TycxIMYylhSgLHsAn91TiGPYXtOuQ8,1617
133
133
  phoenix/server/api/dataloaders/experiment_sequence_number.py,sha256=zM_f78fnqhppLtevrx9iISQSN7w_BNeXT9CoX8jYgAI,1534
@@ -259,15 +259,15 @@ phoenix/server/api/routers/v1/__init__.py,sha256=ngLMPjC7lgZxgKy_Is33KxTRnMzSqy2
259
259
  phoenix/server/api/routers/v1/annotation_configs.py,sha256=xp5lJmKYlRsINCUrRD9-lTAElw2v4hdFndS5BWrxICA,16048
260
260
  phoenix/server/api/routers/v1/annotations.py,sha256=fVl2qeh_ZbWXGvFBTZgeL7aGkkINIScdjuyxnOoSzNM,6817
261
261
  phoenix/server/api/routers/v1/datasets.py,sha256=9iPORLmbOrPKgUUcRDMs6ZczSIz7hvc6bngJy3IbdR0,38331
262
- phoenix/server/api/routers/v1/evaluations.py,sha256=_I0X01J8EZmTlfAGkvMkZ05IB3x691xzUmHV05ov26Y,12914
262
+ phoenix/server/api/routers/v1/evaluations.py,sha256=aBrPO-xCAWyTxydaHq7W2wQFm65k89uVR-H3VWsd6WQ,13062
263
263
  phoenix/server/api/routers/v1/experiment_evaluations.py,sha256=DZ3UK9OoYKElpRcEER7559-KiAqWr-1IXpZ27FbfP3k,5249
264
264
  phoenix/server/api/routers/v1/experiment_runs.py,sha256=LZeCQWQIEOZ9jK5Gp_C4JbiYY6AmnnWe85cVcvdkCLE,7107
265
265
  phoenix/server/api/routers/v1/experiments.py,sha256=hIBecGACzGZEgl93ap_JV52pUv-Ij03QJMRxQhBlktI,20611
266
266
  phoenix/server/api/routers/v1/models.py,sha256=p3gJN-9SWiUYTUTft4bZMsZVCBNTb4nN1Foy68eRZzQ,1997
267
267
  phoenix/server/api/routers/v1/projects.py,sha256=XR6uJxHXXtC1q8LNyS9W6iaj440sv1OKCu-OSBfxEys,12824
268
268
  phoenix/server/api/routers/v1/prompts.py,sha256=chRYcLkOYDJdJfVZVukVTUyIRnLPvsJCg41CuPxOIU8,26695
269
- phoenix/server/api/routers/v1/spans.py,sha256=la_bfxrVPGY-N8qvk9o9ICrGiwl1B6-jbZ0uDjR5QXc,49345
270
- phoenix/server/api/routers/v1/traces.py,sha256=Skn0N_L4ZjoJ7x76PBrqvbKPFiAk8xSe1yxfiOaQ0Gc,11285
269
+ phoenix/server/api/routers/v1/spans.py,sha256=roDDE0RDBGSCircPgzwYWhwwyK33IaW7YliWN-lwxWw,49385
270
+ phoenix/server/api/routers/v1/traces.py,sha256=ho6SXJ0R3g1ROxgtd7hNPo8QSZnA85oG0dgWDokHM2w,11365
271
271
  phoenix/server/api/routers/v1/users.py,sha256=eO8zMtGU33Td2_G1l9D7Z0a4CG1CwBUCj_Z9z2uk7wg,12089
272
272
  phoenix/server/api/routers/v1/utils.py,sha256=oXIOGPzPTkE0ZWUTRCoRIQQ7wTzoSwtWFaUSjlGBqts,4960
273
273
  phoenix/server/api/types/Annotation.py,sha256=gsl8CwjIbDUbZRj4d9USwZ_w_Tkz4i7zuZh9ftV80jA,1132
@@ -284,7 +284,7 @@ phoenix/server/api/types/CostBreakdown.py,sha256=yw9dlb0blGIB_dWNP8yEvDHJztHjpiV
284
284
  phoenix/server/api/types/CreateDatasetPayload.py,sha256=R-6zCmuD0f76RU9Giu78xwTHlASQs6Aq8yzvX1Kxc3g,140
285
285
  phoenix/server/api/types/CronExpression.py,sha256=R7oxuSSX_eTUHQWaoaSueQqWDmkkHr5dBKRN6q-6ROk,331
286
286
  phoenix/server/api/types/DataQualityMetric.py,sha256=Aieg3bHeBFaAf4mqeRcH1zT04sXAtQD8ATSHJt7FaBQ,1538
287
- phoenix/server/api/types/Dataset.py,sha256=StVJmOE996Citau11JtFTmcgLqvN9IeZsHAbe-Y1gkg,12933
287
+ phoenix/server/api/types/Dataset.py,sha256=23dst_glr7kFNC62-q6D9H2hJgrfZnGe7V-Bg72SJgg,15303
288
288
  phoenix/server/api/types/DatasetExample.py,sha256=_9byxGpXfYb-hmFMUJeG7Bw1wsRKSJaHwF2IPAbFpFw,3115
289
289
  phoenix/server/api/types/DatasetExampleRevision.py,sha256=c-jWR6dTguEZTm54IMlFr0Ic84I3nefyDnZb7nF5hnI,874
290
290
  phoenix/server/api/types/DatasetValues.py,sha256=7VbCOLlzOXpZN80-zYF2UGuafRcPsZF-8WQNc0YsKFc,1119
@@ -303,7 +303,7 @@ phoenix/server/api/types/EvaluationSummary.py,sha256=vILYejnfPvMwWEXOwhQZsANvYe3
303
303
  phoenix/server/api/types/Event.py,sha256=iYt_Jx1Roioo0vZ0iPeJTHcTu6NSm4ilVMJ-IMUHAKk,3970
304
304
  phoenix/server/api/types/EventMetadata.py,sha256=-J0tYF9eZTHwCjwxQHY7Gckr2_MNW5OoWT1mydweZNM,635
305
305
  phoenix/server/api/types/ExampleRevisionInterface.py,sha256=gV3Gt9-3Oi5wjaVtepC6nOt3FzTzZFD1KebNnqiw56E,294
306
- phoenix/server/api/types/Experiment.py,sha256=8Hd-8-4Rcym95sPHIBLyTfMuRdyl50YxlKO_fMvdNAA,7830
306
+ phoenix/server/api/types/Experiment.py,sha256=eN0NhUVdcjJyMbyAGoltv30mhunatWS6i2gG97xu5TU,7758
307
307
  phoenix/server/api/types/ExperimentAnnotationSummary.py,sha256=Uk3JtxIrsMoZT5tqc4nJdUOM3XegVzjUyoV3pkjNotE,256
308
308
  phoenix/server/api/types/ExperimentComparison.py,sha256=PXFcB0e8aaJ391yRsuRJr9_dvTZI1RAzF93oC_-HtxU,461
309
309
  phoenix/server/api/types/ExperimentRun.py,sha256=_fcwDLuURV0yviOlkjWAgJJwcCPdz-xGR6VX3UKf73s,6541
@@ -392,10 +392,10 @@ phoenix/server/static/apple-touch-icon-76x76.png,sha256=CT_xT12I0u2i0WU8JzBZBuOQ
392
392
  phoenix/server/static/apple-touch-icon.png,sha256=fOfpjqGpWYbJ0eAurKsyoZP1EAs6ZVooBJ_SGk2ZkDs,3801
393
393
  phoenix/server/static/favicon.ico,sha256=bY0vvCKRftemZfPShwZtE93DiiQdaYaozkPGwNFr6H8,34494
394
394
  phoenix/server/static/modernizr.js,sha256=mvK-XtkNqjOral-QvzoqsyOMECXIMu5BQwSVN_wcU9c,2564
395
- phoenix/server/static/.vite/manifest.json,sha256=f81ZT-s_X796lx4IwpiRFFg00JHq2_CI-G-1_tXxmSE,2328
396
- phoenix/server/static/assets/components-BC-SDWO9.js,sha256=_i4WPHkV2y5M3u6arDbZrRwF-qa9oWlXsK5q5CpvKsY,664485
397
- phoenix/server/static/assets/index-CjTh13rR.js,sha256=SSBxvFh8I1jiscNFNZzK69sDapeHOrhjRjhg11cF45Y,63396
398
- phoenix/server/static/assets/pages-D19Jzk-2.js,sha256=KEIJNicB4MfHx29SVywYIktUZBnVmeP3mLMM31uVpxM,1265049
395
+ phoenix/server/static/.vite/manifest.json,sha256=-rNvMLAChq7_eDpTSPXWf7ujX9-KJZOw6I_ONqi1eBY,2328
396
+ phoenix/server/static/assets/components-dCdVienD.js,sha256=a8fwhvVLGgLiQmj6R7zuXEdvLLpormuCrJePqF8ss5k,664533
397
+ phoenix/server/static/assets/index-Bp44T8N2.js,sha256=kaA9zlqpA50LK3xdjN2K3tPAjq6roX3cy6OyCyBS49k,63396
398
+ phoenix/server/static/assets/pages-CA4bKhm9.js,sha256=plhzt6_SL9xsAOva_dTqDk-0yWfre4t_nvg_VJcQeX0,1268001
399
399
  phoenix/server/static/assets/vendor-CqDb5u4o.css,sha256=zIyFiNJKxMaQk8AvtLgt1rR01oO10d1MFndSDKH9Clw,5517
400
400
  phoenix/server/static/assets/vendor-RdRDaQiR.js,sha256=oTxLetZZXJ20yoKNAYExto9V73y8X5zjddWV46K9CWM,2595492
401
401
  phoenix/server/static/assets/vendor-arizeai-DsYDNOqt.js,sha256=0HIkPJXbKTh85nqphdAXYeStRzdaim0IQxRXiXxa21U,121514
@@ -442,9 +442,9 @@ phoenix/utilities/project.py,sha256=auVpARXkDb-JgeX5f2aStyFIkeKvGwN9l7qrFeJMVxI,
442
442
  phoenix/utilities/re.py,sha256=6YyUWIkv0zc2SigsxfOWIHzdpjKA_TZo2iqKq7zJKvw,2081
443
443
  phoenix/utilities/span_store.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
444
444
  phoenix/utilities/template_formatters.py,sha256=gh9PJD6WEGw7TEYXfSst1UR4pWWwmjxMLrDVQ_CkpkQ,2779
445
- arize_phoenix-11.27.0.dist-info/METADATA,sha256=QY0OG7N7HMD-gi2_15QJThyeIXch1CDC86bSt9S8vQA,31634
446
- arize_phoenix-11.27.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
447
- arize_phoenix-11.27.0.dist-info/entry_points.txt,sha256=Pgpn8Upxx9P8z8joPXZWl2LlnAlGc3gcQoVchb06X1Q,94
448
- arize_phoenix-11.27.0.dist-info/licenses/IP_NOTICE,sha256=JBqyyCYYxGDfzQ0TtsQgjts41IJoa-hiwDrBjCb9gHM,469
449
- arize_phoenix-11.27.0.dist-info/licenses/LICENSE,sha256=HFkW9REuMOkvKRACuwLPT0hRydHb3zNg-fdFt94td18,3794
450
- arize_phoenix-11.27.0.dist-info/RECORD,,
445
+ arize_phoenix-11.29.0.dist-info/METADATA,sha256=N5M1wqlrq9TDt1tg8K9INOEqK1VBdOtVqyGPmR28e34,31733
446
+ arize_phoenix-11.29.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
447
+ arize_phoenix-11.29.0.dist-info/entry_points.txt,sha256=Pgpn8Upxx9P8z8joPXZWl2LlnAlGc3gcQoVchb06X1Q,94
448
+ arize_phoenix-11.29.0.dist-info/licenses/IP_NOTICE,sha256=JBqyyCYYxGDfzQ0TtsQgjts41IJoa-hiwDrBjCb9gHM,469
449
+ arize_phoenix-11.29.0.dist-info/licenses/LICENSE,sha256=HFkW9REuMOkvKRACuwLPT0hRydHb3zNg-fdFt94td18,3794
450
+ arize_phoenix-11.29.0.dist-info/RECORD,,
@@ -63,7 +63,7 @@ class DocumentAnnotationQueueInserter(
63
63
  session: AsyncSession,
64
64
  *insertions: Insertables.DocumentAnnotation,
65
65
  ) -> list[DocumentAnnotationDmlEvent]:
66
- records = [dict(as_kv(ins.row)) for ins in insertions]
66
+ records = [{**dict(as_kv(ins.row)), "updated_at": ins.row.updated_at} for ins in insertions]
67
67
  stmt = self._insert_on_conflict(*records).returning(self.table.id)
68
68
  ids = tuple([_ async for _ in await session.stream_scalars(stmt)])
69
69
  return [DocumentAnnotationDmlEvent(ids)]
@@ -99,7 +99,7 @@ class DocumentAnnotationQueueInserter(
99
99
 
100
100
  for p in parcels:
101
101
  if (anno := existing_annos.get(_key(p))) is not None:
102
- if p.received_at <= anno.updated_at:
102
+ if p.item.updated_at <= anno.updated_at:
103
103
  to_discard.append(p)
104
104
  else:
105
105
  to_insert.append(
@@ -57,7 +57,7 @@ class SpanAnnotationQueueInserter(
57
57
  session: AsyncSession,
58
58
  *insertions: Insertables.SpanAnnotation,
59
59
  ) -> list[SpanAnnotationDmlEvent]:
60
- records = [dict(as_kv(ins.row)) for ins in insertions]
60
+ records = [{**dict(as_kv(ins.row)), "updated_at": ins.row.updated_at} for ins in insertions]
61
61
  stmt = self._insert_on_conflict(*records).returning(self.table.id)
62
62
  ids = tuple([_ async for _ in await session.stream_scalars(stmt)])
63
63
  return [SpanAnnotationDmlEvent(ids)]
@@ -92,7 +92,7 @@ class SpanAnnotationQueueInserter(
92
92
 
93
93
  for p in parcels:
94
94
  if (anno := existing_annos.get(_key(p))) is not None:
95
- if p.received_at <= anno.updated_at:
95
+ if p.item.updated_at <= anno.updated_at:
96
96
  to_discard.append(p)
97
97
  else:
98
98
  to_insert.append(
@@ -56,7 +56,7 @@ class TraceAnnotationQueueInserter(
56
56
  session: AsyncSession,
57
57
  *insertions: Insertables.TraceAnnotation,
58
58
  ) -> list[TraceAnnotationDmlEvent]:
59
- records = [dict(as_kv(ins.row)) for ins in insertions]
59
+ records = [{**dict(as_kv(ins.row)), "updated_at": ins.row.updated_at} for ins in insertions]
60
60
  stmt = self._insert_on_conflict(*records).returning(self.table.id)
61
61
  ids = tuple([_ async for _ in await session.stream_scalars(stmt)])
62
62
  return [TraceAnnotationDmlEvent(ids)]
@@ -91,7 +91,7 @@ class TraceAnnotationQueueInserter(
91
91
 
92
92
  for p in parcels:
93
93
  if (anno := existing_annos.get(_key(p))) is not None:
94
- if p.received_at <= anno.updated_at:
94
+ if p.item.updated_at <= anno.updated_at:
95
95
  to_discard.append(p)
96
96
  else:
97
97
  to_insert.append(
@@ -174,6 +174,7 @@ class QueueInserter(ABC, Generic[_PrecursorT, _InsertableT, _RowT, _DmlEventT]):
174
174
  class Precursors(ABC):
175
175
  @dataclass(frozen=True)
176
176
  class SpanAnnotation:
177
+ updated_at: datetime
177
178
  span_id: str
178
179
  obj: models.SpanAnnotation
179
180
 
@@ -182,6 +183,7 @@ class Precursors(ABC):
182
183
  span_rowid: int,
183
184
  ) -> Insertables.SpanAnnotation:
184
185
  return Insertables.SpanAnnotation(
186
+ updated_at=self.updated_at,
185
187
  span_id=self.span_id,
186
188
  obj=self.obj,
187
189
  span_rowid=span_rowid,
@@ -189,6 +191,7 @@ class Precursors(ABC):
189
191
 
190
192
  @dataclass(frozen=True)
191
193
  class TraceAnnotation:
194
+ updated_at: datetime
192
195
  trace_id: str
193
196
  obj: models.TraceAnnotation
194
197
 
@@ -197,6 +200,7 @@ class Precursors(ABC):
197
200
  trace_rowid: int,
198
201
  ) -> Insertables.TraceAnnotation:
199
202
  return Insertables.TraceAnnotation(
203
+ updated_at=self.updated_at,
200
204
  trace_id=self.trace_id,
201
205
  obj=self.obj,
202
206
  trace_rowid=trace_rowid,
@@ -204,6 +208,7 @@ class Precursors(ABC):
204
208
 
205
209
  @dataclass(frozen=True)
206
210
  class DocumentAnnotation:
211
+ updated_at: datetime
207
212
  span_id: str
208
213
  document_position: int
209
214
  obj: models.DocumentAnnotation
@@ -213,6 +218,7 @@ class Precursors(ABC):
213
218
  span_rowid: int,
214
219
  ) -> Insertables.DocumentAnnotation:
215
220
  return Insertables.DocumentAnnotation(
221
+ updated_at=self.updated_at,
216
222
  span_id=self.span_id,
217
223
  document_position=self.document_position,
218
224
  obj=self.obj,
@@ -223,6 +229,7 @@ class Precursors(ABC):
223
229
  class Insertables(ABC):
224
230
  @dataclass(frozen=True)
225
231
  class SpanAnnotation(Precursors.SpanAnnotation):
232
+ updated_at: datetime
226
233
  span_rowid: int
227
234
  identifier: str = ""
228
235
 
@@ -230,10 +237,12 @@ class Insertables(ABC):
230
237
  def row(self) -> models.SpanAnnotation:
231
238
  obj = copy(self.obj)
232
239
  obj.span_rowid = self.span_rowid
240
+ obj.updated_at = self.updated_at
233
241
  return obj
234
242
 
235
243
  @dataclass(frozen=True)
236
244
  class TraceAnnotation(Precursors.TraceAnnotation):
245
+ updated_at: datetime
237
246
  trace_rowid: int
238
247
  identifier: str = ""
239
248
 
@@ -241,10 +250,12 @@ class Insertables(ABC):
241
250
  def row(self) -> models.TraceAnnotation:
242
251
  obj = copy(self.obj)
243
252
  obj.trace_rowid = self.trace_rowid
253
+ obj.updated_at = self.updated_at
244
254
  return obj
245
255
 
246
256
  @dataclass(frozen=True)
247
257
  class DocumentAnnotation(Precursors.DocumentAnnotation):
258
+ updated_at: datetime
248
259
  span_rowid: int
249
260
  identifier: str = ""
250
261
 
@@ -252,4 +263,5 @@ class Insertables(ABC):
252
263
  def row(self) -> models.DocumentAnnotation:
253
264
  obj = copy(self.obj)
254
265
  obj.span_rowid = self.span_rowid
266
+ obj.updated_at = self.updated_at
255
267
  return obj
@@ -23,32 +23,28 @@ class AverageExperimentRunLatencyDataLoader(DataLoader[Key, Result]):
23
23
 
24
24
  async def _load_fn(self, keys: list[Key]) -> list[Result]:
25
25
  experiment_ids = keys
26
- resolved_experiment_ids = (
27
- select(models.Experiment.id)
28
- .where(models.Experiment.id.in_(set(experiment_ids)))
29
- .subquery()
30
- )
31
- query = (
26
+ average_repetition_latency_ms = (
32
27
  select(
33
- resolved_experiment_ids.c.id,
28
+ models.ExperimentRun.experiment_id.label("experiment_id"),
34
29
  func.avg(
35
30
  func.extract("epoch", models.ExperimentRun.end_time)
36
31
  - func.extract("epoch", models.ExperimentRun.start_time)
37
- ),
32
+ ).label("average_repetition_latency_ms"),
38
33
  )
39
- .outerjoin_from(
40
- from_=resolved_experiment_ids,
41
- target=models.ExperimentRun,
42
- onclause=resolved_experiment_ids.c.id == models.ExperimentRun.experiment_id,
43
- )
44
- .group_by(resolved_experiment_ids.c.id)
34
+ .select_from(models.ExperimentRun)
35
+ .where(models.ExperimentRun.experiment_id.in_(experiment_ids))
36
+ .group_by(models.ExperimentRun.dataset_example_id, models.ExperimentRun.experiment_id)
37
+ .subquery()
45
38
  )
39
+ query = select(
40
+ average_repetition_latency_ms.c.experiment_id,
41
+ func.avg(average_repetition_latency_ms.c.average_repetition_latency_ms).label(
42
+ "average_run_latency_ms"
43
+ ),
44
+ ).group_by(average_repetition_latency_ms.c.experiment_id)
46
45
  async with self._db() as session:
47
- avg_latencies = {
48
- experiment_id: avg_latency
49
- async for experiment_id, avg_latency in await session.stream(query)
46
+ average_run_latencies_ms = {
47
+ experiment_id: average_run_latency_ms
48
+ async for experiment_id, average_run_latency_ms in await session.stream(query)
50
49
  }
51
- return [
52
- avg_latencies.get(experiment_id, ValueError(f"Unknown experiment: {experiment_id}"))
53
- for experiment_id in keys
54
- ]
50
+ return [average_run_latencies_ms.get(experiment_id) for experiment_id in keys]
@@ -2,7 +2,7 @@ from collections import defaultdict
2
2
  from dataclasses import dataclass
3
3
  from typing import Optional
4
4
 
5
- from sqlalchemy import func, select
5
+ from sqlalchemy import and_, func, select
6
6
  from strawberry.dataloader import AbstractCache, DataLoader
7
7
  from typing_extensions import TypeAlias
8
8
 
@@ -37,43 +37,97 @@ class ExperimentAnnotationSummaryDataLoader(DataLoader[Key, Result]):
37
37
  async def _load_fn(self, keys: list[Key]) -> list[Result]:
38
38
  experiment_ids = keys
39
39
  summaries: defaultdict[ExperimentID, Result] = defaultdict(list)
40
+ repetition_mean_scores_by_example_subquery = (
41
+ select(
42
+ models.ExperimentRun.experiment_id.label("experiment_id"),
43
+ models.ExperimentRunAnnotation.name.label("annotation_name"),
44
+ func.avg(models.ExperimentRunAnnotation.score).label("mean_repetition_score"),
45
+ )
46
+ .select_from(models.ExperimentRunAnnotation)
47
+ .join(
48
+ models.ExperimentRun,
49
+ models.ExperimentRunAnnotation.experiment_run_id == models.ExperimentRun.id,
50
+ )
51
+ .where(models.ExperimentRun.experiment_id.in_(experiment_ids))
52
+ .group_by(
53
+ models.ExperimentRun.dataset_example_id,
54
+ models.ExperimentRun.experiment_id,
55
+ models.ExperimentRunAnnotation.name,
56
+ )
57
+ .subquery()
58
+ .alias("repetition_mean_scores_by_example")
59
+ )
60
+ repetition_mean_scores_subquery = (
61
+ select(
62
+ repetition_mean_scores_by_example_subquery.c.experiment_id.label("experiment_id"),
63
+ repetition_mean_scores_by_example_subquery.c.annotation_name.label(
64
+ "annotation_name"
65
+ ),
66
+ func.avg(repetition_mean_scores_by_example_subquery.c.mean_repetition_score).label(
67
+ "mean_score"
68
+ ),
69
+ )
70
+ .select_from(repetition_mean_scores_by_example_subquery)
71
+ .group_by(
72
+ repetition_mean_scores_by_example_subquery.c.experiment_id,
73
+ repetition_mean_scores_by_example_subquery.c.annotation_name,
74
+ )
75
+ .subquery()
76
+ .alias("repetition_mean_scores")
77
+ )
78
+ repetitions_subquery = (
79
+ select(
80
+ models.ExperimentRun.experiment_id.label("experiment_id"),
81
+ models.ExperimentRunAnnotation.name.label("annotation_name"),
82
+ func.min(models.ExperimentRunAnnotation.score).label("min_score"),
83
+ func.max(models.ExperimentRunAnnotation.score).label("max_score"),
84
+ func.count().label("count"),
85
+ func.count(models.ExperimentRunAnnotation.error).label("error_count"),
86
+ )
87
+ .select_from(models.ExperimentRunAnnotation)
88
+ .join(
89
+ models.ExperimentRun,
90
+ models.ExperimentRunAnnotation.experiment_run_id == models.ExperimentRun.id,
91
+ )
92
+ .where(models.ExperimentRun.experiment_id.in_(experiment_ids))
93
+ .group_by(models.ExperimentRun.experiment_id, models.ExperimentRunAnnotation.name)
94
+ .subquery()
95
+ )
96
+ run_scores_query = (
97
+ select(
98
+ repetition_mean_scores_subquery.c.experiment_id.label("experiment_id"),
99
+ repetition_mean_scores_subquery.c.annotation_name.label("annotation_name"),
100
+ repetition_mean_scores_subquery.c.mean_score.label("mean_score"),
101
+ repetitions_subquery.c.min_score.label("min_score"),
102
+ repetitions_subquery.c.max_score.label("max_score"),
103
+ repetitions_subquery.c.count.label("count_"),
104
+ repetitions_subquery.c.error_count.label("error_count"),
105
+ )
106
+ .select_from(repetition_mean_scores_subquery)
107
+ .join(
108
+ repetitions_subquery,
109
+ and_(
110
+ repetitions_subquery.c.experiment_id
111
+ == repetition_mean_scores_subquery.c.experiment_id,
112
+ repetitions_subquery.c.annotation_name
113
+ == repetition_mean_scores_subquery.c.annotation_name,
114
+ ),
115
+ )
116
+ .order_by(repetition_mean_scores_subquery.c.annotation_name)
117
+ )
40
118
  async with self._db() as session:
41
- async for (
42
- experiment_id,
43
- annotation_name,
44
- min_score,
45
- max_score,
46
- mean_score,
47
- count,
48
- error_count,
49
- ) in await session.stream(
50
- select(
51
- models.ExperimentRun.experiment_id,
52
- models.ExperimentRunAnnotation.name,
53
- func.min(models.ExperimentRunAnnotation.score),
54
- func.max(models.ExperimentRunAnnotation.score),
55
- func.avg(models.ExperimentRunAnnotation.score),
56
- func.count(),
57
- func.count(models.ExperimentRunAnnotation.error),
58
- )
59
- .join(
60
- models.ExperimentRun,
61
- models.ExperimentRunAnnotation.experiment_run_id == models.ExperimentRun.id,
62
- )
63
- .where(models.ExperimentRun.experiment_id.in_(experiment_ids))
64
- .group_by(models.ExperimentRun.experiment_id, models.ExperimentRunAnnotation.name)
65
- ):
66
- summaries[experiment_id].append(
119
+ async for scores_tuple in await session.stream(run_scores_query):
120
+ summaries[scores_tuple.experiment_id].append(
67
121
  ExperimentAnnotationSummary(
68
- annotation_name=annotation_name,
69
- min_score=min_score,
70
- max_score=max_score,
71
- mean_score=mean_score,
72
- count=count,
73
- error_count=error_count,
122
+ annotation_name=scores_tuple.annotation_name,
123
+ min_score=scores_tuple.min_score,
124
+ max_score=scores_tuple.max_score,
125
+ mean_score=scores_tuple.mean_score,
126
+ count=scores_tuple.count_,
127
+ error_count=scores_tuple.error_count,
74
128
  )
75
129
  )
76
130
  return [
77
131
  sorted(summaries[experiment_id], key=lambda summary: summary.annotation_name)
78
- for experiment_id in keys
132
+ for experiment_id in experiment_ids
79
133
  ]
@@ -1,6 +1,6 @@
1
1
  from typing import Optional
2
2
 
3
- from sqlalchemy import case, func, select
3
+ from sqlalchemy import func, select
4
4
  from strawberry.dataloader import DataLoader
5
5
  from typing_extensions import TypeAlias
6
6
 
@@ -23,36 +23,29 @@ class ExperimentErrorRatesDataLoader(DataLoader[Key, Result]):
23
23
 
24
24
  async def _load_fn(self, keys: list[Key]) -> list[Result]:
25
25
  experiment_ids = keys
26
- resolved_experiment_ids = (
27
- select(models.Experiment.id)
28
- .where(models.Experiment.id.in_(set(experiment_ids)))
29
- .subquery()
30
- )
31
- query = (
26
+ average_repetition_error_rates_subquery = (
32
27
  select(
33
- resolved_experiment_ids.c.id,
34
- case(
35
- (
36
- func.count(models.ExperimentRun.id) != 0,
37
- func.count(models.ExperimentRun.error)
38
- / func.count(models.ExperimentRun.id),
39
- ),
40
- else_=None,
41
- ),
28
+ models.ExperimentRun.experiment_id.label("experiment_id"),
29
+ (
30
+ func.count(models.ExperimentRun.error) / func.count(models.ExperimentRun.id)
31
+ ).label("average_repetition_error_rate"),
42
32
  )
43
- .outerjoin_from(
44
- from_=resolved_experiment_ids,
45
- target=models.ExperimentRun,
46
- onclause=resolved_experiment_ids.c.id == models.ExperimentRun.experiment_id,
47
- )
48
- .group_by(resolved_experiment_ids.c.id)
33
+ .where(models.ExperimentRun.experiment_id.in_(experiment_ids))
34
+ .group_by(models.ExperimentRun.dataset_example_id, models.ExperimentRun.experiment_id)
35
+ .subquery()
36
+ .alias("average_repetition_error_rates")
49
37
  )
38
+ average_run_error_rates_query = select(
39
+ average_repetition_error_rates_subquery.c.experiment_id,
40
+ func.avg(average_repetition_error_rates_subquery.c.average_repetition_error_rate).label(
41
+ "average_run_error_rates"
42
+ ),
43
+ ).group_by(average_repetition_error_rates_subquery.c.experiment_id)
50
44
  async with self._db() as session:
51
- error_rates = {
45
+ average_run_error_rates = {
52
46
  experiment_id: error_rate
53
- async for experiment_id, error_rate in await session.stream(query)
47
+ async for experiment_id, error_rate in await session.stream(
48
+ average_run_error_rates_query
49
+ )
54
50
  }
55
- return [
56
- error_rates.get(experiment_id, ValueError(f"Unknown experiment ID: {experiment_id}"))
57
- for experiment_id in keys
58
- ]
51
+ return [average_run_error_rates.get(experiment_id) for experiment_id in experiment_ids]
@@ -1,5 +1,6 @@
1
1
  import gzip
2
2
  from collections.abc import Callable
3
+ from datetime import datetime, timezone
3
4
  from itertools import chain
4
5
  from typing import Any, Iterator, Optional, Union, cast
5
6
 
@@ -269,6 +270,7 @@ def _document_annotation_factory(
269
270
  Callable[..., Precursors.DocumentAnnotation],
270
271
  ]:
271
272
  return lambda index: lambda **kwargs: Precursors.DocumentAnnotation(
273
+ datetime.now(timezone.utc),
272
274
  span_id=str(index[span_id_idx]),
273
275
  document_position=int(index[document_position_idx]),
274
276
  obj=models.DocumentAnnotation(
@@ -280,6 +282,7 @@ def _document_annotation_factory(
280
282
 
281
283
  def _span_annotation_factory(span_id: str) -> Callable[..., Precursors.SpanAnnotation]:
282
284
  return lambda **kwargs: Precursors.SpanAnnotation(
285
+ datetime.now(timezone.utc),
283
286
  span_id=str(span_id),
284
287
  obj=models.SpanAnnotation(**kwargs),
285
288
  )
@@ -287,6 +290,7 @@ def _span_annotation_factory(span_id: str) -> Callable[..., Precursors.SpanAnnot
287
290
 
288
291
  def _trace_annotation_factory(trace_id: str) -> Callable[..., Precursors.TraceAnnotation]:
289
292
  return lambda **kwargs: Precursors.TraceAnnotation(
293
+ datetime.now(timezone.utc),
290
294
  trace_id=str(trace_id),
291
295
  obj=models.TraceAnnotation(**kwargs),
292
296
  )
@@ -880,6 +880,7 @@ class SpanAnnotationData(V1RoutesBaseModel):
880
880
 
881
881
  def as_precursor(self, *, user_id: Optional[int] = None) -> Precursors.SpanAnnotation:
882
882
  return Precursors.SpanAnnotation(
883
+ datetime.now(timezone.utc),
883
884
  self.span_id,
884
885
  models.SpanAnnotation(
885
886
  name=self.name,
@@ -1,5 +1,6 @@
1
1
  import gzip
2
2
  import zlib
3
+ from datetime import datetime, timezone
3
4
  from typing import Any, Literal, Optional
4
5
 
5
6
  from fastapi import APIRouter, BackgroundTasks, Depends, Header, HTTPException, Path, Query
@@ -134,6 +135,7 @@ class TraceAnnotation(V1RoutesBaseModel):
134
135
 
135
136
  def as_precursor(self, *, user_id: Optional[int] = None) -> Precursors.TraceAnnotation:
136
137
  return Precursors.TraceAnnotation(
138
+ datetime.now(timezone.utc),
137
139
  self.trace_id,
138
140
  models.TraceAnnotation(
139
141
  name=self.name,