arize-phoenix 3.16.1__py3-none-any.whl → 7.7.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of arize-phoenix might be problematic. Click here for more details.

Files changed (338) hide show
  1. arize_phoenix-7.7.1.dist-info/METADATA +261 -0
  2. arize_phoenix-7.7.1.dist-info/RECORD +345 -0
  3. {arize_phoenix-3.16.1.dist-info → arize_phoenix-7.7.1.dist-info}/WHEEL +1 -1
  4. arize_phoenix-7.7.1.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.1.dist-info}/licenses/IP_NOTICE +0 -0
  335. {arize_phoenix-3.16.1.dist-info → arize_phoenix-7.7.1.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
@@ -0,0 +1,111 @@
1
+ import time
2
+ from threading import Thread
3
+
4
+ import psutil
5
+ from prometheus_client import (
6
+ Counter,
7
+ Gauge,
8
+ Summary,
9
+ start_http_server,
10
+ )
11
+ from starlette.middleware.base import BaseHTTPMiddleware, RequestResponseEndpoint
12
+ from starlette.requests import Request
13
+ from starlette.responses import Response
14
+ from starlette.routing import Match
15
+
16
+ REQUESTS_PROCESSING_TIME = Summary(
17
+ name="starlette_requests_processing_time_seconds_summary",
18
+ documentation="Summary of requests processing time by method and path (in seconds)",
19
+ labelnames=["method", "path"],
20
+ )
21
+ EXCEPTIONS = Counter(
22
+ name="starlette_exceptions_total",
23
+ documentation="Total count of exceptions raised by method, path and exception type",
24
+ labelnames=["method", "path", "exception_type"],
25
+ )
26
+ RAM_METRIC = Gauge(
27
+ name="memory_usage_bytes",
28
+ documentation="Memory usage in bytes",
29
+ labelnames=["type"],
30
+ )
31
+ CPU_METRIC = Gauge(
32
+ name="cpu_usage_percent",
33
+ documentation="CPU usage percent",
34
+ labelnames=["core"],
35
+ )
36
+ BULK_LOADER_INSERTION_TIME = Summary(
37
+ name="bulk_loader_insertion_time_seconds_summary",
38
+ documentation="Summary of database insertion time (seconds)",
39
+ )
40
+ BULK_LOADER_SPAN_INSERTIONS = Counter(
41
+ name="bulk_loader_span_insertions_total",
42
+ documentation="Total count of bulk loader span insertions",
43
+ )
44
+ BULK_LOADER_EVALUATION_INSERTIONS = Counter(
45
+ name="bulk_loader_evaluation_insertions_total",
46
+ documentation="Total count of bulk loader evaluation insertions",
47
+ )
48
+ BULK_LOADER_EXCEPTIONS = Counter(
49
+ name="bulk_loader_exceptions_total",
50
+ documentation="Total count of bulk loader exceptions",
51
+ )
52
+
53
+ RATE_LIMITER_CACHE_SIZE = Gauge(
54
+ name="rate_limiter_cache_size",
55
+ documentation="Current size of the rate limiter cache",
56
+ )
57
+
58
+ RATE_LIMITER_THROTTLES = Counter(
59
+ name="rate_limiter_throttles_total",
60
+ documentation="Total count of rate limiter throttles",
61
+ )
62
+
63
+ JWT_STORE_TOKENS_ACTIVE = Gauge(
64
+ name="jwt_store_tokens_active",
65
+ documentation="Current number of refresh tokens in the JWT store",
66
+ )
67
+
68
+ JWT_STORE_API_KEYS_ACTIVE = Gauge(
69
+ name="jwt_store_api_keys_active",
70
+ documentation="Current number of API keys in the JWT store",
71
+ )
72
+
73
+
74
+ class PrometheusMiddleware(BaseHTTPMiddleware):
75
+ async def dispatch(self, request: Request, call_next: RequestResponseEndpoint) -> Response:
76
+ for route in request.app.routes:
77
+ match, _ = route.matches(request.scope)
78
+ if match is Match.FULL:
79
+ path = route.path
80
+ break
81
+ else:
82
+ return await call_next(request)
83
+ method = request.method
84
+ start_time = time.perf_counter()
85
+ try:
86
+ response = await call_next(request)
87
+ except BaseException as e:
88
+ EXCEPTIONS.labels(method=method, path=path, exception_type=type(e).__name__).inc()
89
+ raise
90
+ end_time = time.perf_counter()
91
+ REQUESTS_PROCESSING_TIME.labels(method=method, path=path).observe(end_time - start_time)
92
+ return response
93
+
94
+
95
+ def start_prometheus() -> None:
96
+ Thread(target=gather_system_data, daemon=True).start()
97
+ start_http_server(9090, addr="::")
98
+
99
+
100
+ def gather_system_data() -> None:
101
+ while True:
102
+ time.sleep(1)
103
+
104
+ ram = psutil.virtual_memory()
105
+ swap = psutil.swap_memory()
106
+
107
+ RAM_METRIC.labels(type="virtual").set(ram.used)
108
+ RAM_METRIC.labels(type="swap").set(swap.used)
109
+
110
+ for core, percent in enumerate(psutil.cpu_percent(interval=1, percpu=True)):
111
+ CPU_METRIC.labels(core=core).set(percent)
@@ -0,0 +1,188 @@
1
+ import re
2
+ import time
3
+ from collections import defaultdict
4
+ from collections.abc import Callable, Coroutine
5
+ from functools import partial
6
+ from typing import (
7
+ Any,
8
+ Optional,
9
+ Pattern, # import from re module when we drop support for 3.8
10
+ Union,
11
+ )
12
+
13
+ from fastapi import HTTPException, Request
14
+
15
+ from phoenix.config import get_env_enable_prometheus
16
+ from phoenix.exceptions import PhoenixException
17
+
18
+
19
+ class UnavailableTokensError(PhoenixException):
20
+ pass
21
+
22
+
23
+ class TokenBucket:
24
+ """
25
+ An implementation of the token-bucket algorithm for use as a rate limiter.
26
+
27
+ Args:
28
+ per_second_request_rate (float): The allowed request rate.
29
+ enforcement_window_minutes (float): The time window over which the rate limit is enforced.
30
+ """
31
+
32
+ def __init__(
33
+ self,
34
+ per_second_request_rate: float,
35
+ enforcement_window_seconds: float = 1,
36
+ ):
37
+ self.enforcement_window = enforcement_window_seconds
38
+ self.rate = per_second_request_rate
39
+
40
+ now = time.time()
41
+ self.last_checked = now
42
+ self.tokens = self.max_tokens()
43
+
44
+ def max_tokens(self) -> float:
45
+ return self.rate * self.enforcement_window
46
+
47
+ def available_tokens(self) -> float:
48
+ now = time.time()
49
+ time_since_last_checked = now - self.last_checked
50
+ self.tokens = min(self.max_tokens(), self.rate * time_since_last_checked + self.tokens)
51
+ self.last_checked = now
52
+ return self.tokens
53
+
54
+ def make_request_if_ready(self) -> None:
55
+ if self.available_tokens() < 1:
56
+ if get_env_enable_prometheus():
57
+ from phoenix.server.prometheus import RATE_LIMITER_THROTTLES
58
+
59
+ RATE_LIMITER_THROTTLES.inc()
60
+ raise UnavailableTokensError
61
+ self.tokens -= 1
62
+
63
+
64
+ class ServerRateLimiter:
65
+ """
66
+ This rate limiter holds a cache of token buckets that enforce rate limits.
67
+
68
+ The cache is kept in partitions that rotate every `partition_seconds`. Each user's rate limiter
69
+ can be accessed from all active partitions, the number of active partitions is set with
70
+ `active_partitions`. This guarantees that a user's rate limiter will sit in the cache for at
71
+ least:
72
+
73
+ minimum_cache_lifetime = (active_partitions - 1) * partition_seconds
74
+
75
+ Every time the cache is accessed, inactive partitions are purged. If enough time has passed,
76
+ the entire cache is purged.
77
+ """
78
+
79
+ def __init__(
80
+ self,
81
+ per_second_rate_limit: float = 0.5,
82
+ enforcement_window_seconds: float = 5,
83
+ partition_seconds: float = 60,
84
+ active_partitions: int = 2,
85
+ ):
86
+ self.bucket_factory = partial(
87
+ TokenBucket,
88
+ per_second_request_rate=per_second_rate_limit,
89
+ enforcement_window_seconds=enforcement_window_seconds,
90
+ )
91
+ self.partition_seconds = partition_seconds
92
+ self.active_partitions = active_partitions
93
+ self.num_partitions = active_partitions + 2 # two overflow partitions to avoid edge cases
94
+ self._reset_rate_limiters()
95
+ self._last_cleanup_time = time.time()
96
+
97
+ def _reset_rate_limiters(self) -> None:
98
+ self.cache_partitions: list[defaultdict[Any, TokenBucket]] = [
99
+ defaultdict(self.bucket_factory) for _ in range(self.num_partitions)
100
+ ]
101
+
102
+ def _current_partition_index(self, timestamp: float) -> int:
103
+ return (
104
+ int(timestamp // self.partition_seconds) % self.num_partitions
105
+ ) # a cyclic bucket index
106
+
107
+ def _active_partition_indices(self, current_index: int) -> list[int]:
108
+ return [(current_index - ii) % self.num_partitions for ii in range(self.active_partitions)]
109
+
110
+ def _inactive_partition_indices(self, current_index: int) -> list[int]:
111
+ active_indices = set(self._active_partition_indices(current_index))
112
+ all_indices = set(range(self.num_partitions))
113
+ return list(all_indices - active_indices)
114
+
115
+ def _cleanup_expired_limiters(self, request_time: float) -> None:
116
+ time_since_last_cleanup = request_time - self._last_cleanup_time
117
+ if time_since_last_cleanup >= ((self.num_partitions - 1) * self.partition_seconds):
118
+ # Reset the cache to avoid "looping" back to the same partitions
119
+ self._reset_rate_limiters()
120
+ self._last_cleanup_time = request_time
121
+ return
122
+
123
+ current_partition_index = self._current_partition_index(request_time)
124
+ inactive_indices = self._inactive_partition_indices(current_partition_index)
125
+ for ii in inactive_indices:
126
+ self.cache_partitions[ii] = defaultdict(self.bucket_factory)
127
+ self._last_cleanup_time = request_time
128
+
129
+ def _fetch_token_bucket(self, key: str, request_time: float) -> TokenBucket:
130
+ current_partition_index = self._current_partition_index(request_time)
131
+ active_indices = self._active_partition_indices(current_partition_index)
132
+ bucket: Optional[TokenBucket] = None
133
+ for ii in active_indices:
134
+ partition = self.cache_partitions[ii]
135
+ if key in partition:
136
+ bucket = partition.pop(key)
137
+ break
138
+
139
+ current_partition = self.cache_partitions[current_partition_index]
140
+ if key not in current_partition and bucket is not None:
141
+ current_partition[key] = bucket
142
+ return current_partition[key]
143
+
144
+ def make_request(self, key: str) -> None:
145
+ request_time = time.time()
146
+ self._cleanup_expired_limiters(request_time)
147
+ rate_limiter = self._fetch_token_bucket(key, request_time)
148
+ rate_limiter.make_request_if_ready()
149
+ if get_env_enable_prometheus():
150
+ from phoenix.server.prometheus import RATE_LIMITER_CACHE_SIZE
151
+
152
+ RATE_LIMITER_CACHE_SIZE.set(sum(len(partition) for partition in self.cache_partitions))
153
+
154
+
155
+ def fastapi_ip_rate_limiter(
156
+ rate_limiter: ServerRateLimiter, paths: Optional[list[Union[str, Pattern[str]]]] = None
157
+ ) -> Callable[[Request], Coroutine[Any, Any, Request]]:
158
+ async def dependency(request: Request) -> Request:
159
+ if paths is None or any(path_match(request.url.path, path) for path in paths):
160
+ client = request.client
161
+ if client: # bypasses rate limiter if no client
162
+ client_ip = client.host
163
+ try:
164
+ rate_limiter.make_request(client_ip)
165
+ except UnavailableTokensError:
166
+ raise HTTPException(status_code=429, detail="Too Many Requests")
167
+ return request
168
+
169
+ return dependency
170
+
171
+
172
+ def fastapi_route_rate_limiter(
173
+ rate_limiter: ServerRateLimiter,
174
+ ) -> Callable[[Request], Coroutine[Any, Any, Request]]:
175
+ async def dependency(request: Request) -> Request:
176
+ try:
177
+ rate_limiter.make_request(request.url.path)
178
+ except UnavailableTokensError:
179
+ raise HTTPException(status_code=429, detail="Too Many Requests")
180
+ return request
181
+
182
+ return dependency
183
+
184
+
185
+ def path_match(path: str, match_pattern: Union[str, Pattern[str]]) -> bool:
186
+ if isinstance(match_pattern, re.Pattern):
187
+ return bool(match_pattern.match(path))
188
+ return path == match_pattern
@@ -0,0 +1,87 @@
1
+ {
2
+ "_components-Cy9nwIvF.js": {
3
+ "file": "assets/components-Cy9nwIvF.js",
4
+ "name": "components",
5
+ "imports": [
6
+ "_vendor-DvC8cT4X.js",
7
+ "_pages-CUi2xCVQ.js",
8
+ "_vendor-arizeai-Do1793cv.js",
9
+ "_vendor-codemirror-BzwZPyJM.js",
10
+ "_vendor-three-DwGkEfCM.js"
11
+ ]
12
+ },
13
+ "_pages-CUi2xCVQ.js": {
14
+ "file": "assets/pages-CUi2xCVQ.js",
15
+ "name": "pages",
16
+ "imports": [
17
+ "_vendor-DvC8cT4X.js",
18
+ "_vendor-arizeai-Do1793cv.js",
19
+ "_components-Cy9nwIvF.js",
20
+ "_vendor-codemirror-BzwZPyJM.js",
21
+ "_vendor-recharts-_Jb7JjhG.js"
22
+ ]
23
+ },
24
+ "_vendor-!~{003}~.js": {
25
+ "file": "assets/vendor-DxkFTwjz.css",
26
+ "src": "_vendor-!~{003}~.js"
27
+ },
28
+ "_vendor-DvC8cT4X.js": {
29
+ "file": "assets/vendor-DvC8cT4X.js",
30
+ "name": "vendor",
31
+ "imports": [
32
+ "_vendor-three-DwGkEfCM.js"
33
+ ],
34
+ "css": [
35
+ "assets/vendor-DxkFTwjz.css"
36
+ ]
37
+ },
38
+ "_vendor-arizeai-Do1793cv.js": {
39
+ "file": "assets/vendor-arizeai-Do1793cv.js",
40
+ "name": "vendor-arizeai",
41
+ "imports": [
42
+ "_vendor-DvC8cT4X.js"
43
+ ]
44
+ },
45
+ "_vendor-codemirror-BzwZPyJM.js": {
46
+ "file": "assets/vendor-codemirror-BzwZPyJM.js",
47
+ "name": "vendor-codemirror",
48
+ "imports": [
49
+ "_vendor-DvC8cT4X.js",
50
+ "_vendor-shiki-Cl9QBraO.js"
51
+ ]
52
+ },
53
+ "_vendor-recharts-_Jb7JjhG.js": {
54
+ "file": "assets/vendor-recharts-_Jb7JjhG.js",
55
+ "name": "vendor-recharts",
56
+ "imports": [
57
+ "_vendor-DvC8cT4X.js"
58
+ ]
59
+ },
60
+ "_vendor-shiki-Cl9QBraO.js": {
61
+ "file": "assets/vendor-shiki-Cl9QBraO.js",
62
+ "name": "vendor-shiki",
63
+ "imports": [
64
+ "_vendor-DvC8cT4X.js"
65
+ ]
66
+ },
67
+ "_vendor-three-DwGkEfCM.js": {
68
+ "file": "assets/vendor-three-DwGkEfCM.js",
69
+ "name": "vendor-three"
70
+ },
71
+ "index.tsx": {
72
+ "file": "assets/index-BKvHIxkk.js",
73
+ "name": "index",
74
+ "src": "index.tsx",
75
+ "isEntry": true,
76
+ "imports": [
77
+ "_vendor-DvC8cT4X.js",
78
+ "_vendor-arizeai-Do1793cv.js",
79
+ "_pages-CUi2xCVQ.js",
80
+ "_components-Cy9nwIvF.js",
81
+ "_vendor-three-DwGkEfCM.js",
82
+ "_vendor-codemirror-BzwZPyJM.js",
83
+ "_vendor-shiki-Cl9QBraO.js",
84
+ "_vendor-recharts-_Jb7JjhG.js"
85
+ ]
86
+ }
87
+ }