arize-phoenix 4.9.0__tar.gz → 4.10.1__tar.gz

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 (277) hide show
  1. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/PKG-INFO +1 -1
  2. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/pyproject.toml +3 -0
  3. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/db/insertion/evaluation.py +7 -25
  4. arize_phoenix-4.10.1/src/phoenix/db/insertion/helpers.py +95 -0
  5. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/db/insertion/span.py +12 -16
  6. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/pointcloud/umap_parameters.py +52 -52
  7. arize_phoenix-4.10.1/src/phoenix/server/api/input_types/CreateSpanAnnotationsInput.py +16 -0
  8. arize_phoenix-4.10.1/src/phoenix/server/api/input_types/CreateTraceAnnotationsInput.py +16 -0
  9. arize_phoenix-4.10.1/src/phoenix/server/api/input_types/DeleteAnnotationsInput.py +9 -0
  10. arize_phoenix-4.10.1/src/phoenix/server/api/input_types/PatchAnnotationsInput.py +17 -0
  11. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/api/mutations/__init__.py +8 -1
  12. arize_phoenix-4.10.1/src/phoenix/server/api/mutations/span_annotations_mutations.py +108 -0
  13. arize_phoenix-4.10.1/src/phoenix/server/api/mutations/trace_annotations_mutations.py +108 -0
  14. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/api/queries.py +1 -0
  15. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/api/routers/v1/__init__.py +2 -0
  16. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/api/routers/v1/experiment_evaluations.py +3 -10
  17. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/api/routers/v1/experiments.py +4 -5
  18. arize_phoenix-4.10.1/src/phoenix/server/api/routers/v1/spans.py +275 -0
  19. arize_phoenix-4.10.1/src/phoenix/server/api/routers/v1/traces.py +228 -0
  20. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/api/types/AnnotatorKind.py +7 -1
  21. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/api/types/ExperimentRunAnnotation.py +3 -3
  22. arize_phoenix-4.10.1/src/phoenix/server/api/types/SpanAnnotation.py +45 -0
  23. arize_phoenix-4.10.1/src/phoenix/server/api/types/TraceAnnotation.py +45 -0
  24. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/main.py +1 -1
  25. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/static/index.js +562 -554
  26. arize_phoenix-4.10.1/src/phoenix/version.py +1 -0
  27. arize_phoenix-4.9.0/src/phoenix/db/insertion/helpers.py +0 -54
  28. arize_phoenix-4.9.0/src/phoenix/server/api/routers/v1/spans.py +0 -130
  29. arize_phoenix-4.9.0/src/phoenix/server/api/routers/v1/traces.py +0 -79
  30. arize_phoenix-4.9.0/src/phoenix/version.py +0 -1
  31. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/.gitignore +0 -0
  32. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/IP_NOTICE +0 -0
  33. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/LICENSE +0 -0
  34. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/README.md +0 -0
  35. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/examples/manually-instrumented-chatbot/chat-service/chat/__init__.py +0 -0
  36. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/examples/manually-instrumented-chatbot/chat-service/chat/app.py +0 -0
  37. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/examples/manually-instrumented-chatbot/chat-service/chat/types.py +0 -0
  38. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/examples/manually-instrumented-chatbot/frontend/Dockerfile +0 -0
  39. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/examples/manually-instrumented-chatbot/frontend/Makefile +0 -0
  40. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/examples/manually-instrumented-chatbot/frontend/__init__.py +0 -0
  41. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/examples/manually-instrumented-chatbot/frontend/pyproject.toml +0 -0
  42. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/examples/manually-instrumented-chatbot/frontend/requirements.txt +0 -0
  43. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/examples/manually-instrumented-chatbot/frontend/schema.json +0 -0
  44. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/__init__.py +0 -0
  45. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/config.py +0 -0
  46. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/core/__init__.py +0 -0
  47. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/core/embedding_dimension.py +0 -0
  48. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/core/model.py +0 -0
  49. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/core/model_schema.py +0 -0
  50. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/core/model_schema_adapter.py +0 -0
  51. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/datetime_utils.py +0 -0
  52. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/db/README.md +0 -0
  53. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/db/__init__.py +0 -0
  54. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/db/alembic.ini +0 -0
  55. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/db/bulk_inserter.py +0 -0
  56. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/db/engines.py +0 -0
  57. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/db/helpers.py +0 -0
  58. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/db/insertion/__init__.py +0 -0
  59. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/db/insertion/dataset.py +0 -0
  60. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/db/migrate.py +0 -0
  61. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/db/migrations/__init__.py +0 -0
  62. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/db/migrations/env.py +0 -0
  63. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/db/migrations/script.py.mako +0 -0
  64. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/db/migrations/types.py +0 -0
  65. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/db/migrations/versions/10460e46d750_datasets.py +0 -0
  66. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/db/migrations/versions/cf03bd6bae1d_init.py +0 -0
  67. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/db/models.py +0 -0
  68. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/exceptions.py +0 -0
  69. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/experiments/__init__.py +0 -0
  70. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/experiments/evaluators/__init__.py +0 -0
  71. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/experiments/evaluators/base.py +0 -0
  72. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/experiments/evaluators/code_evaluators.py +0 -0
  73. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/experiments/evaluators/llm_evaluators.py +0 -0
  74. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/experiments/evaluators/utils.py +0 -0
  75. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/experiments/functions.py +0 -0
  76. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/experiments/tracing.py +0 -0
  77. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/experiments/types.py +0 -0
  78. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/experiments/utils.py +0 -0
  79. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/inferences/__init__.py +0 -0
  80. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/inferences/errors.py +0 -0
  81. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/inferences/fixtures.py +0 -0
  82. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/inferences/inferences.py +0 -0
  83. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/inferences/schema.py +0 -0
  84. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/inferences/validation.py +0 -0
  85. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/metrics/README.md +0 -0
  86. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/metrics/__init__.py +0 -0
  87. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/metrics/binning.py +0 -0
  88. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/metrics/metrics.py +0 -0
  89. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/metrics/mixins.py +0 -0
  90. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/metrics/retrieval_metrics.py +0 -0
  91. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/metrics/timeseries.py +0 -0
  92. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/metrics/wrappers.py +0 -0
  93. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/pointcloud/__init__.py +0 -0
  94. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/pointcloud/clustering.py +0 -0
  95. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/pointcloud/pointcloud.py +0 -0
  96. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/pointcloud/projectors.py +0 -0
  97. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/py.typed +0 -0
  98. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/__init__.py +0 -0
  99. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/api/__init__.py +0 -0
  100. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/api/context.py +0 -0
  101. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/api/dataloaders/__init__.py +0 -0
  102. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/api/dataloaders/average_experiment_run_latency.py +0 -0
  103. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/api/dataloaders/cache/__init__.py +0 -0
  104. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/api/dataloaders/cache/two_tier_cache.py +0 -0
  105. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/api/dataloaders/dataset_example_revisions.py +0 -0
  106. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/api/dataloaders/dataset_example_spans.py +0 -0
  107. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/api/dataloaders/document_evaluation_summaries.py +0 -0
  108. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/api/dataloaders/document_evaluations.py +0 -0
  109. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/api/dataloaders/document_retrieval_metrics.py +0 -0
  110. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/api/dataloaders/evaluation_summaries.py +0 -0
  111. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/api/dataloaders/experiment_annotation_summaries.py +0 -0
  112. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/api/dataloaders/experiment_error_rates.py +0 -0
  113. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/api/dataloaders/experiment_run_counts.py +0 -0
  114. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/api/dataloaders/experiment_sequence_number.py +0 -0
  115. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/api/dataloaders/latency_ms_quantile.py +0 -0
  116. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/api/dataloaders/min_start_or_max_end_times.py +0 -0
  117. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/api/dataloaders/project_by_name.py +0 -0
  118. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/api/dataloaders/record_counts.py +0 -0
  119. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/api/dataloaders/span_descendants.py +0 -0
  120. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/api/dataloaders/span_evaluations.py +0 -0
  121. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/api/dataloaders/span_projects.py +0 -0
  122. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/api/dataloaders/token_counts.py +0 -0
  123. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/api/dataloaders/trace_evaluations.py +0 -0
  124. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/api/dataloaders/trace_row_ids.py +0 -0
  125. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/api/helpers/__init__.py +0 -0
  126. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/api/helpers/dataset_helpers.py +0 -0
  127. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/api/input_types/AddExamplesToDatasetInput.py +0 -0
  128. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/api/input_types/AddSpansToDatasetInput.py +0 -0
  129. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/api/input_types/ClearProjectInput.py +0 -0
  130. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/api/input_types/ClusterInput.py +0 -0
  131. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/api/input_types/Coordinates.py +0 -0
  132. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/api/input_types/CreateDatasetInput.py +0 -0
  133. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/api/input_types/DataQualityMetricInput.py +0 -0
  134. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/api/input_types/DatasetExampleInput.py +0 -0
  135. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/api/input_types/DatasetSort.py +0 -0
  136. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/api/input_types/DatasetVersionSort.py +0 -0
  137. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/api/input_types/DeleteDatasetExamplesInput.py +0 -0
  138. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/api/input_types/DeleteDatasetInput.py +0 -0
  139. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/api/input_types/DeleteExperimentsInput.py +0 -0
  140. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/api/input_types/DimensionFilter.py +0 -0
  141. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/api/input_types/DimensionInput.py +0 -0
  142. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/api/input_types/Granularity.py +0 -0
  143. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/api/input_types/PatchDatasetExamplesInput.py +0 -0
  144. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/api/input_types/PatchDatasetInput.py +0 -0
  145. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/api/input_types/PerformanceMetricInput.py +0 -0
  146. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/api/input_types/SpanSort.py +0 -0
  147. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/api/input_types/TimeRange.py +0 -0
  148. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/api/input_types/__init__.py +0 -0
  149. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/api/interceptor.py +0 -0
  150. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/api/mutations/auth.py +0 -0
  151. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/api/mutations/dataset_mutations.py +0 -0
  152. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/api/mutations/experiment_mutations.py +0 -0
  153. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/api/mutations/export_events_mutations.py +0 -0
  154. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/api/mutations/project_mutations.py +0 -0
  155. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/api/openapi/__init__.py +0 -0
  156. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/api/openapi/main.py +0 -0
  157. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/api/openapi/schema.py +0 -0
  158. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/api/routers/__init__.py +0 -0
  159. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/api/routers/utils.py +0 -0
  160. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/api/routers/v1/dataset_examples.py +0 -0
  161. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/api/routers/v1/datasets.py +0 -0
  162. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/api/routers/v1/evaluations.py +0 -0
  163. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/api/routers/v1/experiment_runs.py +0 -0
  164. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/api/schema.py +0 -0
  165. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/api/types/Cluster.py +0 -0
  166. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/api/types/CreateDatasetPayload.py +0 -0
  167. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/api/types/DataQualityMetric.py +0 -0
  168. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/api/types/Dataset.py +0 -0
  169. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/api/types/DatasetExample.py +0 -0
  170. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/api/types/DatasetExampleRevision.py +0 -0
  171. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/api/types/DatasetValues.py +0 -0
  172. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/api/types/DatasetVersion.py +0 -0
  173. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/api/types/Dimension.py +0 -0
  174. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/api/types/DimensionDataType.py +0 -0
  175. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/api/types/DimensionShape.py +0 -0
  176. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/api/types/DimensionType.py +0 -0
  177. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/api/types/DimensionWithValue.py +0 -0
  178. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/api/types/DocumentEvaluationSummary.py +0 -0
  179. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/api/types/DocumentRetrievalMetrics.py +0 -0
  180. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/api/types/EmbeddingDimension.py +0 -0
  181. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/api/types/EmbeddingMetadata.py +0 -0
  182. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/api/types/Evaluation.py +0 -0
  183. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/api/types/EvaluationSummary.py +0 -0
  184. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/api/types/Event.py +0 -0
  185. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/api/types/EventMetadata.py +0 -0
  186. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/api/types/ExampleRevisionInterface.py +0 -0
  187. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/api/types/Experiment.py +0 -0
  188. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/api/types/ExperimentAnnotationSummary.py +0 -0
  189. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/api/types/ExperimentComparison.py +0 -0
  190. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/api/types/ExperimentRun.py +0 -0
  191. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/api/types/ExportedFile.py +0 -0
  192. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/api/types/Functionality.py +0 -0
  193. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/api/types/Inferences.py +0 -0
  194. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/api/types/InferencesRole.py +0 -0
  195. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/api/types/MimeType.py +0 -0
  196. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/api/types/Model.py +0 -0
  197. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/api/types/NumericRange.py +0 -0
  198. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/api/types/PerformanceMetric.py +0 -0
  199. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/api/types/Project.py +0 -0
  200. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/api/types/PromptResponse.py +0 -0
  201. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/api/types/Retrieval.py +0 -0
  202. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/api/types/ScalarDriftMetricEnum.py +0 -0
  203. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/api/types/Segments.py +0 -0
  204. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/api/types/SortDir.py +0 -0
  205. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/api/types/Span.py +0 -0
  206. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/api/types/TimeSeries.py +0 -0
  207. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/api/types/Trace.py +0 -0
  208. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/api/types/UMAPPoints.py +0 -0
  209. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/api/types/ValidationResult.py +0 -0
  210. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/api/types/VectorDriftMetricEnum.py +0 -0
  211. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/api/types/__init__.py +0 -0
  212. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/api/types/node.py +0 -0
  213. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/api/types/pagination.py +0 -0
  214. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/api/utils.py +0 -0
  215. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/app.py +0 -0
  216. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/grpc_server.py +0 -0
  217. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/openapi/__init__.py +0 -0
  218. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/openapi/docs.py +0 -0
  219. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/prometheus.py +0 -0
  220. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/static/apple-touch-icon-114x114.png +0 -0
  221. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/static/apple-touch-icon-120x120.png +0 -0
  222. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/static/apple-touch-icon-144x144.png +0 -0
  223. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/static/apple-touch-icon-152x152.png +0 -0
  224. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/static/apple-touch-icon-180x180.png +0 -0
  225. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/static/apple-touch-icon-72x72.png +0 -0
  226. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/static/apple-touch-icon-76x76.png +0 -0
  227. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/static/apple-touch-icon.png +0 -0
  228. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/static/favicon.ico +0 -0
  229. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/static/index.css +0 -0
  230. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/static/modernizr.js +0 -0
  231. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/telemetry.py +0 -0
  232. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/templates/__init__.py +0 -0
  233. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/templates/index.html +0 -0
  234. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/server/thread_server.py +0 -0
  235. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/services.py +0 -0
  236. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/session/__init__.py +0 -0
  237. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/session/client.py +0 -0
  238. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/session/data_extractor.py +0 -0
  239. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/session/evaluation.py +0 -0
  240. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/session/session.py +0 -0
  241. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/settings.py +0 -0
  242. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/trace/__init__.py +0 -0
  243. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/trace/attributes.py +0 -0
  244. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/trace/dsl/README.md +0 -0
  245. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/trace/dsl/__init__.py +0 -0
  246. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/trace/dsl/filter.py +0 -0
  247. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/trace/dsl/helpers.py +0 -0
  248. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/trace/dsl/query.py +0 -0
  249. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/trace/errors.py +0 -0
  250. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/trace/evaluation_conventions.py +0 -0
  251. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/trace/exporter.py +0 -0
  252. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/trace/fixtures.py +0 -0
  253. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/trace/langchain/__init__.py +0 -0
  254. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/trace/langchain/instrumentor.py +0 -0
  255. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/trace/llama_index/__init__.py +0 -0
  256. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/trace/llama_index/callback.py +0 -0
  257. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/trace/openai/__init__.py +0 -0
  258. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/trace/openai/instrumentor.py +0 -0
  259. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/trace/otel.py +0 -0
  260. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/trace/projects.py +0 -0
  261. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/trace/schemas.py +0 -0
  262. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/trace/span_evaluations.py +0 -0
  263. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/trace/span_json_decoder.py +0 -0
  264. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/trace/span_json_encoder.py +0 -0
  265. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/trace/trace_dataset.py +0 -0
  266. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/trace/utils.py +0 -0
  267. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/trace/v1/__init__.py +0 -0
  268. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/trace/v1/evaluation_pb2.py +0 -0
  269. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/trace/v1/evaluation_pb2.pyi +0 -0
  270. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/utilities/__init__.py +0 -0
  271. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/utilities/deprecation.py +0 -0
  272. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/utilities/error_handling.py +0 -0
  273. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/utilities/json.py +0 -0
  274. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/utilities/logging.py +0 -0
  275. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/utilities/project.py +0 -0
  276. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/utilities/re.py +0 -0
  277. {arize_phoenix-4.9.0 → arize_phoenix-4.10.1}/src/phoenix/utilities/span_store.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: arize-phoenix
3
- Version: 4.9.0
3
+ Version: 4.10.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
@@ -403,3 +403,6 @@ select = ["E", "F", "W", "I", "NPY201"]
403
403
 
404
404
  [tool.ruff.lint.isort]
405
405
  force-single-line = false
406
+
407
+ [tool.ruff.format]
408
+ line-ending = "lf"
@@ -6,7 +6,7 @@ from typing_extensions import assert_never
6
6
 
7
7
  from phoenix.db import models
8
8
  from phoenix.db.helpers import SupportedSQLDialect, num_docs_col
9
- from phoenix.db.insertion.helpers import OnConflict, insert_on_conflict
9
+ from phoenix.db.insertion.helpers import insert_on_conflict
10
10
  from phoenix.exceptions import PhoenixException
11
11
  from phoenix.trace import v1 as pb
12
12
 
@@ -87,18 +87,12 @@ async def _insert_trace_evaluation(
87
87
  metadata_={}, # `metadata_` must match ORM
88
88
  annotator_kind="LLM",
89
89
  )
90
- set_ = dict(values)
91
- set_.pop("metadata_")
92
- set_["metadata"] = values["metadata_"] # `metadata` must match database
93
90
  await session.execute(
94
91
  insert_on_conflict(
92
+ values,
95
93
  dialect=dialect,
96
94
  table=models.TraceAnnotation,
97
- values=values,
98
- constraint="uq_trace_annotations_name_trace_rowid",
99
- column_names=("name", "trace_rowid"),
100
- on_conflict=OnConflict.DO_UPDATE,
101
- set_=set_,
95
+ unique_by=("name", "trace_rowid"),
102
96
  )
103
97
  )
104
98
  return TraceEvaluationInsertionEvent(project_rowid, evaluation_name)
@@ -135,18 +129,12 @@ async def _insert_span_evaluation(
135
129
  metadata_={}, # `metadata_` must match ORM
136
130
  annotator_kind="LLM",
137
131
  )
138
- set_ = dict(values)
139
- set_.pop("metadata_")
140
- set_["metadata"] = values["metadata_"] # `metadata` must match database
141
132
  await session.execute(
142
133
  insert_on_conflict(
134
+ values,
143
135
  dialect=dialect,
144
136
  table=models.SpanAnnotation,
145
- values=values,
146
- constraint="uq_span_annotations_name_span_rowid",
147
- column_names=("name", "span_rowid"),
148
- on_conflict=OnConflict.DO_UPDATE,
149
- set_=set_,
137
+ unique_by=("name", "span_rowid"),
150
138
  )
151
139
  )
152
140
  return SpanEvaluationInsertionEvent(project_rowid, evaluation_name)
@@ -192,18 +180,12 @@ async def _insert_document_evaluation(
192
180
  metadata_={}, # `metadata_` must match ORM
193
181
  annotator_kind="LLM",
194
182
  )
195
- set_ = dict(values)
196
- set_.pop("metadata_")
197
- set_["metadata"] = values["metadata_"] # `metadata` must match database
198
183
  await session.execute(
199
184
  insert_on_conflict(
185
+ values,
200
186
  dialect=dialect,
201
187
  table=models.DocumentAnnotation,
202
- values=values,
203
- constraint="uq_document_annotations_name_span_rowid_document_position",
204
- column_names=("name", "span_rowid", "document_position"),
205
- on_conflict=OnConflict.DO_UPDATE,
206
- set_=set_,
188
+ unique_by=("name", "span_rowid", "document_position"),
207
189
  )
208
190
  )
209
191
  return DocumentEvaluationInsertionEvent(project_rowid, evaluation_name)
@@ -0,0 +1,95 @@
1
+ from abc import ABC
2
+ from enum import Enum, auto
3
+ from typing import (
4
+ Any,
5
+ Awaitable,
6
+ Callable,
7
+ Iterable,
8
+ Iterator,
9
+ Mapping,
10
+ Optional,
11
+ Sequence,
12
+ Tuple,
13
+ Type,
14
+ )
15
+
16
+ from sqlalchemy import Insert
17
+ from sqlalchemy.dialects.postgresql import insert as insert_postgresql
18
+ from sqlalchemy.dialects.sqlite import insert as insert_sqlite
19
+ from sqlalchemy.ext.asyncio import AsyncSession
20
+ from sqlalchemy.sql.elements import KeyedColumnElement
21
+ from typing_extensions import TypeAlias, assert_never
22
+
23
+ from phoenix.db.helpers import SupportedSQLDialect
24
+ from phoenix.db.models import Base
25
+
26
+
27
+ class DataManipulationEvent(ABC):
28
+ """
29
+ Execution of DML (Data Manipulation Language) statements.
30
+ """
31
+
32
+
33
+ DataManipulation: TypeAlias = Callable[[AsyncSession], Awaitable[Optional[DataManipulationEvent]]]
34
+
35
+
36
+ class OnConflict(Enum):
37
+ DO_NOTHING = auto()
38
+ DO_UPDATE = auto()
39
+
40
+
41
+ def insert_on_conflict(
42
+ *records: Mapping[str, Any],
43
+ table: Type[Base],
44
+ dialect: SupportedSQLDialect,
45
+ unique_by: Sequence[str],
46
+ on_conflict: OnConflict = OnConflict.DO_UPDATE,
47
+ set_: Optional[Mapping[str, Any]] = None,
48
+ ) -> Insert:
49
+ """
50
+ Dialect specific insertion statement using ON CONFLICT DO syntax.
51
+ """
52
+ if on_conflict is OnConflict.DO_UPDATE:
53
+ # postegresql rejects duplicate updates for the same record
54
+ seen = set()
55
+ unique_records = []
56
+ for v in reversed(records):
57
+ if (k := tuple(v.get(name) for name in unique_by)) in seen:
58
+ continue
59
+ unique_records.append(v)
60
+ seen.add(k)
61
+ records = tuple(reversed(unique_records))
62
+ constraint = "_".join(("uq", table.__tablename__, *unique_by))
63
+ if dialect is SupportedSQLDialect.POSTGRESQL:
64
+ stmt_postgresql = insert_postgresql(table).values(records)
65
+ if on_conflict is OnConflict.DO_NOTHING:
66
+ return stmt_postgresql.on_conflict_do_nothing(constraint=constraint)
67
+ if on_conflict is OnConflict.DO_UPDATE:
68
+ return stmt_postgresql.on_conflict_do_update(
69
+ constraint=constraint,
70
+ set_=set_ if set_ else dict(_clean(stmt_postgresql.excluded.items())),
71
+ )
72
+ assert_never(on_conflict)
73
+ if dialect is SupportedSQLDialect.SQLITE:
74
+ stmt_sqlite = insert_sqlite(table).values(records)
75
+ if on_conflict is OnConflict.DO_NOTHING:
76
+ return stmt_sqlite.on_conflict_do_nothing(unique_by)
77
+ if on_conflict is OnConflict.DO_UPDATE:
78
+ return stmt_sqlite.on_conflict_do_update(
79
+ unique_by,
80
+ set_=set_ if set_ else dict(_clean(stmt_sqlite.excluded.items())),
81
+ )
82
+ assert_never(on_conflict)
83
+ assert_never(dialect)
84
+
85
+
86
+ def _clean(
87
+ kv: Iterable[Tuple[str, KeyedColumnElement[Any]]],
88
+ ) -> Iterator[Tuple[str, KeyedColumnElement[Any]]]:
89
+ for k, v in kv:
90
+ if v.primary_key or v.foreign_keys or k == "created_at":
91
+ continue
92
+ if k == "metadata_":
93
+ yield "metadata", v
94
+ else:
95
+ yield k, v
@@ -26,17 +26,14 @@ async def insert_span(
26
26
  project_name: str,
27
27
  ) -> Optional[SpanInsertionEvent]:
28
28
  dialect = SupportedSQLDialect(session.bind.dialect.name)
29
- project_rowid = await session.scalar(
30
- insert_on_conflict(
31
- dialect=dialect,
32
- table=models.Project,
33
- constraint="uq_projects_name",
34
- column_names=("name",),
35
- values=dict(name=project_name),
36
- on_conflict=OnConflict.DO_UPDATE,
37
- set_=dict(name=project_name),
38
- ).returning(models.Project.id)
39
- )
29
+ if (
30
+ project_rowid := await session.scalar(
31
+ select(models.Project.id).where(models.Project.name == project_name)
32
+ )
33
+ ) is None:
34
+ project_rowid = await session.scalar(
35
+ insert(models.Project).values(dict(name=project_name)).returning(models.Project.id)
36
+ )
40
37
  assert project_rowid is not None
41
38
  if trace := await session.scalar(
42
39
  select(models.Trace).where(models.Trace.trace_id == span.context.trace_id)
@@ -88,11 +85,7 @@ async def insert_span(
88
85
  cumulative_llm_token_count_completion += cast(int, accumulation[2] or 0)
89
86
  span_rowid = await session.scalar(
90
87
  insert_on_conflict(
91
- dialect=dialect,
92
- table=models.Span,
93
- constraint="uq_spans_span_id",
94
- column_names=("span_id",),
95
- values=dict(
88
+ dict(
96
89
  span_id=span.context.span_id,
97
90
  trace_rowid=trace_rowid,
98
91
  parent_id=span.parent_id,
@@ -108,6 +101,9 @@ async def insert_span(
108
101
  cumulative_llm_token_count_prompt=cumulative_llm_token_count_prompt,
109
102
  cumulative_llm_token_count_completion=cumulative_llm_token_count_completion,
110
103
  ),
104
+ dialect=dialect,
105
+ table=models.Span,
106
+ unique_by=("span_id",),
111
107
  on_conflict=OnConflict.DO_NOTHING,
112
108
  ).returning(models.Span.id)
113
109
  )
@@ -1,52 +1,52 @@
1
- from dataclasses import dataclass
2
- from typing import Any, Mapping, Optional
3
-
4
- DEFAULT_MIN_DIST = 0.0
5
- DEFAULT_N_NEIGHBORS = 30
6
- DEFAULT_N_SAMPLES = 500
7
-
8
- MIN_NEIGHBORS = 5
9
- MAX_NEIGHBORS = 100
10
- MIN_SAMPLES = 1
11
- MAX_SAMPLES = 1000
12
- MIN_MIN_DIST = 0.0
13
- MAX_MIN_DIST = 0.99
14
-
15
-
16
- @dataclass
17
- class UMAPParameters:
18
- min_dist: float = DEFAULT_MIN_DIST
19
- n_neighbors: int = DEFAULT_N_NEIGHBORS
20
- n_samples: int = DEFAULT_N_SAMPLES
21
-
22
- def __post_init__(self) -> None:
23
- if not isinstance(self.min_dist, float) or not (
24
- MIN_MIN_DIST <= self.min_dist <= MAX_MIN_DIST
25
- ):
26
- raise ValueError(
27
- f"minDist must be float type, and between {MIN_MIN_DIST} and {MAX_MIN_DIST}"
28
- )
29
-
30
- if not isinstance(self.n_neighbors, int) or not (
31
- MIN_NEIGHBORS <= self.n_neighbors <= MAX_NEIGHBORS
32
- ):
33
- raise ValueError(
34
- f"nNeighbors must be int type, and between {MIN_NEIGHBORS} and {MAX_NEIGHBORS}"
35
- )
36
-
37
- if not isinstance(self.n_samples, int) or not (
38
- MIN_SAMPLES <= self.n_samples <= MAX_SAMPLES
39
- ):
40
- raise ValueError(
41
- f"nSamples must be int type, and between {MIN_SAMPLES} and {MAX_SAMPLES}"
42
- )
43
-
44
-
45
- def get_umap_parameters(default_umap_parameters: Optional[Mapping[str, Any]]) -> UMAPParameters:
46
- if not default_umap_parameters:
47
- return UMAPParameters()
48
- return UMAPParameters(
49
- min_dist=float(default_umap_parameters.get("min_dist", DEFAULT_MIN_DIST)),
50
- n_neighbors=int(default_umap_parameters.get("n_neighbors", DEFAULT_N_NEIGHBORS)),
51
- n_samples=int(default_umap_parameters.get("n_samples", DEFAULT_N_SAMPLES)),
52
- )
1
+ from dataclasses import dataclass
2
+ from typing import Any, Mapping, Optional
3
+
4
+ DEFAULT_MIN_DIST = 0.0
5
+ DEFAULT_N_NEIGHBORS = 30
6
+ DEFAULT_N_SAMPLES = 500
7
+
8
+ MIN_NEIGHBORS = 5
9
+ MAX_NEIGHBORS = 100
10
+ MIN_SAMPLES = 1
11
+ MAX_SAMPLES = 1000
12
+ MIN_MIN_DIST = 0.0
13
+ MAX_MIN_DIST = 0.99
14
+
15
+
16
+ @dataclass
17
+ class UMAPParameters:
18
+ min_dist: float = DEFAULT_MIN_DIST
19
+ n_neighbors: int = DEFAULT_N_NEIGHBORS
20
+ n_samples: int = DEFAULT_N_SAMPLES
21
+
22
+ def __post_init__(self) -> None:
23
+ if not isinstance(self.min_dist, float) or not (
24
+ MIN_MIN_DIST <= self.min_dist <= MAX_MIN_DIST
25
+ ):
26
+ raise ValueError(
27
+ f"minDist must be float type, and between {MIN_MIN_DIST} and {MAX_MIN_DIST}"
28
+ )
29
+
30
+ if not isinstance(self.n_neighbors, int) or not (
31
+ MIN_NEIGHBORS <= self.n_neighbors <= MAX_NEIGHBORS
32
+ ):
33
+ raise ValueError(
34
+ f"nNeighbors must be int type, and between {MIN_NEIGHBORS} and {MAX_NEIGHBORS}"
35
+ )
36
+
37
+ if not isinstance(self.n_samples, int) or not (
38
+ MIN_SAMPLES <= self.n_samples <= MAX_SAMPLES
39
+ ):
40
+ raise ValueError(
41
+ f"nSamples must be int type, and between {MIN_SAMPLES} and {MAX_SAMPLES}"
42
+ )
43
+
44
+
45
+ def get_umap_parameters(default_umap_parameters: Optional[Mapping[str, Any]]) -> UMAPParameters:
46
+ if not default_umap_parameters:
47
+ return UMAPParameters()
48
+ return UMAPParameters(
49
+ min_dist=float(default_umap_parameters.get("min_dist", DEFAULT_MIN_DIST)),
50
+ n_neighbors=int(default_umap_parameters.get("n_neighbors", DEFAULT_N_NEIGHBORS)),
51
+ n_samples=int(default_umap_parameters.get("n_samples", DEFAULT_N_SAMPLES)),
52
+ )
@@ -0,0 +1,16 @@
1
+ from typing import Optional
2
+
3
+ import strawberry
4
+ from strawberry.relay import GlobalID
5
+ from strawberry.scalars import JSON
6
+
7
+
8
+ @strawberry.input
9
+ class CreateSpanAnnotationsInput:
10
+ span_id: GlobalID
11
+ name: str
12
+ annotator_kind: str
13
+ label: Optional[str] = None
14
+ score: Optional[float] = None
15
+ explanation: Optional[str] = None
16
+ metadata: JSON = strawberry.field(default_factory=dict)
@@ -0,0 +1,16 @@
1
+ from typing import Optional
2
+
3
+ import strawberry
4
+ from strawberry.relay import GlobalID
5
+ from strawberry.scalars import JSON
6
+
7
+
8
+ @strawberry.input
9
+ class CreateTraceAnnotationsInput:
10
+ trace_id: GlobalID
11
+ name: str
12
+ annotator_kind: str
13
+ label: Optional[str] = None
14
+ score: Optional[float] = None
15
+ explanation: Optional[str] = None
16
+ metadata: JSON = strawberry.field(default_factory=dict)
@@ -0,0 +1,9 @@
1
+ from typing import List
2
+
3
+ import strawberry
4
+ from strawberry.relay import GlobalID
5
+
6
+
7
+ @strawberry.input
8
+ class DeleteAnnotationsInput:
9
+ annotation_ids: List[GlobalID]
@@ -0,0 +1,17 @@
1
+ from typing import Optional
2
+
3
+ import strawberry
4
+ from strawberry import UNSET
5
+ from strawberry.relay import GlobalID
6
+ from strawberry.scalars import JSON
7
+
8
+
9
+ @strawberry.input
10
+ class PatchAnnotationsInput:
11
+ annotation_id: GlobalID
12
+ name: Optional[str] = UNSET
13
+ annotator_kind: Optional[str] = UNSET
14
+ label: Optional[str] = UNSET
15
+ score: Optional[float] = UNSET
16
+ explanation: Optional[str] = UNSET
17
+ metadata: Optional[JSON] = UNSET
@@ -4,10 +4,17 @@ from phoenix.server.api.mutations.dataset_mutations import DatasetMutationMixin
4
4
  from phoenix.server.api.mutations.experiment_mutations import ExperimentMutationMixin
5
5
  from phoenix.server.api.mutations.export_events_mutations import ExportEventsMutationMixin
6
6
  from phoenix.server.api.mutations.project_mutations import ProjectMutationMixin
7
+ from phoenix.server.api.mutations.span_annotations_mutations import SpanAnnotationMutationMixin
8
+ from phoenix.server.api.mutations.trace_annotations_mutations import TraceAnnotationMutationMixin
7
9
 
8
10
 
9
11
  @strawberry.type
10
12
  class Mutation(
11
- ProjectMutationMixin, DatasetMutationMixin, ExperimentMutationMixin, ExportEventsMutationMixin
13
+ ProjectMutationMixin,
14
+ DatasetMutationMixin,
15
+ ExperimentMutationMixin,
16
+ ExportEventsMutationMixin,
17
+ SpanAnnotationMutationMixin,
18
+ TraceAnnotationMutationMixin,
12
19
  ):
13
20
  pass
@@ -0,0 +1,108 @@
1
+ from typing import List, Sequence
2
+
3
+ import strawberry
4
+ from sqlalchemy import delete, insert, update
5
+ from strawberry import UNSET
6
+ from strawberry.types import Info
7
+
8
+ from phoenix.db import models
9
+ from phoenix.server.api.context import Context
10
+ from phoenix.server.api.input_types.CreateSpanAnnotationsInput import CreateSpanAnnotationsInput
11
+ from phoenix.server.api.input_types.DeleteAnnotationsInput import DeleteAnnotationsInput
12
+ from phoenix.server.api.input_types.PatchAnnotationsInput import PatchAnnotationsInput
13
+ from phoenix.server.api.mutations.auth import IsAuthenticated
14
+ from phoenix.server.api.types.node import from_global_id_with_expected_type
15
+ from phoenix.server.api.types.SpanAnnotation import SpanAnnotation, to_gql_span_annotation
16
+
17
+
18
+ @strawberry.type
19
+ class SpanAnnotationMutationPayload:
20
+ span_annotations: List[SpanAnnotation]
21
+
22
+
23
+ @strawberry.type
24
+ class SpanAnnotationMutationMixin:
25
+ @strawberry.mutation(permission_classes=[IsAuthenticated]) # type: ignore
26
+ async def create_span_annotations(
27
+ self, info: Info[Context, None], input: List[CreateSpanAnnotationsInput]
28
+ ) -> SpanAnnotationMutationPayload:
29
+ inserted_annotations: Sequence[models.SpanAnnotation] = []
30
+ async with info.context.db() as session:
31
+ values_list = [
32
+ dict(
33
+ span_rowid=from_global_id_with_expected_type(annotation.span_id, "Span"),
34
+ name=annotation.name,
35
+ label=annotation.label,
36
+ score=annotation.score,
37
+ explanation=annotation.explanation,
38
+ annotator_kind=annotation.annotator_kind,
39
+ metadata_=annotation.metadata,
40
+ )
41
+ for annotation in input
42
+ ]
43
+ stmt = (
44
+ insert(models.SpanAnnotation).values(values_list).returning(models.SpanAnnotation)
45
+ )
46
+ result = await session.scalars(stmt)
47
+ inserted_annotations = result.all()
48
+
49
+ return SpanAnnotationMutationPayload(
50
+ span_annotations=[
51
+ to_gql_span_annotation(annotation) for annotation in inserted_annotations
52
+ ]
53
+ )
54
+
55
+ @strawberry.mutation(permission_classes=[IsAuthenticated]) # type: ignore
56
+ async def patch_span_annotations(
57
+ self, info: Info[Context, None], input: List[PatchAnnotationsInput]
58
+ ) -> SpanAnnotationMutationPayload:
59
+ patched_annotations = []
60
+ async with info.context.db() as session:
61
+ for annotation in input:
62
+ span_annotation_id = from_global_id_with_expected_type(
63
+ annotation.annotation_id, "SpanAnnotation"
64
+ )
65
+ patch = {
66
+ column.key: patch_value
67
+ for column, patch_value, column_is_nullable in (
68
+ (models.SpanAnnotation.name, annotation.name, False),
69
+ (models.SpanAnnotation.annotator_kind, annotation.annotator_kind, False),
70
+ (models.SpanAnnotation.label, annotation.label, True),
71
+ (models.SpanAnnotation.score, annotation.score, True),
72
+ (models.SpanAnnotation.explanation, annotation.explanation, True),
73
+ (models.SpanAnnotation.metadata_, annotation.metadata, False),
74
+ )
75
+ if patch_value is not UNSET and (patch_value is not None or column_is_nullable)
76
+ }
77
+ span_annotation = await session.scalar(
78
+ update(models.SpanAnnotation)
79
+ .where(models.SpanAnnotation.id == span_annotation_id)
80
+ .values(**patch)
81
+ .returning(models.SpanAnnotation)
82
+ )
83
+ if span_annotation is not None:
84
+ patched_annotations.append(to_gql_span_annotation(span_annotation))
85
+
86
+ return SpanAnnotationMutationPayload(span_annotations=patched_annotations)
87
+
88
+ @strawberry.mutation(permission_classes=[IsAuthenticated]) # type: ignore
89
+ async def delete_span_annotations(
90
+ self, info: Info[Context, None], input: DeleteAnnotationsInput
91
+ ) -> SpanAnnotationMutationPayload:
92
+ span_annotation_ids = [
93
+ from_global_id_with_expected_type(global_id, "SpanAnnotation")
94
+ for global_id in input.annotation_ids
95
+ ]
96
+ async with info.context.db() as session:
97
+ stmt = (
98
+ delete(models.SpanAnnotation)
99
+ .where(models.SpanAnnotation.id.in_(span_annotation_ids))
100
+ .returning(models.SpanAnnotation)
101
+ )
102
+ result = await session.scalars(stmt)
103
+ deleted_annotations = result.all()
104
+
105
+ deleted_annotations_gql = [
106
+ to_gql_span_annotation(annotation) for annotation in deleted_annotations
107
+ ]
108
+ return SpanAnnotationMutationPayload(span_annotations=deleted_annotations_gql)
@@ -0,0 +1,108 @@
1
+ from typing import List, Sequence
2
+
3
+ import strawberry
4
+ from sqlalchemy import delete, insert, update
5
+ from strawberry import UNSET
6
+ from strawberry.types import Info
7
+
8
+ from phoenix.db import models
9
+ from phoenix.server.api.context import Context
10
+ from phoenix.server.api.input_types.CreateTraceAnnotationsInput import CreateTraceAnnotationsInput
11
+ from phoenix.server.api.input_types.DeleteAnnotationsInput import DeleteAnnotationsInput
12
+ from phoenix.server.api.input_types.PatchAnnotationsInput import PatchAnnotationsInput
13
+ from phoenix.server.api.mutations.auth import IsAuthenticated
14
+ from phoenix.server.api.types.node import from_global_id_with_expected_type
15
+ from phoenix.server.api.types.TraceAnnotation import TraceAnnotation, to_gql_trace_annotation
16
+
17
+
18
+ @strawberry.type
19
+ class TraceAnnotationMutationPayload:
20
+ trace_annotations: List[TraceAnnotation]
21
+
22
+
23
+ @strawberry.type
24
+ class TraceAnnotationMutationMixin:
25
+ @strawberry.mutation(permission_classes=[IsAuthenticated]) # type: ignore
26
+ async def create_trace_annotations(
27
+ self, info: Info[Context, None], input: List[CreateTraceAnnotationsInput]
28
+ ) -> TraceAnnotationMutationPayload:
29
+ inserted_annotations: Sequence[models.TraceAnnotation] = []
30
+ async with info.context.db() as session:
31
+ values_list = [
32
+ dict(
33
+ trace_rowid=from_global_id_with_expected_type(annotation.trace_id, "Trace"),
34
+ name=annotation.name,
35
+ label=annotation.label,
36
+ score=annotation.score,
37
+ explanation=annotation.explanation,
38
+ annotator_kind=annotation.annotator_kind,
39
+ metadata_=annotation.metadata,
40
+ )
41
+ for annotation in input
42
+ ]
43
+ stmt = (
44
+ insert(models.TraceAnnotation).values(values_list).returning(models.TraceAnnotation)
45
+ )
46
+ result = await session.scalars(stmt)
47
+ inserted_annotations = result.all()
48
+
49
+ return TraceAnnotationMutationPayload(
50
+ trace_annotations=[
51
+ to_gql_trace_annotation(annotation) for annotation in inserted_annotations
52
+ ]
53
+ )
54
+
55
+ @strawberry.mutation(permission_classes=[IsAuthenticated]) # type: ignore
56
+ async def patch_trace_annotations(
57
+ self, info: Info[Context, None], input: List[PatchAnnotationsInput]
58
+ ) -> TraceAnnotationMutationPayload:
59
+ patched_annotations = []
60
+ async with info.context.db() as session:
61
+ for annotation in input:
62
+ trace_annotation_id = from_global_id_with_expected_type(
63
+ annotation.annotation_id, "TraceAnnotation"
64
+ )
65
+ patch = {
66
+ column.key: patch_value
67
+ for column, patch_value, column_is_nullable in (
68
+ (models.TraceAnnotation.name, annotation.name, False),
69
+ (models.TraceAnnotation.annotator_kind, annotation.annotator_kind, False),
70
+ (models.TraceAnnotation.label, annotation.label, True),
71
+ (models.TraceAnnotation.score, annotation.score, True),
72
+ (models.TraceAnnotation.explanation, annotation.explanation, True),
73
+ (models.TraceAnnotation.metadata_, annotation.metadata, False),
74
+ )
75
+ if patch_value is not UNSET and (patch_value is not None or column_is_nullable)
76
+ }
77
+ trace_annotation = await session.scalar(
78
+ update(models.TraceAnnotation)
79
+ .where(models.TraceAnnotation.id == trace_annotation_id)
80
+ .values(**patch)
81
+ .returning(models.TraceAnnotation)
82
+ )
83
+ if trace_annotation:
84
+ patched_annotations.append(to_gql_trace_annotation(trace_annotation))
85
+
86
+ return TraceAnnotationMutationPayload(trace_annotations=patched_annotations)
87
+
88
+ @strawberry.mutation(permission_classes=[IsAuthenticated]) # type: ignore
89
+ async def delete_trace_annotations(
90
+ self, info: Info[Context, None], input: DeleteAnnotationsInput
91
+ ) -> TraceAnnotationMutationPayload:
92
+ trace_annotation_ids = [
93
+ from_global_id_with_expected_type(global_id, "TraceAnnotation")
94
+ for global_id in input.annotation_ids
95
+ ]
96
+ async with info.context.db() as session:
97
+ stmt = (
98
+ delete(models.TraceAnnotation)
99
+ .where(models.TraceAnnotation.id.in_(trace_annotation_ids))
100
+ .returning(models.TraceAnnotation)
101
+ )
102
+ result = await session.scalars(stmt)
103
+ deleted_annotations = result.all()
104
+
105
+ deleted_annotations_gql = [
106
+ to_gql_trace_annotation(annotation) for annotation in deleted_annotations
107
+ ]
108
+ return TraceAnnotationMutationPayload(trace_annotations=deleted_annotations_gql)
@@ -92,6 +92,7 @@ class Query:
92
92
  models.Project.name == models.Experiment.project_name,
93
93
  )
94
94
  .where(models.Experiment.project_name.is_(None))
95
+ .order_by(models.Project.id)
95
96
  )
96
97
  async with info.context.db() as session:
97
98
  projects = await session.stream_scalars(stmt)
@@ -40,8 +40,10 @@ V1_ROUTES = [
40
40
  Route("/v1/evaluations", evaluations.post_evaluations, methods=["POST"]),
41
41
  Route("/v1/evaluations", evaluations.get_evaluations, methods=["GET"]),
42
42
  Route("/v1/traces", traces.post_traces, methods=["POST"]),
43
+ Route("/v1/trace_annotations", traces.annotate_traces, methods=["POST"]),
43
44
  Route("/v1/spans", spans.query_spans_handler, methods=["POST"]),
44
45
  Route("/v1/spans", spans.get_spans_handler, methods=["GET"]),
46
+ Route("/v1/span_annotations", spans.annotate_spans, methods=["POST"]),
45
47
  Route("/v1/datasets/upload", datasets.post_datasets_upload, methods=["POST"]),
46
48
  Route("/v1/datasets", datasets.list_datasets, methods=["GET"]),
47
49
  Route("/v1/datasets/{id:str}", datasets.delete_dataset_by_id, methods=["DELETE"]),