arize-phoenix 3.13.1__py3-none-any.whl → 3.14.1__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.

@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: arize-phoenix
3
- Version: 3.13.1
3
+ Version: 3.14.1
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
@@ -18,7 +18,7 @@ Classifier: Programming Language :: Python :: 3.11
18
18
  Classifier: Programming Language :: Python :: 3.12
19
19
  Requires-Python: <3.13,>=3.8
20
20
  Requires-Dist: ddsketch
21
- Requires-Dist: hdbscan<1.0.0,>=0.8.33
21
+ Requires-Dist: hdbscan>=0.8.33
22
22
  Requires-Dist: jinja2
23
23
  Requires-Dist: numpy
24
24
  Requires-Dist: openinference-instrumentation-langchain>=0.1.12
@@ -33,13 +33,14 @@ Requires-Dist: protobuf<5.0,>=3.20
33
33
  Requires-Dist: psutil
34
34
  Requires-Dist: pyarrow
35
35
  Requires-Dist: requests
36
- Requires-Dist: scikit-learn<1.3.0
36
+ Requires-Dist: scikit-learn
37
37
  Requires-Dist: scipy
38
38
  Requires-Dist: sortedcontainers
39
39
  Requires-Dist: starlette
40
40
  Requires-Dist: strawberry-graphql==0.208.2
41
41
  Requires-Dist: tqdm
42
- Requires-Dist: typing-extensions<5,>=4.5
42
+ Requires-Dist: typing-extensions>=4.5; python_version < '3.12'
43
+ Requires-Dist: typing-extensions>=4.6; python_version >= '3.12'
43
44
  Requires-Dist: umap-learn
44
45
  Requires-Dist: uvicorn
45
46
  Requires-Dist: wrapt
@@ -1,17 +1,17 @@
1
1
  phoenix/__init__.py,sha256=mrgR7rvpc7LnBeyLzCX9wmRP4kJSJdfKDbj2zDiDjG8,2311
2
- phoenix/config.py,sha256=sec_E60Dc2jFMouomk7I_0gitjs1AHU6yb-8Wcs7sTI,3740
2
+ phoenix/config.py,sha256=eJ7V0eQMc1vT_0LRF1HDXMWbnGLYa4c36WHYfUHrNAE,3872
3
3
  phoenix/datetime_utils.py,sha256=D955QLrkgrrSdUM6NyqbCeAu2SMsjhR5rHVQEsVUdng,2773
4
4
  phoenix/exceptions.py,sha256=X5k9ipUDfwSCwZB-H5zFJLas86Gf9tAx0W4l5TZxp5k,108
5
5
  phoenix/py.typed,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
6
6
  phoenix/services.py,sha256=f6AeyKTuOpy9RCcTCjVH3gx5nYZhbTMFOuv1WSUOB5o,4992
7
- phoenix/version.py,sha256=Rsh0woAU1cKSQjhLniHIaCty32a8fng3lOIuCavl-rU,23
7
+ phoenix/version.py,sha256=LqsaoTVDMdgtxip-MfWDWKiCOsrtXES8F2MkGSR9EWQ,23
8
8
  phoenix/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
9
  phoenix/core/embedding_dimension.py,sha256=zKGbcvwOXgLf-yrJBpQyKtd-LEOPRKHnUToyAU8Owis,87
10
10
  phoenix/core/model.py,sha256=C-kDATyJEgP-oqYVKOiQM76Ljs66F6VZdT93_b8kTGk,4725
11
11
  phoenix/core/model_schema.py,sha256=lQaTvKS34yurHOJ53YD020uURLfgG3dqKC1NLQftOjA,50222
12
12
  phoenix/core/model_schema_adapter.py,sha256=3GkyzqUST4fYi-Bgs8qAam5hwMCdQRZTDLjZ9Bnzdm4,8268
13
- phoenix/core/project.py,sha256=5bitRrQ-aG70k21ht0HUDJZkMl3dpI_HyLmBO7rLB-c,24673
14
- phoenix/core/traces.py,sha256=OnvOo4epNGMptOF1-O2FQgDV1c9TywQq5yqvj9ph3Mw,2902
13
+ phoenix/core/project.py,sha256=z-Yrwyg50TqP48mZ8nQC2ZSxrebrJL_OoMp5EIZi_NE,24635
14
+ phoenix/core/traces.py,sha256=_AiOt1XRNMU_XTRXZBsjdYuF_zHn87Vm80mW0Y7PA9Q,2935
15
15
  phoenix/datasets/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
16
16
  phoenix/datasets/dataset.py,sha256=scKVZ7zc6Dpc_ntt-pWhzY-KWqOJEwKePuyNnKSVTGE,30515
17
17
  phoenix/datasets/errors.py,sha256=cGp9vxnw4SewFoWBV3ZGMkhE0Kh73lPIv3Ppz_H_RoA,8261
@@ -55,8 +55,8 @@ phoenix/pointcloud/pointcloud.py,sha256=4zAIkKs2xOUbchpj4XDAV-iPMXrfAJ15TG6rlIYG
55
55
  phoenix/pointcloud/projectors.py,sha256=zO_RrtDYSv2rqVOfIP2_9Cv11Dc8EmcZR94xhFcBYPU,1057
56
56
  phoenix/pointcloud/umap_parameters.py,sha256=lJsEOrbSuSiqI7g4Yt6xj7kgYxEqoep4ZHWLr6VWBqw,1760
57
57
  phoenix/server/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
58
- phoenix/server/app.py,sha256=KMHQsy8Sypp8Ua6UrWvqByLt2FHXoKirnkakf-90JbU,6774
59
- phoenix/server/main.py,sha256=gGaQ0jqbsiGG1Enlr7UwL5Woc1k4kJyZhTa6OplwxgM,9090
58
+ phoenix/server/app.py,sha256=cNEskfuSyG7XGdt802VZin-keJk78f8Fm0jaFE94Py8,6887
59
+ phoenix/server/main.py,sha256=nprpPH6eBAKwJ7OjY1bZxQtcukfJpv7b_IuTVwchZWM,10351
60
60
  phoenix/server/thread_server.py,sha256=dP6cm6Cf08jNhDA1TRlVZpziu1YgtPDmaeIJMm725eI,2154
61
61
  phoenix/server/api/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
62
62
  phoenix/server/api/context.py,sha256=wjCzq4QlszKG1iN-xgu5rRLYPqdvTFqX02aFYPipNoQ,512
@@ -74,9 +74,9 @@ phoenix/server/api/input_types/SpanSort.py,sha256=3ken7KaDUwoZfhaSiP9QRhkNPgGrCY
74
74
  phoenix/server/api/input_types/TimeRange.py,sha256=yzx-gxj8mDeGLft1FzU_x1MVEgIG5Pt6-f8PUVDgipQ,522
75
75
  phoenix/server/api/input_types/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
76
76
  phoenix/server/api/routers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
77
- phoenix/server/api/routers/evaluation_handler.py,sha256=Gj9xqU8TCgukLTirBT2KIcWkNm0Gg6mrtS2v8-tALiQ,3847
78
- phoenix/server/api/routers/span_handler.py,sha256=3cdUZNpxtUvk9aqMu_nO-EANffRz7vcc84MijbjjE-M,2393
79
- phoenix/server/api/routers/trace_handler.py,sha256=Ig601fM9AhJ-yhcj-w-PXo3cSCfVXVwXTGDerTFDpUw,2477
77
+ phoenix/server/api/routers/evaluation_handler.py,sha256=viXigPYCOzN8bgmu_OcfEwl8jHkwarAw65bnGpvlR98,3852
78
+ phoenix/server/api/routers/span_handler.py,sha256=CdW8pYQW2y2eSW5Y-d6ce2pgmo4v16vGC6ohVo1bkwg,2398
79
+ phoenix/server/api/routers/trace_handler.py,sha256=GVEl5y3QvnElmUVJCPUtQnVAbC6kOvL6JjOfx53JCLI,2412
80
80
  phoenix/server/api/routers/utils.py,sha256=M41BoH-fl37izhRuN2aX7lWm7jOC20A_3uClv9TVUUY,583
81
81
  phoenix/server/api/types/Cluster.py,sha256=R08ZKrLl1KK8colxHU57N5XIOTMUwg5ZI50ofPoxxSM,5618
82
82
  phoenix/server/api/types/DataQualityMetric.py,sha256=zRKsNvHBu-NdcsunuLhqFpZhi6ks-HMqA1PJD27jTak,590
@@ -109,7 +109,7 @@ phoenix/server/api/types/Retrieval.py,sha256=OhMK2ncjoyp5h1yjKhjlKpoTbQrMHuxmgSF
109
109
  phoenix/server/api/types/ScalarDriftMetricEnum.py,sha256=IUAcRPpgL41WdoIgK6cNk2Te38SspXGyEs-S1fY23_A,232
110
110
  phoenix/server/api/types/Segments.py,sha256=B6UUWjalZONjWjl_l61A6USPSu15ICXRgzZ4m3vA1yw,2921
111
111
  phoenix/server/api/types/SortDir.py,sha256=OUpXhlCzCxPoXSDkJJygEs9Rw9pMymfaZUG5zPTrw4Y,152
112
- phoenix/server/api/types/Span.py,sha256=1o7DeeR7Ytj7XgXrS9Oy5N7ZjSOMXxEGCINs0sjh6Lc,12291
112
+ phoenix/server/api/types/Span.py,sha256=obTJMMeSUD8qIen_XIKW0PDdadlFu2pj4dPpxjb7uI8,12328
113
113
  phoenix/server/api/types/TimeSeries.py,sha256=QbLfxHnwYsMsirpq4tx9us6ha7YtAVzK4m8mAL3fMt0,5200
114
114
  phoenix/server/api/types/UMAPPoints.py,sha256=8l9RJXi308qty4MdHb2pBbiU6ZuLbrRRxXNbPhXoxKI,1639
115
115
  phoenix/server/api/types/ValidationResult.py,sha256=pHwdYk4J7SJ5xhlWWHg_6qWkfk4rjOx-bSkGHvkDE3Q,142
@@ -127,7 +127,7 @@ phoenix/server/static/apple-touch-icon-76x76.png,sha256=CT_xT12I0u2i0WU8JzBZBuOQ
127
127
  phoenix/server/static/apple-touch-icon.png,sha256=fOfpjqGpWYbJ0eAurKsyoZP1EAs6ZVooBJ_SGk2ZkDs,3801
128
128
  phoenix/server/static/favicon.ico,sha256=bY0vvCKRftemZfPShwZtE93DiiQdaYaozkPGwNFr6H8,34494
129
129
  phoenix/server/static/index.css,sha256=KKGpx4iwF91VGRm0YN-4cn8oC-oIqC6HecoPf0x3ZM8,1885
130
- phoenix/server/static/index.js,sha256=QUF1RlcKrm9BGS4JjbWlBaC4ZnaaxDXUwxiyiNHvhos,3175708
130
+ phoenix/server/static/index.js,sha256=-z9E12Tquch7keYjnWNreqYiDvwwCa7f3Q95hoRtlqM,3175772
131
131
  phoenix/server/static/modernizr.js,sha256=mvK-XtkNqjOral-QvzoqsyOMECXIMu5BQwSVN_wcU9c,2564
132
132
  phoenix/server/templates/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
133
133
  phoenix/server/templates/index.html,sha256=lO2wGA5XsftPg03rw_VcyaYf_4vegtlWbIT5ms4fA_c,1982
@@ -136,6 +136,9 @@ phoenix/session/client.py,sha256=9qlybwGGdPHql7n6Mye8feuh5_5AMq34xSajvcPn2yg,585
136
136
  phoenix/session/data_extractor.py,sha256=0Kf-2mKY_YbYoD2fZkAYpKdFgsXrC3OKQ5d2iZsGgAI,1947
137
137
  phoenix/session/evaluation.py,sha256=YCv1XkWHi7vM_W5V7rorrrAxadv78wuMPeCVJvf5-oE,5444
138
138
  phoenix/session/session.py,sha256=1RV6FBNqNDDrPFYU2qLffmI4RCRKy22KCLZFcH9oqJA,24556
139
+ phoenix/storage/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
140
+ phoenix/storage/spanstore/__init__.py,sha256=Q1tfrRcYCTVbxQGWGCkfb2i1YTweiYixQZ-kNG1ViZQ,228
141
+ phoenix/storage/spanstore/text_file.py,sha256=Fzpl_UDVN6_Qa8kpjhDkjdO7xb_I3g-UuTseenkV42c,2853
139
142
  phoenix/trace/__init__.py,sha256=DOuudYVyWlhUnIy-E_aqvJ6ulmk_q7ghuctuXQCGCJA,763
140
143
  phoenix/trace/errors.py,sha256=wB1z8qdPckngdfU-TORToekvg3344oNFAA83_hC2yFY,180
141
144
  phoenix/trace/evaluation_conventions.py,sha256=t8jydM3U0-T5YpiQKRJ3tWdWGlHtzKyttYdw-ddvPOk,1048
@@ -166,8 +169,9 @@ phoenix/trace/v1/evaluation_pb2.pyi,sha256=cCbbx06gwQmaH14s3J1X25TtaARh-k1abbxQd
166
169
  phoenix/utilities/__init__.py,sha256=8w1Ivw0KO9YKWrhcdnO73cSVqP9VHAp0pSfsi_oDiuQ,672
167
170
  phoenix/utilities/error_handling.py,sha256=7b5rpGFj9EWZ8yrZK1IHvxB89suWk3lggDayUQcvZds,1946
168
171
  phoenix/utilities/logging.py,sha256=lDXd6EGaamBNcQxL4vP1au9-i_SXe0OraUDiJOcszSw,222
169
- arize_phoenix-3.13.1.dist-info/METADATA,sha256=NZMGN1YFEUG9zf2NNNPC9L3XDxTc-eGoMrI_M7SgNx8,29131
170
- arize_phoenix-3.13.1.dist-info/WHEEL,sha256=TJPnKdtrSue7xZ_AVGkp9YXcvDrobsjBds1du3Nx6dc,87
171
- arize_phoenix-3.13.1.dist-info/licenses/IP_NOTICE,sha256=JBqyyCYYxGDfzQ0TtsQgjts41IJoa-hiwDrBjCb9gHM,469
172
- arize_phoenix-3.13.1.dist-info/licenses/LICENSE,sha256=HFkW9REuMOkvKRACuwLPT0hRydHb3zNg-fdFt94td18,3794
173
- arize_phoenix-3.13.1.dist-info/RECORD,,
172
+ phoenix/utilities/project.py,sha256=AzyuKxh3IByoKapA-AIRzZNFw_c0zh9RNjhh3BT_YWI,416
173
+ arize_phoenix-3.14.1.dist-info/METADATA,sha256=6YUOVi4UayTUu5A0jlCo5MssavFZfc-ze7X3euYsxRQ,29204
174
+ arize_phoenix-3.14.1.dist-info/WHEEL,sha256=TJPnKdtrSue7xZ_AVGkp9YXcvDrobsjBds1du3Nx6dc,87
175
+ arize_phoenix-3.14.1.dist-info/licenses/IP_NOTICE,sha256=JBqyyCYYxGDfzQ0TtsQgjts41IJoa-hiwDrBjCb9gHM,469
176
+ arize_phoenix-3.14.1.dist-info/licenses/LICENSE,sha256=HFkW9REuMOkvKRACuwLPT0hRydHb3zNg-fdFt94td18,3794
177
+ arize_phoenix-3.14.1.dist-info/RECORD,,
phoenix/config.py CHANGED
@@ -56,6 +56,13 @@ def get_working_dir() -> Path:
56
56
  return Path.home().resolve() / ".phoenix"
57
57
 
58
58
 
59
+ def get_storage_dir() -> Path:
60
+ """
61
+ Get the directory for storing traces.
62
+ """
63
+ return get_working_dir() / "storage"
64
+
65
+
59
66
  PHOENIX_DIR = Path(__file__).resolve().parent
60
67
  # Server config
61
68
  SERVER_DIR = PHOENIX_DIR / "server"
phoenix/core/project.py CHANGED
@@ -82,7 +82,6 @@ _ParentSpanID: TypeAlias = SpanID
82
82
  _ChildSpanID: TypeAlias = SpanID
83
83
  _ProjectName: TypeAlias = str
84
84
 
85
- DEFAULT_PROJECT_NAME: str = "default"
86
85
 
87
86
  EvaluationName: TypeAlias = str
88
87
  DocumentPosition: TypeAlias = int
phoenix/core/traces.py CHANGED
@@ -9,12 +9,12 @@ from typing_extensions import assert_never
9
9
 
10
10
  import phoenix.trace.v1 as pb
11
11
  from phoenix.core.project import (
12
- DEFAULT_PROJECT_NAME,
13
12
  END_OF_QUEUE,
14
13
  Project,
15
14
  _ProjectName,
16
15
  )
17
16
  from phoenix.trace.schemas import Span
17
+ from phoenix.utilities.project import DEFAULT_PROJECT_NAME
18
18
 
19
19
  _SpanItem = Tuple[Span, _ProjectName]
20
20
  _EvalItem = Tuple[pb.Evaluation, _ProjectName]
@@ -15,11 +15,11 @@ from starlette.status import (
15
15
  )
16
16
 
17
17
  import phoenix.trace.v1 as pb
18
- from phoenix.core.project import DEFAULT_PROJECT_NAME
19
18
  from phoenix.core.traces import Traces
20
19
  from phoenix.server.api.routers.utils import table_to_bytes
21
20
  from phoenix.session.evaluation import encode_evaluations
22
21
  from phoenix.trace.span_evaluations import Evaluations
22
+ from phoenix.utilities.project import DEFAULT_PROJECT_NAME
23
23
 
24
24
 
25
25
  class EvaluationHandler(HTTPEndpoint):
@@ -7,11 +7,11 @@ from starlette.requests import Request
7
7
  from starlette.responses import Response, StreamingResponse
8
8
  from starlette.status import HTTP_404_NOT_FOUND, HTTP_422_UNPROCESSABLE_ENTITY
9
9
 
10
- from phoenix.core.project import DEFAULT_PROJECT_NAME
11
10
  from phoenix.core.traces import Traces
12
11
  from phoenix.server.api.routers.utils import df_to_bytes, from_iso_format
13
12
  from phoenix.trace.dsl import SpanQuery
14
13
  from phoenix.utilities import query_spans
14
+ from phoenix.utilities.project import DEFAULT_PROJECT_NAME
15
15
 
16
16
 
17
17
  class SpanHandler(HTTPEndpoint):
@@ -1,25 +1,27 @@
1
1
  import asyncio
2
2
  import gzip
3
3
  import zlib
4
- from typing import Iterable, Optional
4
+ from typing import Optional
5
5
 
6
6
  from google.protobuf.message import DecodeError
7
- from openinference.semconv.resource import ResourceAttributes
8
7
  from opentelemetry.proto.collector.trace.v1.trace_service_pb2 import (
9
8
  ExportTraceServiceRequest,
10
9
  )
11
- from opentelemetry.proto.common.v1.common_pb2 import KeyValue
10
+ from opentelemetry.proto.trace.v1.trace_pb2 import TracesData
12
11
  from starlette.endpoints import HTTPEndpoint
13
12
  from starlette.requests import Request
14
13
  from starlette.responses import Response
15
14
  from starlette.status import HTTP_415_UNSUPPORTED_MEDIA_TYPE, HTTP_422_UNPROCESSABLE_ENTITY
16
15
 
17
16
  from phoenix.core.traces import Traces
17
+ from phoenix.storage.spanstore import SpanStore
18
18
  from phoenix.trace.otel import decode
19
+ from phoenix.utilities.project import get_project_name
19
20
 
20
21
 
21
22
  class TraceHandler(HTTPEndpoint):
22
23
  traces: Traces
24
+ store: Optional[SpanStore]
23
25
 
24
26
  async def post(self, request: Request) -> Response:
25
27
  content_type = request.headers.get("content-type")
@@ -47,17 +49,12 @@ class TraceHandler(HTTPEndpoint):
47
49
  content="Request body is invalid ExportTraceServiceRequest",
48
50
  status_code=HTTP_422_UNPROCESSABLE_ENTITY,
49
51
  )
52
+ if self.store:
53
+ self.store.save(TracesData(resource_spans=req.resource_spans))
50
54
  for resource_spans in req.resource_spans:
51
- project_name = _get_project_name(resource_spans.resource.attributes)
55
+ project_name = get_project_name(resource_spans.resource.attributes)
52
56
  for scope_span in resource_spans.scope_spans:
53
57
  for span in scope_span.spans:
54
58
  self.traces.put(decode(span), project_name=project_name)
55
59
  await asyncio.sleep(0)
56
60
  return Response()
57
-
58
-
59
- def _get_project_name(attributes: Iterable[KeyValue]) -> Optional[str]:
60
- for kv in attributes:
61
- if kv.key == ResourceAttributes.PROJECT_NAME and (v := kv.value.string_value):
62
- return v
63
- return None
@@ -10,13 +10,14 @@ from strawberry import ID, UNSET
10
10
  from strawberry.types import Info
11
11
 
12
12
  import phoenix.trace.schemas as trace_schema
13
- from phoenix.core.project import DEFAULT_PROJECT_NAME, Project, WrappedSpan
13
+ from phoenix.core.project import Project, WrappedSpan
14
14
  from phoenix.metrics.retrieval_metrics import RetrievalMetrics
15
15
  from phoenix.server.api.context import Context
16
16
  from phoenix.server.api.types.DocumentRetrievalMetrics import DocumentRetrievalMetrics
17
17
  from phoenix.server.api.types.Evaluation import DocumentEvaluation, SpanEvaluation
18
18
  from phoenix.server.api.types.MimeType import MimeType
19
19
  from phoenix.trace.schemas import ComputedAttributes, SpanID
20
+ from phoenix.utilities.project import DEFAULT_PROJECT_NAME
20
21
 
21
22
  EMBEDDING_EMBEDDINGS = SpanAttributes.EMBEDDING_EMBEDDINGS
22
23
  EMBEDDING_VECTOR = EmbeddingAttributes.EMBEDDING_VECTOR
phoenix/server/app.py CHANGED
@@ -28,6 +28,7 @@ from phoenix.server.api.routers.evaluation_handler import EvaluationHandler
28
28
  from phoenix.server.api.routers.span_handler import SpanHandler
29
29
  from phoenix.server.api.routers.trace_handler import TraceHandler
30
30
  from phoenix.server.api.schema import schema
31
+ from phoenix.storage.spanstore import SpanStore
31
32
 
32
33
  logger = logging.getLogger(__name__)
33
34
 
@@ -147,6 +148,7 @@ def create_app(
147
148
  umap_params: UMAPParameters,
148
149
  corpus: Optional[Model] = None,
149
150
  traces: Optional[Traces] = None,
151
+ span_store: Optional[SpanStore] = None,
150
152
  debug: bool = False,
151
153
  read_only: bool = False,
152
154
  ) -> Starlette:
@@ -173,7 +175,7 @@ def create_app(
173
175
  ),
174
176
  Route(
175
177
  "/v1/traces",
176
- type("TraceEndpoint", (TraceHandler,), {"traces": traces}),
178
+ type("TraceEndpoint", (TraceHandler,), {"traces": traces, "store": span_store}),
177
179
  ),
178
180
  Route(
179
181
  "/v1/evaluations",
phoenix/server/main.py CHANGED
@@ -11,7 +11,13 @@ from typing import Iterable, Optional, Protocol, TypeVar
11
11
  import pkg_resources
12
12
  from uvicorn import Config, Server
13
13
 
14
- from phoenix.config import EXPORT_DIR, get_env_host, get_env_port, get_pids_path
14
+ from phoenix.config import (
15
+ EXPORT_DIR,
16
+ get_env_host,
17
+ get_env_port,
18
+ get_pids_path,
19
+ get_storage_dir,
20
+ )
15
21
  from phoenix.core.model_schema_adapter import create_model_from_datasets
16
22
  from phoenix.core.traces import Traces
17
23
  from phoenix.datasets.dataset import EMPTY_DATASET, Dataset
@@ -23,6 +29,8 @@ from phoenix.pointcloud.umap_parameters import (
23
29
  UMAPParameters,
24
30
  )
25
31
  from phoenix.server.app import create_app
32
+ from phoenix.storage.spanstore import SpanStore
33
+ from phoenix.storage.spanstore.text_file import TextFileSpanStoreImpl
26
34
  from phoenix.trace.fixtures import (
27
35
  TRACES_FIXTURES,
28
36
  _download_traces_fixture,
@@ -31,6 +39,7 @@ from phoenix.trace.fixtures import (
31
39
  )
32
40
  from phoenix.trace.otel import decode, encode
33
41
  from phoenix.trace.span_json_decoder import json_string_to_span
42
+ from phoenix.utilities.project import get_project_name
34
43
 
35
44
  logger = logging.getLogger(__name__)
36
45
 
@@ -99,6 +108,15 @@ def _load_items(
99
108
  queue.put(item)
100
109
 
101
110
 
111
+ def _load_from_store(traces: Traces, span_store: SpanStore) -> None:
112
+ for traces_data in span_store.load():
113
+ for resource_spans in traces_data.resource_spans:
114
+ project_name = get_project_name(resource_spans.resource.attributes)
115
+ for scope_span in resource_spans.scope_spans:
116
+ for span in scope_span.spans:
117
+ traces.put(decode(span), project_name=project_name)
118
+
119
+
102
120
  DEFAULT_UMAP_PARAMS_STR = f"{DEFAULT_MIN_DIST},{DEFAULT_N_NEIGHBORS},{DEFAULT_N_SAMPLES}"
103
121
 
104
122
  if __name__ == "__main__":
@@ -124,6 +142,8 @@ if __name__ == "__main__":
124
142
  parser.add_argument("--debug", action="store_false")
125
143
  subparsers = parser.add_subparsers(dest="command", required=True)
126
144
  serve_parser = subparsers.add_parser("serve")
145
+ experimental_parser = subparsers.add_parser("extremely-dangerous-experimental-span-storage")
146
+ experimental_parser.add_argument("--storage-path", type=str, required=False)
127
147
  datasets_parser = subparsers.add_parser("datasets")
128
148
  datasets_parser.add_argument("--primary", type=str, required=True)
129
149
  datasets_parser.add_argument("--reference", type=str, required=False)
@@ -145,6 +165,7 @@ if __name__ == "__main__":
145
165
  demo_parser.add_argument("--simulate-streaming", action="store_true")
146
166
  args = parser.parse_args()
147
167
  export_path = Path(args.export_path) if args.export_path else EXPORT_DIR
168
+ span_store: Optional[SpanStore] = None
148
169
  if args.command == "datasets":
149
170
  primary_dataset_name = args.primary
150
171
  reference_dataset_name = args.reference
@@ -179,12 +200,19 @@ if __name__ == "__main__":
179
200
  )
180
201
  trace_dataset_name = args.trace_fixture
181
202
  simulate_streaming = args.simulate_streaming
203
+ elif args.command == "extremely-dangerous-experimental-span-storage":
204
+ span_store_path = (
205
+ get_storage_dir() if args.storage_path is None else Path(args.storage_path)
206
+ )
207
+ span_store = TextFileSpanStoreImpl(span_store_path)
182
208
 
183
209
  model = create_model_from_datasets(
184
210
  primary_dataset,
185
211
  reference_dataset,
186
212
  )
187
213
  traces = Traces()
214
+ if span_store:
215
+ Thread(target=_load_from_store, args=(traces, span_store), daemon=True).start()
188
216
  if trace_dataset_name is not None:
189
217
  fixture_spans = list(
190
218
  # Apply `encode` here because legacy jsonl files contains UUIDs as strings.
@@ -221,6 +249,7 @@ if __name__ == "__main__":
221
249
  corpus=None if corpus_dataset is None else create_model_from_datasets(corpus_dataset),
222
250
  debug=args.debug,
223
251
  read_only=read_only,
252
+ span_store=span_store,
224
253
  )
225
254
  host = args.host or get_env_host()
226
255
  port = args.port or get_env_port()