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

Files changed (338) hide show
  1. arize_phoenix-7.7.0.dist-info/METADATA +261 -0
  2. arize_phoenix-7.7.0.dist-info/RECORD +345 -0
  3. {arize_phoenix-3.16.1.dist-info → arize_phoenix-7.7.0.dist-info}/WHEEL +1 -1
  4. arize_phoenix-7.7.0.dist-info/entry_points.txt +3 -0
  5. phoenix/__init__.py +86 -14
  6. phoenix/auth.py +309 -0
  7. phoenix/config.py +675 -45
  8. phoenix/core/model.py +32 -30
  9. phoenix/core/model_schema.py +102 -109
  10. phoenix/core/model_schema_adapter.py +48 -45
  11. phoenix/datetime_utils.py +24 -3
  12. phoenix/db/README.md +54 -0
  13. phoenix/db/__init__.py +4 -0
  14. phoenix/db/alembic.ini +85 -0
  15. phoenix/db/bulk_inserter.py +294 -0
  16. phoenix/db/engines.py +208 -0
  17. phoenix/db/enums.py +20 -0
  18. phoenix/db/facilitator.py +113 -0
  19. phoenix/db/helpers.py +159 -0
  20. phoenix/db/insertion/constants.py +2 -0
  21. phoenix/db/insertion/dataset.py +227 -0
  22. phoenix/db/insertion/document_annotation.py +171 -0
  23. phoenix/db/insertion/evaluation.py +191 -0
  24. phoenix/db/insertion/helpers.py +98 -0
  25. phoenix/db/insertion/span.py +193 -0
  26. phoenix/db/insertion/span_annotation.py +158 -0
  27. phoenix/db/insertion/trace_annotation.py +158 -0
  28. phoenix/db/insertion/types.py +256 -0
  29. phoenix/db/migrate.py +86 -0
  30. phoenix/db/migrations/data_migration_scripts/populate_project_sessions.py +199 -0
  31. phoenix/db/migrations/env.py +114 -0
  32. phoenix/db/migrations/script.py.mako +26 -0
  33. phoenix/db/migrations/versions/10460e46d750_datasets.py +317 -0
  34. phoenix/db/migrations/versions/3be8647b87d8_add_token_columns_to_spans_table.py +126 -0
  35. phoenix/db/migrations/versions/4ded9e43755f_create_project_sessions_table.py +66 -0
  36. phoenix/db/migrations/versions/cd164e83824f_users_and_tokens.py +157 -0
  37. phoenix/db/migrations/versions/cf03bd6bae1d_init.py +280 -0
  38. phoenix/db/models.py +807 -0
  39. phoenix/exceptions.py +5 -1
  40. phoenix/experiments/__init__.py +6 -0
  41. phoenix/experiments/evaluators/__init__.py +29 -0
  42. phoenix/experiments/evaluators/base.py +158 -0
  43. phoenix/experiments/evaluators/code_evaluators.py +184 -0
  44. phoenix/experiments/evaluators/llm_evaluators.py +473 -0
  45. phoenix/experiments/evaluators/utils.py +236 -0
  46. phoenix/experiments/functions.py +772 -0
  47. phoenix/experiments/tracing.py +86 -0
  48. phoenix/experiments/types.py +726 -0
  49. phoenix/experiments/utils.py +25 -0
  50. phoenix/inferences/__init__.py +0 -0
  51. phoenix/{datasets → inferences}/errors.py +6 -5
  52. phoenix/{datasets → inferences}/fixtures.py +49 -42
  53. phoenix/{datasets/dataset.py → inferences/inferences.py} +121 -105
  54. phoenix/{datasets → inferences}/schema.py +11 -11
  55. phoenix/{datasets → inferences}/validation.py +13 -14
  56. phoenix/logging/__init__.py +3 -0
  57. phoenix/logging/_config.py +90 -0
  58. phoenix/logging/_filter.py +6 -0
  59. phoenix/logging/_formatter.py +69 -0
  60. phoenix/metrics/__init__.py +5 -4
  61. phoenix/metrics/binning.py +4 -3
  62. phoenix/metrics/metrics.py +2 -1
  63. phoenix/metrics/mixins.py +7 -6
  64. phoenix/metrics/retrieval_metrics.py +2 -1
  65. phoenix/metrics/timeseries.py +5 -4
  66. phoenix/metrics/wrappers.py +9 -3
  67. phoenix/pointcloud/clustering.py +5 -5
  68. phoenix/pointcloud/pointcloud.py +7 -5
  69. phoenix/pointcloud/projectors.py +5 -6
  70. phoenix/pointcloud/umap_parameters.py +53 -52
  71. phoenix/server/api/README.md +28 -0
  72. phoenix/server/api/auth.py +44 -0
  73. phoenix/server/api/context.py +152 -9
  74. phoenix/server/api/dataloaders/__init__.py +91 -0
  75. phoenix/server/api/dataloaders/annotation_summaries.py +139 -0
  76. phoenix/server/api/dataloaders/average_experiment_run_latency.py +54 -0
  77. phoenix/server/api/dataloaders/cache/__init__.py +3 -0
  78. phoenix/server/api/dataloaders/cache/two_tier_cache.py +68 -0
  79. phoenix/server/api/dataloaders/dataset_example_revisions.py +131 -0
  80. phoenix/server/api/dataloaders/dataset_example_spans.py +38 -0
  81. phoenix/server/api/dataloaders/document_evaluation_summaries.py +144 -0
  82. phoenix/server/api/dataloaders/document_evaluations.py +31 -0
  83. phoenix/server/api/dataloaders/document_retrieval_metrics.py +89 -0
  84. phoenix/server/api/dataloaders/experiment_annotation_summaries.py +79 -0
  85. phoenix/server/api/dataloaders/experiment_error_rates.py +58 -0
  86. phoenix/server/api/dataloaders/experiment_run_annotations.py +36 -0
  87. phoenix/server/api/dataloaders/experiment_run_counts.py +49 -0
  88. phoenix/server/api/dataloaders/experiment_sequence_number.py +44 -0
  89. phoenix/server/api/dataloaders/latency_ms_quantile.py +188 -0
  90. phoenix/server/api/dataloaders/min_start_or_max_end_times.py +85 -0
  91. phoenix/server/api/dataloaders/project_by_name.py +31 -0
  92. phoenix/server/api/dataloaders/record_counts.py +116 -0
  93. phoenix/server/api/dataloaders/session_io.py +79 -0
  94. phoenix/server/api/dataloaders/session_num_traces.py +30 -0
  95. phoenix/server/api/dataloaders/session_num_traces_with_error.py +32 -0
  96. phoenix/server/api/dataloaders/session_token_usages.py +41 -0
  97. phoenix/server/api/dataloaders/session_trace_latency_ms_quantile.py +55 -0
  98. phoenix/server/api/dataloaders/span_annotations.py +26 -0
  99. phoenix/server/api/dataloaders/span_dataset_examples.py +31 -0
  100. phoenix/server/api/dataloaders/span_descendants.py +57 -0
  101. phoenix/server/api/dataloaders/span_projects.py +33 -0
  102. phoenix/server/api/dataloaders/token_counts.py +124 -0
  103. phoenix/server/api/dataloaders/trace_by_trace_ids.py +25 -0
  104. phoenix/server/api/dataloaders/trace_root_spans.py +32 -0
  105. phoenix/server/api/dataloaders/user_roles.py +30 -0
  106. phoenix/server/api/dataloaders/users.py +33 -0
  107. phoenix/server/api/exceptions.py +48 -0
  108. phoenix/server/api/helpers/__init__.py +12 -0
  109. phoenix/server/api/helpers/dataset_helpers.py +217 -0
  110. phoenix/server/api/helpers/experiment_run_filters.py +763 -0
  111. phoenix/server/api/helpers/playground_clients.py +948 -0
  112. phoenix/server/api/helpers/playground_registry.py +70 -0
  113. phoenix/server/api/helpers/playground_spans.py +455 -0
  114. phoenix/server/api/input_types/AddExamplesToDatasetInput.py +16 -0
  115. phoenix/server/api/input_types/AddSpansToDatasetInput.py +14 -0
  116. phoenix/server/api/input_types/ChatCompletionInput.py +38 -0
  117. phoenix/server/api/input_types/ChatCompletionMessageInput.py +24 -0
  118. phoenix/server/api/input_types/ClearProjectInput.py +15 -0
  119. phoenix/server/api/input_types/ClusterInput.py +2 -2
  120. phoenix/server/api/input_types/CreateDatasetInput.py +12 -0
  121. phoenix/server/api/input_types/CreateSpanAnnotationInput.py +18 -0
  122. phoenix/server/api/input_types/CreateTraceAnnotationInput.py +18 -0
  123. phoenix/server/api/input_types/DataQualityMetricInput.py +5 -2
  124. phoenix/server/api/input_types/DatasetExampleInput.py +14 -0
  125. phoenix/server/api/input_types/DatasetSort.py +17 -0
  126. phoenix/server/api/input_types/DatasetVersionSort.py +16 -0
  127. phoenix/server/api/input_types/DeleteAnnotationsInput.py +7 -0
  128. phoenix/server/api/input_types/DeleteDatasetExamplesInput.py +13 -0
  129. phoenix/server/api/input_types/DeleteDatasetInput.py +7 -0
  130. phoenix/server/api/input_types/DeleteExperimentsInput.py +7 -0
  131. phoenix/server/api/input_types/DimensionFilter.py +4 -4
  132. phoenix/server/api/input_types/GenerativeModelInput.py +17 -0
  133. phoenix/server/api/input_types/Granularity.py +1 -1
  134. phoenix/server/api/input_types/InvocationParameters.py +162 -0
  135. phoenix/server/api/input_types/PatchAnnotationInput.py +19 -0
  136. phoenix/server/api/input_types/PatchDatasetExamplesInput.py +35 -0
  137. phoenix/server/api/input_types/PatchDatasetInput.py +14 -0
  138. phoenix/server/api/input_types/PerformanceMetricInput.py +5 -2
  139. phoenix/server/api/input_types/ProjectSessionSort.py +29 -0
  140. phoenix/server/api/input_types/SpanAnnotationSort.py +17 -0
  141. phoenix/server/api/input_types/SpanSort.py +134 -69
  142. phoenix/server/api/input_types/TemplateOptions.py +10 -0
  143. phoenix/server/api/input_types/TraceAnnotationSort.py +17 -0
  144. phoenix/server/api/input_types/UserRoleInput.py +9 -0
  145. phoenix/server/api/mutations/__init__.py +28 -0
  146. phoenix/server/api/mutations/api_key_mutations.py +167 -0
  147. phoenix/server/api/mutations/chat_mutations.py +593 -0
  148. phoenix/server/api/mutations/dataset_mutations.py +591 -0
  149. phoenix/server/api/mutations/experiment_mutations.py +75 -0
  150. phoenix/server/api/{types/ExportEventsMutation.py → mutations/export_events_mutations.py} +21 -18
  151. phoenix/server/api/mutations/project_mutations.py +57 -0
  152. phoenix/server/api/mutations/span_annotations_mutations.py +128 -0
  153. phoenix/server/api/mutations/trace_annotations_mutations.py +127 -0
  154. phoenix/server/api/mutations/user_mutations.py +329 -0
  155. phoenix/server/api/openapi/__init__.py +0 -0
  156. phoenix/server/api/openapi/main.py +17 -0
  157. phoenix/server/api/openapi/schema.py +16 -0
  158. phoenix/server/api/queries.py +738 -0
  159. phoenix/server/api/routers/__init__.py +11 -0
  160. phoenix/server/api/routers/auth.py +284 -0
  161. phoenix/server/api/routers/embeddings.py +26 -0
  162. phoenix/server/api/routers/oauth2.py +488 -0
  163. phoenix/server/api/routers/v1/__init__.py +64 -0
  164. phoenix/server/api/routers/v1/datasets.py +1017 -0
  165. phoenix/server/api/routers/v1/evaluations.py +362 -0
  166. phoenix/server/api/routers/v1/experiment_evaluations.py +115 -0
  167. phoenix/server/api/routers/v1/experiment_runs.py +167 -0
  168. phoenix/server/api/routers/v1/experiments.py +308 -0
  169. phoenix/server/api/routers/v1/pydantic_compat.py +78 -0
  170. phoenix/server/api/routers/v1/spans.py +267 -0
  171. phoenix/server/api/routers/v1/traces.py +208 -0
  172. phoenix/server/api/routers/v1/utils.py +95 -0
  173. phoenix/server/api/schema.py +44 -241
  174. phoenix/server/api/subscriptions.py +597 -0
  175. phoenix/server/api/types/Annotation.py +21 -0
  176. phoenix/server/api/types/AnnotationSummary.py +55 -0
  177. phoenix/server/api/types/AnnotatorKind.py +16 -0
  178. phoenix/server/api/types/ApiKey.py +27 -0
  179. phoenix/server/api/types/AuthMethod.py +9 -0
  180. phoenix/server/api/types/ChatCompletionMessageRole.py +11 -0
  181. phoenix/server/api/types/ChatCompletionSubscriptionPayload.py +46 -0
  182. phoenix/server/api/types/Cluster.py +25 -24
  183. phoenix/server/api/types/CreateDatasetPayload.py +8 -0
  184. phoenix/server/api/types/DataQualityMetric.py +31 -13
  185. phoenix/server/api/types/Dataset.py +288 -63
  186. phoenix/server/api/types/DatasetExample.py +85 -0
  187. phoenix/server/api/types/DatasetExampleRevision.py +34 -0
  188. phoenix/server/api/types/DatasetVersion.py +14 -0
  189. phoenix/server/api/types/Dimension.py +32 -31
  190. phoenix/server/api/types/DocumentEvaluationSummary.py +9 -8
  191. phoenix/server/api/types/EmbeddingDimension.py +56 -49
  192. phoenix/server/api/types/Evaluation.py +25 -31
  193. phoenix/server/api/types/EvaluationSummary.py +30 -50
  194. phoenix/server/api/types/Event.py +20 -20
  195. phoenix/server/api/types/ExampleRevisionInterface.py +14 -0
  196. phoenix/server/api/types/Experiment.py +152 -0
  197. phoenix/server/api/types/ExperimentAnnotationSummary.py +13 -0
  198. phoenix/server/api/types/ExperimentComparison.py +17 -0
  199. phoenix/server/api/types/ExperimentRun.py +119 -0
  200. phoenix/server/api/types/ExperimentRunAnnotation.py +56 -0
  201. phoenix/server/api/types/GenerativeModel.py +9 -0
  202. phoenix/server/api/types/GenerativeProvider.py +85 -0
  203. phoenix/server/api/types/Inferences.py +80 -0
  204. phoenix/server/api/types/InferencesRole.py +23 -0
  205. phoenix/server/api/types/LabelFraction.py +7 -0
  206. phoenix/server/api/types/MimeType.py +2 -2
  207. phoenix/server/api/types/Model.py +54 -54
  208. phoenix/server/api/types/PerformanceMetric.py +8 -5
  209. phoenix/server/api/types/Project.py +407 -142
  210. phoenix/server/api/types/ProjectSession.py +139 -0
  211. phoenix/server/api/types/Segments.py +4 -4
  212. phoenix/server/api/types/Span.py +221 -176
  213. phoenix/server/api/types/SpanAnnotation.py +43 -0
  214. phoenix/server/api/types/SpanIOValue.py +15 -0
  215. phoenix/server/api/types/SystemApiKey.py +9 -0
  216. phoenix/server/api/types/TemplateLanguage.py +10 -0
  217. phoenix/server/api/types/TimeSeries.py +19 -15
  218. phoenix/server/api/types/TokenUsage.py +11 -0
  219. phoenix/server/api/types/Trace.py +154 -0
  220. phoenix/server/api/types/TraceAnnotation.py +45 -0
  221. phoenix/server/api/types/UMAPPoints.py +7 -7
  222. phoenix/server/api/types/User.py +60 -0
  223. phoenix/server/api/types/UserApiKey.py +45 -0
  224. phoenix/server/api/types/UserRole.py +15 -0
  225. phoenix/server/api/types/node.py +4 -112
  226. phoenix/server/api/types/pagination.py +156 -57
  227. phoenix/server/api/utils.py +34 -0
  228. phoenix/server/app.py +864 -115
  229. phoenix/server/bearer_auth.py +163 -0
  230. phoenix/server/dml_event.py +136 -0
  231. phoenix/server/dml_event_handler.py +256 -0
  232. phoenix/server/email/__init__.py +0 -0
  233. phoenix/server/email/sender.py +97 -0
  234. phoenix/server/email/templates/__init__.py +0 -0
  235. phoenix/server/email/templates/password_reset.html +19 -0
  236. phoenix/server/email/types.py +11 -0
  237. phoenix/server/grpc_server.py +102 -0
  238. phoenix/server/jwt_store.py +505 -0
  239. phoenix/server/main.py +305 -116
  240. phoenix/server/oauth2.py +52 -0
  241. phoenix/server/openapi/__init__.py +0 -0
  242. phoenix/server/prometheus.py +111 -0
  243. phoenix/server/rate_limiters.py +188 -0
  244. phoenix/server/static/.vite/manifest.json +87 -0
  245. phoenix/server/static/assets/components-Cy9nwIvF.js +2125 -0
  246. phoenix/server/static/assets/index-BKvHIxkk.js +113 -0
  247. phoenix/server/static/assets/pages-CUi2xCVQ.js +4449 -0
  248. phoenix/server/static/assets/vendor-DvC8cT4X.js +894 -0
  249. phoenix/server/static/assets/vendor-DxkFTwjz.css +1 -0
  250. phoenix/server/static/assets/vendor-arizeai-Do1793cv.js +662 -0
  251. phoenix/server/static/assets/vendor-codemirror-BzwZPyJM.js +24 -0
  252. phoenix/server/static/assets/vendor-recharts-_Jb7JjhG.js +59 -0
  253. phoenix/server/static/assets/vendor-shiki-Cl9QBraO.js +5 -0
  254. phoenix/server/static/assets/vendor-three-DwGkEfCM.js +2998 -0
  255. phoenix/server/telemetry.py +68 -0
  256. phoenix/server/templates/index.html +82 -23
  257. phoenix/server/thread_server.py +3 -3
  258. phoenix/server/types.py +275 -0
  259. phoenix/services.py +27 -18
  260. phoenix/session/client.py +743 -68
  261. phoenix/session/data_extractor.py +31 -7
  262. phoenix/session/evaluation.py +3 -9
  263. phoenix/session/session.py +263 -219
  264. phoenix/settings.py +22 -0
  265. phoenix/trace/__init__.py +2 -22
  266. phoenix/trace/attributes.py +338 -0
  267. phoenix/trace/dsl/README.md +116 -0
  268. phoenix/trace/dsl/filter.py +663 -213
  269. phoenix/trace/dsl/helpers.py +73 -21
  270. phoenix/trace/dsl/query.py +574 -201
  271. phoenix/trace/exporter.py +24 -19
  272. phoenix/trace/fixtures.py +368 -32
  273. phoenix/trace/otel.py +71 -219
  274. phoenix/trace/projects.py +3 -2
  275. phoenix/trace/schemas.py +33 -11
  276. phoenix/trace/span_evaluations.py +21 -16
  277. phoenix/trace/span_json_decoder.py +6 -4
  278. phoenix/trace/span_json_encoder.py +2 -2
  279. phoenix/trace/trace_dataset.py +47 -32
  280. phoenix/trace/utils.py +21 -4
  281. phoenix/utilities/__init__.py +0 -26
  282. phoenix/utilities/client.py +132 -0
  283. phoenix/utilities/deprecation.py +31 -0
  284. phoenix/utilities/error_handling.py +3 -2
  285. phoenix/utilities/json.py +109 -0
  286. phoenix/utilities/logging.py +8 -0
  287. phoenix/utilities/project.py +2 -2
  288. phoenix/utilities/re.py +49 -0
  289. phoenix/utilities/span_store.py +0 -23
  290. phoenix/utilities/template_formatters.py +99 -0
  291. phoenix/version.py +1 -1
  292. arize_phoenix-3.16.1.dist-info/METADATA +0 -495
  293. arize_phoenix-3.16.1.dist-info/RECORD +0 -178
  294. phoenix/core/project.py +0 -619
  295. phoenix/core/traces.py +0 -96
  296. phoenix/experimental/evals/__init__.py +0 -73
  297. phoenix/experimental/evals/evaluators.py +0 -413
  298. phoenix/experimental/evals/functions/__init__.py +0 -4
  299. phoenix/experimental/evals/functions/classify.py +0 -453
  300. phoenix/experimental/evals/functions/executor.py +0 -353
  301. phoenix/experimental/evals/functions/generate.py +0 -138
  302. phoenix/experimental/evals/functions/processing.py +0 -76
  303. phoenix/experimental/evals/models/__init__.py +0 -14
  304. phoenix/experimental/evals/models/anthropic.py +0 -175
  305. phoenix/experimental/evals/models/base.py +0 -170
  306. phoenix/experimental/evals/models/bedrock.py +0 -221
  307. phoenix/experimental/evals/models/litellm.py +0 -134
  308. phoenix/experimental/evals/models/openai.py +0 -448
  309. phoenix/experimental/evals/models/rate_limiters.py +0 -246
  310. phoenix/experimental/evals/models/vertex.py +0 -173
  311. phoenix/experimental/evals/models/vertexai.py +0 -186
  312. phoenix/experimental/evals/retrievals.py +0 -96
  313. phoenix/experimental/evals/templates/__init__.py +0 -50
  314. phoenix/experimental/evals/templates/default_templates.py +0 -472
  315. phoenix/experimental/evals/templates/template.py +0 -195
  316. phoenix/experimental/evals/utils/__init__.py +0 -172
  317. phoenix/experimental/evals/utils/threads.py +0 -27
  318. phoenix/server/api/helpers.py +0 -11
  319. phoenix/server/api/routers/evaluation_handler.py +0 -109
  320. phoenix/server/api/routers/span_handler.py +0 -70
  321. phoenix/server/api/routers/trace_handler.py +0 -60
  322. phoenix/server/api/types/DatasetRole.py +0 -23
  323. phoenix/server/static/index.css +0 -6
  324. phoenix/server/static/index.js +0 -7447
  325. phoenix/storage/span_store/__init__.py +0 -23
  326. phoenix/storage/span_store/text_file.py +0 -85
  327. phoenix/trace/dsl/missing.py +0 -60
  328. phoenix/trace/langchain/__init__.py +0 -3
  329. phoenix/trace/langchain/instrumentor.py +0 -35
  330. phoenix/trace/llama_index/__init__.py +0 -3
  331. phoenix/trace/llama_index/callback.py +0 -102
  332. phoenix/trace/openai/__init__.py +0 -3
  333. phoenix/trace/openai/instrumentor.py +0 -30
  334. {arize_phoenix-3.16.1.dist-info → arize_phoenix-7.7.0.dist-info}/licenses/IP_NOTICE +0 -0
  335. {arize_phoenix-3.16.1.dist-info → arize_phoenix-7.7.0.dist-info}/licenses/LICENSE +0 -0
  336. /phoenix/{datasets → db/insertion}/__init__.py +0 -0
  337. /phoenix/{experimental → db/migrations}/__init__.py +0 -0
  338. /phoenix/{storage → db/migrations/data_migration_scripts}/__init__.py +0 -0
phoenix/settings.py ADDED
@@ -0,0 +1,22 @@
1
+ import logging
2
+ from dataclasses import dataclass, field
3
+
4
+ from phoenix.config import LoggingMode
5
+
6
+
7
+ @dataclass
8
+ class _Settings:
9
+ """Settings for Phoenix, lazily initialized."""
10
+
11
+ # By default, don't log migrations
12
+ log_migrations: bool = field(default=False)
13
+ # By default, Phoenix does not configure its loggers and acts as a library
14
+ logging_mode: LoggingMode = field(default=LoggingMode.DEFAULT)
15
+ # By default, log level is INFO
16
+ logging_level: int = field(default=logging.INFO)
17
+ # By default, log level is WARNING
18
+ db_logging_level: int = field(default=logging.WARNING)
19
+
20
+
21
+ # Singleton instance of the settings
22
+ Settings = _Settings()
phoenix/trace/__init__.py CHANGED
@@ -1,5 +1,4 @@
1
- import contextlib
2
- from typing import Iterator
1
+ from openinference.instrumentation import suppress_tracing
3
2
 
4
3
  from .projects import using_project
5
4
  from .span_evaluations import DocumentEvaluations, Evaluations, SpanEvaluations, TraceEvaluations
@@ -12,24 +11,5 @@ __all__ = [
12
11
  "DocumentEvaluations",
13
12
  "TraceEvaluations",
14
13
  "using_project",
14
+ "suppress_tracing",
15
15
  ]
16
-
17
-
18
- @contextlib.contextmanager
19
- def suppress_tracing() -> Iterator[None]:
20
- """
21
- Context manager to pause OpenTelemetry instrumentation.
22
-
23
- Examples:
24
- with suppress_tracing():
25
- # No tracing will occur within this block
26
- ...
27
- """
28
- try:
29
- from opentelemetry.context import _SUPPRESS_INSTRUMENTATION_KEY, attach, detach, set_value
30
- except ImportError:
31
- yield
32
- return
33
- token = attach(set_value(_SUPPRESS_INSTRUMENTATION_KEY, True))
34
- yield
35
- detach(token)
@@ -0,0 +1,338 @@
1
+ """
2
+ Span attribute keys have a special relationship with the `.` separator. When
3
+ a span attribute is ingested from protobuf, it's in the form of a key value
4
+ pair such as `("llm.token_count.completion", 123)`. What we need to do is to split
5
+ the key by the `.` separator and turn it into part of a nested dictionary such
6
+ as {"llm": {"token_count": {"completion": 123}}}. We also need to reverse this
7
+ process, which is to flatten the nested dictionary into a list of key value
8
+ pairs. This module provides functions to do both of these operations.
9
+
10
+ Note that digit keys are treated as indices of a nested array. For example,
11
+ the digits inside `("retrieval.documents.0.document.content", 'A')` and
12
+ `("retrieval.documents.1.document.content": 'B')` turn the sub-keys following
13
+ them into a nested list of dictionaries i.e.
14
+ {`retrieval: {"documents": [{"document": {"content": "A"}}, {"document":
15
+ {"content": "B"}}]}`.
16
+ """
17
+
18
+ import inspect
19
+ import json
20
+ from collections import defaultdict
21
+ from collections.abc import Iterable, Iterator, Mapping, Sequence
22
+ from typing import Any, Optional, Union, cast
23
+
24
+ import numpy as np
25
+ from openinference.semconv import trace
26
+ from openinference.semconv.trace import DocumentAttributes, SpanAttributes
27
+ from typing_extensions import assert_never
28
+
29
+ DOCUMENT_METADATA = DocumentAttributes.DOCUMENT_METADATA
30
+ LLM_PROMPT_TEMPLATE_VARIABLES = SpanAttributes.LLM_PROMPT_TEMPLATE_VARIABLES
31
+ METADATA = SpanAttributes.METADATA
32
+ TOOL_PARAMETERS = SpanAttributes.TOOL_PARAMETERS
33
+
34
+ # attributes interpreted as JSON strings during ingestion
35
+ JSON_STRING_ATTRIBUTES = (
36
+ DOCUMENT_METADATA,
37
+ LLM_PROMPT_TEMPLATE_VARIABLES,
38
+ METADATA,
39
+ TOOL_PARAMETERS,
40
+ )
41
+
42
+ SEMANTIC_CONVENTIONS: list[str] = sorted(
43
+ # e.g. "input.value", "llm.token_count.total", etc.
44
+ (
45
+ cast(str, getattr(klass, attr))
46
+ for name in dir(trace)
47
+ if name.endswith("Attributes") and inspect.isclass(klass := getattr(trace, name))
48
+ for attr in dir(klass)
49
+ if attr.isupper()
50
+ ),
51
+ key=len,
52
+ reverse=True,
53
+ ) # sorted so the longer strings go first
54
+
55
+
56
+ def unflatten(
57
+ key_value_pairs: Iterable[tuple[str, Any]],
58
+ *,
59
+ prefix_exclusions: Sequence[str] = (),
60
+ separator: str = ".",
61
+ ) -> dict[str, Any]:
62
+ # `prefix_exclusions` is intended to contain the semantic conventions
63
+ trie = _build_trie(key_value_pairs, separator=separator, prefix_exclusions=prefix_exclusions)
64
+ return dict(_walk(trie, separator=separator))
65
+
66
+
67
+ def flatten(
68
+ obj: Union[Mapping[str, Any], Iterable[Any]],
69
+ *,
70
+ prefix: str = "",
71
+ separator: str = ".",
72
+ recurse_on_sequence: bool = False,
73
+ json_string_attributes: Optional[Sequence[str]] = None,
74
+ ) -> Iterator[tuple[str, Any]]:
75
+ """
76
+ Flatten a nested dictionary or a sequence of dictionaries into a list of
77
+ key value pairs. If `recurse_on_sequence` is True, then the function will
78
+ also recursively flatten nested sequences of dictionaries. If
79
+ `json_string_attributes` is provided, then the function will interpret the
80
+ attributes in the list as JSON strings and convert them into dictionaries.
81
+ The `prefix` argument is used to prefix the keys in the output list, but
82
+ it's mostly used internally to facilitate recursion.
83
+ """
84
+ if isinstance(obj, Mapping):
85
+ yield from _flatten_mapping(
86
+ obj,
87
+ prefix=prefix,
88
+ recurse_on_sequence=recurse_on_sequence,
89
+ json_string_attributes=json_string_attributes,
90
+ separator=separator,
91
+ )
92
+ elif isinstance(obj, Iterable):
93
+ yield from _flatten_sequence(
94
+ obj,
95
+ prefix=prefix,
96
+ recurse_on_sequence=recurse_on_sequence,
97
+ json_string_attributes=json_string_attributes,
98
+ separator=separator,
99
+ )
100
+ else:
101
+ assert_never(obj)
102
+
103
+
104
+ def has_mapping(sequence: Iterable[Any]) -> bool:
105
+ """
106
+ Check if a sequence contains a dictionary. We don't flatten sequences that
107
+ only contain primitive types, such as strings, integers, etc. Conversely,
108
+ we'll only un-flatten digit sub-keys if it can be interpreted the index of
109
+ an array of dictionaries.
110
+ """
111
+ for item in sequence:
112
+ if isinstance(item, Mapping):
113
+ return True
114
+ return False
115
+
116
+
117
+ def get_attribute_value(
118
+ attributes: Optional[Mapping[str, Any]],
119
+ key: str,
120
+ separator: str = ".",
121
+ ) -> Optional[Any]:
122
+ """
123
+ Get the value of a nested attribute from a dictionary. The `key` is a
124
+ string that represents the path to the attribute, where each level is
125
+ separated by the `separator`. For example, if the dictionary is
126
+ `{"a": {"b": {"c": 1}}}` and the key is `"a.b.c"`, then the function
127
+ will return `1`. If the key is `"a.b"`, then the function will return
128
+ `{"c": 1}`.
129
+ """
130
+ if not (attributes and isinstance(attributes, dict)):
131
+ return None
132
+ sub_keys = key.split(separator)
133
+ for sub_key in sub_keys[:-1]:
134
+ attributes = attributes.get(sub_key)
135
+ if not (attributes and isinstance(attributes, dict)):
136
+ return None
137
+ return attributes.get(sub_keys[-1])
138
+
139
+
140
+ def load_json_strings(key_values: Iterable[tuple[str, Any]]) -> Iterator[tuple[str, Any]]:
141
+ for key, value in key_values:
142
+ if key.endswith(JSON_STRING_ATTRIBUTES):
143
+ try:
144
+ dict_value = json.loads(value)
145
+ except Exception:
146
+ yield key, value
147
+ else:
148
+ if dict_value:
149
+ yield key, dict_value
150
+ else:
151
+ yield key, value
152
+
153
+
154
+ def _partition_with_prefix_exclusion(
155
+ key: str,
156
+ separator: str = ".",
157
+ prefix_exclusions: Sequence[str] = (),
158
+ ) -> tuple[str, str, str]:
159
+ """
160
+ Partition `key` by `separator`, but exclude prefixes in `prefix_exclusions`,
161
+ which is usually the list of semantic conventions. `prefix_exclusions` should
162
+ be sorted by length from the longest to the shortest
163
+ """
164
+ for prefix in prefix_exclusions:
165
+ if key.startswith(prefix) and (
166
+ len(key) == len(prefix) or key[len(prefix) :].startswith(separator)
167
+ ):
168
+ return prefix, separator, key[len(prefix) + len(separator) :]
169
+ return key.partition(separator)
170
+
171
+
172
+ class _Trie(defaultdict[Union[str, int], "_Trie"]):
173
+ """
174
+ Prefix Tree with special handling for indices (i.e. all-digit keys). Indices
175
+ represent the position of an element in a nested list, while branches represent
176
+ the keys of a nested dictionary.
177
+ """
178
+
179
+ def __init__(self) -> None:
180
+ super().__init__(_Trie)
181
+ self.value: Any = None
182
+ self.indices: set[int] = set()
183
+ self.branches: set[Union[str, int]] = set()
184
+
185
+ def set_value(self, value: Any) -> None:
186
+ self.value = value
187
+ # value and indices must not coexist
188
+ self.branches.update(self.indices)
189
+ self.indices.clear()
190
+
191
+ def add_index(self, index: int) -> "_Trie":
192
+ if self.value is not None:
193
+ self.branches.add(index)
194
+ elif index not in self.branches:
195
+ self.indices.add(index)
196
+ return self[index]
197
+
198
+ def add_branch(self, branch: Union[str, int]) -> "_Trie":
199
+ if branch in self.indices:
200
+ self.indices.discard(cast(int, branch))
201
+ self.branches.add(branch)
202
+ return self[branch]
203
+
204
+
205
+ def _build_trie(
206
+ key_value_pairs: Iterable[tuple[str, Any]],
207
+ *,
208
+ prefix_exclusions: Sequence[str] = (),
209
+ separator: str = ".",
210
+ ) -> _Trie:
211
+ """
212
+ Build a Trie (a.k.a. prefix tree) from `key_value_pairs`, by partitioning the keys by
213
+ separator. Each partition is a branch in the Trie. Special handling is done for partitions
214
+ that are all digits, e.g. "0", "12", etc., which are converted to integers and collected
215
+ as indices.
216
+ """
217
+ trie = _Trie()
218
+ for key, value in key_value_pairs:
219
+ if value is None:
220
+ continue
221
+ t = trie
222
+ while True:
223
+ prefix, _, suffix = _partition_with_prefix_exclusion(
224
+ key,
225
+ separator,
226
+ prefix_exclusions,
227
+ )
228
+ if prefix.isdigit():
229
+ index = int(prefix)
230
+ t = t.add_index(index) if suffix else t.add_branch(index)
231
+ else:
232
+ t = t.add_branch(prefix)
233
+ if not suffix:
234
+ break
235
+ key = suffix
236
+ t.set_value(value)
237
+ return trie
238
+
239
+
240
+ def _walk(
241
+ trie: _Trie,
242
+ *,
243
+ prefix: str = "",
244
+ separator: str = ".",
245
+ ) -> Iterator[tuple[str, Any]]:
246
+ """
247
+ Walk the Trie and yield key value pairs. If the Trie node has a value, then
248
+ yield the prefix and the value. If the Trie node has indices, then yield the
249
+ prefix and a list of dictionaries. If the Trie node has branches, then yield
250
+ the prefix and a dictionary.
251
+ """
252
+ if trie.value is not None:
253
+ yield prefix, trie.value
254
+ elif prefix and trie.indices:
255
+ yield (
256
+ prefix,
257
+ [dict(_walk(trie[index], separator=separator)) for index in sorted(trie.indices)],
258
+ )
259
+ elif trie.indices:
260
+ for index in trie.indices:
261
+ yield from _walk(trie[index], prefix=f"{index}", separator=separator)
262
+ elif prefix:
263
+ yield prefix, dict(_walk(trie, separator=separator))
264
+ return
265
+ for branch in trie.branches:
266
+ new_prefix = f"{prefix}{separator}{branch}" if prefix else f"{branch}"
267
+ yield from _walk(trie[branch], prefix=new_prefix, separator=separator)
268
+
269
+
270
+ def _flatten_mapping(
271
+ mapping: Mapping[str, Any],
272
+ *,
273
+ prefix: str = "",
274
+ recurse_on_sequence: bool = False,
275
+ json_string_attributes: Optional[Sequence[str]] = None,
276
+ separator: str = ".",
277
+ ) -> Iterator[tuple[str, Any]]:
278
+ """
279
+ Flatten a nested dictionary into a list of key value pairs. If `recurse_on_sequence`
280
+ is True, then the function will also recursively flatten nested sequences of dictionaries.
281
+ If `json_string_attributes` is provided, then the function will interpret the attributes
282
+ in the list as JSON strings and convert them into dictionaries. The `prefix` argument is
283
+ used to prefix the keys in the output list, but it's mostly used internally to facilitate
284
+ recursion.
285
+ """
286
+ for key, value in mapping.items():
287
+ prefixed_key = f"{prefix}{separator}{key}" if prefix else key
288
+ if isinstance(value, Mapping):
289
+ if json_string_attributes and prefixed_key.endswith(JSON_STRING_ATTRIBUTES):
290
+ yield prefixed_key, json.dumps(value)
291
+ else:
292
+ yield from _flatten_mapping(
293
+ value,
294
+ prefix=prefixed_key,
295
+ recurse_on_sequence=recurse_on_sequence,
296
+ json_string_attributes=json_string_attributes,
297
+ separator=separator,
298
+ )
299
+ elif (isinstance(value, Sequence) or isinstance(value, np.ndarray)) and recurse_on_sequence:
300
+ yield from _flatten_sequence(
301
+ value,
302
+ prefix=prefixed_key,
303
+ recurse_on_sequence=recurse_on_sequence,
304
+ json_string_attributes=json_string_attributes,
305
+ separator=separator,
306
+ )
307
+ elif value is not None:
308
+ yield prefixed_key, value
309
+
310
+
311
+ def _flatten_sequence(
312
+ sequence: Iterable[Any],
313
+ *,
314
+ prefix: str = "",
315
+ recurse_on_sequence: bool = False,
316
+ json_string_attributes: Optional[Sequence[str]] = None,
317
+ separator: str = ".",
318
+ ) -> Iterator[tuple[str, Any]]:
319
+ """
320
+ Flatten a sequence of dictionaries into a list of key value pairs. If `recurse_on_sequence`
321
+ is True, then the function will also recursively flatten nested sequences of dictionaries.
322
+ If `json_string_attributes` is provided, then the function will interpret the attributes
323
+ in the list as JSON strings and convert them into dictionaries. The `prefix` argument is
324
+ used to prefix the keys in the output list, but it's mostly used internally to facilitate
325
+ recursion.
326
+ """
327
+ if isinstance(sequence, str) or not has_mapping(sequence):
328
+ yield prefix, sequence
329
+ for idx, obj in enumerate(sequence):
330
+ if not isinstance(obj, Mapping):
331
+ continue
332
+ yield from _flatten_mapping(
333
+ obj,
334
+ prefix=f"{prefix}{separator}{idx}" if prefix else f"{idx}",
335
+ recurse_on_sequence=recurse_on_sequence,
336
+ json_string_attributes=json_string_attributes,
337
+ separator=separator,
338
+ )
@@ -0,0 +1,116 @@
1
+ This Phoenix module uses Python's `ast` module. The code snippets below provides a basic introduction to the `ast` module.
2
+
3
+ # Abstract Syntax Tree (AST)
4
+
5
+ The idea is that any Python expression can be parsed into an AST, and then transformed into a different one. The new AST can then be compiled back into a Python expression and evaluated at runtime.
6
+
7
+ ```python
8
+ import ast
9
+ ```
10
+
11
+ # Constant
12
+ https://docs.python.org/3/library/ast.html#ast.Constant
13
+
14
+ ```python
15
+ print(ast.dump(ast.parse("None", mode="eval").body, indent=4))
16
+ print(ast.dump(ast.parse("1", mode="eval").body, indent=4))
17
+ print(ast.dump(ast.parse("'xyz'", mode="eval").body, indent=4))
18
+ ```
19
+ ### Output
20
+ ```python
21
+ Constant(value=None)
22
+ Constant(value=1)
23
+ Constant(value='xyz')
24
+ ```
25
+
26
+ # Name
27
+ https://docs.python.org/3/library/ast.html#ast.Name
28
+
29
+ ```python
30
+ print(ast.dump(ast.parse("xyz", mode="eval").body, indent=4))
31
+ ```
32
+ ### Output
33
+ ```python
34
+ Name(id='xyz', ctx=Load())
35
+ ```
36
+
37
+ # Compilation and Evaluation
38
+ https://docs.python.org/3/library/functions.html#compile
39
+
40
+ ```python
41
+ parsed = ast.parse("xyz", mode="eval")
42
+ compiled = compile(parsed, filename="", mode="eval")
43
+
44
+ eval(compiled, {"xyz": 42})
45
+ ```
46
+ ### Output
47
+ ```python
48
+ 42
49
+ ```
50
+
51
+ # Attribute
52
+ https://docs.python.org/3/library/ast.html#ast.Attribute
53
+
54
+ ```python
55
+ print(ast.dump(ast.parse("llm.token_count.completion", mode="eval").body, indent=4))
56
+ ```
57
+ ### Output
58
+ ```python
59
+ Attribute(
60
+ value=Attribute(
61
+ value=Name(id='llm', ctx=Load()),
62
+ attr='token_count',
63
+ ctx=Load()),
64
+ attr='completion',
65
+ ctx=Load())
66
+ ```
67
+
68
+ # Subscript
69
+ https://docs.python.org/3/library/ast.html#ast.Subscript
70
+
71
+ ```python
72
+ print(ast.dump(ast.parse("attributes[['llm', 'token_count', 'completion']]", mode="eval").body, indent=4))
73
+ ```
74
+ ### Output
75
+ ```python
76
+ Subscript(
77
+ value=Name(id='attributes', ctx=Load()),
78
+ slice=List(
79
+ elts=[
80
+ Constant(value='llm'),
81
+ Constant(value='token_count'),
82
+ Constant(value='completion')],
83
+ ctx=Load()),
84
+ ctx=Load())
85
+ ```
86
+
87
+ # Translation of Attribute to Subscript
88
+ https://docs.python.org/3/library/ast.html#ast.NodeTransformer
89
+
90
+ ```python
91
+ class Translator(ast.NodeTransformer):
92
+ def visit_Attribute(self, node):
93
+ path = []
94
+ while isinstance(node, ast.Attribute):
95
+ path.append(node.attr)
96
+ node = node.value
97
+ if isinstance(node, ast.Name):
98
+ path.append(node.id)
99
+ break
100
+ return ast.Subscript(
101
+ value=ast.Name(id='attributes', ctx=ast.Load()),
102
+ slice=ast.List(
103
+ elts=[ast.Constant(value=p) for p in reversed(path)],
104
+ ctx=ast.Load(),
105
+ ),
106
+ ctx=ast.Load(),
107
+ )
108
+
109
+ parsed = ast.parse("llm.token_count.completion", mode="eval")
110
+ translated = Translator().visit(parsed)
111
+ print(ast.unparse(translated))
112
+ ```
113
+ ### Output
114
+ ```python
115
+ attributes[['llm', 'token_count', 'completion']]
116
+ ```