arize-phoenix 9.3.0__py3-none-any.whl → 9.5.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 (24) hide show
  1. {arize_phoenix-9.3.0.dist-info → arize_phoenix-9.5.0.dist-info}/METADATA +4 -4
  2. {arize_phoenix-9.3.0.dist-info → arize_phoenix-9.5.0.dist-info}/RECORD +22 -22
  3. phoenix/db/README.md +1 -1
  4. phoenix/experiments/functions.py +11 -2
  5. phoenix/server/api/types/Project.py +126 -5
  6. phoenix/server/main.py +1 -1
  7. phoenix/server/static/.vite/manifest.json +40 -40
  8. phoenix/server/static/assets/{components-lafqiyl4.js → components-DpK7N6zE.js} +196 -199
  9. phoenix/server/static/assets/{index-CGZJ_sQk.js → index-BXA0RjaV.js} +20 -11
  10. phoenix/server/static/assets/{pages-Bm_Z5Ute.js → pages-jHwPRLA2.js} +780 -678
  11. phoenix/server/static/assets/vendor-CToBXdDM.js +905 -0
  12. phoenix/server/static/assets/vendor-WIZid84E.css +1 -0
  13. phoenix/server/static/assets/{vendor-arizeai-DJ2Xx4kZ.js → vendor-arizeai-BhbMHqQs.js} +2 -2
  14. phoenix/server/static/assets/{vendor-codemirror-CC0itEC8.js → vendor-codemirror-CeLHFooz.js} +1 -1
  15. phoenix/server/static/assets/{vendor-recharts-ZOUPDkCE.js → vendor-recharts-PlWJHgM9.js} +2 -2
  16. phoenix/server/static/assets/{vendor-shiki-CnRwDBHX.js → vendor-shiki-CPwL2jwA.js} +1 -1
  17. phoenix/trace/dsl/query.py +2 -2
  18. phoenix/version.py +1 -1
  19. phoenix/server/static/assets/vendor-BvFaCsmL.js +0 -903
  20. phoenix/server/static/assets/vendor-Cg6lcjUC.css +0 -1
  21. {arize_phoenix-9.3.0.dist-info → arize_phoenix-9.5.0.dist-info}/WHEEL +0 -0
  22. {arize_phoenix-9.3.0.dist-info → arize_phoenix-9.5.0.dist-info}/entry_points.txt +0 -0
  23. {arize_phoenix-9.3.0.dist-info → arize_phoenix-9.5.0.dist-info}/licenses/IP_NOTICE +0 -0
  24. {arize_phoenix-9.3.0.dist-info → arize_phoenix-9.5.0.dist-info}/licenses/LICENSE +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: arize-phoenix
3
- Version: 9.3.0
3
+ Version: 9.5.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
@@ -49,7 +49,7 @@ Requires-Dist: scipy
49
49
  Requires-Dist: sqlalchemy[asyncio]<3,>=2.0.4
50
50
  Requires-Dist: sqlean-py>=3.45.1
51
51
  Requires-Dist: starlette
52
- Requires-Dist: strawberry-graphql==0.267.0
52
+ Requires-Dist: strawberry-graphql==0.269.0
53
53
  Requires-Dist: tqdm
54
54
  Requires-Dist: typing-extensions>=4.6
55
55
  Requires-Dist: uvicorn
@@ -71,7 +71,7 @@ Requires-Dist: opentelemetry-sdk; extra == 'container'
71
71
  Requires-Dist: opentelemetry-semantic-conventions; extra == 'container'
72
72
  Requires-Dist: prometheus-client; extra == 'container'
73
73
  Requires-Dist: py-grpc-prometheus; extra == 'container'
74
- Requires-Dist: strawberry-graphql[opentelemetry]==0.267.0; extra == 'container'
74
+ Requires-Dist: strawberry-graphql[opentelemetry]==0.269.0; extra == 'container'
75
75
  Requires-Dist: umap-learn; extra == 'container'
76
76
  Requires-Dist: uvloop; (platform_system != 'Windows') and extra == 'container'
77
77
  Provides-Extra: dev
@@ -102,7 +102,7 @@ Requires-Dist: pytest-postgresql; extra == 'dev'
102
102
  Requires-Dist: pytest-xdist; extra == 'dev'
103
103
  Requires-Dist: pytest==8.3.3; extra == 'dev'
104
104
  Requires-Dist: ruff==0.6.9; extra == 'dev'
105
- Requires-Dist: strawberry-graphql[debug-server,opentelemetry]==0.267.0; extra == 'dev'
105
+ Requires-Dist: strawberry-graphql[debug-server,opentelemetry]==0.269.0; extra == 'dev'
106
106
  Requires-Dist: tabulate; extra == 'dev'
107
107
  Requires-Dist: tox-uv==1.11.3; extra == 'dev'
108
108
  Requires-Dist: tox==4.18.1; extra == 'dev'
@@ -6,13 +6,13 @@ 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=x87BX7hWGQQZbrW_vrYqFR_izCGfO9gFc--JXUG4Tdk,754
9
- phoenix/version.py,sha256=98aO5neM06RyEivP9sOpULw7NBhuSEezR_8VgK-Nuj8,22
9
+ phoenix/version.py,sha256=T_7n4J2JLWQ42N9H4Lbyh67nFa5HaqLPNGqgJpswgHk,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
13
13
  phoenix/core/model_schema.py,sha256=9JChtyXtid0GzEYImDcNA8Ppk7jmCO5xC5dBIUMNYjE,50504
14
14
  phoenix/core/model_schema_adapter.py,sha256=y1RXPYGt2HLTGpkB2X-XVQvRJXYGmJ9EL4m7TpOMvj0,8456
15
- phoenix/db/README.md,sha256=7BFJuecWJjFSxEYmoSv4CvoFHHszX4tFy3HJExOxS-4,8661
15
+ phoenix/db/README.md,sha256=z3jgbp6zYJV9NfcoQw1ODz9UWJLO3rVvnAnwqO6sa0I,8680
16
16
  phoenix/db/__init__.py,sha256=pDjEFXukHmJBM-1D8RjmXkvLsz85YWNxMQczt81ec3A,118
17
17
  phoenix/db/alembic.ini,sha256=GIS6HpHaKaJbbuahZg1Rc1D2_QqyCkV9r58wdARGf6w,3262
18
18
  phoenix/db/bulk_inserter.py,sha256=faNjuwLqqsw4ky8sa4D0h9u5TvEDTOjrccUW89L008E,12725
@@ -55,7 +55,7 @@ phoenix/db/types/identifier.py,sha256=Opr3_1di6e5ncrBDn30WfBSr-jN_VGBnkkA4BMuSoy
55
55
  phoenix/db/types/model_provider.py,sha256=96UMeqiy5X9PmYMOWA6dZAmI_BSV3yVxt9HEVYGe5Ns,157
56
56
  phoenix/db/types/trace_retention.py,sha256=UoLVX4efhv-mcF32zHpMX_hXWe8474cmhEyPmYqeMfI,9573
57
57
  phoenix/experiments/__init__.py,sha256=6JGwgUd7xCbGpuHqYZlsmErmYvVgv7N_j43bn3dUqsk,123
58
- phoenix/experiments/functions.py,sha256=rqhKgo7F0jMlkR_7zTZ6UqknrSF44v2XgEd-JR5ISmY,32818
58
+ phoenix/experiments/functions.py,sha256=hmTLqNgrWGFWQlQnWvNgOrpeWStGvbzQkD-1Tx-wM94,33270
59
59
  phoenix/experiments/tracing.py,sha256=seNh9rBH-rtQe8_FPI_VJj1rbo3ADcP6wDvERkMoxNc,2858
60
60
  phoenix/experiments/types.py,sha256=yntt6fnAny1U4Q9Y5Mm4ZYIb9319OaJovl-kyXFtGQE,23475
61
61
  phoenix/experiments/utils.py,sha256=MZ1-OnTcavk_KUtbfGqt55Fk9TGtJpYG_K71WsN-zDk,785
@@ -95,7 +95,7 @@ phoenix/server/dml_event.py,sha256=MjJmVEKytq75chBOSyvYDusUnEbg1pHpIjR3pZkUaJA,2
95
95
  phoenix/server/dml_event_handler.py,sha256=EZLXmCvx4pJrCkz29gxwKwmvmUkTtPCHw6klR-XM8qE,8258
96
96
  phoenix/server/grpc_server.py,sha256=dod29zE_Zlir7NyLcdVM8GH3P8sy-9ykzfaBfVifyE4,4656
97
97
  phoenix/server/jwt_store.py,sha256=asxzY4_ZBM2FWAMstHvhvnKUP_0AA3v3xPTL2IOgNqY,16831
98
- phoenix/server/main.py,sha256=Twpig08FSHIwICMr-2qGNLNoCrgk6EjyHrrxiBwS_VU,18029
98
+ phoenix/server/main.py,sha256=8iOi3HhYjnpbAZLe7D0KxFOj2MyjTueaSeDI8BldHC8,18048
99
99
  phoenix/server/oauth2.py,sha256=EV4wcCwG0N7cJRcfGNURdP5rZgRVCeRDvXyle19A27Y,2064
100
100
  phoenix/server/prometheus.py,sha256=1KjvSfjSa2-BPjDybVMM_Kag316CsN-Zwt64YNr_snc,7825
101
101
  phoenix/server/rate_limiters.py,sha256=cFc73D2NaxqNZZDbwfIDw4So-fRVOJPBtqxOZ8Qky_s,7155
@@ -291,7 +291,7 @@ phoenix/server/api/types/MimeType.py,sha256=Zpi6zCalkSFgsvhzvOs-O1gYA04usAi9H__Q
291
291
  phoenix/server/api/types/Model.py,sha256=8UIFqMe1q-2ufBNg-gxHusV8wM1h-KbfLUeJjyVcMvQ,8081
292
292
  phoenix/server/api/types/NumericRange.py,sha256=afEjgF97Go_OvmjMggbPBt-zGM8IONewAyEiKEHRds0,192
293
293
  phoenix/server/api/types/PerformanceMetric.py,sha256=KFkmJDqP43eDUtARQOUqR7NYcxvL6Vh2uisHWU6H3ko,387
294
- phoenix/server/api/types/Project.py,sha256=k83Wzs-ofzWErNT6cSYI2r1WuxBWkJMfYe9QIJXMfGI,23426
294
+ phoenix/server/api/types/Project.py,sha256=l1mz-j0ECEuW6aQrSMtDaLiosysjecs-tRqpxXqNBWQ,28804
295
295
  phoenix/server/api/types/ProjectSession.py,sha256=fyfVtpUpFOTnBx8DFnH3dr7WXAssN0ooAgrQSSi7kEI,4677
296
296
  phoenix/server/api/types/ProjectTraceRetentionPolicy.py,sha256=tYy2kgalPDyuaYZr0VUHjH0YpXaiF_QOzg5yfaV_c7c,3782
297
297
  phoenix/server/api/types/Prompt.py,sha256=ccP4eq1e38xbF0afclGWLOuDpBVpNbJ3AOSRClF8yFQ,4955
@@ -342,16 +342,16 @@ phoenix/server/static/apple-touch-icon-76x76.png,sha256=CT_xT12I0u2i0WU8JzBZBuOQ
342
342
  phoenix/server/static/apple-touch-icon.png,sha256=fOfpjqGpWYbJ0eAurKsyoZP1EAs6ZVooBJ_SGk2ZkDs,3801
343
343
  phoenix/server/static/favicon.ico,sha256=bY0vvCKRftemZfPShwZtE93DiiQdaYaozkPGwNFr6H8,34494
344
344
  phoenix/server/static/modernizr.js,sha256=mvK-XtkNqjOral-QvzoqsyOMECXIMu5BQwSVN_wcU9c,2564
345
- phoenix/server/static/.vite/manifest.json,sha256=8LVZsEiJBCIYQiPOPcfi-03CsjFEFF4JbUqNJN5Btmk,2165
346
- phoenix/server/static/assets/components-lafqiyl4.js,sha256=-y-Hwz1t1Z3n1vHOejYul1Hrn6elBpMlxyRiY1zzu1k,534495
347
- phoenix/server/static/assets/index-CGZJ_sQk.js,sha256=2EJl8zpWFvAsbCY_TRGLc4trzpaJFCgq0i76eIMy_-A,60669
348
- phoenix/server/static/assets/pages-Bm_Z5Ute.js,sha256=tv45YzzhNqWUTIObHCwZ6h4ZhEc7SzodcJpMv6NXAYw,1020732
349
- phoenix/server/static/assets/vendor-BvFaCsmL.js,sha256=oNH_RaseAQvA3HmtHr-Pf40kGCueGoRsz9_1NXGIICY,2661112
350
- phoenix/server/static/assets/vendor-Cg6lcjUC.css,sha256=nZrkr0u6NNElFGvpWHk9GTHeGoibCXCli1bE7mXZGZg,1816
351
- phoenix/server/static/assets/vendor-arizeai-DJ2Xx4kZ.js,sha256=6NXeJGecPCNtx8424NHvOLPf3SyzTTjbAFvBzHb4FD4,193248
352
- phoenix/server/static/assets/vendor-codemirror-CC0itEC8.js,sha256=AHvFAf7RbR6NiOWOyFx6_NTZIHDFntzK76dX0Biycqw,781264
353
- phoenix/server/static/assets/vendor-recharts-ZOUPDkCE.js,sha256=tfMG__KV1jVYleooEbcXAemqhfrj7KOuyrCZI4EpmZs,282151
354
- phoenix/server/static/assets/vendor-shiki-CnRwDBHX.js,sha256=CLDfG9Y-KpLVO-kY_W2lDG42SXjFiY8clrESzlwEjug,8980312
345
+ phoenix/server/static/.vite/manifest.json,sha256=G8gBwLzRf4U016UfDRFEhhJl5YH5dGNHsDnzSEedR4Q,2165
346
+ phoenix/server/static/assets/components-DpK7N6zE.js,sha256=IVHCYHDk1KKozLI2rurH65X7PVrkik2G4XIvyIKtduQ,535614
347
+ phoenix/server/static/assets/index-BXA0RjaV.js,sha256=Vv39xY1j-wkgUXMGjC1Eff_RB8g4PafLOboJuDbIbSs,60240
348
+ phoenix/server/static/assets/pages-jHwPRLA2.js,sha256=6z8FwfIi37BVvsgfRhd33sqRor__LbTmRQE5cqpmQZ8,1036277
349
+ phoenix/server/static/assets/vendor-CToBXdDM.js,sha256=q_UwZrhCRrNhrvFyv3OO6bW52jM1TDiYk3aTj-NgdLU,2744392
350
+ phoenix/server/static/assets/vendor-WIZid84E.css,sha256=spZD2r7XL5GfLO13ln-IuXfnjAref8l6g_n_AvxxOlI,5517
351
+ phoenix/server/static/assets/vendor-arizeai-BhbMHqQs.js,sha256=l3G1o-P_IYcqQWOHBcSpT5RextOH2myGl58ZSN7NvcQ,193248
352
+ phoenix/server/static/assets/vendor-codemirror-CeLHFooz.js,sha256=TzeLFkY7fiI1tUZYGvlHgZcdHwwclMtUItntKu6SdbA,781264
353
+ phoenix/server/static/assets/vendor-recharts-PlWJHgM9.js,sha256=2F5ko_UqDn7-C7v7rh_bI17wbUA7kZA_JTzja3kD6uk,282151
354
+ phoenix/server/static/assets/vendor-shiki-CPwL2jwA.js,sha256=goWi7CncK66zsIjhFTExUD-0cWQSFSGtpXjb1GYflcw,8980312
355
355
  phoenix/server/static/assets/vendor-three-C5WAXd5r.js,sha256=ELkg06u70N7h8oFmvqdoHyPuUf9VgGEWeT4LKFx4VWo,620975
356
356
  phoenix/server/templates/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
357
357
  phoenix/server/templates/index.html,sha256=e8_jdi7Eo19SK7DI_gglkTW094D17E0VAegoMmmmvIc,4330
@@ -378,7 +378,7 @@ phoenix/trace/dsl/README.md,sha256=ihmP9zGUC5V-TDbzKla76LuyDqPDQIBUH2BORwxNI68,2
378
378
  phoenix/trace/dsl/__init__.py,sha256=WIQIjJg362XD3s50OsPJJ0xbDsGp41bSv7vDllLrPuA,144
379
379
  phoenix/trace/dsl/filter.py,sha256=FYOzH1gkwlKBYepscZzwHeeXnJdTadC_m4v0cYRs00Y,31557
380
380
  phoenix/trace/dsl/helpers.py,sha256=pJESAz7sDNE7HTVCFzxIx5sEsyUSh7RIN7yqmndAaF8,4171
381
- phoenix/trace/dsl/query.py,sha256=qi5k83Ch7mouSzd8w8dkMHVJ9C45tYXZLtb4lMwToZM,36350
381
+ phoenix/trace/dsl/query.py,sha256=I7iDsJk1cwxYh0EUFXLu2DDqPU5WYxVa_ntVOe5v8s0,36350
382
382
  phoenix/trace/v1/__init__.py,sha256=-IbAD0ruESMjvQLvGAg9CTfjBUATFDx1OXseDPis6-0,88
383
383
  phoenix/trace/v1/evaluation_pb2.py,sha256=8sXvv2BW_vqD30MOMbmkeE2zpmm7ncik21kl3e-HzeQ,2254
384
384
  phoenix/trace/v1/evaluation_pb2.pyi,sha256=cCbbx06gwQmaH14s3J1X25TtaARh-k1abbxQdQCXGm8,4500
@@ -392,9 +392,9 @@ phoenix/utilities/project.py,sha256=auVpARXkDb-JgeX5f2aStyFIkeKvGwN9l7qrFeJMVxI,
392
392
  phoenix/utilities/re.py,sha256=6YyUWIkv0zc2SigsxfOWIHzdpjKA_TZo2iqKq7zJKvw,2081
393
393
  phoenix/utilities/span_store.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
394
394
  phoenix/utilities/template_formatters.py,sha256=gh9PJD6WEGw7TEYXfSst1UR4pWWwmjxMLrDVQ_CkpkQ,2779
395
- arize_phoenix-9.3.0.dist-info/METADATA,sha256=y8PYGl1AvfkPLUvOFGV1hiuMTDHgOL673u9P02BcSl8,25590
396
- arize_phoenix-9.3.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
397
- arize_phoenix-9.3.0.dist-info/entry_points.txt,sha256=Pgpn8Upxx9P8z8joPXZWl2LlnAlGc3gcQoVchb06X1Q,94
398
- arize_phoenix-9.3.0.dist-info/licenses/IP_NOTICE,sha256=JBqyyCYYxGDfzQ0TtsQgjts41IJoa-hiwDrBjCb9gHM,469
399
- arize_phoenix-9.3.0.dist-info/licenses/LICENSE,sha256=HFkW9REuMOkvKRACuwLPT0hRydHb3zNg-fdFt94td18,3794
400
- arize_phoenix-9.3.0.dist-info/RECORD,,
395
+ arize_phoenix-9.5.0.dist-info/METADATA,sha256=yEwAElNirOhrgg9eu_9QRI9_aeRC1_QUACrcjhOyfOA,25590
396
+ arize_phoenix-9.5.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
397
+ arize_phoenix-9.5.0.dist-info/entry_points.txt,sha256=Pgpn8Upxx9P8z8joPXZWl2LlnAlGc3gcQoVchb06X1Q,94
398
+ arize_phoenix-9.5.0.dist-info/licenses/IP_NOTICE,sha256=JBqyyCYYxGDfzQ0TtsQgjts41IJoa-hiwDrBjCb9gHM,469
399
+ arize_phoenix-9.5.0.dist-info/licenses/LICENSE,sha256=HFkW9REuMOkvKRACuwLPT0hRydHb3zNg-fdFt94td18,3794
400
+ arize_phoenix-9.5.0.dist-info/RECORD,,
phoenix/db/README.md CHANGED
@@ -37,7 +37,7 @@ alembic upgrade head
37
37
 
38
38
  If the above command fails, it may be necessary to undo partially applied changes from a failed migration by first running down-migrations. This can be accomplished by identifying the ID of the migration revision you wish to return to. Revisions are defined [here](./migrations/versions/).
39
39
 
40
- ⚠️ Running down-migrations can result in lost data. Only run down-migrations if you know what you are doing and consider backing up your database first. If you have any questions or doubts, contact the Phoenix team in the `#phoenix-support` channel of the [Arize AI Slack community](https://join.slack.com/t/arize-ai/shared_invite/zt-1px8dcmlf-fmThhDFD_V_48oU7ALan4Q) or via GitHub.
40
+ ⚠️ Running down-migrations can result in lost data. Only run down-migrations if you know what you are doing and consider backing up your database first. If you have any questions or doubts, contact the Phoenix team in the `#phoenix-support` channel of the [Arize AI Slack community](https://arize-ai.slack.com/join/shared_invite/zt-2w57bhem8-hq24MB6u7yE_ZF_ilOYSBw#/shared-invite/email) or via GitHub.
41
41
 
42
42
  ```bash
43
43
  alembic downgrade <revision-id>
@@ -116,8 +116,17 @@ def run_experiment(
116
116
  - `metadata`: Metadata associated with the dataset example
117
117
  - `example`: The dataset `Example` object with all associated fields
118
118
 
119
- An `evaluator` is either a synchronous or asynchronous function that returns either a boolean
120
- or numeric "score". If the `evaluator` is a function of one argument then that argument will be
119
+ An `evaluator` is either a synchronous or asynchronous function that returns an evaluation
120
+ result object, which can take any of the following forms.
121
+
122
+ - phoenix.experiments.types.EvaluationResult with optional fields for score, label, explanation
123
+ and metadata
124
+ - a `bool`, which will be interpreted as a score of 0 or 1 plus a label of "True" or "False"
125
+ - a `float`, which will be interpreted as a score
126
+ - a `str`, which will be interpreted as a label
127
+ - a 2-`tuple` of (`float`, `str`), which will be interpreted as (score, explanation)
128
+
129
+ If the `evaluator` is a function of one argument then that argument will be
121
130
  bound to the `output` of the task. Alternatively, the `evaluator` can be a function of any
122
131
  combination of specific argument names that will be bound to special values:
123
132
 
@@ -1,13 +1,14 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import operator
4
- from datetime import datetime
4
+ from datetime import datetime, timedelta
5
5
  from typing import TYPE_CHECKING, Annotated, Any, ClassVar, Optional
6
6
 
7
7
  import strawberry
8
8
  from aioitertools.itertools import islice
9
9
  from openinference.semconv.trace import SpanAttributes
10
10
  from sqlalchemy import desc, distinct, func, or_, select
11
+ from sqlalchemy.dialects import postgresql, sqlite
11
12
  from sqlalchemy.sql.elements import ColumnElement
12
13
  from sqlalchemy.sql.expression import tuple_
13
14
  from strawberry import ID, UNSET, Private, lazy
@@ -17,6 +18,7 @@ from typing_extensions import assert_never
17
18
 
18
19
  from phoenix.datetime_utils import right_open_time_range
19
20
  from phoenix.db import models
21
+ from phoenix.db.helpers import SupportedSQLDialect
20
22
  from phoenix.server.api.context import Context
21
23
  from phoenix.server.api.input_types.ProjectSessionSort import (
22
24
  ProjectSessionColumn,
@@ -38,6 +40,7 @@ from phoenix.server.api.types.pagination import (
38
40
  from phoenix.server.api.types.ProjectSession import ProjectSession, to_gql_project_session
39
41
  from phoenix.server.api.types.SortDir import SortDir
40
42
  from phoenix.server.api.types.Span import Span
43
+ from phoenix.server.api.types.TimeSeries import TimeSeries, TimeSeriesDataPoint
41
44
  from phoenix.server.api.types.Trace import Trace
42
45
  from phoenix.server.api.types.ValidationResult import ValidationResult
43
46
  from phoenix.trace.dsl import SpanFilter
@@ -528,19 +531,46 @@ class Project(Node):
528
531
  return info.context.last_updated_at.get(self._table, self.project_rowid)
529
532
 
530
533
  @strawberry.field
531
- async def validate_span_filter_condition(self, condition: str) -> ValidationResult:
534
+ async def validate_span_filter_condition(
535
+ self,
536
+ info: Info[Context, None],
537
+ condition: str,
538
+ ) -> ValidationResult:
539
+ """Validates a span filter condition by attempting to compile it for both SQLite and PostgreSQL.
540
+
541
+ This method checks if the provided filter condition is syntactically valid and can be compiled
542
+ into SQL queries for both SQLite and PostgreSQL databases. It does not execute the query,
543
+ only validates its syntax. Any exception during compilation (syntax errors, invalid expressions,
544
+ etc.) will result in an invalid validation result.
545
+
546
+ Args:
547
+ condition (str): The span filter condition string to validate.
548
+
549
+ Returns:
550
+ ValidationResult: A result object containing:
551
+ - is_valid (bool): True if the condition is valid, False otherwise
552
+ - error_message (Optional[str]): Error message if validation fails, None if valid
553
+ """ # noqa: E501
532
554
  # This query is too expensive to run on every validation
533
555
  # valid_eval_names = await self.span_annotation_names()
534
556
  try:
535
- SpanFilter(
557
+ span_filter = SpanFilter(
536
558
  condition=condition,
537
559
  # valid_eval_names=valid_eval_names,
538
560
  )
561
+ stmt = span_filter(select(models.Span))
562
+ dialect = info.context.db.dialect
563
+ if dialect is SupportedSQLDialect.POSTGRESQL:
564
+ str(stmt.compile(dialect=sqlite.dialect())) # type: ignore[no-untyped-call]
565
+ elif dialect is SupportedSQLDialect.SQLITE:
566
+ str(stmt.compile(dialect=postgresql.dialect())) # type: ignore[no-untyped-call]
567
+ else:
568
+ assert_never(dialect)
539
569
  return ValidationResult(is_valid=True, error_message=None)
540
- except SyntaxError as e:
570
+ except Exception as e:
541
571
  return ValidationResult(
542
572
  is_valid=False,
543
- error_message=e.msg,
573
+ error_message=str(e),
544
574
  )
545
575
 
546
576
  @strawberry.field
@@ -610,6 +640,97 @@ class Project(Node):
610
640
  )
611
641
  return updated_at
612
642
 
643
+ @strawberry.field(
644
+ description="Hourly span count for the project.",
645
+ ) # type: ignore
646
+ async def span_count_time_series(
647
+ self,
648
+ info: Info[Context, None],
649
+ time_range: Optional[TimeRange] = UNSET,
650
+ ) -> SpanCountTimeSeries:
651
+ """Returns a time series of span counts grouped by hour for the project.
652
+
653
+ This field provides hourly aggregated span counts, which can be useful for
654
+ visualizing span activity over time. The data points represent the number
655
+ of spans that started in each hour.
656
+
657
+ Args:
658
+ info: The GraphQL info object containing context information.
659
+ time_range: Optional time range to filter the spans. If provided, only
660
+ spans that started within this range will be counted.
661
+
662
+ Returns:
663
+ A SpanCountTimeSeries object containing data points with timestamps
664
+ (rounded to the nearest hour) and corresponding span counts.
665
+
666
+ Notes:
667
+ - The timestamps are rounded down to the nearest hour.
668
+ - If a time range is provided, the start time is rounded down to the
669
+ nearest hour, and the end time is rounded up to the nearest hour.
670
+ - The SQL query is optimized for both PostgreSQL and SQLite databases.
671
+ """
672
+ # Determine the appropriate SQL function to truncate timestamps to hours
673
+ # based on the database dialect
674
+ if info.context.db.dialect is SupportedSQLDialect.POSTGRESQL:
675
+ # PostgreSQL uses date_trunc for timestamp truncation
676
+ hour = func.date_trunc("hour", models.Span.start_time)
677
+ elif info.context.db.dialect is SupportedSQLDialect.SQLITE:
678
+ # SQLite uses strftime for timestamp formatting
679
+ hour = func.strftime("%Y-%m-%dT%H:00:00.000+00:00", models.Span.start_time)
680
+ else:
681
+ assert_never(info.context.db.dialect)
682
+
683
+ # Build the base query to count spans grouped by hour
684
+ stmt = (
685
+ select(hour, func.count())
686
+ .join(models.Trace)
687
+ .where(models.Trace.project_rowid == self.project_rowid)
688
+ .group_by(hour)
689
+ .order_by(hour)
690
+ )
691
+
692
+ # Apply time range filtering if provided
693
+ if time_range:
694
+ if t := time_range.start:
695
+ # Round down to nearest hour for the start time
696
+ start = t.replace(minute=0, second=0, microsecond=0)
697
+ stmt = stmt.where(start <= models.Span.start_time)
698
+ if t := time_range.end:
699
+ # Round up to nearest hour for the end time
700
+ # If the time is already at the start of an hour, use it as is
701
+ if t.minute == 0 and t.second == 0 and t.microsecond == 0:
702
+ end = t
703
+ else:
704
+ # Otherwise, round up to the next hour
705
+ end = t.replace(minute=0, second=0, microsecond=0) + timedelta(hours=1)
706
+ stmt = stmt.where(models.Span.start_time < end)
707
+
708
+ # Execute the query and convert the results to a time series
709
+ async with info.context.db() as session:
710
+ data = await session.stream(stmt)
711
+ return SpanCountTimeSeries(
712
+ data=[
713
+ TimeSeriesDataPoint(
714
+ timestamp=_as_datetime(t),
715
+ value=v,
716
+ )
717
+ async for t, v in data
718
+ ]
719
+ )
720
+
721
+
722
+ @strawberry.type
723
+ class SpanCountTimeSeries(TimeSeries):
724
+ """A time series of span count"""
725
+
613
726
 
614
727
  INPUT_VALUE = SpanAttributes.INPUT_VALUE.split(".")
615
728
  OUTPUT_VALUE = SpanAttributes.OUTPUT_VALUE.split(".")
729
+
730
+
731
+ def _as_datetime(value: Any) -> datetime:
732
+ if isinstance(value, datetime):
733
+ return value
734
+ if isinstance(value, str):
735
+ return datetime.fromisoformat(value)
736
+ raise ValueError(f"Cannot convert {value} to datetime")
phoenix/server/main.py CHANGED
@@ -92,7 +92,7 @@ _WELCOME_MESSAGE = Environment(loader=BaseLoader()).from_string("""
92
92
 
93
93
  |
94
94
  | 🌎 Join our Community 🌎
95
- | https://join.slack.com/t/arize-ai/shared_invite/zt-1px8dcmlf-fmThhDFD_V_48oU7ALan4Q
95
+ | https://arize-ai.slack.com/join/shared_invite/zt-2w57bhem8-hq24MB6u7yE_ZF_ilOYSBw#/shared-invite/email
96
96
  |
97
97
  | ⭐️ Leave us a Star ⭐️
98
98
  | https://github.com/Arize-ai/phoenix
@@ -1,67 +1,67 @@
1
1
  {
2
- "_components-lafqiyl4.js": {
3
- "file": "assets/components-lafqiyl4.js",
2
+ "_components-DpK7N6zE.js": {
3
+ "file": "assets/components-DpK7N6zE.js",
4
4
  "name": "components",
5
5
  "imports": [
6
- "_vendor-BvFaCsmL.js",
7
- "_pages-Bm_Z5Ute.js",
8
- "_vendor-arizeai-DJ2Xx4kZ.js",
9
- "_vendor-codemirror-CC0itEC8.js",
6
+ "_vendor-CToBXdDM.js",
7
+ "_pages-jHwPRLA2.js",
8
+ "_vendor-arizeai-BhbMHqQs.js",
9
+ "_vendor-codemirror-CeLHFooz.js",
10
10
  "_vendor-three-C5WAXd5r.js"
11
11
  ]
12
12
  },
13
- "_pages-Bm_Z5Ute.js": {
14
- "file": "assets/pages-Bm_Z5Ute.js",
13
+ "_pages-jHwPRLA2.js": {
14
+ "file": "assets/pages-jHwPRLA2.js",
15
15
  "name": "pages",
16
16
  "imports": [
17
- "_vendor-BvFaCsmL.js",
18
- "_vendor-arizeai-DJ2Xx4kZ.js",
19
- "_components-lafqiyl4.js",
20
- "_vendor-codemirror-CC0itEC8.js",
21
- "_vendor-recharts-ZOUPDkCE.js"
17
+ "_vendor-CToBXdDM.js",
18
+ "_vendor-arizeai-BhbMHqQs.js",
19
+ "_components-DpK7N6zE.js",
20
+ "_vendor-codemirror-CeLHFooz.js",
21
+ "_vendor-recharts-PlWJHgM9.js"
22
22
  ]
23
23
  },
24
- "_vendor-BvFaCsmL.js": {
25
- "file": "assets/vendor-BvFaCsmL.js",
24
+ "_vendor-CToBXdDM.js": {
25
+ "file": "assets/vendor-CToBXdDM.js",
26
26
  "name": "vendor",
27
27
  "imports": [
28
28
  "_vendor-three-C5WAXd5r.js"
29
29
  ],
30
30
  "css": [
31
- "assets/vendor-Cg6lcjUC.css"
31
+ "assets/vendor-WIZid84E.css"
32
32
  ]
33
33
  },
34
- "_vendor-Cg6lcjUC.css": {
35
- "file": "assets/vendor-Cg6lcjUC.css",
36
- "src": "_vendor-Cg6lcjUC.css"
34
+ "_vendor-WIZid84E.css": {
35
+ "file": "assets/vendor-WIZid84E.css",
36
+ "src": "_vendor-WIZid84E.css"
37
37
  },
38
- "_vendor-arizeai-DJ2Xx4kZ.js": {
39
- "file": "assets/vendor-arizeai-DJ2Xx4kZ.js",
38
+ "_vendor-arizeai-BhbMHqQs.js": {
39
+ "file": "assets/vendor-arizeai-BhbMHqQs.js",
40
40
  "name": "vendor-arizeai",
41
41
  "imports": [
42
- "_vendor-BvFaCsmL.js"
42
+ "_vendor-CToBXdDM.js"
43
43
  ]
44
44
  },
45
- "_vendor-codemirror-CC0itEC8.js": {
46
- "file": "assets/vendor-codemirror-CC0itEC8.js",
45
+ "_vendor-codemirror-CeLHFooz.js": {
46
+ "file": "assets/vendor-codemirror-CeLHFooz.js",
47
47
  "name": "vendor-codemirror",
48
48
  "imports": [
49
- "_vendor-BvFaCsmL.js",
50
- "_vendor-shiki-CnRwDBHX.js"
49
+ "_vendor-CToBXdDM.js",
50
+ "_vendor-shiki-CPwL2jwA.js"
51
51
  ]
52
52
  },
53
- "_vendor-recharts-ZOUPDkCE.js": {
54
- "file": "assets/vendor-recharts-ZOUPDkCE.js",
53
+ "_vendor-recharts-PlWJHgM9.js": {
54
+ "file": "assets/vendor-recharts-PlWJHgM9.js",
55
55
  "name": "vendor-recharts",
56
56
  "imports": [
57
- "_vendor-BvFaCsmL.js"
57
+ "_vendor-CToBXdDM.js"
58
58
  ]
59
59
  },
60
- "_vendor-shiki-CnRwDBHX.js": {
61
- "file": "assets/vendor-shiki-CnRwDBHX.js",
60
+ "_vendor-shiki-CPwL2jwA.js": {
61
+ "file": "assets/vendor-shiki-CPwL2jwA.js",
62
62
  "name": "vendor-shiki",
63
63
  "imports": [
64
- "_vendor-BvFaCsmL.js"
64
+ "_vendor-CToBXdDM.js"
65
65
  ]
66
66
  },
67
67
  "_vendor-three-C5WAXd5r.js": {
@@ -69,19 +69,19 @@
69
69
  "name": "vendor-three"
70
70
  },
71
71
  "index.tsx": {
72
- "file": "assets/index-CGZJ_sQk.js",
72
+ "file": "assets/index-BXA0RjaV.js",
73
73
  "name": "index",
74
74
  "src": "index.tsx",
75
75
  "isEntry": true,
76
76
  "imports": [
77
- "_vendor-BvFaCsmL.js",
78
- "_vendor-arizeai-DJ2Xx4kZ.js",
79
- "_pages-Bm_Z5Ute.js",
80
- "_components-lafqiyl4.js",
77
+ "_vendor-CToBXdDM.js",
78
+ "_vendor-arizeai-BhbMHqQs.js",
79
+ "_pages-jHwPRLA2.js",
80
+ "_components-DpK7N6zE.js",
81
81
  "_vendor-three-C5WAXd5r.js",
82
- "_vendor-codemirror-CC0itEC8.js",
83
- "_vendor-shiki-CnRwDBHX.js",
84
- "_vendor-recharts-ZOUPDkCE.js"
82
+ "_vendor-codemirror-CeLHFooz.js",
83
+ "_vendor-shiki-CPwL2jwA.js",
84
+ "_vendor-recharts-PlWJHgM9.js"
85
85
  ]
86
86
  }
87
87
  }