arize-phoenix 8.31.0__py3-none-any.whl → 8.32.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.4
2
2
  Name: arize-phoenix
3
- Version: 8.31.0
3
+ Version: 8.32.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
@@ -214,6 +214,7 @@ Phoenix is built on top of OpenTelemetry and is vendor, language, and framework
214
214
  | [AWS Bedrock](https://docs.arize.com/phoenix/tracing/integrations-tracing/bedrock) | `openinference-instrumentation-bedrock` | [![PyPI Version](https://img.shields.io/pypi/v/openinference-instrumentation-bedrock.svg)](https://pypi.python.org/pypi/openinference-instrumentation-bedrock) |
215
215
  | [LangChain](https://docs.arize.com/phoenix/tracing/integrations-tracing/langchain) | `openinference-instrumentation-langchain` | [![PyPI Version](https://img.shields.io/pypi/v/openinference-instrumentation-langchain.svg)](https://pypi.python.org/pypi/openinference-instrumentation-langchain) |
216
216
  | [MistralAI](https://docs.arize.com/phoenix/tracing/integrations-tracing/mistralai) | `openinference-instrumentation-mistralai` | [![PyPI Version](https://img.shields.io/pypi/v/openinference-instrumentation-mistralai.svg)](https://pypi.python.org/pypi/openinference-instrumentation-mistralai) |
217
+ | [Google GenAI](https://docs.arize.com/phoenix/tracing/integrations-tracing/google-gen-ai) | `openinference-instrumentation-google-genai` | [![PyPI Version](https://img.shields.io/pypi/v/openinference-instrumentation-google-genai.svg)](https://pypi.python.org/pypi/openinference-instrumentation-google-genai) |
217
218
  | [Guardrails](https://docs.arize.com/phoenix/tracing/integrations-tracing/guardrails) | `openinference-instrumentation-guardrails` | [![PyPI Version](https://img.shields.io/pypi/v/openinference-instrumentation-guardrails.svg)](https://pypi.python.org/pypi/openinference-instrumentation-guardrails) |
218
219
  | [VertexAI](https://docs.arize.com/phoenix/tracing/integrations-tracing/vertexai) | `openinference-instrumentation-vertexai` | [![PyPI Version](https://img.shields.io/pypi/v/openinference-instrumentation-vertexai.svg)](https://pypi.python.org/pypi/openinference-instrumentation-vertexai) |
219
220
  | [CrewAI](https://docs.arize.com/phoenix/tracing/integrations-tracing/crewai) | `openinference-instrumentation-crewai` | [![PyPI Version](https://img.shields.io/pypi/v/openinference-instrumentation-crewai.svg)](https://pypi.python.org/pypi/openinference-instrumentation-crewai) |
@@ -248,6 +249,7 @@ Join our community to connect with thousands of AI builders.
248
249
  - 🐞 Report bugs with [GitHub Issues](https://github.com/Arize-ai/phoenix/issues).
249
250
  - 𝕏 Follow us on [𝕏](https://twitter.com/ArizePhoenix).
250
251
  - 🗺️ Check out our [roadmap](https://github.com/orgs/Arize-ai/projects/45) to see where we're heading next.
252
+ - 🧑‍🏫 Deep dive into everything [Agents](http://arize.com/ai-agents/) and [LLM Evaluations](https://arize.com/llm-evaluation) on Arize's Learning Hubs.
251
253
 
252
254
  ## Breaking Changes
253
255
 
@@ -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=ngkyKGVatX3cO2WJdo2hKdaVKP-xJCMvqthvga6kJss,5196
8
8
  phoenix/settings.py,sha256=x87BX7hWGQQZbrW_vrYqFR_izCGfO9gFc--JXUG4Tdk,754
9
- phoenix/version.py,sha256=Sa-CqA7DAHrfdGC8Wbr_rWQJ6kTLqJ_S7kdFFAVwwmg,23
9
+ phoenix/version.py,sha256=UaNVMdObIxyLeFhiJhW0aDRn8ABTA1EjarJjwY2CmEY,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
@@ -16,12 +16,13 @@ phoenix/db/README.md,sha256=7BFJuecWJjFSxEYmoSv4CvoFHHszX4tFy3HJExOxS-4,8661
16
16
  phoenix/db/__init__.py,sha256=pDjEFXukHmJBM-1D8RjmXkvLsz85YWNxMQczt81ec3A,118
17
17
  phoenix/db/alembic.ini,sha256=GIS6HpHaKaJbbuahZg1Rc1D2_QqyCkV9r58wdARGf6w,3262
18
18
  phoenix/db/bulk_inserter.py,sha256=faNjuwLqqsw4ky8sa4D0h9u5TvEDTOjrccUW89L008E,12725
19
- phoenix/db/engines.py,sha256=gxcP2aNy_JyKHv1MO4d2UM47GTMy1jDcN-FQETZ3iNA,7348
19
+ phoenix/db/engines.py,sha256=tB_8iWMDz0folryVvw29sbBUxJOB2XZ-Xx0Uexj3uns,6889
20
20
  phoenix/db/enums.py,sha256=tt7iovXLhVTLZ3_LbHNGgcI44SnNjXfkKtLAZG57T54,428
21
21
  phoenix/db/facilitator.py,sha256=6CeUMNTkNXrdviSQspY9ktFpVX-JHXcMJz6L0PuHxrI,7481
22
22
  phoenix/db/helpers.py,sha256=rbbHcl-STzcEpcXCYx6jbKzko7r3ggrWHHsXjZ48HsM,5352
23
23
  phoenix/db/migrate.py,sha256=oUrXH8yEbcpL4eh09aSCuUiSrhFli0eT5D_j4ZmYChY,2797
24
24
  phoenix/db/models.py,sha256=TbHgtT7WWdkQK-OtLsUqp3MwP23HGV1IaSAWTqCf5ac,45707
25
+ phoenix/db/pg_config.py,sha256=h6mB7qF7t4Zk6VGvAiyefHGVu74o-yJynaWzeE39k9Y,6001
25
26
  phoenix/db/insertion/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
26
27
  phoenix/db/insertion/constants.py,sha256=8wifm7X-1XvroZ__R2Gc96NsgLhTDn0zXl4lehlXtcA,70
27
28
  phoenix/db/insertion/dataset.py,sha256=I9OC1ouVx7m6BH_c8hvcxW1dWGRAtpvXee29yBTuFkg,7136
@@ -220,7 +221,7 @@ phoenix/server/api/routers/v1/experiments.py,sha256=V9_sxqLTE1MKGFu9H3FEdGKr70lY
220
221
  phoenix/server/api/routers/v1/models.py,sha256=r0nM2kFJ3mxDqgc5vFr1cjNuyOPs3RIKE_DS2VMdF48,1749
221
222
  phoenix/server/api/routers/v1/projects.py,sha256=qv4RffYVGCJQuJ3FzaTVkueY8qKY0-GUbG9eSdm7oRY,14181
222
223
  phoenix/server/api/routers/v1/prompts.py,sha256=aBOUBwLDzZDIzJQkxJcR8ZKnakNJOLMwzsLKINSs1mA,26545
223
- phoenix/server/api/routers/v1/spans.py,sha256=uoU_bwIgz86fuvPjP5sX8goDyuCcnsTig-x3f17p60U,9625
224
+ phoenix/server/api/routers/v1/spans.py,sha256=kvWRbMy5LjEPOw1Hd3PyncSspRYNi6ISe3Wr3qRpHa0,9751
224
225
  phoenix/server/api/routers/v1/traces.py,sha256=hSv35QIB4mwFgp53rOpz3zWIiSwbZzQnjafD790QuJU,7908
225
226
  phoenix/server/api/routers/v1/utils.py,sha256=SoRl0Dc8By15ZckhNcXg2QRrqYjMvgTjVcqrZ6MwVmo,3065
226
227
  phoenix/server/api/types/Annotation.py,sha256=r1JDvkWoFKeLRysos3l1exqKfs8zoklVv2tNSG9kFzQ,849
@@ -319,10 +320,10 @@ phoenix/server/static/apple-touch-icon-76x76.png,sha256=CT_xT12I0u2i0WU8JzBZBuOQ
319
320
  phoenix/server/static/apple-touch-icon.png,sha256=fOfpjqGpWYbJ0eAurKsyoZP1EAs6ZVooBJ_SGk2ZkDs,3801
320
321
  phoenix/server/static/favicon.ico,sha256=bY0vvCKRftemZfPShwZtE93DiiQdaYaozkPGwNFr6H8,34494
321
322
  phoenix/server/static/modernizr.js,sha256=mvK-XtkNqjOral-QvzoqsyOMECXIMu5BQwSVN_wcU9c,2564
322
- phoenix/server/static/.vite/manifest.json,sha256=5ikTlLYIjdjOVgckDRctdD8qzVkeLjZbq7Ej0yMPFqU,2165
323
- phoenix/server/static/assets/components-C0W7rSGA.js,sha256=AMKAUIAq-84OfX-YRErFUSsj7De-FBf4XGHXMRsLz4I,456411
324
- phoenix/server/static/assets/index-BmLpy9qA.js,sha256=b1J1-ZmyqBD4P6UPPtEs_nXqVsgBoA-qi1ewdJgu0oE,60460
325
- phoenix/server/static/assets/pages-wYat6c3P.js,sha256=x8xm3-E1ceUhC6MznDcQkbU63ljqLwo-nj34dTfRqRk,864937
323
+ phoenix/server/static/.vite/manifest.json,sha256=lkhQOaQx8cjiJQeVrrDfp2_pSvKAS8o-DDrzjdLXY7Y,2165
324
+ phoenix/server/static/assets/components-x-gKFJ8C.js,sha256=X4qsvxt0Au4bURJd_ntsb_5zDbCCf6ExCyWjTc3QANc,458101
325
+ phoenix/server/static/assets/index-B0CbpsxD.js,sha256=0Y1a5THRDD7MIVBniyp-LQRcFniB0L6GuLI7LIorVNo,60460
326
+ phoenix/server/static/assets/pages-BU4VdyeH.js,sha256=7rSqUn1XhMhJJdw8u6lEcvwWGpVJI0ciX1y3Z5euwF0,866121
326
327
  phoenix/server/static/assets/vendor-BfhM_F1u.js,sha256=S90L1KRZOw_NX6C9FENfLs6bSuEzf7zVDAqjZAZJZgE,2514280
327
328
  phoenix/server/static/assets/vendor-Cg6lcjUC.css,sha256=nZrkr0u6NNElFGvpWHk9GTHeGoibCXCli1bE7mXZGZg,1816
328
329
  phoenix/server/static/assets/vendor-arizeai-CxXYQNUl.js,sha256=V0umBNhH2psKhzwNKvLylYrqVfHu4I6NDSqejmR2OIU,193248
@@ -333,7 +334,7 @@ phoenix/server/static/assets/vendor-three-C5WAXd5r.js,sha256=ELkg06u70N7h8oFmvqd
333
334
  phoenix/server/templates/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
334
335
  phoenix/server/templates/index.html,sha256=e8_jdi7Eo19SK7DI_gglkTW094D17E0VAegoMmmmvIc,4330
335
336
  phoenix/session/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
336
- phoenix/session/client.py,sha256=51UCIZ_k4Wd4b8f4Yi888MOie6nhF8E_5in41eBTUVU,35234
337
+ phoenix/session/client.py,sha256=uw5WlCuFcN_eEj7Ko2bhJVcaihEIp7Evy50KnL6Sq-k,35602
337
338
  phoenix/session/data_extractor.py,sha256=Y0RzYFaNy9fQj8PEIeQ76TBZ90_E1FW7bXu3K5x0EZY,2782
338
339
  phoenix/session/evaluation.py,sha256=Q3fOMNELvqkk-b6a6PKc8pDJdsNQ0ZbTpseUSA2NKqs,5300
339
340
  phoenix/session/session.py,sha256=twE8tld8knShmTpZp6R80CUt2jau3RmfdfZAQCLSTKU,27621
@@ -342,7 +343,7 @@ phoenix/trace/attributes.py,sha256=hyEKYZWPCP4NRmW7VmiC2voa3TH7FYKUBR9DYiVfXlw,1
342
343
  phoenix/trace/errors.py,sha256=wB1z8qdPckngdfU-TORToekvg3344oNFAA83_hC2yFY,180
343
344
  phoenix/trace/evaluation_conventions.py,sha256=t8jydM3U0-T5YpiQKRJ3tWdWGlHtzKyttYdw-ddvPOk,1048
344
345
  phoenix/trace/exporter.py,sha256=bUXh8fjJIbHurrnt4bAm-cCWqUN5FqNsIc8DZzzklkQ,4695
345
- phoenix/trace/fixtures.py,sha256=k5gduf21C5NbZx95O_WB7mqGnrRiig0ti2VVYQiB9vo,19537
346
+ phoenix/trace/fixtures.py,sha256=1c7fsyvmxC53Fib9T_Qxp_Ly3OZdDbkLQ0XpFzikEjk,20298
346
347
  phoenix/trace/otel.py,sha256=RJSbAuzS4KBS0t-fntXQaaYwv7FmIXRMrw65DI67z8k,10622
347
348
  phoenix/trace/projects.py,sha256=9dKv1aiKL4IYMFsg2xnC6EOIRO0YHtkR5o9ALHbMK9g,2178
348
349
  phoenix/trace/schemas.py,sha256=Su6e567Bei9oo6PsWO2srTcPAj9C2bMgbGtx64Sgqeg,6332
@@ -355,7 +356,7 @@ phoenix/trace/dsl/README.md,sha256=ihmP9zGUC5V-TDbzKla76LuyDqPDQIBUH2BORwxNI68,2
355
356
  phoenix/trace/dsl/__init__.py,sha256=WIQIjJg362XD3s50OsPJJ0xbDsGp41bSv7vDllLrPuA,144
356
357
  phoenix/trace/dsl/filter.py,sha256=yCmQKvIh7SA5Q-bQ-MKMlYY34lZnG39MLEmL2A93MTw,30472
357
358
  phoenix/trace/dsl/helpers.py,sha256=pJESAz7sDNE7HTVCFzxIx5sEsyUSh7RIN7yqmndAaF8,4171
358
- phoenix/trace/dsl/query.py,sha256=ePqfY6ssyHX3E1QQYsxMz_mr-1Uiq6GKQHEb_-3wrdg,31753
359
+ phoenix/trace/dsl/query.py,sha256=qi5k83Ch7mouSzd8w8dkMHVJ9C45tYXZLtb4lMwToZM,36350
359
360
  phoenix/trace/v1/__init__.py,sha256=-IbAD0ruESMjvQLvGAg9CTfjBUATFDx1OXseDPis6-0,88
360
361
  phoenix/trace/v1/evaluation_pb2.py,sha256=8sXvv2BW_vqD30MOMbmkeE2zpmm7ncik21kl3e-HzeQ,2254
361
362
  phoenix/trace/v1/evaluation_pb2.pyi,sha256=cCbbx06gwQmaH14s3J1X25TtaARh-k1abbxQdQCXGm8,4500
@@ -369,9 +370,9 @@ phoenix/utilities/project.py,sha256=auVpARXkDb-JgeX5f2aStyFIkeKvGwN9l7qrFeJMVxI,
369
370
  phoenix/utilities/re.py,sha256=6YyUWIkv0zc2SigsxfOWIHzdpjKA_TZo2iqKq7zJKvw,2081
370
371
  phoenix/utilities/span_store.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
371
372
  phoenix/utilities/template_formatters.py,sha256=gh9PJD6WEGw7TEYXfSst1UR4pWWwmjxMLrDVQ_CkpkQ,2779
372
- arize_phoenix-8.31.0.dist-info/METADATA,sha256=gg_zdpnAgEqbF0nuN1D4eG-nsk8is9bAVoccNYraMdY,24478
373
- arize_phoenix-8.31.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
374
- arize_phoenix-8.31.0.dist-info/entry_points.txt,sha256=Pgpn8Upxx9P8z8joPXZWl2LlnAlGc3gcQoVchb06X1Q,94
375
- arize_phoenix-8.31.0.dist-info/licenses/IP_NOTICE,sha256=JBqyyCYYxGDfzQ0TtsQgjts41IJoa-hiwDrBjCb9gHM,469
376
- arize_phoenix-8.31.0.dist-info/licenses/LICENSE,sha256=HFkW9REuMOkvKRACuwLPT0hRydHb3zNg-fdFt94td18,3794
377
- arize_phoenix-8.31.0.dist-info/RECORD,,
373
+ arize_phoenix-8.32.1.dist-info/METADATA,sha256=Tmih_769p0j9crLeyhY-u6bNxpEXH8MlLXKuMKxC0bg,24950
374
+ arize_phoenix-8.32.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
375
+ arize_phoenix-8.32.1.dist-info/entry_points.txt,sha256=Pgpn8Upxx9P8z8joPXZWl2LlnAlGc3gcQoVchb06X1Q,94
376
+ arize_phoenix-8.32.1.dist-info/licenses/IP_NOTICE,sha256=JBqyyCYYxGDfzQ0TtsQgjts41IJoa-hiwDrBjCb9gHM,469
377
+ arize_phoenix-8.32.1.dist-info/licenses/LICENSE,sha256=HFkW9REuMOkvKRACuwLPT0hRydHb3zNg-fdFt94td18,3794
378
+ arize_phoenix-8.32.1.dist-info/RECORD,,
phoenix/db/engines.py CHANGED
@@ -1,10 +1,13 @@
1
+ from __future__ import annotations
2
+
1
3
  import asyncio
2
4
  import json
5
+ import logging
3
6
  from collections.abc import Callable
4
7
  from datetime import datetime
5
8
  from enum import Enum
6
9
  from sqlite3 import Connection
7
- from typing import Any, Mapping
10
+ from typing import Any
8
11
 
9
12
  import aiosqlite
10
13
  import numpy as np
@@ -18,10 +21,13 @@ from phoenix.config import LoggingMode, get_env_database_schema
18
21
  from phoenix.db.helpers import SupportedSQLDialect
19
22
  from phoenix.db.migrate import migrate_in_thread
20
23
  from phoenix.db.models import init_models
24
+ from phoenix.db.pg_config import get_pg_config
21
25
  from phoenix.settings import Settings
22
26
 
23
27
  sqlean.extensions.enable("text", "stats")
24
28
 
29
+ logger = logging.getLogger(__name__)
30
+
25
31
 
26
32
  def set_sqlite_pragma(connection: Connection, _: Any) -> None:
27
33
  cursor = connection.cursor()
@@ -163,15 +169,20 @@ def aio_postgresql_engine(
163
169
  log_to_stdout: bool = False,
164
170
  log_migrations_to_stdout: bool = True,
165
171
  ) -> AsyncEngine:
172
+ asyncpg_url, asyncpg_args = get_pg_config(url, "asyncpg")
166
173
  engine = create_async_engine(
167
- url=url.set(query=_asyncpg_url_query(url.query)),
174
+ url=asyncpg_url,
175
+ connect_args=asyncpg_args,
168
176
  echo=log_to_stdout,
169
177
  json_serializer=_dumps,
170
178
  )
171
179
  if not migrate:
172
180
  return engine
181
+
182
+ psycopg_url, psycopg_args = get_pg_config(url, "psycopg")
173
183
  sync_engine = sqlalchemy.create_engine(
174
- url=url.set(drivername="postgresql+psycopg", query=_psycopg_url_query(url.query)),
184
+ url=psycopg_url,
185
+ connect_args=psycopg_args,
175
186
  echo=log_migrations_to_stdout,
176
187
  json_serializer=_dumps,
177
188
  )
@@ -181,24 +192,6 @@ def aio_postgresql_engine(
181
192
  return engine
182
193
 
183
194
 
184
- def _asyncpg_url_query(query: Mapping[str, Any]) -> dict[str, Any]:
185
- ans = dict(query)
186
- if sslmode := (ans.pop("sslmode", None) or ans.pop("ssl", None)):
187
- # https://github.com/MagicStack/asyncpg/issues/737
188
- ans["ssl"] = sslmode
189
- return ans
190
-
191
-
192
- def _psycopg_url_query(query: Mapping[str, Any]) -> dict[str, Any]:
193
- ans = dict(query)
194
- if sslmode := (ans.pop("sslmode", None) or ans.pop("ssl", None)):
195
- ans["sslmode"] = sslmode
196
- # prepared_statement_cache_size is only used by asyncpg, see:
197
- # https://docs.sqlalchemy.org/en/20/dialects/postgresql.html#prepared-statement-cache
198
- ans.pop("prepared_statement_cache_size", None)
199
- return ans
200
-
201
-
202
195
  def _dumps(obj: Any) -> str:
203
196
  return json.dumps(obj, cls=_Encoder)
204
197
 
@@ -0,0 +1,197 @@
1
+ from __future__ import annotations
2
+
3
+ import ssl
4
+ from typing import Any, Container, Final, Literal, Mapping, TypedDict, get_type_hints
5
+
6
+ from sqlalchemy import URL
7
+ from typing_extensions import assert_never
8
+
9
+
10
+ def get_pg_config(
11
+ url: URL,
12
+ driver: Literal["psycopg", "asyncpg"],
13
+ ) -> tuple[URL, dict[str, Any]]:
14
+ """Convert SQLAlchemy URL to driver-specific configuration.
15
+
16
+ Args:
17
+ url: SQLAlchemy URL
18
+ driver: "psycopg" or "asyncpg"
19
+
20
+ Returns:
21
+ Tuple of (base_url, connect_args):
22
+ - base_url: URL with driver prefix and non-SSL parameters
23
+ - connect_args: SSL configuration for the driver
24
+ """
25
+ # Create new URL with appropriate driver
26
+ query = url.query
27
+ ssl_args = _get_ssl_args(query)
28
+
29
+ # Create base URL without SSL parameters
30
+ base_url = url.set(
31
+ drivername=f"postgresql+{driver}",
32
+ query={k: v for k, v in query.items() if k not in _SSL_KEYS},
33
+ )
34
+
35
+ # Get appropriate SSL configuration based on driver
36
+ if driver == "psycopg":
37
+ connect_args = dict(ssl_args)
38
+ # Remove asyncpg-specific parameters from base URL
39
+ base_url = base_url.set(query=_remove_asyncpg_only_params(base_url.query))
40
+ elif driver == "asyncpg":
41
+ # Only create SSL context if we have SSL parameters and sslmode is not disable
42
+ if ssl_args and ssl_args.get("sslmode") != "disable":
43
+ connect_args = {"ssl": _get_ssl_context(ssl_args)}
44
+ else:
45
+ connect_args = {}
46
+ else:
47
+ assert_never(driver)
48
+ return base_url, connect_args
49
+
50
+
51
+ class _SSLArgs(TypedDict, total=False):
52
+ """SSL parameters for PostgreSQL connections.
53
+
54
+ All fields are optional. Only includes parameters that can be converted to ssl.SSLContext.
55
+
56
+ Attributes:
57
+ sslmode: SSL mode (disable, allow, prefer, require, verify-ca, verify-full)
58
+ sslrootcert: Path to root CA certificate
59
+ sslcert: Path to client certificate
60
+ sslkey: Path to client private key
61
+ sslpassword: Password for private key
62
+ sslcrl: Path to CRL file
63
+ sslcrldir: Path to CRL directory
64
+ sslsni: Enable SNI (0 or 1)
65
+ """
66
+
67
+ sslmode: str
68
+ sslrootcert: str
69
+ sslcert: str
70
+ sslkey: str
71
+ sslpassword: str
72
+ sslcrl: str
73
+ sslcrldir: str
74
+ sslsni: str
75
+
76
+
77
+ _SSL_KEYS: Final[Container[str]] = get_type_hints(_SSLArgs).keys()
78
+
79
+
80
+ def _get_ssl_args(
81
+ query_params: Mapping[str, str | tuple[str, ...]],
82
+ ) -> _SSLArgs:
83
+ """Extract SSL parameters from a SQLAlchemy URL query.
84
+
85
+ Args:
86
+ query_params: SQLAlchemy URL query parameters
87
+
88
+ Returns:
89
+ Dictionary of SSL parameters
90
+ """
91
+ result: _SSLArgs = {}
92
+
93
+ def get_str(key: str) -> str | None:
94
+ if value := query_params.get(key):
95
+ if not isinstance(value, str):
96
+ raise ValueError(f"Invalid value type for {key}: {type(value)}")
97
+ return value
98
+ return None
99
+
100
+ if sslmode := get_str("sslmode"):
101
+ result["sslmode"] = sslmode
102
+
103
+ if ca_cert := get_str("sslrootcert"):
104
+ result["sslrootcert"] = ca_cert
105
+
106
+ if cert := get_str("sslcert"):
107
+ result["sslcert"] = cert
108
+
109
+ if key := get_str("sslkey"):
110
+ result["sslkey"] = key
111
+
112
+ if password := get_str("sslpassword"):
113
+ result["sslpassword"] = password
114
+
115
+ if crl := get_str("sslcrl"):
116
+ result["sslcrl"] = crl
117
+
118
+ if crl_dir := get_str("sslcrldir"):
119
+ result["sslcrldir"] = crl_dir
120
+
121
+ if sslsni := get_str("sslsni"):
122
+ result["sslsni"] = sslsni
123
+
124
+ return result
125
+
126
+
127
+ def _get_ssl_context(ssl_args: _SSLArgs) -> ssl.SSLContext:
128
+ """Convert PostgreSQL SSL parameters to an SSL context.
129
+
130
+ Args:
131
+ ssl_args: PostgreSQL SSL parameters from _SSLArgs TypedDict
132
+
133
+ Returns:
134
+ Configured SSL context with:
135
+ - Root CA certificate for server verification (if provided)
136
+ - Client certificate and key for mutual TLS (if provided)
137
+ - SSL mode appropriate verification settings
138
+ - Certificate revocation list checking (if provided)
139
+ """
140
+ # Create SSL context
141
+ ssl_context = ssl.create_default_context()
142
+
143
+ # Load CA certificate if provided
144
+ if ca_cert := ssl_args.get("sslrootcert"):
145
+ ssl_context.load_verify_locations(cafile=ca_cert)
146
+
147
+ # Load client certificates if provided
148
+ if (cert := ssl_args.get("sslcert")) and (key := ssl_args.get("sslkey")):
149
+ ssl_context.load_cert_chain(
150
+ certfile=cert,
151
+ keyfile=key,
152
+ password=ssl_args.get("sslpassword"),
153
+ )
154
+
155
+ # Load CRL if provided
156
+ if crl := ssl_args.get("sslcrl"):
157
+ ssl_context.load_verify_locations(cafile=crl)
158
+ if crl_dir := ssl_args.get("sslcrldir"):
159
+ ssl_context.load_verify_locations(capath=crl_dir)
160
+
161
+ # Set verification mode based on sslmode
162
+ sslmode = ssl_args.get("sslmode", "prefer")
163
+ if sslmode == "verify-full":
164
+ # Full verification: certificate and hostname
165
+ ssl_context.check_hostname = True
166
+ ssl_context.verify_mode = ssl.CERT_REQUIRED
167
+ elif sslmode == "verify-ca":
168
+ # Certificate verification only
169
+ ssl_context.check_hostname = False
170
+ ssl_context.verify_mode = ssl.CERT_REQUIRED
171
+ else: # require, prefer, allow, disable
172
+ # No verification, just encryption
173
+ ssl_context.check_hostname = False
174
+ ssl_context.verify_mode = ssl.CERT_NONE
175
+
176
+ return ssl_context
177
+
178
+
179
+ def _remove_asyncpg_only_params(
180
+ query: Mapping[str, str | tuple[str, ...]],
181
+ ) -> dict[str, str | tuple[str, ...]]:
182
+ """Remove asyncpg-specific parameters from a SQLAlchemy URL query.
183
+
184
+ Args:
185
+ query: SQLAlchemy URL query parameters
186
+
187
+ Returns:
188
+ Dictionary of query parameters with asyncpg-specific parameters removed
189
+ """
190
+ return {k: v for k, v in query.items() if k not in _ASYNCPG_ONLY_KEYS}
191
+
192
+
193
+ # Asyncpg-specific parameter keys
194
+ _ASYNCPG_ONLY_KEYS: Final[tuple[str, ...]] = (
195
+ "prepared_statement_cache_size",
196
+ # Add other asyncpg-specific parameters here if needed
197
+ )
@@ -47,6 +47,7 @@ class QuerySpansRequestBody(V1RoutesBaseModel):
47
47
  end_time: Optional[datetime] = None
48
48
  limit: int = DEFAULT_SPAN_LIMIT
49
49
  root_spans_only: Optional[bool] = None
50
+ orphan_span_as_root_span: bool = True
50
51
  project_name: Optional[str] = Field(
51
52
  default=None,
52
53
  description=(
@@ -116,6 +117,7 @@ async def query_spans_handler(
116
117
  ),
117
118
  limit=request_body.limit,
118
119
  root_spans_only=request_body.root_spans_only,
120
+ orphan_span_as_root_span=request_body.orphan_span_as_root_span,
119
121
  )
120
122
  )
121
123
  if not results:
@@ -1,22 +1,22 @@
1
1
  {
2
- "_components-C0W7rSGA.js": {
3
- "file": "assets/components-C0W7rSGA.js",
2
+ "_components-x-gKFJ8C.js": {
3
+ "file": "assets/components-x-gKFJ8C.js",
4
4
  "name": "components",
5
5
  "imports": [
6
6
  "_vendor-BfhM_F1u.js",
7
- "_pages-wYat6c3P.js",
7
+ "_pages-BU4VdyeH.js",
8
8
  "_vendor-arizeai-CxXYQNUl.js",
9
9
  "_vendor-codemirror-B0NIFPOL.js",
10
10
  "_vendor-three-C5WAXd5r.js"
11
11
  ]
12
12
  },
13
- "_pages-wYat6c3P.js": {
14
- "file": "assets/pages-wYat6c3P.js",
13
+ "_pages-BU4VdyeH.js": {
14
+ "file": "assets/pages-BU4VdyeH.js",
15
15
  "name": "pages",
16
16
  "imports": [
17
17
  "_vendor-BfhM_F1u.js",
18
18
  "_vendor-arizeai-CxXYQNUl.js",
19
- "_components-C0W7rSGA.js",
19
+ "_components-x-gKFJ8C.js",
20
20
  "_vendor-codemirror-B0NIFPOL.js",
21
21
  "_vendor-recharts-CrrDFWK1.js"
22
22
  ]
@@ -69,15 +69,15 @@
69
69
  "name": "vendor-three"
70
70
  },
71
71
  "index.tsx": {
72
- "file": "assets/index-BmLpy9qA.js",
72
+ "file": "assets/index-B0CbpsxD.js",
73
73
  "name": "index",
74
74
  "src": "index.tsx",
75
75
  "isEntry": true,
76
76
  "imports": [
77
77
  "_vendor-BfhM_F1u.js",
78
78
  "_vendor-arizeai-CxXYQNUl.js",
79
- "_pages-wYat6c3P.js",
80
- "_components-C0W7rSGA.js",
79
+ "_pages-BU4VdyeH.js",
80
+ "_components-x-gKFJ8C.js",
81
81
  "_vendor-three-C5WAXd5r.js",
82
82
  "_vendor-codemirror-B0NIFPOL.js",
83
83
  "_vendor-shiki-C5bJ-RPf.js",