arize-phoenix 3.16.0__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.0.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 -247
  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 +13 -107
  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.0.dist-info/METADATA +0 -495
  293. arize_phoenix-3.16.0.dist-info/RECORD +0 -178
  294. phoenix/core/project.py +0 -617
  295. phoenix/core/traces.py +0 -100
  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.0.dist-info → arize_phoenix-7.7.0.dist-info}/licenses/IP_NOTICE +0 -0
  335. {arize_phoenix-3.16.0.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
@@ -1,30 +1,83 @@
1
- from enum import Enum
2
- from functools import partial
3
- from typing import Any, Iterable, Iterator, Optional, Protocol
1
+ from dataclasses import dataclass
2
+ from enum import Enum, auto
3
+ from typing import Any, Optional, Protocol
4
4
 
5
- import pandas as pd
6
5
  import strawberry
7
- from openinference.semconv.trace import SpanAttributes
6
+ from sqlalchemy import and_, desc, nulls_last
7
+ from sqlalchemy.orm import InstrumentedAttribute
8
+ from sqlalchemy.sql.expression import Select
8
9
  from strawberry import UNSET
9
10
  from typing_extensions import assert_never
10
11
 
11
12
  import phoenix.trace.v1 as pb
12
- from phoenix.core.project import WrappedSpan
13
+ from phoenix.db import models
14
+ from phoenix.server.api.types.pagination import CursorSortColumnDataType
13
15
  from phoenix.server.api.types.SortDir import SortDir
14
- from phoenix.trace.schemas import ComputedAttributes, SpanID
16
+ from phoenix.trace.schemas import SpanID
15
17
 
16
18
 
17
19
  @strawberry.enum
18
20
  class SpanColumn(Enum):
19
- startTime = "start_time"
20
- endTime = "end_time"
21
- latencyMs = ComputedAttributes.LATENCY_MS
22
- tokenCountTotal = SpanAttributes.LLM_TOKEN_COUNT_TOTAL
23
- tokenCountPrompt = SpanAttributes.LLM_TOKEN_COUNT_PROMPT
24
- tokenCountCompletion = SpanAttributes.LLM_TOKEN_COUNT_COMPLETION
25
- cumulativeTokenCountTotal = ComputedAttributes.CUMULATIVE_LLM_TOKEN_COUNT_TOTAL
26
- cumulativeTokenCountPrompt = ComputedAttributes.CUMULATIVE_LLM_TOKEN_COUNT_PROMPT
27
- cumulativeTokenCountCompletion = ComputedAttributes.CUMULATIVE_LLM_TOKEN_COUNT_COMPLETION
21
+ startTime = auto()
22
+ endTime = auto()
23
+ latencyMs = auto()
24
+ tokenCountTotal = auto()
25
+ tokenCountPrompt = auto()
26
+ tokenCountCompletion = auto()
27
+ cumulativeTokenCountTotal = auto()
28
+ cumulativeTokenCountPrompt = auto()
29
+ cumulativeTokenCountCompletion = auto()
30
+
31
+ @property
32
+ def column_name(self) -> str:
33
+ return "f{self.name}_span_sort_column"
34
+
35
+ @property
36
+ def orm_expression(self) -> Any:
37
+ expr: Any
38
+ if self is SpanColumn.startTime:
39
+ expr = models.Span.start_time
40
+ elif self is SpanColumn.endTime:
41
+ expr = models.Span.end_time
42
+ elif self is SpanColumn.latencyMs:
43
+ expr = models.Span.latency_ms
44
+ elif self is SpanColumn.tokenCountTotal:
45
+ expr = models.Span.llm_token_count_total
46
+ elif self is SpanColumn.tokenCountPrompt:
47
+ expr = models.Span.llm_token_count_prompt
48
+ elif self is SpanColumn.tokenCountCompletion:
49
+ expr = models.Span.llm_token_count_completion
50
+ elif self is SpanColumn.cumulativeTokenCountTotal:
51
+ expr = (
52
+ models.Span.cumulative_llm_token_count_prompt
53
+ + models.Span.cumulative_llm_token_count_completion
54
+ )
55
+ elif self is SpanColumn.cumulativeTokenCountPrompt:
56
+ expr = models.Span.cumulative_llm_token_count_prompt
57
+ elif self is SpanColumn.cumulativeTokenCountCompletion:
58
+ expr = models.Span.cumulative_llm_token_count_completion
59
+ else:
60
+ assert_never(self)
61
+ return expr.label(self.column_name)
62
+
63
+ @property
64
+ def data_type(self) -> CursorSortColumnDataType:
65
+ if (
66
+ self is SpanColumn.cumulativeTokenCountTotal
67
+ or self is SpanColumn.cumulativeTokenCountPrompt
68
+ or self is SpanColumn.cumulativeTokenCountCompletion
69
+ ):
70
+ return CursorSortColumnDataType.INT
71
+ if (
72
+ self is SpanColumn.latencyMs
73
+ or self is SpanColumn.tokenCountTotal
74
+ or self is SpanColumn.tokenCountPrompt
75
+ or self is SpanColumn.tokenCountCompletion
76
+ ):
77
+ return CursorSortColumnDataType.FLOAT
78
+ if self is SpanColumn.startTime or self is SpanColumn.endTime:
79
+ return CursorSortColumnDataType.DATETIME
80
+ assert_never(self)
28
81
 
29
82
 
30
83
  @strawberry.enum
@@ -32,6 +85,29 @@ class EvalAttr(Enum):
32
85
  score = "score"
33
86
  label = "label"
34
87
 
88
+ @property
89
+ def column_name(self) -> str:
90
+ return f"{self.value}_eval_sort_column"
91
+
92
+ @property
93
+ def orm_expression(self) -> Any:
94
+ expr: InstrumentedAttribute[Any]
95
+ if self is EvalAttr.score:
96
+ expr = models.SpanAnnotation.score
97
+ elif self is EvalAttr.label:
98
+ expr = models.SpanAnnotation.label
99
+ else:
100
+ assert_never(self)
101
+ return expr.label(self.column_name)
102
+
103
+ @property
104
+ def data_type(self) -> CursorSortColumnDataType:
105
+ if self is EvalAttr.label:
106
+ return CursorSortColumnDataType.STRING
107
+ if self is EvalAttr.score:
108
+ return CursorSortColumnDataType.FLOAT
109
+ assert_never(self)
110
+
35
111
 
36
112
  @strawberry.input
37
113
  class EvalResultKey:
@@ -43,6 +119,15 @@ class SupportsGetSpanEvaluation(Protocol):
43
119
  def get_span_evaluation(self, span_id: SpanID, name: str) -> Optional[pb.Evaluation]: ...
44
120
 
45
121
 
122
+ @dataclass(frozen=True)
123
+ class SpanSortConfig:
124
+ stmt: Select[Any]
125
+ orm_expression: Any
126
+ dir: SortDir
127
+ column_name: str
128
+ column_data_type: CursorSortColumnDataType
129
+
130
+
46
131
  @strawberry.input(
47
132
  description="The sort key and direction for span connections. Must "
48
133
  "specify one and only one of either `col` or `evalResultKey`."
@@ -52,58 +137,38 @@ class SpanSort:
52
137
  eval_result_key: Optional[EvalResultKey] = UNSET
53
138
  dir: SortDir
54
139
 
55
- def __call__(
56
- self,
57
- spans: Iterable[WrappedSpan],
58
- evals: Optional[SupportsGetSpanEvaluation] = None,
59
- ) -> Iterator[WrappedSpan]:
60
- """
61
- Sorts the spans by the given key (column or eval) and direction
62
- """
63
- if self.eval_result_key:
64
- get_sort_key_value = partial(
65
- _get_eval_result_value,
66
- eval_name=self.eval_result_key.name,
67
- eval_attr=self.eval_result_key.attr,
68
- evals=evals,
140
+ def update_orm_expr(self, stmt: Select[Any]) -> SpanSortConfig:
141
+ if (col := self.col) and not self.eval_result_key:
142
+ expr = col.orm_expression
143
+ stmt = stmt.add_columns(expr)
144
+ if self.dir == SortDir.desc:
145
+ expr = desc(expr)
146
+ return SpanSortConfig(
147
+ stmt=stmt.order_by(nulls_last(expr)),
148
+ orm_expression=col.orm_expression,
149
+ dir=self.dir,
150
+ column_name=col.column_name,
151
+ column_data_type=col.data_type,
69
152
  )
70
- else:
71
- get_sort_key_value = partial(
72
- _get_column_value,
73
- span_column=self.col or SpanColumn.startTime,
153
+ if (eval_result_key := self.eval_result_key) and not col:
154
+ eval_name = eval_result_key.name
155
+ eval_attr = eval_result_key.attr
156
+ expr = eval_result_key.attr.orm_expression
157
+ stmt = stmt.add_columns(expr)
158
+ if self.dir == SortDir.desc:
159
+ expr = desc(expr)
160
+ stmt = stmt.join(
161
+ models.SpanAnnotation,
162
+ onclause=and_(
163
+ models.SpanAnnotation.span_rowid == models.Span.id,
164
+ models.SpanAnnotation.name == eval_name,
165
+ ),
166
+ ).order_by(expr)
167
+ return SpanSortConfig(
168
+ stmt=stmt,
169
+ orm_expression=eval_result_key.attr.orm_expression,
170
+ dir=self.dir,
171
+ column_name=eval_attr.column_name,
172
+ column_data_type=eval_attr.data_type,
74
173
  )
75
- yield from pd.Series(spans, dtype=object).sort_values(
76
- key=lambda series: series.apply(get_sort_key_value),
77
- ascending=self.dir.value == SortDir.asc.value,
78
- )
79
-
80
-
81
- def _get_column_value(span: WrappedSpan, span_column: SpanColumn) -> Any:
82
- if span_column is SpanColumn.startTime:
83
- return span.start_time
84
- if span_column is SpanColumn.endTime:
85
- return span.end_time
86
- return span[span_column.value]
87
-
88
-
89
- def _get_eval_result_value(
90
- span: WrappedSpan,
91
- eval_name: str,
92
- eval_attr: EvalAttr,
93
- evals: Optional[SupportsGetSpanEvaluation] = None,
94
- ) -> Any:
95
- """
96
- Returns the evaluation result for the given span
97
- """
98
- if evals is None:
99
- return None
100
- span_id = span.context.span_id
101
- evaluation = evals.get_span_evaluation(span_id, eval_name)
102
- if evaluation is None:
103
- return None
104
- result = evaluation.result
105
- if eval_attr is EvalAttr.score:
106
- return result.score.value if result.HasField("score") else None
107
- if eval_attr is EvalAttr.label:
108
- return result.label.value if result.HasField("label") else None
109
- assert_never(eval_attr)
174
+ raise ValueError("Exactly one of `col` or `evalResultKey` must be specified on `SpanSort`.")
@@ -0,0 +1,10 @@
1
+ import strawberry
2
+ from strawberry.scalars import JSON
3
+
4
+ from phoenix.server.api.types.TemplateLanguage import TemplateLanguage
5
+
6
+
7
+ @strawberry.input
8
+ class TemplateOptions:
9
+ variables: JSON
10
+ language: TemplateLanguage
@@ -0,0 +1,17 @@
1
+ from enum import Enum
2
+
3
+ import strawberry
4
+
5
+ from phoenix.server.api.types.SortDir import SortDir
6
+
7
+
8
+ @strawberry.enum
9
+ class TraceAnnotationColumn(Enum):
10
+ createdAt = "created_at"
11
+ name = "name"
12
+
13
+
14
+ @strawberry.input(description="The sort key and direction for TraceAnnotation connections")
15
+ class TraceAnnotationSort:
16
+ col: TraceAnnotationColumn
17
+ dir: SortDir
@@ -0,0 +1,9 @@
1
+ from enum import Enum
2
+
3
+ import strawberry
4
+
5
+
6
+ @strawberry.enum
7
+ class UserRoleInput(Enum):
8
+ ADMIN = "ADMIN"
9
+ MEMBER = "MEMBER"
@@ -0,0 +1,28 @@
1
+ import strawberry
2
+
3
+ from phoenix.server.api.mutations.api_key_mutations import ApiKeyMutationMixin
4
+ from phoenix.server.api.mutations.chat_mutations import (
5
+ ChatCompletionMutationMixin,
6
+ )
7
+ from phoenix.server.api.mutations.dataset_mutations import DatasetMutationMixin
8
+ from phoenix.server.api.mutations.experiment_mutations import ExperimentMutationMixin
9
+ from phoenix.server.api.mutations.export_events_mutations import ExportEventsMutationMixin
10
+ from phoenix.server.api.mutations.project_mutations import ProjectMutationMixin
11
+ from phoenix.server.api.mutations.span_annotations_mutations import SpanAnnotationMutationMixin
12
+ from phoenix.server.api.mutations.trace_annotations_mutations import TraceAnnotationMutationMixin
13
+ from phoenix.server.api.mutations.user_mutations import UserMutationMixin
14
+
15
+
16
+ @strawberry.type
17
+ class Mutation(
18
+ ApiKeyMutationMixin,
19
+ DatasetMutationMixin,
20
+ ExperimentMutationMixin,
21
+ ExportEventsMutationMixin,
22
+ ProjectMutationMixin,
23
+ SpanAnnotationMutationMixin,
24
+ TraceAnnotationMutationMixin,
25
+ UserMutationMixin,
26
+ ChatCompletionMutationMixin,
27
+ ):
28
+ pass
@@ -0,0 +1,167 @@
1
+ from datetime import datetime, timezone
2
+ from typing import Optional
3
+
4
+ import strawberry
5
+ from sqlalchemy import select
6
+ from strawberry import UNSET
7
+ from strawberry.relay import GlobalID
8
+ from strawberry.types import Info
9
+
10
+ from phoenix.db import enums, models
11
+ from phoenix.server.api.auth import IsAdmin, IsLocked, IsNotReadOnly
12
+ from phoenix.server.api.context import Context
13
+ from phoenix.server.api.exceptions import Unauthorized
14
+ from phoenix.server.api.queries import Query
15
+ from phoenix.server.api.types.node import from_global_id_with_expected_type
16
+ from phoenix.server.api.types.SystemApiKey import SystemApiKey
17
+ from phoenix.server.api.types.UserApiKey import UserApiKey
18
+ from phoenix.server.bearer_auth import PhoenixUser
19
+ from phoenix.server.types import ApiKeyAttributes, ApiKeyClaims, ApiKeyId, UserId
20
+
21
+
22
+ @strawberry.type
23
+ class CreateSystemApiKeyMutationPayload:
24
+ jwt: str
25
+ api_key: SystemApiKey
26
+ query: Query
27
+
28
+
29
+ @strawberry.input
30
+ class CreateApiKeyInput:
31
+ name: str
32
+ description: Optional[str] = UNSET
33
+ expires_at: Optional[datetime] = UNSET
34
+
35
+
36
+ @strawberry.type
37
+ class CreateUserApiKeyMutationPayload:
38
+ jwt: str
39
+ api_key: UserApiKey
40
+ query: Query
41
+
42
+
43
+ @strawberry.input
44
+ class CreateUserApiKeyInput:
45
+ name: str
46
+ description: Optional[str] = UNSET
47
+ expires_at: Optional[datetime] = UNSET
48
+
49
+
50
+ @strawberry.input
51
+ class DeleteApiKeyInput:
52
+ id: GlobalID
53
+
54
+
55
+ @strawberry.type
56
+ class DeleteApiKeyMutationPayload:
57
+ apiKeyId: GlobalID
58
+ query: Query
59
+
60
+
61
+ @strawberry.type
62
+ class ApiKeyMutationMixin:
63
+ @strawberry.mutation(permission_classes=[IsNotReadOnly, IsAdmin, IsLocked]) # type: ignore
64
+ async def create_system_api_key(
65
+ self, info: Info[Context, None], input: CreateApiKeyInput
66
+ ) -> CreateSystemApiKeyMutationPayload:
67
+ assert (token_store := info.context.token_store) is not None
68
+ user_role = enums.UserRole.SYSTEM
69
+ async with info.context.db() as session:
70
+ # Get the system user - note this could be pushed into a dataloader
71
+ system_user = await session.scalar(
72
+ select(models.User)
73
+ .join(models.UserRole) # Join User with UserRole
74
+ .where(models.UserRole.name == user_role.value) # Filter where role is SYSTEM
75
+ .order_by(models.User.id)
76
+ .limit(1)
77
+ )
78
+ if system_user is None:
79
+ raise ValueError("System user not found")
80
+ issued_at = datetime.now(timezone.utc)
81
+ claims = ApiKeyClaims(
82
+ subject=UserId(system_user.id),
83
+ issued_at=issued_at,
84
+ expiration_time=input.expires_at or None,
85
+ attributes=ApiKeyAttributes(
86
+ user_role=user_role,
87
+ name=input.name,
88
+ description=input.description,
89
+ ),
90
+ )
91
+ token, token_id = await token_store.create_api_key(claims)
92
+ return CreateSystemApiKeyMutationPayload(
93
+ jwt=token,
94
+ api_key=SystemApiKey(
95
+ id_attr=int(token_id),
96
+ name=input.name,
97
+ description=input.description or None,
98
+ created_at=issued_at,
99
+ expires_at=input.expires_at or None,
100
+ ),
101
+ query=Query(),
102
+ )
103
+
104
+ @strawberry.mutation(permission_classes=[IsNotReadOnly, IsLocked]) # type: ignore
105
+ async def create_user_api_key(
106
+ self, info: Info[Context, None], input: CreateUserApiKeyInput
107
+ ) -> CreateUserApiKeyMutationPayload:
108
+ assert (token_store := info.context.token_store) is not None
109
+ try:
110
+ user = info.context.request.user # type: ignore
111
+ assert isinstance(user, PhoenixUser)
112
+ except AttributeError:
113
+ raise ValueError("User not found")
114
+ issued_at = datetime.now(timezone.utc)
115
+ claims = ApiKeyClaims(
116
+ subject=user.identity,
117
+ issued_at=issued_at,
118
+ expiration_time=input.expires_at or None,
119
+ attributes=ApiKeyAttributes(
120
+ user_role=enums.UserRole.MEMBER,
121
+ name=input.name,
122
+ description=input.description,
123
+ ),
124
+ )
125
+ token, token_id = await token_store.create_api_key(claims)
126
+ return CreateUserApiKeyMutationPayload(
127
+ jwt=token,
128
+ api_key=UserApiKey(
129
+ id_attr=int(token_id),
130
+ name=input.name,
131
+ description=input.description or None,
132
+ created_at=issued_at,
133
+ expires_at=input.expires_at or None,
134
+ user_id=int(user.identity),
135
+ ),
136
+ query=Query(),
137
+ )
138
+
139
+ @strawberry.mutation(permission_classes=[IsNotReadOnly, IsAdmin]) # type: ignore
140
+ async def delete_system_api_key(
141
+ self, info: Info[Context, None], input: DeleteApiKeyInput
142
+ ) -> DeleteApiKeyMutationPayload:
143
+ assert (token_store := info.context.token_store) is not None
144
+ api_key_id = from_global_id_with_expected_type(
145
+ input.id, expected_type_name=SystemApiKey.__name__
146
+ )
147
+ await token_store.revoke(ApiKeyId(api_key_id))
148
+ return DeleteApiKeyMutationPayload(apiKeyId=input.id, query=Query())
149
+
150
+ @strawberry.mutation(permission_classes=[IsNotReadOnly]) # type: ignore
151
+ async def delete_user_api_key(
152
+ self, info: Info[Context, None], input: DeleteApiKeyInput
153
+ ) -> DeleteApiKeyMutationPayload:
154
+ assert (token_store := info.context.token_store) is not None
155
+ api_key_id = from_global_id_with_expected_type(
156
+ input.id, expected_type_name=UserApiKey.__name__
157
+ )
158
+ async with info.context.db() as session:
159
+ api_key = await session.scalar(
160
+ select(models.ApiKey).where(models.ApiKey.id == api_key_id)
161
+ )
162
+ if api_key is None:
163
+ raise ValueError(f"API key with id {input.id} not found")
164
+ if int((user := info.context.user).identity) != api_key.user_id and not user.is_admin:
165
+ raise Unauthorized("User not authorized to delete")
166
+ await token_store.revoke(ApiKeyId(api_key_id))
167
+ return DeleteApiKeyMutationPayload(apiKeyId=input.id, query=Query())