arize-phoenix 8.19.1__py3-none-any.whl → 8.21.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-8.19.1.dist-info → arize_phoenix-8.21.0.dist-info}/METADATA +3 -3
- {arize_phoenix-8.19.1.dist-info → arize_phoenix-8.21.0.dist-info}/RECORD +19 -19
- phoenix/config.py +133 -32
- phoenix/server/api/types/Project.py +19 -16
- phoenix/server/static/.vite/manifest.json +40 -40
- phoenix/server/static/assets/{components-C28SkolB.js → components-BAc4OPED.js} +411 -272
- phoenix/server/static/assets/{index-WrtAISug.js → index-Du53xkjY.js} +7 -2
- phoenix/server/static/assets/{pages-eLqonMjF.js → pages-Dz-gbBPF.js} +338 -331
- phoenix/server/static/assets/{vendor-CwC1K6mo.js → vendor-CEisxXSv.js} +161 -161
- phoenix/server/static/assets/{vendor-arizeai-B7-2AIAM.js → vendor-arizeai-BCTsSnvS.js} +1 -1
- phoenix/server/static/assets/{vendor-codemirror-D3xhkqGE.js → vendor-codemirror-DIWnRs_7.js} +1 -1
- phoenix/server/static/assets/{vendor-recharts-B_VgO7bY.js → vendor-recharts-Bame54mG.js} +1 -1
- phoenix/server/static/assets/{vendor-shiki-CK18CSqG.js → vendor-shiki-Cc73E4D-.js} +1 -1
- phoenix/session/session.py +2 -2
- phoenix/version.py +1 -1
- {arize_phoenix-8.19.1.dist-info → arize_phoenix-8.21.0.dist-info}/WHEEL +0 -0
- {arize_phoenix-8.19.1.dist-info → arize_phoenix-8.21.0.dist-info}/entry_points.txt +0 -0
- {arize_phoenix-8.19.1.dist-info → arize_phoenix-8.21.0.dist-info}/licenses/IP_NOTICE +0 -0
- {arize_phoenix-8.19.1.dist-info → arize_phoenix-8.21.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.
|
|
3
|
+
Version: 8.21.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
|
|
@@ -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.262.
|
|
74
|
+
Requires-Dist: strawberry-graphql[opentelemetry]==0.262.5; 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.262.
|
|
105
|
+
Requires-Dist: strawberry-graphql[debug-server,opentelemetry]==0.262.5; 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'
|
|
@@ -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=
|
|
3
|
+
phoenix/config.py,sha256=dSUZUhHVkfMP5yNs_fyvM96ZcZvY7KlyrDKkVflS9aM,35855
|
|
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=x87BX7hWGQQZbrW_vrYqFR_izCGfO9gFc--JXUG4Tdk,754
|
|
9
|
-
phoenix/version.py,sha256=
|
|
9
|
+
phoenix/version.py,sha256=IcFew5OfUbzsX2QRcX00Ngf0cInzeuF_IhyKYRXmTmI,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=qBFraOtmwCCnWJltKNP18DDG0mULXigytlFsa6YOz6k,4837
|
|
@@ -267,7 +267,7 @@ phoenix/server/api/types/MimeType.py,sha256=Zpi6zCalkSFgsvhzvOs-O1gYA04usAi9H__Q
|
|
|
267
267
|
phoenix/server/api/types/Model.py,sha256=8UIFqMe1q-2ufBNg-gxHusV8wM1h-KbfLUeJjyVcMvQ,8081
|
|
268
268
|
phoenix/server/api/types/NumericRange.py,sha256=afEjgF97Go_OvmjMggbPBt-zGM8IONewAyEiKEHRds0,192
|
|
269
269
|
phoenix/server/api/types/PerformanceMetric.py,sha256=KFkmJDqP43eDUtARQOUqR7NYcxvL6Vh2uisHWU6H3ko,387
|
|
270
|
-
phoenix/server/api/types/Project.py,sha256=
|
|
270
|
+
phoenix/server/api/types/Project.py,sha256=d9bEG5nejolqFmgpNF-LtXJK8kFxVo_5qmHRH3YAf-8,20639
|
|
271
271
|
phoenix/server/api/types/ProjectSession.py,sha256=fyfVtpUpFOTnBx8DFnH3dr7WXAssN0ooAgrQSSi7kEI,4677
|
|
272
272
|
phoenix/server/api/types/Prompt.py,sha256=ccP4eq1e38xbF0afclGWLOuDpBVpNbJ3AOSRClF8yFQ,4955
|
|
273
273
|
phoenix/server/api/types/PromptLabel.py,sha256=g3IDSPYRZwb0qpMAk93R6J96jgYULUYGOciTnpeh3sI,1321
|
|
@@ -314,16 +314,16 @@ phoenix/server/static/apple-touch-icon-76x76.png,sha256=CT_xT12I0u2i0WU8JzBZBuOQ
|
|
|
314
314
|
phoenix/server/static/apple-touch-icon.png,sha256=fOfpjqGpWYbJ0eAurKsyoZP1EAs6ZVooBJ_SGk2ZkDs,3801
|
|
315
315
|
phoenix/server/static/favicon.ico,sha256=bY0vvCKRftemZfPShwZtE93DiiQdaYaozkPGwNFr6H8,34494
|
|
316
316
|
phoenix/server/static/modernizr.js,sha256=mvK-XtkNqjOral-QvzoqsyOMECXIMu5BQwSVN_wcU9c,2564
|
|
317
|
-
phoenix/server/static/.vite/manifest.json,sha256=
|
|
318
|
-
phoenix/server/static/assets/components-
|
|
319
|
-
phoenix/server/static/assets/index-
|
|
320
|
-
phoenix/server/static/assets/pages-
|
|
317
|
+
phoenix/server/static/.vite/manifest.json,sha256=aDi-Uk9t65RmDnEk-9GWEFulGc6ogxP_FMVJrkIPZvI,2165
|
|
318
|
+
phoenix/server/static/assets/components-BAc4OPED.js,sha256=VL_AageEdDLGbyR_LF0v8sfcbYtLllsbqrOflzE2OLo,455490
|
|
319
|
+
phoenix/server/static/assets/index-Du53xkjY.js,sha256=kqiASYQH1J0Zg5c2Jz5XL4UhDOW8pqhhAfJITONFot8,60397
|
|
320
|
+
phoenix/server/static/assets/pages-Dz-gbBPF.js,sha256=gBI9zPk6YJhSia8YDopK6JgtsxwqVeheWpLY76N43gw,862644
|
|
321
|
+
phoenix/server/static/assets/vendor-CEisxXSv.js,sha256=TS8_RMXjkexQB-ENo06BZS77xnf8TMf68k03NhEZyVE,2510162
|
|
321
322
|
phoenix/server/static/assets/vendor-Cg6lcjUC.css,sha256=nZrkr0u6NNElFGvpWHk9GTHeGoibCXCli1bE7mXZGZg,1816
|
|
322
|
-
phoenix/server/static/assets/vendor-
|
|
323
|
-
phoenix/server/static/assets/vendor-
|
|
324
|
-
phoenix/server/static/assets/vendor-
|
|
325
|
-
phoenix/server/static/assets/vendor-
|
|
326
|
-
phoenix/server/static/assets/vendor-shiki-CK18CSqG.js,sha256=jEit22_c1RfyEMpL4lSaxSFdAo14sDGi6OluXw8dzsA,8980312
|
|
323
|
+
phoenix/server/static/assets/vendor-arizeai-BCTsSnvS.js,sha256=bOMRhNcnPkdTP12LEkBKJmvRBJwA5BKMxZvpkAX_irg,193248
|
|
324
|
+
phoenix/server/static/assets/vendor-codemirror-DIWnRs_7.js,sha256=JkqAk2m_o589elkpaXuwlOaq0lfDrLK09-6D9eBBcKQ,781264
|
|
325
|
+
phoenix/server/static/assets/vendor-recharts-Bame54mG.js,sha256=fhgG0ZqpDuiVwh71xq7Xb9vHxjnhxHOhp179cTnNwpM,282109
|
|
326
|
+
phoenix/server/static/assets/vendor-shiki-Cc73E4D-.js,sha256=YEuUQiR8772y7nz6Eu1rdhl5evg5XJkRGxuwKcxCNqk,8980312
|
|
327
327
|
phoenix/server/static/assets/vendor-three-C5WAXd5r.js,sha256=ELkg06u70N7h8oFmvqdoHyPuUf9VgGEWeT4LKFx4VWo,620975
|
|
328
328
|
phoenix/server/templates/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
329
329
|
phoenix/server/templates/index.html,sha256=e8_jdi7Eo19SK7DI_gglkTW094D17E0VAegoMmmmvIc,4330
|
|
@@ -331,7 +331,7 @@ phoenix/session/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
|
331
331
|
phoenix/session/client.py,sha256=nXSn2Zmf9wTxgSe11Y9kKSLClkG8pNEAJgfEmy4mPm8,35234
|
|
332
332
|
phoenix/session/data_extractor.py,sha256=Y0RzYFaNy9fQj8PEIeQ76TBZ90_E1FW7bXu3K5x0EZY,2782
|
|
333
333
|
phoenix/session/evaluation.py,sha256=Q3fOMNELvqkk-b6a6PKc8pDJdsNQ0ZbTpseUSA2NKqs,5300
|
|
334
|
-
phoenix/session/session.py,sha256=
|
|
334
|
+
phoenix/session/session.py,sha256=acCqJlPeczUDQcowXGvb8vcR1GcWqOxAn7oRBK3IL7U,27560
|
|
335
335
|
phoenix/trace/__init__.py,sha256=ujk_uYjM8gmm-YqnyXxF-kekfwid0bcaPMTtNNcaw6U,407
|
|
336
336
|
phoenix/trace/attributes.py,sha256=hyEKYZWPCP4NRmW7VmiC2voa3TH7FYKUBR9DYiVfXlw,12627
|
|
337
337
|
phoenix/trace/errors.py,sha256=wB1z8qdPckngdfU-TORToekvg3344oNFAA83_hC2yFY,180
|
|
@@ -364,9 +364,9 @@ phoenix/utilities/project.py,sha256=auVpARXkDb-JgeX5f2aStyFIkeKvGwN9l7qrFeJMVxI,
|
|
|
364
364
|
phoenix/utilities/re.py,sha256=6YyUWIkv0zc2SigsxfOWIHzdpjKA_TZo2iqKq7zJKvw,2081
|
|
365
365
|
phoenix/utilities/span_store.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
366
366
|
phoenix/utilities/template_formatters.py,sha256=gh9PJD6WEGw7TEYXfSst1UR4pWWwmjxMLrDVQ_CkpkQ,2779
|
|
367
|
-
arize_phoenix-8.
|
|
368
|
-
arize_phoenix-8.
|
|
369
|
-
arize_phoenix-8.
|
|
370
|
-
arize_phoenix-8.
|
|
371
|
-
arize_phoenix-8.
|
|
372
|
-
arize_phoenix-8.
|
|
367
|
+
arize_phoenix-8.21.0.dist-info/METADATA,sha256=0DkKUK_8BHpdYpiMIfmCLhA8bRmpQeHmmh0PgqoMmUU,21378
|
|
368
|
+
arize_phoenix-8.21.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
369
|
+
arize_phoenix-8.21.0.dist-info/entry_points.txt,sha256=Pgpn8Upxx9P8z8joPXZWl2LlnAlGc3gcQoVchb06X1Q,94
|
|
370
|
+
arize_phoenix-8.21.0.dist-info/licenses/IP_NOTICE,sha256=JBqyyCYYxGDfzQ0TtsQgjts41IJoa-hiwDrBjCb9gHM,469
|
|
371
|
+
arize_phoenix-8.21.0.dist-info/licenses/LICENSE,sha256=HFkW9REuMOkvKRACuwLPT0hRydHb3zNg-fdFt94td18,3794
|
|
372
|
+
arize_phoenix-8.21.0.dist-info/RECORD,,
|
phoenix/config.py
CHANGED
|
@@ -7,9 +7,10 @@ from datetime import timedelta
|
|
|
7
7
|
from enum import Enum
|
|
8
8
|
from importlib.metadata import version
|
|
9
9
|
from pathlib import Path
|
|
10
|
-
from typing import Optional, cast, overload
|
|
10
|
+
from typing import Any, Optional, Union, cast, overload
|
|
11
11
|
from urllib.parse import quote_plus, urlparse
|
|
12
12
|
|
|
13
|
+
import wrapt
|
|
13
14
|
from email_validator import EmailNotValidError, validate_email
|
|
14
15
|
|
|
15
16
|
from phoenix.utilities.logging import log_a_list
|
|
@@ -144,7 +145,7 @@ ENV_PHOENIX_DISABLE_RATE_LIMIT = "PHOENIX_DISABLE_RATE_LIMIT"
|
|
|
144
145
|
ENV_PHOENIX_SECRET = "PHOENIX_SECRET"
|
|
145
146
|
ENV_PHOENIX_DEFAULT_ADMIN_INITIAL_PASSWORD = "PHOENIX_DEFAULT_ADMIN_INITIAL_PASSWORD"
|
|
146
147
|
"""
|
|
147
|
-
The initial password for the default admin account, which defaults to
|
|
148
|
+
The initial password for the default admin account, which defaults to 'admin' if not
|
|
148
149
|
explicitly set. Note that changing this value will have no effect if the default admin
|
|
149
150
|
record already exists in the database. In such cases, the default admin password must
|
|
150
151
|
be updated manually in the application.
|
|
@@ -615,17 +616,138 @@ GENERATED_INFERENCES_NAME_PREFIX = "phoenix_inferences_"
|
|
|
615
616
|
WORKING_DIR = get_working_dir()
|
|
616
617
|
"""The work directory for saving, loading, and exporting data."""
|
|
617
618
|
|
|
618
|
-
ROOT_DIR = WORKING_DIR
|
|
619
|
-
EXPORT_DIR = ROOT_DIR / "exports"
|
|
620
|
-
INFERENCES_DIR = ROOT_DIR / "inferences"
|
|
621
|
-
TRACE_DATASETS_DIR = ROOT_DIR / "trace_datasets"
|
|
622
619
|
|
|
620
|
+
class DirectoryError(Exception):
|
|
621
|
+
def __init__(self, message: Optional[str] = None) -> None:
|
|
622
|
+
if message is None:
|
|
623
|
+
message = (
|
|
624
|
+
"Local storage is not configured. Please set the "
|
|
625
|
+
"PHOENIX_WORKING_DIR environment variable to fix this."
|
|
626
|
+
)
|
|
627
|
+
super().__init__(message)
|
|
628
|
+
|
|
629
|
+
|
|
630
|
+
def get_env_postgres_connection_str() -> Optional[str]:
|
|
631
|
+
pg_user = os.getenv(ENV_PHOENIX_POSTGRES_USER)
|
|
632
|
+
pg_password = os.getenv(ENV_PHOENIX_POSTGRES_PASSWORD)
|
|
633
|
+
pg_host = os.getenv(ENV_PHOENIX_POSTGRES_HOST)
|
|
634
|
+
pg_port = os.getenv(ENV_PHOENIX_POSTGRES_PORT)
|
|
635
|
+
pg_db = os.getenv(ENV_PHOENIX_POSTGRES_DB)
|
|
636
|
+
|
|
637
|
+
if pg_host and ":" in pg_host:
|
|
638
|
+
pg_host, parsed_port = pg_host.split(":")
|
|
639
|
+
pg_port = pg_port or parsed_port # use the explicitly set port if provided
|
|
640
|
+
|
|
641
|
+
if pg_host and pg_user and pg_password:
|
|
642
|
+
encoded_password = quote_plus(pg_password)
|
|
643
|
+
connection_str = f"postgresql://{pg_user}:{encoded_password}@{pg_host}"
|
|
644
|
+
if pg_port:
|
|
645
|
+
connection_str = f"{connection_str}:{pg_port}"
|
|
646
|
+
if pg_db:
|
|
647
|
+
connection_str = f"{connection_str}/{pg_db}"
|
|
648
|
+
|
|
649
|
+
return connection_str
|
|
650
|
+
return None
|
|
651
|
+
|
|
652
|
+
|
|
653
|
+
def _no_local_storage() -> bool:
|
|
654
|
+
"""
|
|
655
|
+
Check if we're using a postgres database by checking if postgres connection string is set
|
|
656
|
+
and a working directory was not explicitly set.
|
|
657
|
+
"""
|
|
658
|
+
return get_env_postgres_connection_str() is not None and getenv(ENV_PHOENIX_WORKING_DIR) is None
|
|
659
|
+
|
|
660
|
+
|
|
661
|
+
class RestrictedPath(wrapt.ObjectProxy): # type: ignore[misc]
|
|
662
|
+
"""
|
|
663
|
+
This wraps pathlib.Path and will raise a DirectoryError if no local storage is configured.
|
|
664
|
+
|
|
665
|
+
Users can forego configuring a working directory if they are using a postgres database. If this
|
|
666
|
+
condition is met, the working directory path wrapped by this object will raise an error when
|
|
667
|
+
accessed in any way.
|
|
668
|
+
"""
|
|
669
|
+
|
|
670
|
+
def __init__(self, wrapped: Union[str, Path]) -> None:
|
|
671
|
+
super().__init__(Path(wrapped))
|
|
672
|
+
self.__wrapped__: Path
|
|
673
|
+
|
|
674
|
+
def _check_forbidden(self) -> None:
|
|
675
|
+
if _no_local_storage():
|
|
676
|
+
raise DirectoryError()
|
|
677
|
+
return
|
|
678
|
+
|
|
679
|
+
def __getattr__(self, name: str) -> Any:
|
|
680
|
+
attr = getattr(self.__wrapped__, name)
|
|
681
|
+
|
|
682
|
+
if callable(attr):
|
|
683
|
+
|
|
684
|
+
def wrapped_attr(*args: Any, **kwargs: Any) -> Any:
|
|
685
|
+
result = attr(*args, **kwargs)
|
|
686
|
+
if isinstance(result, Path):
|
|
687
|
+
self._check_forbidden()
|
|
688
|
+
return RestrictedPath(result)
|
|
689
|
+
elif hasattr(result, "__iter__") and not isinstance(result, (str, bytes)):
|
|
690
|
+
return (RestrictedPath(p) if isinstance(p, Path) else p for p in result)
|
|
691
|
+
return result
|
|
623
692
|
|
|
624
|
-
|
|
693
|
+
return wrapped_attr
|
|
694
|
+
else:
|
|
695
|
+
if isinstance(attr, Path):
|
|
696
|
+
self._check_forbidden()
|
|
697
|
+
return RestrictedPath(attr)
|
|
698
|
+
return attr
|
|
699
|
+
|
|
700
|
+
def __str__(self) -> str:
|
|
701
|
+
self._check_forbidden()
|
|
702
|
+
return str(self.__wrapped__)
|
|
703
|
+
|
|
704
|
+
def __repr__(self) -> str:
|
|
705
|
+
return f"<RestrictedPath({repr(self.__wrapped__)})>"
|
|
706
|
+
|
|
707
|
+
def __fspath__(self) -> str:
|
|
708
|
+
self._check_forbidden()
|
|
709
|
+
return str(self.__wrapped__)
|
|
710
|
+
|
|
711
|
+
def __truediv__(self, other: Union[str, Path]) -> Path:
|
|
712
|
+
self._check_forbidden()
|
|
713
|
+
return self.__wrapped__ / other
|
|
714
|
+
|
|
715
|
+
def __itruediv__(self, other: Union[str, Path]) -> Path:
|
|
716
|
+
self.__wrapped__ /= other
|
|
717
|
+
self._check_forbidden()
|
|
718
|
+
return self.__wrapped__
|
|
719
|
+
|
|
720
|
+
def __eq__(self, other: object) -> bool:
|
|
721
|
+
if isinstance(other, RestrictedPath):
|
|
722
|
+
return bool(self.__wrapped__ == other.__wrapped__)
|
|
723
|
+
return bool(self.__wrapped__ == other)
|
|
724
|
+
|
|
725
|
+
def __hash__(self) -> int:
|
|
726
|
+
return hash(self.__wrapped__)
|
|
727
|
+
|
|
728
|
+
def __len__(self) -> int:
|
|
729
|
+
return len(self.__wrapped__.parts)
|
|
730
|
+
|
|
731
|
+
def __contains__(self, item: str) -> bool:
|
|
732
|
+
return item in self.__wrapped__.parts
|
|
733
|
+
|
|
734
|
+
|
|
735
|
+
ROOT_DIR = RestrictedPath(WORKING_DIR)
|
|
736
|
+
EXPORT_DIR = RestrictedPath(WORKING_DIR / "exports")
|
|
737
|
+
INFERENCES_DIR = RestrictedPath(WORKING_DIR / "inferences")
|
|
738
|
+
TRACE_DATASETS_DIR = RestrictedPath(WORKING_DIR / "trace_datasets")
|
|
739
|
+
|
|
740
|
+
|
|
741
|
+
def ensure_working_dir_if_needed() -> None:
|
|
625
742
|
"""
|
|
626
743
|
Ensure the working directory exists. This is needed because the working directory
|
|
627
744
|
must exist before certain operations can be performed.
|
|
745
|
+
|
|
746
|
+
This is bypassed if a postgres database is configured and a working directory is not set.
|
|
628
747
|
"""
|
|
748
|
+
if _no_local_storage():
|
|
749
|
+
pass
|
|
750
|
+
|
|
629
751
|
logger.info(f"📋 Ensuring phoenix working directory: {WORKING_DIR}")
|
|
630
752
|
try:
|
|
631
753
|
for path in (
|
|
@@ -644,8 +766,8 @@ def ensure_working_dir() -> None:
|
|
|
644
766
|
raise
|
|
645
767
|
|
|
646
768
|
|
|
647
|
-
# Invoke
|
|
648
|
-
|
|
769
|
+
# Invoke ensure_working_dir_if_needed() to ensure the working directory exists
|
|
770
|
+
ensure_working_dir_if_needed()
|
|
649
771
|
|
|
650
772
|
|
|
651
773
|
def get_exported_files(directory: Path) -> list[Path]:
|
|
@@ -662,6 +784,8 @@ def get_exported_files(directory: Path) -> list[Path]:
|
|
|
662
784
|
list: list[Path]
|
|
663
785
|
List of paths of the exported files.
|
|
664
786
|
"""
|
|
787
|
+
if _no_local_storage():
|
|
788
|
+
return [] # Do not attempt to access local storage
|
|
665
789
|
return list(directory.glob("*.parquet"))
|
|
666
790
|
|
|
667
791
|
|
|
@@ -734,29 +858,6 @@ def get_env_database_connection_str() -> str:
|
|
|
734
858
|
return f"sqlite:///{working_dir}/phoenix.db"
|
|
735
859
|
|
|
736
860
|
|
|
737
|
-
def get_env_postgres_connection_str() -> Optional[str]:
|
|
738
|
-
pg_user = os.getenv(ENV_PHOENIX_POSTGRES_USER)
|
|
739
|
-
pg_password = os.getenv(ENV_PHOENIX_POSTGRES_PASSWORD)
|
|
740
|
-
pg_host = os.getenv(ENV_PHOENIX_POSTGRES_HOST)
|
|
741
|
-
pg_port = os.getenv(ENV_PHOENIX_POSTGRES_PORT)
|
|
742
|
-
pg_db = os.getenv(ENV_PHOENIX_POSTGRES_DB)
|
|
743
|
-
|
|
744
|
-
if pg_host and ":" in pg_host:
|
|
745
|
-
pg_host, parsed_port = pg_host.split(":")
|
|
746
|
-
pg_port = pg_port or parsed_port # use the explicitly set port if provided
|
|
747
|
-
|
|
748
|
-
if pg_host and pg_user and pg_password:
|
|
749
|
-
encoded_password = quote_plus(pg_password)
|
|
750
|
-
connection_str = f"postgresql://{pg_user}:{encoded_password}@{pg_host}"
|
|
751
|
-
if pg_port:
|
|
752
|
-
connection_str = f"{connection_str}:{pg_port}"
|
|
753
|
-
if pg_db:
|
|
754
|
-
connection_str = f"{connection_str}/{pg_db}"
|
|
755
|
-
|
|
756
|
-
return connection_str
|
|
757
|
-
return None
|
|
758
|
-
|
|
759
|
-
|
|
760
861
|
def get_env_database_schema() -> Optional[str]:
|
|
761
862
|
if get_env_database_connection_str().startswith("sqlite"):
|
|
762
863
|
return None
|
|
@@ -236,21 +236,6 @@ class Project(Node):
|
|
|
236
236
|
stmt = stmt.where(time_range.start <= models.Span.start_time)
|
|
237
237
|
if time_range.end:
|
|
238
238
|
stmt = stmt.where(models.Span.start_time < time_range.end)
|
|
239
|
-
if root_spans_only:
|
|
240
|
-
# A root span is either a span with no parent_id or an orphan span
|
|
241
|
-
# (a span whose parent_id references a span that doesn't exist in the database)
|
|
242
|
-
if orphan_span_as_root_span:
|
|
243
|
-
# Include both types of root spans
|
|
244
|
-
parent_spans = select(models.Span.span_id).alias("parent_spans")
|
|
245
|
-
stmt = stmt.where(
|
|
246
|
-
~select(1).where(models.Span.parent_id == parent_spans.c.span_id).exists()
|
|
247
|
-
# Note: We avoid using an OR clause with Span.parent_id.is_(None) here
|
|
248
|
-
# because it significantly degraded PostgreSQL performance (>10x worse)
|
|
249
|
-
# during testing.
|
|
250
|
-
)
|
|
251
|
-
else:
|
|
252
|
-
# Only include explicit root spans (spans with parent_id = NULL)
|
|
253
|
-
stmt = stmt.where(models.Span.parent_id.is_(None))
|
|
254
239
|
if filter_condition:
|
|
255
240
|
span_filter = SpanFilter(condition=filter_condition)
|
|
256
241
|
stmt = span_filter(stmt)
|
|
@@ -274,11 +259,29 @@ class Project(Node):
|
|
|
274
259
|
)
|
|
275
260
|
else:
|
|
276
261
|
stmt = stmt.where(models.Span.id > cursor.rowid)
|
|
262
|
+
stmt = stmt.order_by(cursor_rowid_column)
|
|
263
|
+
if root_spans_only:
|
|
264
|
+
# A root span is either a span with no parent_id or an orphan span
|
|
265
|
+
# (a span whose parent_id references a span that doesn't exist in the database)
|
|
266
|
+
if orphan_span_as_root_span:
|
|
267
|
+
# Include both types of root spans
|
|
268
|
+
parent_spans = select(models.Span.span_id).alias("parent_spans")
|
|
269
|
+
candidate_spans = stmt.add_columns(models.Span.parent_id).cte("candidate_spans")
|
|
270
|
+
stmt = select(candidate_spans).where(
|
|
271
|
+
or_(
|
|
272
|
+
candidate_spans.c.parent_id.is_(None),
|
|
273
|
+
~select(1)
|
|
274
|
+
.where(candidate_spans.c.parent_id == parent_spans.c.span_id)
|
|
275
|
+
.exists(),
|
|
276
|
+
)
|
|
277
|
+
)
|
|
278
|
+
else:
|
|
279
|
+
# Only include explicit root spans (spans with parent_id = NULL)
|
|
280
|
+
stmt = stmt.where(models.Span.parent_id.is_(None))
|
|
277
281
|
if first:
|
|
278
282
|
stmt = stmt.limit(
|
|
279
283
|
first + 1 # overfetch by one to determine whether there's a next page
|
|
280
284
|
)
|
|
281
|
-
stmt = stmt.order_by(cursor_rowid_column)
|
|
282
285
|
cursors_and_nodes = []
|
|
283
286
|
async with info.context.db() as session:
|
|
284
287
|
span_records = await session.stream(stmt)
|
|
@@ -1,32 +1,28 @@
|
|
|
1
1
|
{
|
|
2
|
-
"_components-
|
|
3
|
-
"file": "assets/components-
|
|
2
|
+
"_components-BAc4OPED.js": {
|
|
3
|
+
"file": "assets/components-BAc4OPED.js",
|
|
4
4
|
"name": "components",
|
|
5
5
|
"imports": [
|
|
6
|
-
"_vendor-
|
|
7
|
-
"_pages-
|
|
8
|
-
"_vendor-arizeai-
|
|
9
|
-
"_vendor-codemirror-
|
|
6
|
+
"_vendor-CEisxXSv.js",
|
|
7
|
+
"_pages-Dz-gbBPF.js",
|
|
8
|
+
"_vendor-arizeai-BCTsSnvS.js",
|
|
9
|
+
"_vendor-codemirror-DIWnRs_7.js",
|
|
10
10
|
"_vendor-three-C5WAXd5r.js"
|
|
11
11
|
]
|
|
12
12
|
},
|
|
13
|
-
"_pages-
|
|
14
|
-
"file": "assets/pages-
|
|
13
|
+
"_pages-Dz-gbBPF.js": {
|
|
14
|
+
"file": "assets/pages-Dz-gbBPF.js",
|
|
15
15
|
"name": "pages",
|
|
16
16
|
"imports": [
|
|
17
|
-
"_vendor-
|
|
18
|
-
"_vendor-arizeai-
|
|
19
|
-
"_components-
|
|
20
|
-
"_vendor-codemirror-
|
|
21
|
-
"_vendor-recharts-
|
|
17
|
+
"_vendor-CEisxXSv.js",
|
|
18
|
+
"_vendor-arizeai-BCTsSnvS.js",
|
|
19
|
+
"_components-BAc4OPED.js",
|
|
20
|
+
"_vendor-codemirror-DIWnRs_7.js",
|
|
21
|
+
"_vendor-recharts-Bame54mG.js"
|
|
22
22
|
]
|
|
23
23
|
},
|
|
24
|
-
"_vendor-
|
|
25
|
-
"file": "assets/vendor-
|
|
26
|
-
"src": "_vendor-Cg6lcjUC.css"
|
|
27
|
-
},
|
|
28
|
-
"_vendor-CwC1K6mo.js": {
|
|
29
|
-
"file": "assets/vendor-CwC1K6mo.js",
|
|
24
|
+
"_vendor-CEisxXSv.js": {
|
|
25
|
+
"file": "assets/vendor-CEisxXSv.js",
|
|
30
26
|
"name": "vendor",
|
|
31
27
|
"imports": [
|
|
32
28
|
"_vendor-three-C5WAXd5r.js"
|
|
@@ -35,33 +31,37 @@
|
|
|
35
31
|
"assets/vendor-Cg6lcjUC.css"
|
|
36
32
|
]
|
|
37
33
|
},
|
|
38
|
-
"_vendor-
|
|
39
|
-
"file": "assets/vendor-
|
|
34
|
+
"_vendor-Cg6lcjUC.css": {
|
|
35
|
+
"file": "assets/vendor-Cg6lcjUC.css",
|
|
36
|
+
"src": "_vendor-Cg6lcjUC.css"
|
|
37
|
+
},
|
|
38
|
+
"_vendor-arizeai-BCTsSnvS.js": {
|
|
39
|
+
"file": "assets/vendor-arizeai-BCTsSnvS.js",
|
|
40
40
|
"name": "vendor-arizeai",
|
|
41
41
|
"imports": [
|
|
42
|
-
"_vendor-
|
|
42
|
+
"_vendor-CEisxXSv.js"
|
|
43
43
|
]
|
|
44
44
|
},
|
|
45
|
-
"_vendor-codemirror-
|
|
46
|
-
"file": "assets/vendor-codemirror-
|
|
45
|
+
"_vendor-codemirror-DIWnRs_7.js": {
|
|
46
|
+
"file": "assets/vendor-codemirror-DIWnRs_7.js",
|
|
47
47
|
"name": "vendor-codemirror",
|
|
48
48
|
"imports": [
|
|
49
|
-
"_vendor-
|
|
50
|
-
"_vendor-shiki-
|
|
49
|
+
"_vendor-CEisxXSv.js",
|
|
50
|
+
"_vendor-shiki-Cc73E4D-.js"
|
|
51
51
|
]
|
|
52
52
|
},
|
|
53
|
-
"_vendor-recharts-
|
|
54
|
-
"file": "assets/vendor-recharts-
|
|
53
|
+
"_vendor-recharts-Bame54mG.js": {
|
|
54
|
+
"file": "assets/vendor-recharts-Bame54mG.js",
|
|
55
55
|
"name": "vendor-recharts",
|
|
56
56
|
"imports": [
|
|
57
|
-
"_vendor-
|
|
57
|
+
"_vendor-CEisxXSv.js"
|
|
58
58
|
]
|
|
59
59
|
},
|
|
60
|
-
"_vendor-shiki-
|
|
61
|
-
"file": "assets/vendor-shiki-
|
|
60
|
+
"_vendor-shiki-Cc73E4D-.js": {
|
|
61
|
+
"file": "assets/vendor-shiki-Cc73E4D-.js",
|
|
62
62
|
"name": "vendor-shiki",
|
|
63
63
|
"imports": [
|
|
64
|
-
"_vendor-
|
|
64
|
+
"_vendor-CEisxXSv.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-
|
|
72
|
+
"file": "assets/index-Du53xkjY.js",
|
|
73
73
|
"name": "index",
|
|
74
74
|
"src": "index.tsx",
|
|
75
75
|
"isEntry": true,
|
|
76
76
|
"imports": [
|
|
77
|
-
"_vendor-
|
|
78
|
-
"_vendor-arizeai-
|
|
79
|
-
"_pages-
|
|
80
|
-
"_components-
|
|
77
|
+
"_vendor-CEisxXSv.js",
|
|
78
|
+
"_vendor-arizeai-BCTsSnvS.js",
|
|
79
|
+
"_pages-Dz-gbBPF.js",
|
|
80
|
+
"_components-BAc4OPED.js",
|
|
81
81
|
"_vendor-three-C5WAXd5r.js",
|
|
82
|
-
"_vendor-codemirror-
|
|
83
|
-
"_vendor-shiki-
|
|
84
|
-
"_vendor-recharts-
|
|
82
|
+
"_vendor-codemirror-DIWnRs_7.js",
|
|
83
|
+
"_vendor-shiki-Cc73E4D-.js",
|
|
84
|
+
"_vendor-recharts-Bame54mG.js"
|
|
85
85
|
]
|
|
86
86
|
}
|
|
87
87
|
}
|