arize-phoenix 4.26.0__py3-none-any.whl → 4.27.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.
- {arize_phoenix-4.26.0.dist-info → arize_phoenix-4.27.0.dist-info}/METADATA +2 -3
- {arize_phoenix-4.26.0.dist-info → arize_phoenix-4.27.0.dist-info}/RECORD +18 -18
- phoenix/inferences/fixtures.py +14 -8
- phoenix/server/api/routers/v1/experiments.py +55 -1
- phoenix/server/api/routers/v1/utils.py +2 -2
- phoenix/server/app.py +149 -26
- phoenix/server/main.py +71 -7
- phoenix/server/static/.vite/manifest.json +9 -9
- phoenix/server/static/assets/{components-1Ahruijo.js → components-1MfQimGx.js} +3 -3
- phoenix/server/static/assets/index-B263sE2x.js +100 -0
- phoenix/server/static/assets/{pages-CFS6mPnW.js → pages-CqZDVx20.js} +180 -171
- phoenix/session/client.py +17 -1
- phoenix/trace/dsl/helpers.py +3 -1
- phoenix/trace/fixtures.py +47 -4
- phoenix/version.py +1 -1
- phoenix/server/static/assets/index-BEE_RWJx.js +0 -100
- {arize_phoenix-4.26.0.dist-info → arize_phoenix-4.27.0.dist-info}/WHEEL +0 -0
- {arize_phoenix-4.26.0.dist-info → arize_phoenix-4.27.0.dist-info}/licenses/IP_NOTICE +0 -0
- {arize_phoenix-4.26.0.dist-info → arize_phoenix-4.27.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: arize-phoenix
|
|
3
|
-
Version: 4.
|
|
3
|
+
Version: 4.27.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
|
|
@@ -45,8 +45,7 @@ Requires-Dist: pydantic!=2.0.*,<3,>=1.0
|
|
|
45
45
|
Requires-Dist: pyjwt
|
|
46
46
|
Requires-Dist: python-multipart
|
|
47
47
|
Requires-Dist: scikit-learn
|
|
48
|
-
Requires-Dist: scipy
|
|
49
|
-
Requires-Dist: scipy; platform_system != 'Darwin'
|
|
48
|
+
Requires-Dist: scipy
|
|
50
49
|
Requires-Dist: sqlalchemy[asyncio]<3,>=2.0.4
|
|
51
50
|
Requires-Dist: sqlean-py>=3.45.1
|
|
52
51
|
Requires-Dist: starlette
|
|
@@ -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=OyML4t2XGnlqF0JXA9_uccL8HslTABxep9Ci7MViKEU,5216
|
|
8
8
|
phoenix/settings.py,sha256=cO-qgis_S27nHirTobYI9hHPfZH18R--WMmxNdsVUwc,273
|
|
9
|
-
phoenix/version.py,sha256=
|
|
9
|
+
phoenix/version.py,sha256=3tdrXCYXhzGl0HhTFxiRhMv5mTezDVgvqXVYnKeIJeo,23
|
|
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=km_a--PBHOuA337ClRw9xqhOHhrUT6Rl9pz_zV0JYkQ,4843
|
|
@@ -51,7 +51,7 @@ phoenix/experiments/evaluators/llm_evaluators.py,sha256=zyGhxXBDNi1qoj_8I95PRSwj
|
|
|
51
51
|
phoenix/experiments/evaluators/utils.py,sha256=XYqB0bOljyR0GewmR_mm9Ndl_q95EkjjDqfXd7YVqTk,9303
|
|
52
52
|
phoenix/inferences/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
53
53
|
phoenix/inferences/errors.py,sha256=cGp9vxnw4SewFoWBV3ZGMkhE0Kh73lPIv3Ppz_H_RoA,8261
|
|
54
|
-
phoenix/inferences/fixtures.py,sha256=
|
|
54
|
+
phoenix/inferences/fixtures.py,sha256=oTtfjkI9ULxQ9bKtt91QGSDd3eyJ6T1ZylGiJf1iueo,20892
|
|
55
55
|
phoenix/inferences/inferences.py,sha256=r-ByeW_AU6cu199iJMn_Td3XywqtRfrLS7cDuHaayUA,31147
|
|
56
56
|
phoenix/inferences/schema.py,sha256=UYej9IJ6pFeNW3fq721kJy16ONso_xVDm78Q68G4hl4,6643
|
|
57
57
|
phoenix/inferences/validation.py,sha256=fdmbsjUBwtacRiVFdh9aem-QrgPfq_OlEmPdascWluc,8297
|
|
@@ -69,11 +69,11 @@ phoenix/pointcloud/pointcloud.py,sha256=4zAIkKs2xOUbchpj4XDAV-iPMXrfAJ15TG6rlIYG
|
|
|
69
69
|
phoenix/pointcloud/projectors.py,sha256=zO_RrtDYSv2rqVOfIP2_9Cv11Dc8EmcZR94xhFcBYPU,1057
|
|
70
70
|
phoenix/pointcloud/umap_parameters.py,sha256=3UQSjrysVOvq2V4KNpTMqNqNiK0BsTZnPBHWZ4fyJtQ,1708
|
|
71
71
|
phoenix/server/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
72
|
-
phoenix/server/app.py,sha256=
|
|
72
|
+
phoenix/server/app.py,sha256=rPzpaEpTSViIP-RLbHzYfxAf7zplKOEbFHxCb40YXFc,26954
|
|
73
73
|
phoenix/server/dml_event.py,sha256=MpjCFqljxvgb9OB5Cez9vJesb3oHb3XxXictynBfcis,2851
|
|
74
74
|
phoenix/server/dml_event_handler.py,sha256=6p-PucctivelVHfO-_9zNxWZYPr_eGjDF3bKjLtc5co,8251
|
|
75
75
|
phoenix/server/grpc_server.py,sha256=jllxDNkpLQxDkvej4RhTokobowbvydF-SU8gSw1MTCc,3378
|
|
76
|
-
phoenix/server/main.py,sha256=
|
|
76
|
+
phoenix/server/main.py,sha256=KcyiOtU7pJrWASTih4huF53WizXUdjCpWSqY6glk-mA,14037
|
|
77
77
|
phoenix/server/prometheus.py,sha256=j9DHB2fERuq_ZKmwVaqR-9wx5WcPPuU1Cm5Bhg5241Y,2996
|
|
78
78
|
phoenix/server/telemetry.py,sha256=T_2OKrxNViAeaANlNspEekg_Y5uZIFWvKAnpz8Aoqvk,2762
|
|
79
79
|
phoenix/server/thread_server.py,sha256=RwXQGP_QhGD7le6WB7xEygEEuwBl5Ck_Zo8xGIYGi9M,2135
|
|
@@ -160,11 +160,11 @@ phoenix/server/api/routers/v1/datasets.py,sha256=l3Hlc9AVyvX5GdT9iOXBsV-i4c_vtnC
|
|
|
160
160
|
phoenix/server/api/routers/v1/evaluations.py,sha256=FSfz9MTi8s65F07abDXlb9-y97fDZSYbqsCXpimwO7g,12628
|
|
161
161
|
phoenix/server/api/routers/v1/experiment_evaluations.py,sha256=RTQnjupjmh07xowjq77ajbuAZhzIEfYxA4ZtECvGwOU,4844
|
|
162
162
|
phoenix/server/api/routers/v1/experiment_runs.py,sha256=0G7GgGcZv9dzK47tsPp-p4k5O7W4F_aNRrsNuJN7mho,6393
|
|
163
|
-
phoenix/server/api/routers/v1/experiments.py,sha256=
|
|
163
|
+
phoenix/server/api/routers/v1/experiments.py,sha256=3u275sGuYSiMyzC_obbjK3mf6aYb7SkY2c_wOg3z4xg,11751
|
|
164
164
|
phoenix/server/api/routers/v1/pydantic_compat.py,sha256=FeK8oe2brqu-djsoqRxiKL4tw5cHmi89OHVfCFxYsAo,2890
|
|
165
165
|
phoenix/server/api/routers/v1/spans.py,sha256=MAkMLrONFtItQxkHJde_Wpvz0jsgydegxVZOkZkRUsU,8781
|
|
166
166
|
phoenix/server/api/routers/v1/traces.py,sha256=HJDmYKMATL40dZEJro6uQ3imbCZBzk3nUun9d21jcDs,7799
|
|
167
|
-
phoenix/server/api/routers/v1/utils.py,sha256=
|
|
167
|
+
phoenix/server/api/routers/v1/utils.py,sha256=ph2tC3crWewKhzM2JnX-gAelEHfGLxZeFKXHVWrddmI,3086
|
|
168
168
|
phoenix/server/api/types/Annotation.py,sha256=7Ym7iuVcbwHlw2yIRylz4nATAF_Cm-Z17qcjiooj1cc,751
|
|
169
169
|
phoenix/server/api/types/AnnotationSummary.py,sha256=8B2LIROqcrPOi8hvYygsblKvSEBfSrysnKOV7F36hgA,1518
|
|
170
170
|
phoenix/server/api/types/AnnotatorKind.py,sha256=rPgGdbN1Gvc109sGQ_ZH-gfJbp93V9wlarzTEJNtUwI,236
|
|
@@ -237,10 +237,10 @@ phoenix/server/static/apple-touch-icon-76x76.png,sha256=CT_xT12I0u2i0WU8JzBZBuOQ
|
|
|
237
237
|
phoenix/server/static/apple-touch-icon.png,sha256=fOfpjqGpWYbJ0eAurKsyoZP1EAs6ZVooBJ_SGk2ZkDs,3801
|
|
238
238
|
phoenix/server/static/favicon.ico,sha256=bY0vvCKRftemZfPShwZtE93DiiQdaYaozkPGwNFr6H8,34494
|
|
239
239
|
phoenix/server/static/modernizr.js,sha256=mvK-XtkNqjOral-QvzoqsyOMECXIMu5BQwSVN_wcU9c,2564
|
|
240
|
-
phoenix/server/static/.vite/manifest.json,sha256=
|
|
241
|
-
phoenix/server/static/assets/components-
|
|
242
|
-
phoenix/server/static/assets/index-
|
|
243
|
-
phoenix/server/static/assets/pages-
|
|
240
|
+
phoenix/server/static/.vite/manifest.json,sha256=NCacyzu0qbu92qY-iwPA7JHvzK56ZJnp_usNyMD3fZw,1929
|
|
241
|
+
phoenix/server/static/assets/components-1MfQimGx.js,sha256=NfJgri_ChJVeYEExpGOvfr5SLFuX_bZcvjfEVGH3HWI,187209
|
|
242
|
+
phoenix/server/static/assets/index-B263sE2x.js,sha256=PZG-hlU6oncPYh7r6tfdNm5pjuL94SFLHg4fCyr5Oe8,7515
|
|
243
|
+
phoenix/server/static/assets/pages-CqZDVx20.js,sha256=_Lor33vSFj6dZBGvOK4fPt1vXj9pndZFpyyi5BG_8AY,467596
|
|
244
244
|
phoenix/server/static/assets/vendor-DxkFTwjz.css,sha256=nZrkr0u6NNElFGvpWHk9GTHeGoibCXCli1bE7mXZGZg,1816
|
|
245
245
|
phoenix/server/static/assets/vendor-aSQri0vz.js,sha256=x_07SENutKMhtJ9HgFqkQHvwsDTfPkMmzQznY3HY7Zo,1359197
|
|
246
246
|
phoenix/server/static/assets/vendor-arizeai-CsdcB1NH.js,sha256=VEn7hFJXcHV_DODmeDi9pEpF_D2NQ1bZYewbPe3BhIw,304008
|
|
@@ -250,7 +250,7 @@ phoenix/server/static/assets/vendor-three-DwGkEfCM.js,sha256=0D12ZgKzfKCTSdSTKJB
|
|
|
250
250
|
phoenix/server/templates/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
251
251
|
phoenix/server/templates/index.html,sha256=dAm0IClgJUdT5AOmjZvtgMg8F_xGrRGv95SAkUyx_kg,4325
|
|
252
252
|
phoenix/session/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
253
|
-
phoenix/session/client.py,sha256=
|
|
253
|
+
phoenix/session/client.py,sha256=SqnGTinAIiPGhAXFKu95MTiCHJKn4MMgfk2od2wW9s4,33291
|
|
254
254
|
phoenix/session/data_extractor.py,sha256=gkEM3WWZAlWGMfRgQopAQlid4cSi6GNco-sdrGir0qc,2788
|
|
255
255
|
phoenix/session/evaluation.py,sha256=3a33ilo-WU0F_Ze26lkCMMwni0GGceSXRRBLvP3CI1o,5352
|
|
256
256
|
phoenix/session/session.py,sha256=3DSpXj_mlRAKnb9aNUkDjph19SLHx32IJSKYcR3r7cw,26994
|
|
@@ -259,7 +259,7 @@ phoenix/trace/attributes.py,sha256=B_OrzVaxZwFkrAFXZyicYoIti1UdUysURsvUS2GyW1U,1
|
|
|
259
259
|
phoenix/trace/errors.py,sha256=wB1z8qdPckngdfU-TORToekvg3344oNFAA83_hC2yFY,180
|
|
260
260
|
phoenix/trace/evaluation_conventions.py,sha256=t8jydM3U0-T5YpiQKRJ3tWdWGlHtzKyttYdw-ddvPOk,1048
|
|
261
261
|
phoenix/trace/exporter.py,sha256=eAYemdvDCHMugDJiaR29BFFMTQBdf3oerdkz34Cl3hE,4736
|
|
262
|
-
phoenix/trace/fixtures.py,sha256=
|
|
262
|
+
phoenix/trace/fixtures.py,sha256=EHfqgvPoux6KkckX00WeG2Vhas8H5vqqFBMTztwgV-s,16857
|
|
263
263
|
phoenix/trace/otel.py,sha256=WA720jvRadiZBAKjsYoPyXzypHwbyEK2OZRVUwtbjB8,9976
|
|
264
264
|
phoenix/trace/projects.py,sha256=2BwlNjFE-uwpqYtCu5YyBiYZk9wRPpM13vh3-Cv7GkA,2157
|
|
265
265
|
phoenix/trace/schemas.py,sha256=HpWSyzec0yDHEQXEDuwyLbhpvKrqkGps8BJqGiIFj8Y,5978
|
|
@@ -271,7 +271,7 @@ phoenix/trace/utils.py,sha256=1SEQr37cdHOM0P3BdL1dszArj3Zm-VJQyb1BcJs_qO8,1833
|
|
|
271
271
|
phoenix/trace/dsl/README.md,sha256=ihmP9zGUC5V-TDbzKla76LuyDqPDQIBUH2BORwxNI68,2902
|
|
272
272
|
phoenix/trace/dsl/__init__.py,sha256=WIQIjJg362XD3s50OsPJJ0xbDsGp41bSv7vDllLrPuA,144
|
|
273
273
|
phoenix/trace/dsl/filter.py,sha256=9NwATCUOgJ4Pms8XsEcinROUuxZ9UW-ISV09o65Ms70,32600
|
|
274
|
-
phoenix/trace/dsl/helpers.py,sha256=
|
|
274
|
+
phoenix/trace/dsl/helpers.py,sha256=STQtbmF3yI97GM4yH_V--mrGe1JqldUJJc5LO1o4NWo,3919
|
|
275
275
|
phoenix/trace/dsl/query.py,sha256=W0t-tiXh2WIVb96lzFAGQOQ-U46uKux78d4KL3rW-PE,30316
|
|
276
276
|
phoenix/trace/langchain/__init__.py,sha256=F37GfD1pd5Kuw7R7iRUM1zXXpO8xEcycNZh5dwqBXNk,109
|
|
277
277
|
phoenix/trace/langchain/instrumentor.py,sha256=zdh9uZfG7HWna6Wug_agS7MxSbUlfV-nhf3jWFZm61U,1412
|
|
@@ -291,8 +291,8 @@ phoenix/utilities/logging.py,sha256=lDXd6EGaamBNcQxL4vP1au9-i_SXe0OraUDiJOcszSw,
|
|
|
291
291
|
phoenix/utilities/project.py,sha256=8IJuMM4yUMoooPi37sictGj8Etu9rGmq6RFtc9848cQ,436
|
|
292
292
|
phoenix/utilities/re.py,sha256=PDve_OLjRTM8yQQJHC8-n3HdIONi7aNils3ZKRZ5uBM,2045
|
|
293
293
|
phoenix/utilities/span_store.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
294
|
-
arize_phoenix-4.
|
|
295
|
-
arize_phoenix-4.
|
|
296
|
-
arize_phoenix-4.
|
|
297
|
-
arize_phoenix-4.
|
|
298
|
-
arize_phoenix-4.
|
|
294
|
+
arize_phoenix-4.27.0.dist-info/METADATA,sha256=P6671H1bFkfBHk7FF-1pEwq0_9AoN8zR0RRwVJRqZ2w,11936
|
|
295
|
+
arize_phoenix-4.27.0.dist-info/WHEEL,sha256=1yFddiXMmvYK7QYTqtRNtX66WJ0Mz8PYEiEUoOUUxRY,87
|
|
296
|
+
arize_phoenix-4.27.0.dist-info/licenses/IP_NOTICE,sha256=JBqyyCYYxGDfzQ0TtsQgjts41IJoa-hiwDrBjCb9gHM,469
|
|
297
|
+
arize_phoenix-4.27.0.dist-info/licenses/LICENSE,sha256=HFkW9REuMOkvKRACuwLPT0hRydHb3zNg-fdFt94td18,3794
|
|
298
|
+
arize_phoenix-4.27.0.dist-info/RECORD,,
|
phoenix/inferences/fixtures.py
CHANGED
|
@@ -422,7 +422,7 @@ def get_inferences(
|
|
|
422
422
|
Downloads primary and reference inferences for a fixture if they are not found
|
|
423
423
|
locally.
|
|
424
424
|
"""
|
|
425
|
-
fixture =
|
|
425
|
+
fixture = get_fixture_by_name(fixture_name=fixture_name)
|
|
426
426
|
if no_internet:
|
|
427
427
|
paths = {role: INFERENCES_DIR / path for role, path in fixture.paths()}
|
|
428
428
|
else:
|
|
@@ -436,9 +436,11 @@ def get_inferences(
|
|
|
436
436
|
if fixture.reference_file_name is not None:
|
|
437
437
|
reference_inferences = Inferences(
|
|
438
438
|
read_parquet(paths[InferencesRole.REFERENCE]),
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
439
|
+
(
|
|
440
|
+
fixture.reference_schema
|
|
441
|
+
if fixture.reference_schema is not None
|
|
442
|
+
else fixture.primary_schema
|
|
443
|
+
),
|
|
442
444
|
"training",
|
|
443
445
|
)
|
|
444
446
|
corpus_inferences = None
|
|
@@ -451,10 +453,14 @@ def get_inferences(
|
|
|
451
453
|
return primary_inferences, reference_inferences, corpus_inferences
|
|
452
454
|
|
|
453
455
|
|
|
454
|
-
def
|
|
456
|
+
def get_fixture_by_name(fixture_name: str) -> Fixture:
|
|
455
457
|
"""
|
|
456
|
-
Returns the fixture whose name matches the input name.
|
|
457
|
-
|
|
458
|
+
Returns the fixture whose name matches the input name.
|
|
459
|
+
|
|
460
|
+
Raises
|
|
461
|
+
------
|
|
462
|
+
ValueError
|
|
463
|
+
if the input fixture name does not match any known fixture names.
|
|
458
464
|
"""
|
|
459
465
|
if fixture_name not in NAME_TO_FIXTURE:
|
|
460
466
|
valid_fixture_names = ", ".join(NAME_TO_FIXTURE.keys())
|
|
@@ -496,7 +502,7 @@ def load_example(use_case: str) -> ExampleInferences:
|
|
|
496
502
|
reference).
|
|
497
503
|
|
|
498
504
|
"""
|
|
499
|
-
fixture =
|
|
505
|
+
fixture = get_fixture_by_name(use_case)
|
|
500
506
|
primary_inferences, reference_inferences, corpus_inferences = get_inferences(use_case)
|
|
501
507
|
print(f"📥 Loaded {use_case} example datasets.")
|
|
502
508
|
print("ℹ️ About this use-case:")
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
from datetime import datetime
|
|
2
2
|
from random import getrandbits
|
|
3
|
-
from typing import Any, Dict, Optional
|
|
3
|
+
from typing import Any, Dict, List, Optional
|
|
4
4
|
|
|
5
5
|
from fastapi import APIRouter, HTTPException
|
|
6
6
|
from pydantic import Field
|
|
@@ -252,3 +252,57 @@ async def get_experiment(request: Request, experiment_id: str) -> GetExperimentR
|
|
|
252
252
|
updated_at=experiment.updated_at,
|
|
253
253
|
)
|
|
254
254
|
)
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
class ListExperimentsResponseBody(ResponseBody[List[Experiment]]):
|
|
258
|
+
pass
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
@router.get(
|
|
262
|
+
"/datasets/{dataset_id}/experiments",
|
|
263
|
+
operation_id="listExperiments",
|
|
264
|
+
summary="List experiments by dataset",
|
|
265
|
+
response_description="Experiments retrieved successfully",
|
|
266
|
+
)
|
|
267
|
+
async def list_experiments(
|
|
268
|
+
request: Request,
|
|
269
|
+
dataset_id: str,
|
|
270
|
+
) -> ListExperimentsResponseBody:
|
|
271
|
+
dataset_gid = GlobalID.from_id(dataset_id)
|
|
272
|
+
try:
|
|
273
|
+
dataset_rowid = from_global_id_with_expected_type(dataset_gid, "Dataset")
|
|
274
|
+
except ValueError:
|
|
275
|
+
raise HTTPException(
|
|
276
|
+
detail=f"Dataset with ID {dataset_gid} does not exist",
|
|
277
|
+
status_code=HTTP_404_NOT_FOUND,
|
|
278
|
+
)
|
|
279
|
+
async with request.app.state.db() as session:
|
|
280
|
+
query = (
|
|
281
|
+
select(models.Experiment)
|
|
282
|
+
.where(models.Experiment.dataset_id == dataset_rowid)
|
|
283
|
+
.order_by(models.Experiment.id.desc())
|
|
284
|
+
)
|
|
285
|
+
|
|
286
|
+
result = await session.execute(query)
|
|
287
|
+
experiments = result.scalars().all()
|
|
288
|
+
|
|
289
|
+
if not experiments:
|
|
290
|
+
return ListExperimentsResponseBody(data=[])
|
|
291
|
+
|
|
292
|
+
data = [
|
|
293
|
+
Experiment(
|
|
294
|
+
id=str(GlobalID("Experiment", str(experiment.id))),
|
|
295
|
+
dataset_id=str(GlobalID("Dataset", str(experiment.dataset_id))),
|
|
296
|
+
dataset_version_id=str(
|
|
297
|
+
GlobalID("DatasetVersion", str(experiment.dataset_version_id))
|
|
298
|
+
),
|
|
299
|
+
repetitions=experiment.repetitions,
|
|
300
|
+
metadata=experiment.metadata_,
|
|
301
|
+
project_name=None,
|
|
302
|
+
created_at=experiment.created_at,
|
|
303
|
+
updated_at=experiment.updated_at,
|
|
304
|
+
)
|
|
305
|
+
for experiment in experiments
|
|
306
|
+
]
|
|
307
|
+
|
|
308
|
+
return ListExperimentsResponseBody(data=data)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
from typing import Any, Dict, Generic, List, Optional, TypedDict, Union
|
|
1
|
+
from typing import Any, Dict, Generic, List, Optional, TypedDict, TypeVar, Union
|
|
2
2
|
|
|
3
|
-
from typing_extensions import TypeAlias,
|
|
3
|
+
from typing_extensions import TypeAlias, assert_never
|
|
4
4
|
|
|
5
5
|
from .pydantic_compat import V1RoutesBaseModel
|
|
6
6
|
|
phoenix/server/app.py
CHANGED
|
@@ -2,6 +2,7 @@ import asyncio
|
|
|
2
2
|
import contextlib
|
|
3
3
|
import json
|
|
4
4
|
import logging
|
|
5
|
+
from datetime import datetime, timedelta, timezone
|
|
5
6
|
from functools import cached_property
|
|
6
7
|
from pathlib import Path
|
|
7
8
|
from types import MethodType
|
|
@@ -10,12 +11,14 @@ from typing import (
|
|
|
10
11
|
Any,
|
|
11
12
|
AsyncContextManager,
|
|
12
13
|
AsyncIterator,
|
|
14
|
+
Awaitable,
|
|
13
15
|
Callable,
|
|
14
16
|
Dict,
|
|
15
17
|
Iterable,
|
|
16
18
|
List,
|
|
17
19
|
NamedTuple,
|
|
18
20
|
Optional,
|
|
21
|
+
Set,
|
|
19
22
|
Tuple,
|
|
20
23
|
Union,
|
|
21
24
|
cast,
|
|
@@ -26,11 +29,8 @@ from fastapi import APIRouter, FastAPI
|
|
|
26
29
|
from fastapi.middleware.gzip import GZipMiddleware
|
|
27
30
|
from fastapi.responses import FileResponse
|
|
28
31
|
from fastapi.utils import is_body_allowed_for_status_code
|
|
29
|
-
from sqlalchemy
|
|
30
|
-
|
|
31
|
-
AsyncSession,
|
|
32
|
-
async_sessionmaker,
|
|
33
|
-
)
|
|
32
|
+
from sqlalchemy import select
|
|
33
|
+
from sqlalchemy.ext.asyncio import AsyncEngine, AsyncSession, async_sessionmaker
|
|
34
34
|
from starlette.datastructures import State as StarletteState
|
|
35
35
|
from starlette.exceptions import HTTPException
|
|
36
36
|
from starlette.middleware import Middleware
|
|
@@ -46,12 +46,9 @@ from typing_extensions import TypeAlias
|
|
|
46
46
|
|
|
47
47
|
import phoenix
|
|
48
48
|
import phoenix.trace.v1 as pb
|
|
49
|
-
from phoenix.config import
|
|
50
|
-
DEFAULT_PROJECT_NAME,
|
|
51
|
-
SERVER_DIR,
|
|
52
|
-
server_instrumentation_is_enabled,
|
|
53
|
-
)
|
|
49
|
+
from phoenix.config import DEFAULT_PROJECT_NAME, SERVER_DIR, server_instrumentation_is_enabled
|
|
54
50
|
from phoenix.core.model_schema import Model
|
|
51
|
+
from phoenix.db import models
|
|
55
52
|
from phoenix.db.bulk_inserter import BulkInserter
|
|
56
53
|
from phoenix.db.engines import create_engine
|
|
57
54
|
from phoenix.db.helpers import SupportedSQLDialect
|
|
@@ -93,9 +90,17 @@ from phoenix.server.telemetry import initialize_opentelemetry_tracer_provider
|
|
|
93
90
|
from phoenix.server.types import (
|
|
94
91
|
CanGetLastUpdatedAt,
|
|
95
92
|
CanPutItem,
|
|
93
|
+
DaemonTask,
|
|
96
94
|
DbSessionFactory,
|
|
97
95
|
LastUpdatedAt,
|
|
98
96
|
)
|
|
97
|
+
from phoenix.trace.fixtures import (
|
|
98
|
+
get_evals_from_fixture,
|
|
99
|
+
get_trace_fixture_by_name,
|
|
100
|
+
load_example_traces,
|
|
101
|
+
reset_fixture_span_ids_and_timestamps,
|
|
102
|
+
)
|
|
103
|
+
from phoenix.trace.otel import decode_otlp_span, encode_span_to_otlp
|
|
99
104
|
from phoenix.trace.schemas import Span
|
|
100
105
|
from phoenix.utilities.client import PHOENIX_SERVER_VERSION_HEADER
|
|
101
106
|
|
|
@@ -103,12 +108,23 @@ if TYPE_CHECKING:
|
|
|
103
108
|
from opentelemetry.trace import TracerProvider
|
|
104
109
|
|
|
105
110
|
logger = logging.getLogger(__name__)
|
|
111
|
+
logger.setLevel(logging.INFO)
|
|
106
112
|
logger.addHandler(logging.NullHandler())
|
|
107
113
|
|
|
108
114
|
router = APIRouter(include_in_schema=False)
|
|
109
115
|
|
|
110
116
|
templates = Jinja2Templates(directory=SERVER_DIR / "templates")
|
|
111
117
|
|
|
118
|
+
"""
|
|
119
|
+
Threshold (in minutes) to determine if database is booted up for the first time.
|
|
120
|
+
|
|
121
|
+
Used to assess whether the `default` project was created recently.
|
|
122
|
+
If so, demo data is automatically ingested upon initial boot up to populate the database.
|
|
123
|
+
"""
|
|
124
|
+
NEW_DB_AGE_THRESHOLD_MINUTES = 2
|
|
125
|
+
|
|
126
|
+
ProjectName: TypeAlias = str
|
|
127
|
+
|
|
112
128
|
|
|
113
129
|
class AppConfig(NamedTuple):
|
|
114
130
|
has_inferences: bool
|
|
@@ -226,9 +242,99 @@ def _db(engine: AsyncEngine) -> Callable[[], AsyncContextManager[AsyncSession]]:
|
|
|
226
242
|
return factory
|
|
227
243
|
|
|
228
244
|
|
|
245
|
+
class Scaffolder(DaemonTask):
|
|
246
|
+
def __init__(
|
|
247
|
+
self,
|
|
248
|
+
db: DbSessionFactory,
|
|
249
|
+
queue_span: Callable[[Span, ProjectName], Awaitable[None]],
|
|
250
|
+
queue_evaluation: Callable[[pb.Evaluation], Awaitable[None]],
|
|
251
|
+
tracing_fixture_names: Set[str] = set(),
|
|
252
|
+
force_fixture_ingestion: bool = False,
|
|
253
|
+
) -> None:
|
|
254
|
+
super().__init__()
|
|
255
|
+
self._db = db
|
|
256
|
+
self._queue_span = queue_span
|
|
257
|
+
self._queue_evaluation = queue_evaluation
|
|
258
|
+
self._tracing_fixtures = set(
|
|
259
|
+
get_trace_fixture_by_name(name) for name in tracing_fixture_names
|
|
260
|
+
)
|
|
261
|
+
self._force_fixture_ingestion = force_fixture_ingestion
|
|
262
|
+
|
|
263
|
+
async def __aenter__(self) -> None:
|
|
264
|
+
await self.start()
|
|
265
|
+
|
|
266
|
+
async def __aexit__(self, *args: Any, **kwargs: Any) -> None:
|
|
267
|
+
await self.stop()
|
|
268
|
+
|
|
269
|
+
async def _run(self) -> None:
|
|
270
|
+
"""
|
|
271
|
+
Main entry point for Scaffolder.
|
|
272
|
+
Determines whether to load fixtures and handles them.
|
|
273
|
+
"""
|
|
274
|
+
if await self._should_load_fixtures():
|
|
275
|
+
logger.info("Loading trace fixtures.")
|
|
276
|
+
await self._handle_tracing_fixtures()
|
|
277
|
+
logger.info("Finished loading fixtures.")
|
|
278
|
+
else:
|
|
279
|
+
logger.info("DB is not new, avoid loading demo fixtures.")
|
|
280
|
+
|
|
281
|
+
async def _should_load_fixtures(self) -> bool:
|
|
282
|
+
if self._force_fixture_ingestion:
|
|
283
|
+
return True
|
|
284
|
+
|
|
285
|
+
async with self._db() as session:
|
|
286
|
+
created_at = await session.scalar(
|
|
287
|
+
select(models.Project.created_at).where(models.Project.name == "default")
|
|
288
|
+
)
|
|
289
|
+
if created_at is None:
|
|
290
|
+
return False
|
|
291
|
+
|
|
292
|
+
is_new_db = datetime.now(timezone.utc) - created_at < timedelta(
|
|
293
|
+
minutes=NEW_DB_AGE_THRESHOLD_MINUTES
|
|
294
|
+
)
|
|
295
|
+
return is_new_db
|
|
296
|
+
|
|
297
|
+
async def _handle_tracing_fixtures(self) -> None:
|
|
298
|
+
"""
|
|
299
|
+
Main handler for processing trace fixtures. Process each fixture by
|
|
300
|
+
loading its trace dataframe, gettting and processings its
|
|
301
|
+
spans and evals, and queuing.
|
|
302
|
+
"""
|
|
303
|
+
loop = asyncio.get_running_loop()
|
|
304
|
+
for fixture in self._tracing_fixtures:
|
|
305
|
+
try:
|
|
306
|
+
trace_ds = await loop.run_in_executor(None, load_example_traces, fixture.name)
|
|
307
|
+
|
|
308
|
+
fixture_spans, fixture_evals = await loop.run_in_executor(
|
|
309
|
+
None,
|
|
310
|
+
reset_fixture_span_ids_and_timestamps,
|
|
311
|
+
(
|
|
312
|
+
# Apply `encode` here because legacy jsonl files contains UUIDs as strings.
|
|
313
|
+
# `encode` removes the hyphens in the UUIDs.
|
|
314
|
+
decode_otlp_span(encode_span_to_otlp(span))
|
|
315
|
+
for span in trace_ds.to_spans()
|
|
316
|
+
),
|
|
317
|
+
get_evals_from_fixture(fixture.name),
|
|
318
|
+
)
|
|
319
|
+
|
|
320
|
+
project_name = fixture.project_name or fixture.name
|
|
321
|
+
logger.info(f"Loading '{project_name}' fixtures...")
|
|
322
|
+
for span in fixture_spans:
|
|
323
|
+
await self._queue_span(span, project_name)
|
|
324
|
+
for evaluation in fixture_evals:
|
|
325
|
+
await self._queue_evaluation(evaluation)
|
|
326
|
+
|
|
327
|
+
except FileNotFoundError:
|
|
328
|
+
logger.warning(f"Fixture file not found for '{fixture.name}'")
|
|
329
|
+
except ValueError as e:
|
|
330
|
+
logger.error(f"Error processing fixture '{fixture.name}': {e}")
|
|
331
|
+
except Exception as e:
|
|
332
|
+
logger.error(f"Unexpected error processing fixture '{fixture.name}': {e}")
|
|
333
|
+
|
|
334
|
+
|
|
229
335
|
def _lifespan(
|
|
230
336
|
*,
|
|
231
|
-
|
|
337
|
+
db: DbSessionFactory,
|
|
232
338
|
bulk_inserter: BulkInserter,
|
|
233
339
|
dml_event_handler: DmlEventHandler,
|
|
234
340
|
tracer_provider: Optional["TracerProvider"] = None,
|
|
@@ -236,11 +342,13 @@ def _lifespan(
|
|
|
236
342
|
startup_callbacks: Iterable[Callable[[], None]] = (),
|
|
237
343
|
shutdown_callbacks: Iterable[Callable[[], None]] = (),
|
|
238
344
|
read_only: bool = False,
|
|
345
|
+
tracing_fixture_names: Set[str] = set(),
|
|
346
|
+
force_fixture_ingestion: bool = False,
|
|
239
347
|
) -> StatefulLifespan[FastAPI]:
|
|
240
348
|
@contextlib.asynccontextmanager
|
|
241
349
|
async def lifespan(_: FastAPI) -> AsyncIterator[Dict[str, Any]]:
|
|
242
350
|
global DB_MUTEX
|
|
243
|
-
DB_MUTEX = asyncio.Lock() if dialect is SupportedSQLDialect.SQLITE else None
|
|
351
|
+
DB_MUTEX = asyncio.Lock() if db.dialect is SupportedSQLDialect.SQLITE else None
|
|
244
352
|
async with bulk_inserter as (
|
|
245
353
|
enqueue,
|
|
246
354
|
queue_span,
|
|
@@ -251,7 +359,13 @@ def _lifespan(
|
|
|
251
359
|
disabled=read_only,
|
|
252
360
|
tracer_provider=tracer_provider,
|
|
253
361
|
enable_prometheus=enable_prometheus,
|
|
254
|
-
), dml_event_handler
|
|
362
|
+
), dml_event_handler, Scaffolder(
|
|
363
|
+
db=db,
|
|
364
|
+
queue_span=queue_span,
|
|
365
|
+
queue_evaluation=queue_evaluation,
|
|
366
|
+
tracing_fixture_names=tracing_fixture_names,
|
|
367
|
+
force_fixture_ingestion=force_fixture_ingestion,
|
|
368
|
+
):
|
|
255
369
|
for callback in startup_callbacks:
|
|
256
370
|
callback()
|
|
257
371
|
yield {
|
|
@@ -317,17 +431,19 @@ def create_graphql_router(
|
|
|
317
431
|
dataset_example_spans=DatasetExampleSpansDataLoader(db),
|
|
318
432
|
document_evaluation_summaries=DocumentEvaluationSummaryDataLoader(
|
|
319
433
|
db,
|
|
320
|
-
cache_map=
|
|
321
|
-
|
|
322
|
-
|
|
434
|
+
cache_map=(
|
|
435
|
+
cache_for_dataloaders.document_evaluation_summary
|
|
436
|
+
if cache_for_dataloaders
|
|
437
|
+
else None
|
|
438
|
+
),
|
|
323
439
|
),
|
|
324
440
|
document_evaluations=DocumentEvaluationsDataLoader(db),
|
|
325
441
|
document_retrieval_metrics=DocumentRetrievalMetricsDataLoader(db),
|
|
326
442
|
annotation_summaries=AnnotationSummaryDataLoader(
|
|
327
443
|
db,
|
|
328
|
-
cache_map=
|
|
329
|
-
|
|
330
|
-
|
|
444
|
+
cache_map=(
|
|
445
|
+
cache_for_dataloaders.annotation_summary if cache_for_dataloaders else None
|
|
446
|
+
),
|
|
331
447
|
),
|
|
332
448
|
experiment_annotation_summaries=ExperimentAnnotationSummaryDataLoader(db),
|
|
333
449
|
experiment_error_rates=ExperimentErrorRatesDataLoader(db),
|
|
@@ -335,15 +451,17 @@ def create_graphql_router(
|
|
|
335
451
|
experiment_sequence_number=ExperimentSequenceNumberDataLoader(db),
|
|
336
452
|
latency_ms_quantile=LatencyMsQuantileDataLoader(
|
|
337
453
|
db,
|
|
338
|
-
cache_map=
|
|
339
|
-
|
|
340
|
-
|
|
454
|
+
cache_map=(
|
|
455
|
+
cache_for_dataloaders.latency_ms_quantile if cache_for_dataloaders else None
|
|
456
|
+
),
|
|
341
457
|
),
|
|
342
458
|
min_start_or_max_end_times=MinStartOrMaxEndTimeDataLoader(
|
|
343
459
|
db,
|
|
344
|
-
cache_map=
|
|
345
|
-
|
|
346
|
-
|
|
460
|
+
cache_map=(
|
|
461
|
+
cache_for_dataloaders.min_start_or_max_end_time
|
|
462
|
+
if cache_for_dataloaders
|
|
463
|
+
else None
|
|
464
|
+
),
|
|
347
465
|
),
|
|
348
466
|
record_counts=RecordCountDataLoader(
|
|
349
467
|
db,
|
|
@@ -438,6 +556,8 @@ def create_app(
|
|
|
438
556
|
startup_callbacks: Iterable[Callable[[], None]] = (),
|
|
439
557
|
shutdown_callbacks: Iterable[Callable[[], None]] = (),
|
|
440
558
|
secret: Optional[str] = None,
|
|
559
|
+
tracing_fixture_names: Set[str] = set(),
|
|
560
|
+
force_fixture_ingestion: bool = False,
|
|
441
561
|
) -> FastAPI:
|
|
442
562
|
startup_callbacks_list: List[Callable[[], None]] = list(startup_callbacks)
|
|
443
563
|
shutdown_callbacks_list: List[Callable[[], None]] = list(shutdown_callbacks)
|
|
@@ -510,11 +630,12 @@ def create_app(
|
|
|
510
630
|
prometheus_middlewares = [Middleware(PrometheusMiddleware)]
|
|
511
631
|
else:
|
|
512
632
|
prometheus_middlewares = []
|
|
633
|
+
|
|
513
634
|
app = FastAPI(
|
|
514
635
|
title="Arize-Phoenix REST API",
|
|
515
636
|
version=REST_API_VERSION,
|
|
516
637
|
lifespan=_lifespan(
|
|
517
|
-
|
|
638
|
+
db=db,
|
|
518
639
|
read_only=read_only,
|
|
519
640
|
bulk_inserter=bulk_inserter,
|
|
520
641
|
dml_event_handler=dml_event_handler,
|
|
@@ -522,6 +643,8 @@ def create_app(
|
|
|
522
643
|
enable_prometheus=enable_prometheus,
|
|
523
644
|
shutdown_callbacks=shutdown_callbacks_list,
|
|
524
645
|
startup_callbacks=startup_callbacks_list,
|
|
646
|
+
tracing_fixture_names=tracing_fixture_names,
|
|
647
|
+
force_fixture_ingestion=force_fixture_ingestion,
|
|
525
648
|
),
|
|
526
649
|
middleware=[
|
|
527
650
|
Middleware(HeadersMiddleware),
|