arize-phoenix 4.30.2__py3-none-any.whl → 4.32.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.

@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: arize-phoenix
3
- Version: 4.30.2
3
+ Version: 4.32.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
@@ -99,7 +99,10 @@ Requires-Dist: pytest==8.3.2; extra == 'dev'
99
99
  Requires-Dist: ruff==0.6.1; extra == 'dev'
100
100
  Requires-Dist: strawberry-graphql[debug-server,opentelemetry]==0.236.0; extra == 'dev'
101
101
  Requires-Dist: tabulate; extra == 'dev'
102
+ Requires-Dist: tox; extra == 'dev'
103
+ Requires-Dist: tox-uv; extra == 'dev'
102
104
  Requires-Dist: types-tabulate; extra == 'dev'
105
+ Requires-Dist: uv; extra == 'dev'
103
106
  Requires-Dist: uvloop; (platform_system != 'Windows') and extra == 'dev'
104
107
  Provides-Extra: evals
105
108
  Provides-Extra: experimental
@@ -157,10 +160,10 @@ Phoenix runs practically anywhere, including your Jupyter notebook, local machin
157
160
 
158
161
  ## Installation
159
162
 
160
- Install Phoenix via `pip` or `conda` along with extra dependencies for running evals:
163
+ Install Phoenix via `pip` or `conda`
161
164
 
162
165
  ```shell
163
- pip install 'arize-phoenix[evals]'
166
+ pip install arize-phoenix
164
167
  ```
165
168
 
166
169
  Phoenix container images are available via [Docker Hub](https://hub.docker.com/r/arizephoenix/phoenix) and can be deployed using Docker or Kubernetes.
@@ -177,11 +180,6 @@ Join our community to connect with thousands of AI builders.
177
180
  - 💌️ Sign up for our [mailing list](https://phoenix.arize.com/#updates).
178
181
  - 🗺️ Check out our [roadmap](https://github.com/orgs/Arize-ai/projects/45) to see where we're heading next.
179
182
 
180
- ## Thanks
181
-
182
- - [UMAP](https://github.com/lmcinnes/umap) For unlocking the ability to visualize and reason about embeddings
183
- - [HDBSCAN](https://github.com/scikit-learn-contrib/hdbscan) For providing a clustering algorithm to aid in the discovery of drift and performance degradation
184
-
185
183
  ## Breaking Changes
186
184
 
187
185
  See the [migration guide](./MIGRATION.md) for a list of breaking changes.
@@ -1,12 +1,12 @@
1
1
  phoenix/__init__.py,sha256=TGNWqm2UW-l67yIRpOtmqGHVAmdoobSNqUsiTtip7uQ,1542
2
2
  phoenix/auth.py,sha256=ugvGZlseYX9NkpWaSqb8D2kzUBlAPqT45Dx5_VUANqk,1621
3
- phoenix/config.py,sha256=wYA_8GSSz5rnpfIWDjeBL9ehKuTy9jqXaMZnxUqRYEU,10131
3
+ phoenix/config.py,sha256=RfS-0kzEOhirX9EmVMr8cu7qCDtkq8HpxhhctEgh0Yo,10795
4
4
  phoenix/datetime_utils.py,sha256=yDKjwX2Vtqw9h5F_ProtP-TsXidM43uIvmJ_pOzYc9A,3405
5
5
  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=DvmdNpuJDXvg0nMZADyqJJU0JkqI_2lJ8G7e_VN97L0,23
9
+ phoenix/version.py,sha256=_QUskFVbFLd7O3BXo2PEGlMc717xpkYLlK-r6lVv4_I,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
@@ -72,11 +72,11 @@ phoenix/pointcloud/pointcloud.py,sha256=4zAIkKs2xOUbchpj4XDAV-iPMXrfAJ15TG6rlIYG
72
72
  phoenix/pointcloud/projectors.py,sha256=zO_RrtDYSv2rqVOfIP2_9Cv11Dc8EmcZR94xhFcBYPU,1057
73
73
  phoenix/pointcloud/umap_parameters.py,sha256=3UQSjrysVOvq2V4KNpTMqNqNiK0BsTZnPBHWZ4fyJtQ,1708
74
74
  phoenix/server/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
75
- phoenix/server/app.py,sha256=4EPqxjfhFqxzk_JS560Gm8O9_TFtJ6lXW-72CfEzyGU,26920
75
+ phoenix/server/app.py,sha256=i5VhDdqPoQ8i5WlwjnUZAjedx8xOiL16R2OzATg9LQE,28333
76
76
  phoenix/server/dml_event.py,sha256=MpjCFqljxvgb9OB5Cez9vJesb3oHb3XxXictynBfcis,2851
77
- phoenix/server/dml_event_handler.py,sha256=6p-PucctivelVHfO-_9zNxWZYPr_eGjDF3bKjLtc5co,8251
77
+ phoenix/server/dml_event_handler.py,sha256=yU23-DDwXcL35p5EPwFW0oZh6mxQxJrJAQPKcYZYJz4,8310
78
78
  phoenix/server/grpc_server.py,sha256=jllxDNkpLQxDkvej4RhTokobowbvydF-SU8gSw1MTCc,3378
79
- phoenix/server/main.py,sha256=KcyiOtU7pJrWASTih4huF53WizXUdjCpWSqY6glk-mA,14037
79
+ phoenix/server/main.py,sha256=FnETeVWwcsnA4z67gHE9Br8BUCmW8cMQC-YpjCq-Zz0,14634
80
80
  phoenix/server/prometheus.py,sha256=j9DHB2fERuq_ZKmwVaqR-9wx5WcPPuU1Cm5Bhg5241Y,2996
81
81
  phoenix/server/telemetry.py,sha256=T_2OKrxNViAeaANlNspEekg_Y5uZIFWvKAnpz8Aoqvk,2762
82
82
  phoenix/server/thread_server.py,sha256=RwXQGP_QhGD7le6WB7xEygEEuwBl5Ck_Zo8xGIYGi9M,2135
@@ -242,15 +242,15 @@ phoenix/server/static/apple-touch-icon-76x76.png,sha256=CT_xT12I0u2i0WU8JzBZBuOQ
242
242
  phoenix/server/static/apple-touch-icon.png,sha256=fOfpjqGpWYbJ0eAurKsyoZP1EAs6ZVooBJ_SGk2ZkDs,3801
243
243
  phoenix/server/static/favicon.ico,sha256=bY0vvCKRftemZfPShwZtE93DiiQdaYaozkPGwNFr6H8,34494
244
244
  phoenix/server/static/modernizr.js,sha256=mvK-XtkNqjOral-QvzoqsyOMECXIMu5BQwSVN_wcU9c,2564
245
- phoenix/server/static/.vite/manifest.json,sha256=rkEVApCRSYofJpf7L3LFtAc9R6Yv7rijaaUCY2_AnXU,1929
246
- phoenix/server/static/assets/components-CkSg5zK4.js,sha256=kMP22Fi58XDiVcsTUSkisv-SYM1E4y1R8Wp06r_pJNo,244317
247
- phoenix/server/static/assets/index-DTecsU5w.js,sha256=qQbWg4v_Z385UAuIH30xlqhF3dFNhngnauRf3sOpj4o,7515
248
- phoenix/server/static/assets/pages-C6emDFIO.js,sha256=5tmkYOe8IYM-CbQym_pAnkBv4SHEa1B1nNIqOqQ5Yzg,502735
249
- phoenix/server/static/assets/vendor-DsnEJuEV.js,sha256=YyePucCaVO5Wke5vLm6NF6V3_rAomN7TqcBzv8QY6q0,1435199
245
+ phoenix/server/static/.vite/manifest.json,sha256=2equEiHXZGCivKbAQc9U9NATOUVltsQoiM0lhdD4TZE,1929
246
+ phoenix/server/static/assets/components-Ddr-5VpM.js,sha256=kKqWpR-S-axVFd3tD_bfc3OD9HEleq0Pdn_cofQuzDo,244415
247
+ phoenix/server/static/assets/index-BvNNAF8n.js,sha256=-keFoN2koW7IT6Iv_kbUpDDqv-TkpjSccukpYvuI8yo,7602
248
+ phoenix/server/static/assets/pages-CU_QMYRP.js,sha256=iiwCelLTc7KsQeRr5mRW0lnDd8fNFPwWo4NjSKp6J2A,511373
249
+ phoenix/server/static/assets/vendor-Dpio9q2e.js,sha256=1CMQ_vGa7qQyJoLj1WdL23CVQ6y9E94cv1cy_KHNi84,1435263
250
250
  phoenix/server/static/assets/vendor-DxkFTwjz.css,sha256=nZrkr0u6NNElFGvpWHk9GTHeGoibCXCli1bE7mXZGZg,1816
251
- phoenix/server/static/assets/vendor-arizeai-DtynTLNi.js,sha256=7D2zoxiUlFzqSpK-BDO4HlhbruUMmw3_CCDYQq6Mw-4,304008
252
- phoenix/server/static/assets/vendor-codemirror-C5to5cK4.js,sha256=EYP0yvcRy8tInRIiGalhd5czgBYw_bmkLL9D_DDqAo0,516470
253
- phoenix/server/static/assets/vendor-recharts-reihe2SJ.js,sha256=w13k19ajg2pNW1saRn3nKDMZil7RAB1vAg3eilYidsI,282859
251
+ phoenix/server/static/assets/vendor-arizeai-DDzwX9A6.js,sha256=ePHP1b8n-zwCMGEwgfR00aOTSEe8Ogp8gx3FyZ1-SyA,304401
252
+ phoenix/server/static/assets/vendor-codemirror-xRvQKK1G.js,sha256=frhtxbn75y5RLIBSiyAsc5p9Vf8eZgLMaBmTau7-fgU,516470
253
+ phoenix/server/static/assets/vendor-recharts-DoxlB_Wo.js,sha256=kwD-Sn947JKsQjfpH3ybD-O1F-Qvner2KUP9a5Naqbs,282859
254
254
  phoenix/server/static/assets/vendor-three-DwGkEfCM.js,sha256=0D12ZgKzfKCTSdSTKJBFR2RZO_xxeMXrqDp0AszZqHY,620972
255
255
  phoenix/server/templates/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
256
256
  phoenix/server/templates/index.html,sha256=dAm0IClgJUdT5AOmjZvtgMg8F_xGrRGv95SAkUyx_kg,4325
@@ -264,7 +264,7 @@ phoenix/trace/attributes.py,sha256=B_OrzVaxZwFkrAFXZyicYoIti1UdUysURsvUS2GyW1U,1
264
264
  phoenix/trace/errors.py,sha256=wB1z8qdPckngdfU-TORToekvg3344oNFAA83_hC2yFY,180
265
265
  phoenix/trace/evaluation_conventions.py,sha256=t8jydM3U0-T5YpiQKRJ3tWdWGlHtzKyttYdw-ddvPOk,1048
266
266
  phoenix/trace/exporter.py,sha256=eAYemdvDCHMugDJiaR29BFFMTQBdf3oerdkz34Cl3hE,4736
267
- phoenix/trace/fixtures.py,sha256=EHfqgvPoux6KkckX00WeG2Vhas8H5vqqFBMTztwgV-s,16857
267
+ phoenix/trace/fixtures.py,sha256=YsKJJEnqo1f_yJzVrfQnnrmS0LlQl7k37lbLY2pZ7ds,17856
268
268
  phoenix/trace/otel.py,sha256=WA720jvRadiZBAKjsYoPyXzypHwbyEK2OZRVUwtbjB8,9976
269
269
  phoenix/trace/projects.py,sha256=2BwlNjFE-uwpqYtCu5YyBiYZk9wRPpM13vh3-Cv7GkA,2157
270
270
  phoenix/trace/schemas.py,sha256=HpWSyzec0yDHEQXEDuwyLbhpvKrqkGps8BJqGiIFj8Y,5978
@@ -272,7 +272,7 @@ phoenix/trace/span_evaluations.py,sha256=GaADtJLi2njra4aYaie0BIwkSgdxPB_SNseglI4
272
272
  phoenix/trace/span_json_decoder.py,sha256=jkpYSmOUsSptvahOvetrPjVkPIuI4h6b87eizL8wHp8,3230
273
273
  phoenix/trace/span_json_encoder.py,sha256=tzSCIQJbeFBm33K68G8A5M12n_86tCDyuU0WAobxEz4,2010
274
274
  phoenix/trace/trace_dataset.py,sha256=Wq89jJ4hYQ1Qt-Uj11ZNzKQYQeKmGY6NqWStQiiTlMw,14351
275
- phoenix/trace/utils.py,sha256=1SEQr37cdHOM0P3BdL1dszArj3Zm-VJQyb1BcJs_qO8,1833
275
+ phoenix/trace/utils.py,sha256=lWinMM7Tsq_DCsBD5Ck97lxouBHrY4muGNsh8JD3qg4,1694
276
276
  phoenix/trace/dsl/README.md,sha256=ihmP9zGUC5V-TDbzKla76LuyDqPDQIBUH2BORwxNI68,2902
277
277
  phoenix/trace/dsl/__init__.py,sha256=WIQIjJg362XD3s50OsPJJ0xbDsGp41bSv7vDllLrPuA,144
278
278
  phoenix/trace/dsl/filter.py,sha256=9NwATCUOgJ4Pms8XsEcinROUuxZ9UW-ISV09o65Ms70,32600
@@ -296,8 +296,8 @@ phoenix/utilities/logging.py,sha256=lDXd6EGaamBNcQxL4vP1au9-i_SXe0OraUDiJOcszSw,
296
296
  phoenix/utilities/project.py,sha256=8IJuMM4yUMoooPi37sictGj8Etu9rGmq6RFtc9848cQ,436
297
297
  phoenix/utilities/re.py,sha256=PDve_OLjRTM8yQQJHC8-n3HdIONi7aNils3ZKRZ5uBM,2045
298
298
  phoenix/utilities/span_store.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
299
- arize_phoenix-4.30.2.dist-info/METADATA,sha256=qMHw9BdGbxXT2Z0TrtzXy-bv_atoD45Ohw0EF9pMu1k,11977
300
- arize_phoenix-4.30.2.dist-info/WHEEL,sha256=1yFddiXMmvYK7QYTqtRNtX66WJ0Mz8PYEiEUoOUUxRY,87
301
- arize_phoenix-4.30.2.dist-info/licenses/IP_NOTICE,sha256=JBqyyCYYxGDfzQ0TtsQgjts41IJoa-hiwDrBjCb9gHM,469
302
- arize_phoenix-4.30.2.dist-info/licenses/LICENSE,sha256=HFkW9REuMOkvKRACuwLPT0hRydHb3zNg-fdFt94td18,3794
303
- arize_phoenix-4.30.2.dist-info/RECORD,,
299
+ arize_phoenix-4.32.0.dist-info/METADATA,sha256=h2RQifXqJMNa4wFC16Cz8CsS4H_rRUxrmnb_tta0lmY,11741
300
+ arize_phoenix-4.32.0.dist-info/WHEEL,sha256=1yFddiXMmvYK7QYTqtRNtX66WJ0Mz8PYEiEUoOUUxRY,87
301
+ arize_phoenix-4.32.0.dist-info/licenses/IP_NOTICE,sha256=JBqyyCYYxGDfzQ0TtsQgjts41IJoa-hiwDrBjCb9gHM,469
302
+ arize_phoenix-4.32.0.dist-info/licenses/LICENSE,sha256=HFkW9REuMOkvKRACuwLPT0hRydHb3zNg-fdFt94td18,3794
303
+ arize_phoenix-4.32.0.dist-info/RECORD,,
phoenix/config.py CHANGED
@@ -1,4 +1,5 @@
1
1
  import os
2
+ import re
2
3
  import tempfile
3
4
  from logging import getLogger
4
5
  from pathlib import Path
@@ -231,6 +232,14 @@ def get_env_port() -> int:
231
232
  return PORT
232
233
  if port.isnumeric():
233
234
  return int(port)
235
+ if _KUBERNETES_PHOENIX_PORT_PATTERN.match(port) is not None:
236
+ raise ValueError(
237
+ 'If you are deploying Phoenix with Kubernetes using a service named "phoenix", '
238
+ "Kubernetes will automatically generate an environment variable `PHOENIX_PORT` "
239
+ 'of the form "tcp://<IP>:<PORT>" that is not the integer format Phoenix expects. '
240
+ "To resolve this issue, explicitly set the `PHOENIX_PORT` environment variable to "
241
+ "an integer value in your Kubernetes deployment configuration."
242
+ )
234
243
  raise ValueError(
235
244
  f"Invalid value for environment variable {ENV_PHOENIX_PORT}: "
236
245
  f"{port}. Value must be an integer."
@@ -313,3 +322,4 @@ def get_web_base_url() -> str:
313
322
 
314
323
 
315
324
  DEFAULT_PROJECT_NAME = "default"
325
+ _KUBERNETES_PHOENIX_PORT_PATTERN = re.compile(r"^tcp://\d{1,3}[.]\d{1,3}[.]\d{1,3}[.]\d{1,3}:\d+$")
phoenix/server/app.py CHANGED
@@ -2,6 +2,8 @@ import asyncio
2
2
  import contextlib
3
3
  import json
4
4
  import logging
5
+ from contextlib import AsyncExitStack
6
+ from dataclasses import dataclass, field
5
7
  from datetime import datetime, timedelta, timezone
6
8
  from functools import cached_property
7
9
  from pathlib import Path
@@ -18,7 +20,6 @@ from typing import (
18
20
  List,
19
21
  NamedTuple,
20
22
  Optional,
21
- Set,
22
23
  Tuple,
23
24
  Union,
24
25
  cast,
@@ -46,7 +47,13 @@ from typing_extensions import TypeAlias
46
47
 
47
48
  import phoenix
48
49
  import phoenix.trace.v1 as pb
49
- from phoenix.config import DEFAULT_PROJECT_NAME, SERVER_DIR, server_instrumentation_is_enabled
50
+ from phoenix.config import (
51
+ DEFAULT_PROJECT_NAME,
52
+ SERVER_DIR,
53
+ get_env_host,
54
+ get_env_port,
55
+ server_instrumentation_is_enabled,
56
+ )
50
57
  from phoenix.core.model_schema import Model
51
58
  from phoenix.db import models
52
59
  from phoenix.db.bulk_inserter import BulkInserter
@@ -95,10 +102,13 @@ from phoenix.server.types import (
95
102
  LastUpdatedAt,
96
103
  )
97
104
  from phoenix.trace.fixtures import (
105
+ TracesFixture,
106
+ get_dataset_fixtures,
98
107
  get_evals_from_fixture,
99
108
  get_trace_fixture_by_name,
100
109
  load_example_traces,
101
110
  reset_fixture_span_ids_and_timestamps,
111
+ send_dataset_fixtures,
102
112
  )
103
113
  from phoenix.trace.otel import decode_otlp_span, encode_span_to_otlp
104
114
  from phoenix.trace.schemas import Span
@@ -242,25 +252,36 @@ def _db(engine: AsyncEngine) -> Callable[[], AsyncContextManager[AsyncSession]]:
242
252
  return factory
243
253
 
244
254
 
255
+ @dataclass(frozen=True)
256
+ class ScaffolderConfig:
257
+ db: DbSessionFactory
258
+ tracing_fixture_names: Iterable[str] = field(default_factory=list)
259
+ force_fixture_ingestion: bool = False
260
+ scaffold_datasets: bool = False
261
+ phoenix_url: str = f"http://{get_env_host()}:{get_env_port()}"
262
+
263
+
245
264
  class Scaffolder(DaemonTask):
246
265
  def __init__(
247
266
  self,
248
- db: DbSessionFactory,
267
+ config: ScaffolderConfig,
249
268
  queue_span: Callable[[Span, ProjectName], Awaitable[None]],
250
269
  queue_evaluation: Callable[[pb.Evaluation], Awaitable[None]],
251
- tracing_fixture_names: Set[str] = set(),
252
- force_fixture_ingestion: bool = False,
253
270
  ) -> None:
254
271
  super().__init__()
255
- self._db = db
272
+ self._db = config.db
256
273
  self._queue_span = queue_span
257
274
  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
275
+ self._tracing_fixtures = [
276
+ get_trace_fixture_by_name(name) for name in set(config.tracing_fixture_names)
277
+ ]
278
+ self._force_fixture_ingestion = config.force_fixture_ingestion
279
+ self._scaffold_datasets = config.scaffold_datasets
280
+ self._phoenix_url = config.phoenix_url
262
281
 
263
282
  async def __aenter__(self) -> None:
283
+ if not self._tracing_fixtures:
284
+ return
264
285
  await self.start()
265
286
 
266
287
  async def __aexit__(self, *args: Any, **kwargs: Any) -> None:
@@ -272,7 +293,7 @@ class Scaffolder(DaemonTask):
272
293
  Determines whether to load fixtures and handles them.
273
294
  """
274
295
  if await self._should_load_fixtures():
275
- logger.info("Loading trace fixtures.")
296
+ logger.info("Loading trace fixtures...")
276
297
  await self._handle_tracing_fixtures()
277
298
  logger.info("Finished loading fixtures.")
278
299
  else:
@@ -317,6 +338,10 @@ class Scaffolder(DaemonTask):
317
338
  get_evals_from_fixture(fixture.name),
318
339
  )
319
340
 
341
+ # Ingest dataset fixtures
342
+ if self._scaffold_datasets:
343
+ await self._handle_dataset_fixtures(fixture)
344
+
320
345
  project_name = fixture.project_name or fixture.name
321
346
  logger.info(f"Loading '{project_name}' fixtures...")
322
347
  for span in fixture_spans:
@@ -331,6 +356,19 @@ class Scaffolder(DaemonTask):
331
356
  except Exception as e:
332
357
  logger.error(f"Unexpected error processing fixture '{fixture.name}': {e}")
333
358
 
359
+ async def _handle_dataset_fixtures(self, fixture: TracesFixture) -> None:
360
+ loop = asyncio.get_running_loop()
361
+ try:
362
+ dataset_fixtures = await loop.run_in_executor(None, get_dataset_fixtures, fixture.name)
363
+ await loop.run_in_executor(
364
+ None,
365
+ send_dataset_fixtures,
366
+ self._phoenix_url,
367
+ dataset_fixtures,
368
+ )
369
+ except Exception as e:
370
+ logger.error(f"Error processing dataset fixture: {e}")
371
+
334
372
 
335
373
  def _lifespan(
336
374
  *,
@@ -342,30 +380,34 @@ def _lifespan(
342
380
  startup_callbacks: Iterable[Callable[[], None]] = (),
343
381
  shutdown_callbacks: Iterable[Callable[[], None]] = (),
344
382
  read_only: bool = False,
345
- tracing_fixture_names: Set[str] = set(),
346
- force_fixture_ingestion: bool = False,
383
+ scaffolder_config: Optional[ScaffolderConfig] = None,
347
384
  ) -> StatefulLifespan[FastAPI]:
348
385
  @contextlib.asynccontextmanager
349
386
  async def lifespan(_: FastAPI) -> AsyncIterator[Dict[str, Any]]:
350
387
  global DB_MUTEX
351
388
  DB_MUTEX = asyncio.Lock() if db.dialect is SupportedSQLDialect.SQLITE else None
352
- async with bulk_inserter as (
353
- enqueue,
354
- queue_span,
355
- queue_evaluation,
356
- enqueue_operation,
357
- ), GrpcServer(
358
- queue_span,
359
- disabled=read_only,
360
- tracer_provider=tracer_provider,
361
- enable_prometheus=enable_prometheus,
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
- ):
389
+ async with AsyncExitStack() as stack:
390
+ (
391
+ enqueue,
392
+ queue_span,
393
+ queue_evaluation,
394
+ enqueue_operation,
395
+ ) = await stack.enter_async_context(bulk_inserter)
396
+ grpc_server = GrpcServer(
397
+ queue_span,
398
+ disabled=read_only,
399
+ tracer_provider=tracer_provider,
400
+ enable_prometheus=enable_prometheus,
401
+ )
402
+ await stack.enter_async_context(grpc_server)
403
+ await stack.enter_async_context(dml_event_handler)
404
+ if scaffolder_config:
405
+ scaffolder = Scaffolder(
406
+ config=scaffolder_config,
407
+ queue_span=queue_span,
408
+ queue_evaluation=queue_evaluation,
409
+ )
410
+ await stack.enter_async_context(scaffolder)
369
411
  for callback in startup_callbacks:
370
412
  callback()
371
413
  yield {
@@ -557,8 +599,7 @@ def create_app(
557
599
  startup_callbacks: Iterable[Callable[[], None]] = (),
558
600
  shutdown_callbacks: Iterable[Callable[[], None]] = (),
559
601
  secret: Optional[str] = None,
560
- tracing_fixture_names: Set[str] = set(),
561
- force_fixture_ingestion: bool = False,
602
+ scaffolder_config: Optional[ScaffolderConfig] = None,
562
603
  ) -> FastAPI:
563
604
  startup_callbacks_list: List[Callable[[], None]] = list(startup_callbacks)
564
605
  shutdown_callbacks_list: List[Callable[[], None]] = list(shutdown_callbacks)
@@ -644,8 +685,7 @@ def create_app(
644
685
  enable_prometheus=enable_prometheus,
645
686
  shutdown_callbacks=shutdown_callbacks_list,
646
687
  startup_callbacks=startup_callbacks_list,
647
- tracing_fixture_names=tracing_fixture_names,
648
- force_fixture_ingestion=force_fixture_ingestion,
688
+ scaffolder_config=scaffolder_config,
649
689
  ),
650
690
  middleware=[
651
691
  Middleware(HeadersMiddleware),
@@ -186,6 +186,7 @@ class _AnnotationDmlEventHandler(
186
186
  async def __call__(self) -> None:
187
187
  async with self._db() as session:
188
188
  async for row in await session.stream(self._get_stmt()):
189
+ self._last_updated_at.set(Project, row.id)
189
190
  if cache := self._cache_for_dataloaders:
190
191
  self._clear(cache, row.id, row.name)
191
192
 
phoenix/server/main.py CHANGED
@@ -37,6 +37,7 @@ from phoenix.pointcloud.umap_parameters import (
37
37
  UMAPParameters,
38
38
  )
39
39
  from phoenix.server.app import (
40
+ ScaffolderConfig,
40
41
  _db,
41
42
  create_app,
42
43
  create_engine_and_run_migrations,
@@ -143,7 +144,9 @@ if __name__ == "__main__":
143
144
  # Whether the app is running in a development environment
144
145
  parser.add_argument("--dev", action="store_true")
145
146
  parser.add_argument("--no-ui", action="store_true")
147
+
146
148
  subparsers = parser.add_subparsers(dest="command", required=True)
149
+
147
150
  serve_parser = subparsers.add_parser("serve")
148
151
  serve_parser.add_argument(
149
152
  "--with-fixture",
@@ -182,14 +185,27 @@ if __name__ == "__main__":
182
185
  "database is new."
183
186
  ),
184
187
  )
188
+ serve_parser.add_argument(
189
+ "--scaffold-datasets",
190
+ action="store_true", # default is False
191
+ required=False,
192
+ help=(
193
+ "Whether or not to add any datasets defined in "
194
+ "the inputted project or trace fixture. "
195
+ "Default is False. "
196
+ ),
197
+ )
198
+
185
199
  datasets_parser = subparsers.add_parser("datasets")
186
200
  datasets_parser.add_argument("--primary", type=str, required=True)
187
201
  datasets_parser.add_argument("--reference", type=str, required=False)
188
202
  datasets_parser.add_argument("--corpus", type=str, required=False)
189
203
  datasets_parser.add_argument("--trace", type=str, required=False)
204
+
190
205
  fixture_parser = subparsers.add_parser("fixture")
191
206
  fixture_parser.add_argument("fixture", type=str, choices=[fixture.name for fixture in FIXTURES])
192
207
  fixture_parser.add_argument("--primary-only", action="store_true") # Default is False
208
+
193
209
  trace_fixture_parser = subparsers.add_parser("trace-fixture")
194
210
  trace_fixture_parser.add_argument(
195
211
  "fixture", type=str, choices=[fixture.name for fixture in TRACES_FIXTURES]
@@ -197,19 +213,23 @@ if __name__ == "__main__":
197
213
  trace_fixture_parser.add_argument(
198
214
  "--simulate-streaming", action="store_true"
199
215
  ) # Default is False
216
+
200
217
  demo_parser = subparsers.add_parser("demo")
201
218
  demo_parser.add_argument("fixture", type=str, choices=[fixture.name for fixture in FIXTURES])
202
219
  demo_parser.add_argument(
203
220
  "trace_fixture", type=str, choices=[fixture.name for fixture in TRACES_FIXTURES]
204
221
  )
205
222
  demo_parser.add_argument("--simulate-streaming", action="store_true")
206
- args = parser.parse_args()
207
223
 
224
+ args = parser.parse_args()
208
225
  db_connection_str = (
209
226
  args.database_url if args.database_url else get_env_database_connection_str()
210
227
  )
211
228
  export_path = Path(args.export_path) if args.export_path else EXPORT_DIR
229
+
212
230
  force_fixture_ingestion = False
231
+ scaffold_datasets = False
232
+ tracing_fixture_names = set()
213
233
  if args.command == "datasets":
214
234
  primary_inferences_name = args.primary
215
235
  reference_inferences_name = args.reference
@@ -246,7 +266,6 @@ if __name__ == "__main__":
246
266
  simulate_streaming = args.simulate_streaming
247
267
  elif args.command == "serve":
248
268
  # We use sets to avoid duplicates
249
- tracing_fixture_names = set()
250
269
  if args.with_fixture:
251
270
  primary_inferences, reference_inferences, corpus_inferences = get_inferences(
252
271
  str(args.with_fixture),
@@ -264,6 +283,7 @@ if __name__ == "__main__":
264
283
  for fixture in get_trace_fixtures_by_project_name(name)
265
284
  )
266
285
  force_fixture_ingestion = args.force_fixture_ingestion
286
+ scaffold_datasets = args.scaffold_datasets
267
287
  host: Optional[str] = args.host or get_env_host()
268
288
  display_host = host or "localhost"
269
289
  # If the host is "::", the convention is to bind to all interfaces. However, uvicorn
@@ -325,17 +345,25 @@ if __name__ == "__main__":
325
345
  None if corpus_inferences is None else create_model_from_inferences(corpus_inferences)
326
346
  )
327
347
  # Print information about the server
348
+ root_path = urljoin(f"http://{host}:{port}", host_root_path)
328
349
  msg = _WELCOME_MESSAGE.format(
329
350
  version=version("arize-phoenix"),
330
- ui_path=urljoin(f"http://{host}:{port}", host_root_path),
351
+ ui_path=root_path,
331
352
  grpc_path=f"http://{host}:{get_env_grpc_port()}",
332
- http_path=urljoin(urljoin(f"http://{host}:{port}", host_root_path), "v1/traces"),
353
+ http_path=urljoin(root_path, "v1/traces"),
333
354
  storage=get_printable_db_url(db_connection_str),
334
355
  )
335
356
  if authentication_enabled:
336
357
  msg += _EXPERIMENTAL_WARNING.format(auth_enabled=True)
337
358
  if sys.platform.startswith("win"):
338
359
  msg = codecs.encode(msg, "ascii", errors="ignore").decode("ascii").strip()
360
+ scaffolder_config = ScaffolderConfig(
361
+ db=factory,
362
+ tracing_fixture_names=tracing_fixture_names,
363
+ force_fixture_ingestion=force_fixture_ingestion,
364
+ scaffold_datasets=scaffold_datasets,
365
+ phoenix_url=root_path,
366
+ )
339
367
  app = create_app(
340
368
  db=factory,
341
369
  export_path=export_path,
@@ -353,8 +381,7 @@ if __name__ == "__main__":
353
381
  startup_callbacks=[lambda: print(msg)],
354
382
  shutdown_callbacks=instrumentation_cleanups,
355
383
  secret=secret,
356
- tracing_fixture_names=tracing_fixture_names,
357
- force_fixture_ingestion=force_fixture_ingestion,
384
+ scaffolder_config=scaffolder_config,
358
385
  )
359
386
  server = Server(config=Config(app, host=host, port=port, root_path=host_root_path)) # type: ignore
360
387
  Thread(target=_write_pid_file_when_ready, args=(server,), daemon=True).start()
@@ -1,32 +1,32 @@
1
1
  {
2
- "_components-CkSg5zK4.js": {
3
- "file": "assets/components-CkSg5zK4.js",
2
+ "_components-Ddr-5VpM.js": {
3
+ "file": "assets/components-Ddr-5VpM.js",
4
4
  "name": "components",
5
5
  "imports": [
6
- "_vendor-DsnEJuEV.js",
7
- "_vendor-arizeai-DtynTLNi.js",
8
- "_pages-C6emDFIO.js",
6
+ "_vendor-Dpio9q2e.js",
7
+ "_vendor-arizeai-DDzwX9A6.js",
8
+ "_pages-CU_QMYRP.js",
9
9
  "_vendor-three-DwGkEfCM.js",
10
- "_vendor-codemirror-C5to5cK4.js"
10
+ "_vendor-codemirror-xRvQKK1G.js"
11
11
  ]
12
12
  },
13
- "_pages-C6emDFIO.js": {
14
- "file": "assets/pages-C6emDFIO.js",
13
+ "_pages-CU_QMYRP.js": {
14
+ "file": "assets/pages-CU_QMYRP.js",
15
15
  "name": "pages",
16
16
  "imports": [
17
- "_vendor-DsnEJuEV.js",
18
- "_components-CkSg5zK4.js",
19
- "_vendor-arizeai-DtynTLNi.js",
20
- "_vendor-recharts-reihe2SJ.js",
21
- "_vendor-codemirror-C5to5cK4.js"
17
+ "_vendor-Dpio9q2e.js",
18
+ "_components-Ddr-5VpM.js",
19
+ "_vendor-arizeai-DDzwX9A6.js",
20
+ "_vendor-recharts-DoxlB_Wo.js",
21
+ "_vendor-codemirror-xRvQKK1G.js"
22
22
  ]
23
23
  },
24
24
  "_vendor-!~{003}~.js": {
25
25
  "file": "assets/vendor-DxkFTwjz.css",
26
26
  "src": "_vendor-!~{003}~.js"
27
27
  },
28
- "_vendor-DsnEJuEV.js": {
29
- "file": "assets/vendor-DsnEJuEV.js",
28
+ "_vendor-Dpio9q2e.js": {
29
+ "file": "assets/vendor-Dpio9q2e.js",
30
30
  "name": "vendor",
31
31
  "imports": [
32
32
  "_vendor-three-DwGkEfCM.js"
@@ -35,25 +35,25 @@
35
35
  "assets/vendor-DxkFTwjz.css"
36
36
  ]
37
37
  },
38
- "_vendor-arizeai-DtynTLNi.js": {
39
- "file": "assets/vendor-arizeai-DtynTLNi.js",
38
+ "_vendor-arizeai-DDzwX9A6.js": {
39
+ "file": "assets/vendor-arizeai-DDzwX9A6.js",
40
40
  "name": "vendor-arizeai",
41
41
  "imports": [
42
- "_vendor-DsnEJuEV.js"
42
+ "_vendor-Dpio9q2e.js"
43
43
  ]
44
44
  },
45
- "_vendor-codemirror-C5to5cK4.js": {
46
- "file": "assets/vendor-codemirror-C5to5cK4.js",
45
+ "_vendor-codemirror-xRvQKK1G.js": {
46
+ "file": "assets/vendor-codemirror-xRvQKK1G.js",
47
47
  "name": "vendor-codemirror",
48
48
  "imports": [
49
- "_vendor-DsnEJuEV.js"
49
+ "_vendor-Dpio9q2e.js"
50
50
  ]
51
51
  },
52
- "_vendor-recharts-reihe2SJ.js": {
53
- "file": "assets/vendor-recharts-reihe2SJ.js",
52
+ "_vendor-recharts-DoxlB_Wo.js": {
53
+ "file": "assets/vendor-recharts-DoxlB_Wo.js",
54
54
  "name": "vendor-recharts",
55
55
  "imports": [
56
- "_vendor-DsnEJuEV.js"
56
+ "_vendor-Dpio9q2e.js"
57
57
  ]
58
58
  },
59
59
  "_vendor-three-DwGkEfCM.js": {
@@ -61,18 +61,18 @@
61
61
  "name": "vendor-three"
62
62
  },
63
63
  "index.tsx": {
64
- "file": "assets/index-DTecsU5w.js",
64
+ "file": "assets/index-BvNNAF8n.js",
65
65
  "name": "index",
66
66
  "src": "index.tsx",
67
67
  "isEntry": true,
68
68
  "imports": [
69
- "_vendor-DsnEJuEV.js",
70
- "_vendor-arizeai-DtynTLNi.js",
71
- "_pages-C6emDFIO.js",
72
- "_components-CkSg5zK4.js",
69
+ "_vendor-Dpio9q2e.js",
70
+ "_vendor-arizeai-DDzwX9A6.js",
71
+ "_pages-CU_QMYRP.js",
72
+ "_components-Ddr-5VpM.js",
73
73
  "_vendor-three-DwGkEfCM.js",
74
- "_vendor-recharts-reihe2SJ.js",
75
- "_vendor-codemirror-C5to5cK4.js"
74
+ "_vendor-recharts-DoxlB_Wo.js",
75
+ "_vendor-codemirror-xRvQKK1G.js"
76
76
  ]
77
77
  }
78
78
  }