aeri-python 4.0.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.
Files changed (391) hide show
  1. aeri/__init__.py +72 -0
  2. aeri/_client/_validation.py +204 -0
  3. aeri/_client/attributes.py +188 -0
  4. aeri/_client/client.py +3761 -0
  5. aeri/_client/constants.py +65 -0
  6. aeri/_client/datasets.py +302 -0
  7. aeri/_client/environment_variables.py +158 -0
  8. aeri/_client/get_client.py +149 -0
  9. aeri/_client/observe.py +661 -0
  10. aeri/_client/propagation.py +475 -0
  11. aeri/_client/resource_manager.py +510 -0
  12. aeri/_client/span.py +1519 -0
  13. aeri/_client/span_filter.py +76 -0
  14. aeri/_client/span_processor.py +206 -0
  15. aeri/_client/utils.py +132 -0
  16. aeri/_task_manager/media_manager.py +331 -0
  17. aeri/_task_manager/media_upload_consumer.py +44 -0
  18. aeri/_task_manager/media_upload_queue.py +12 -0
  19. aeri/_task_manager/score_ingestion_consumer.py +208 -0
  20. aeri/_task_manager/task_manager.py +475 -0
  21. aeri/_utils/__init__.py +19 -0
  22. aeri/_utils/environment.py +34 -0
  23. aeri/_utils/error_logging.py +47 -0
  24. aeri/_utils/parse_error.py +99 -0
  25. aeri/_utils/prompt_cache.py +188 -0
  26. aeri/_utils/request.py +137 -0
  27. aeri/_utils/serializer.py +205 -0
  28. aeri/api/.fern/metadata.json +14 -0
  29. aeri/api/__init__.py +836 -0
  30. aeri/api/annotation_queues/__init__.py +82 -0
  31. aeri/api/annotation_queues/client.py +1111 -0
  32. aeri/api/annotation_queues/raw_client.py +2288 -0
  33. aeri/api/annotation_queues/types/__init__.py +84 -0
  34. aeri/api/annotation_queues/types/annotation_queue.py +28 -0
  35. aeri/api/annotation_queues/types/annotation_queue_assignment_request.py +16 -0
  36. aeri/api/annotation_queues/types/annotation_queue_item.py +34 -0
  37. aeri/api/annotation_queues/types/annotation_queue_object_type.py +26 -0
  38. aeri/api/annotation_queues/types/annotation_queue_status.py +22 -0
  39. aeri/api/annotation_queues/types/create_annotation_queue_assignment_response.py +18 -0
  40. aeri/api/annotation_queues/types/create_annotation_queue_item_request.py +25 -0
  41. aeri/api/annotation_queues/types/create_annotation_queue_request.py +20 -0
  42. aeri/api/annotation_queues/types/delete_annotation_queue_assignment_response.py +14 -0
  43. aeri/api/annotation_queues/types/delete_annotation_queue_item_response.py +15 -0
  44. aeri/api/annotation_queues/types/paginated_annotation_queue_items.py +17 -0
  45. aeri/api/annotation_queues/types/paginated_annotation_queues.py +17 -0
  46. aeri/api/annotation_queues/types/update_annotation_queue_item_request.py +15 -0
  47. aeri/api/blob_storage_integrations/__init__.py +73 -0
  48. aeri/api/blob_storage_integrations/client.py +550 -0
  49. aeri/api/blob_storage_integrations/raw_client.py +976 -0
  50. aeri/api/blob_storage_integrations/types/__init__.py +77 -0
  51. aeri/api/blob_storage_integrations/types/blob_storage_export_frequency.py +26 -0
  52. aeri/api/blob_storage_integrations/types/blob_storage_export_mode.py +26 -0
  53. aeri/api/blob_storage_integrations/types/blob_storage_integration_deletion_response.py +14 -0
  54. aeri/api/blob_storage_integrations/types/blob_storage_integration_file_type.py +26 -0
  55. aeri/api/blob_storage_integrations/types/blob_storage_integration_response.py +64 -0
  56. aeri/api/blob_storage_integrations/types/blob_storage_integration_status_response.py +50 -0
  57. aeri/api/blob_storage_integrations/types/blob_storage_integration_type.py +26 -0
  58. aeri/api/blob_storage_integrations/types/blob_storage_integrations_response.py +15 -0
  59. aeri/api/blob_storage_integrations/types/blob_storage_sync_status.py +47 -0
  60. aeri/api/blob_storage_integrations/types/create_blob_storage_integration_request.py +91 -0
  61. aeri/api/client.py +679 -0
  62. aeri/api/comments/__init__.py +44 -0
  63. aeri/api/comments/client.py +407 -0
  64. aeri/api/comments/raw_client.py +750 -0
  65. aeri/api/comments/types/__init__.py +46 -0
  66. aeri/api/comments/types/create_comment_request.py +47 -0
  67. aeri/api/comments/types/create_comment_response.py +17 -0
  68. aeri/api/comments/types/get_comments_response.py +17 -0
  69. aeri/api/commons/__init__.py +210 -0
  70. aeri/api/commons/errors/__init__.py +56 -0
  71. aeri/api/commons/errors/access_denied_error.py +12 -0
  72. aeri/api/commons/errors/error.py +12 -0
  73. aeri/api/commons/errors/method_not_allowed_error.py +12 -0
  74. aeri/api/commons/errors/not_found_error.py +12 -0
  75. aeri/api/commons/errors/unauthorized_error.py +12 -0
  76. aeri/api/commons/types/__init__.py +190 -0
  77. aeri/api/commons/types/base_score.py +90 -0
  78. aeri/api/commons/types/base_score_v1.py +70 -0
  79. aeri/api/commons/types/boolean_score.py +26 -0
  80. aeri/api/commons/types/boolean_score_v1.py +26 -0
  81. aeri/api/commons/types/categorical_score.py +26 -0
  82. aeri/api/commons/types/categorical_score_v1.py +26 -0
  83. aeri/api/commons/types/comment.py +36 -0
  84. aeri/api/commons/types/comment_object_type.py +30 -0
  85. aeri/api/commons/types/config_category.py +15 -0
  86. aeri/api/commons/types/correction_score.py +26 -0
  87. aeri/api/commons/types/create_score_value.py +5 -0
  88. aeri/api/commons/types/dataset.py +49 -0
  89. aeri/api/commons/types/dataset_item.py +58 -0
  90. aeri/api/commons/types/dataset_run.py +63 -0
  91. aeri/api/commons/types/dataset_run_item.py +40 -0
  92. aeri/api/commons/types/dataset_run_with_items.py +19 -0
  93. aeri/api/commons/types/dataset_status.py +22 -0
  94. aeri/api/commons/types/map_value.py +11 -0
  95. aeri/api/commons/types/model.py +125 -0
  96. aeri/api/commons/types/model_price.py +14 -0
  97. aeri/api/commons/types/model_usage_unit.py +42 -0
  98. aeri/api/commons/types/numeric_score.py +17 -0
  99. aeri/api/commons/types/numeric_score_v1.py +17 -0
  100. aeri/api/commons/types/observation.py +142 -0
  101. aeri/api/commons/types/observation_level.py +30 -0
  102. aeri/api/commons/types/observation_v2.py +235 -0
  103. aeri/api/commons/types/observations_view.py +89 -0
  104. aeri/api/commons/types/pricing_tier.py +91 -0
  105. aeri/api/commons/types/pricing_tier_condition.py +68 -0
  106. aeri/api/commons/types/pricing_tier_input.py +76 -0
  107. aeri/api/commons/types/pricing_tier_operator.py +42 -0
  108. aeri/api/commons/types/score.py +201 -0
  109. aeri/api/commons/types/score_config.py +66 -0
  110. aeri/api/commons/types/score_config_data_type.py +26 -0
  111. aeri/api/commons/types/score_data_type.py +30 -0
  112. aeri/api/commons/types/score_source.py +26 -0
  113. aeri/api/commons/types/score_v1.py +131 -0
  114. aeri/api/commons/types/session.py +25 -0
  115. aeri/api/commons/types/session_with_traces.py +15 -0
  116. aeri/api/commons/types/trace.py +84 -0
  117. aeri/api/commons/types/trace_with_details.py +43 -0
  118. aeri/api/commons/types/trace_with_full_details.py +45 -0
  119. aeri/api/commons/types/usage.py +59 -0
  120. aeri/api/core/__init__.py +111 -0
  121. aeri/api/core/api_error.py +23 -0
  122. aeri/api/core/client_wrapper.py +141 -0
  123. aeri/api/core/datetime_utils.py +30 -0
  124. aeri/api/core/enum.py +20 -0
  125. aeri/api/core/file.py +70 -0
  126. aeri/api/core/force_multipart.py +18 -0
  127. aeri/api/core/http_client.py +711 -0
  128. aeri/api/core/http_response.py +55 -0
  129. aeri/api/core/http_sse/__init__.py +48 -0
  130. aeri/api/core/http_sse/_api.py +114 -0
  131. aeri/api/core/http_sse/_decoders.py +66 -0
  132. aeri/api/core/http_sse/_exceptions.py +7 -0
  133. aeri/api/core/http_sse/_models.py +17 -0
  134. aeri/api/core/jsonable_encoder.py +102 -0
  135. aeri/api/core/pydantic_utilities.py +310 -0
  136. aeri/api/core/query_encoder.py +60 -0
  137. aeri/api/core/remove_none_from_dict.py +11 -0
  138. aeri/api/core/request_options.py +35 -0
  139. aeri/api/core/serialization.py +282 -0
  140. aeri/api/dataset_items/__init__.py +52 -0
  141. aeri/api/dataset_items/client.py +499 -0
  142. aeri/api/dataset_items/raw_client.py +973 -0
  143. aeri/api/dataset_items/types/__init__.py +50 -0
  144. aeri/api/dataset_items/types/create_dataset_item_request.py +37 -0
  145. aeri/api/dataset_items/types/delete_dataset_item_response.py +17 -0
  146. aeri/api/dataset_items/types/paginated_dataset_items.py +17 -0
  147. aeri/api/dataset_run_items/__init__.py +43 -0
  148. aeri/api/dataset_run_items/client.py +323 -0
  149. aeri/api/dataset_run_items/raw_client.py +547 -0
  150. aeri/api/dataset_run_items/types/__init__.py +44 -0
  151. aeri/api/dataset_run_items/types/create_dataset_run_item_request.py +51 -0
  152. aeri/api/dataset_run_items/types/paginated_dataset_run_items.py +17 -0
  153. aeri/api/datasets/__init__.py +55 -0
  154. aeri/api/datasets/client.py +661 -0
  155. aeri/api/datasets/raw_client.py +1368 -0
  156. aeri/api/datasets/types/__init__.py +53 -0
  157. aeri/api/datasets/types/create_dataset_request.py +31 -0
  158. aeri/api/datasets/types/delete_dataset_run_response.py +14 -0
  159. aeri/api/datasets/types/paginated_dataset_runs.py +17 -0
  160. aeri/api/datasets/types/paginated_datasets.py +17 -0
  161. aeri/api/health/__init__.py +44 -0
  162. aeri/api/health/client.py +112 -0
  163. aeri/api/health/errors/__init__.py +42 -0
  164. aeri/api/health/errors/service_unavailable_error.py +13 -0
  165. aeri/api/health/raw_client.py +227 -0
  166. aeri/api/health/types/__init__.py +40 -0
  167. aeri/api/health/types/health_response.py +30 -0
  168. aeri/api/ingestion/__init__.py +169 -0
  169. aeri/api/ingestion/client.py +221 -0
  170. aeri/api/ingestion/raw_client.py +293 -0
  171. aeri/api/ingestion/types/__init__.py +169 -0
  172. aeri/api/ingestion/types/base_event.py +27 -0
  173. aeri/api/ingestion/types/create_event_body.py +14 -0
  174. aeri/api/ingestion/types/create_event_event.py +15 -0
  175. aeri/api/ingestion/types/create_generation_body.py +40 -0
  176. aeri/api/ingestion/types/create_generation_event.py +15 -0
  177. aeri/api/ingestion/types/create_observation_event.py +15 -0
  178. aeri/api/ingestion/types/create_span_body.py +19 -0
  179. aeri/api/ingestion/types/create_span_event.py +15 -0
  180. aeri/api/ingestion/types/ingestion_error.py +17 -0
  181. aeri/api/ingestion/types/ingestion_event.py +155 -0
  182. aeri/api/ingestion/types/ingestion_response.py +17 -0
  183. aeri/api/ingestion/types/ingestion_success.py +15 -0
  184. aeri/api/ingestion/types/ingestion_usage.py +8 -0
  185. aeri/api/ingestion/types/observation_body.py +53 -0
  186. aeri/api/ingestion/types/observation_type.py +54 -0
  187. aeri/api/ingestion/types/open_ai_completion_usage_schema.py +26 -0
  188. aeri/api/ingestion/types/open_ai_response_usage_schema.py +24 -0
  189. aeri/api/ingestion/types/open_ai_usage.py +28 -0
  190. aeri/api/ingestion/types/optional_observation_body.py +36 -0
  191. aeri/api/ingestion/types/score_body.py +75 -0
  192. aeri/api/ingestion/types/score_event.py +15 -0
  193. aeri/api/ingestion/types/sdk_log_body.py +14 -0
  194. aeri/api/ingestion/types/sdk_log_event.py +15 -0
  195. aeri/api/ingestion/types/trace_body.py +36 -0
  196. aeri/api/ingestion/types/trace_event.py +15 -0
  197. aeri/api/ingestion/types/update_event_body.py +14 -0
  198. aeri/api/ingestion/types/update_generation_body.py +40 -0
  199. aeri/api/ingestion/types/update_generation_event.py +15 -0
  200. aeri/api/ingestion/types/update_observation_event.py +15 -0
  201. aeri/api/ingestion/types/update_span_body.py +19 -0
  202. aeri/api/ingestion/types/update_span_event.py +15 -0
  203. aeri/api/ingestion/types/usage_details.py +10 -0
  204. aeri/api/legacy/__init__.py +61 -0
  205. aeri/api/legacy/client.py +105 -0
  206. aeri/api/legacy/metrics_v1/__init__.py +40 -0
  207. aeri/api/legacy/metrics_v1/client.py +214 -0
  208. aeri/api/legacy/metrics_v1/raw_client.py +322 -0
  209. aeri/api/legacy/metrics_v1/types/__init__.py +40 -0
  210. aeri/api/legacy/metrics_v1/types/metrics_response.py +19 -0
  211. aeri/api/legacy/observations_v1/__init__.py +43 -0
  212. aeri/api/legacy/observations_v1/client.py +523 -0
  213. aeri/api/legacy/observations_v1/raw_client.py +759 -0
  214. aeri/api/legacy/observations_v1/types/__init__.py +44 -0
  215. aeri/api/legacy/observations_v1/types/observations.py +17 -0
  216. aeri/api/legacy/observations_v1/types/observations_views.py +17 -0
  217. aeri/api/legacy/raw_client.py +13 -0
  218. aeri/api/legacy/score_v1/__init__.py +43 -0
  219. aeri/api/legacy/score_v1/client.py +329 -0
  220. aeri/api/legacy/score_v1/raw_client.py +545 -0
  221. aeri/api/legacy/score_v1/types/__init__.py +44 -0
  222. aeri/api/legacy/score_v1/types/create_score_request.py +75 -0
  223. aeri/api/legacy/score_v1/types/create_score_response.py +17 -0
  224. aeri/api/llm_connections/__init__.py +55 -0
  225. aeri/api/llm_connections/client.py +311 -0
  226. aeri/api/llm_connections/raw_client.py +541 -0
  227. aeri/api/llm_connections/types/__init__.py +53 -0
  228. aeri/api/llm_connections/types/llm_adapter.py +38 -0
  229. aeri/api/llm_connections/types/llm_connection.py +77 -0
  230. aeri/api/llm_connections/types/paginated_llm_connections.py +17 -0
  231. aeri/api/llm_connections/types/upsert_llm_connection_request.py +69 -0
  232. aeri/api/media/__init__.py +58 -0
  233. aeri/api/media/client.py +427 -0
  234. aeri/api/media/raw_client.py +739 -0
  235. aeri/api/media/types/__init__.py +56 -0
  236. aeri/api/media/types/get_media_response.py +55 -0
  237. aeri/api/media/types/get_media_upload_url_request.py +51 -0
  238. aeri/api/media/types/get_media_upload_url_response.py +28 -0
  239. aeri/api/media/types/media_content_type.py +232 -0
  240. aeri/api/media/types/patch_media_body.py +43 -0
  241. aeri/api/metrics/__init__.py +40 -0
  242. aeri/api/metrics/client.py +422 -0
  243. aeri/api/metrics/raw_client.py +530 -0
  244. aeri/api/metrics/types/__init__.py +40 -0
  245. aeri/api/metrics/types/metrics_v2response.py +19 -0
  246. aeri/api/models/__init__.py +43 -0
  247. aeri/api/models/client.py +523 -0
  248. aeri/api/models/raw_client.py +993 -0
  249. aeri/api/models/types/__init__.py +44 -0
  250. aeri/api/models/types/create_model_request.py +103 -0
  251. aeri/api/models/types/paginated_models.py +17 -0
  252. aeri/api/observations/__init__.py +43 -0
  253. aeri/api/observations/client.py +522 -0
  254. aeri/api/observations/raw_client.py +641 -0
  255. aeri/api/observations/types/__init__.py +44 -0
  256. aeri/api/observations/types/observations_v2meta.py +21 -0
  257. aeri/api/observations/types/observations_v2response.py +28 -0
  258. aeri/api/opentelemetry/__init__.py +67 -0
  259. aeri/api/opentelemetry/client.py +276 -0
  260. aeri/api/opentelemetry/raw_client.py +291 -0
  261. aeri/api/opentelemetry/types/__init__.py +65 -0
  262. aeri/api/opentelemetry/types/otel_attribute.py +27 -0
  263. aeri/api/opentelemetry/types/otel_attribute_value.py +46 -0
  264. aeri/api/opentelemetry/types/otel_resource.py +24 -0
  265. aeri/api/opentelemetry/types/otel_resource_span.py +32 -0
  266. aeri/api/opentelemetry/types/otel_scope.py +34 -0
  267. aeri/api/opentelemetry/types/otel_scope_span.py +28 -0
  268. aeri/api/opentelemetry/types/otel_span.py +76 -0
  269. aeri/api/opentelemetry/types/otel_trace_response.py +16 -0
  270. aeri/api/organizations/__init__.py +73 -0
  271. aeri/api/organizations/client.py +756 -0
  272. aeri/api/organizations/raw_client.py +1707 -0
  273. aeri/api/organizations/types/__init__.py +71 -0
  274. aeri/api/organizations/types/delete_membership_request.py +16 -0
  275. aeri/api/organizations/types/membership_deletion_response.py +17 -0
  276. aeri/api/organizations/types/membership_request.py +18 -0
  277. aeri/api/organizations/types/membership_response.py +20 -0
  278. aeri/api/organizations/types/membership_role.py +30 -0
  279. aeri/api/organizations/types/memberships_response.py +15 -0
  280. aeri/api/organizations/types/organization_api_key.py +31 -0
  281. aeri/api/organizations/types/organization_api_keys_response.py +19 -0
  282. aeri/api/organizations/types/organization_project.py +25 -0
  283. aeri/api/organizations/types/organization_projects_response.py +15 -0
  284. aeri/api/projects/__init__.py +67 -0
  285. aeri/api/projects/client.py +760 -0
  286. aeri/api/projects/raw_client.py +1577 -0
  287. aeri/api/projects/types/__init__.py +65 -0
  288. aeri/api/projects/types/api_key_deletion_response.py +18 -0
  289. aeri/api/projects/types/api_key_list.py +23 -0
  290. aeri/api/projects/types/api_key_response.py +30 -0
  291. aeri/api/projects/types/api_key_summary.py +35 -0
  292. aeri/api/projects/types/organization.py +22 -0
  293. aeri/api/projects/types/project.py +34 -0
  294. aeri/api/projects/types/project_deletion_response.py +15 -0
  295. aeri/api/projects/types/projects.py +15 -0
  296. aeri/api/prompt_version/__init__.py +4 -0
  297. aeri/api/prompt_version/client.py +157 -0
  298. aeri/api/prompt_version/raw_client.py +264 -0
  299. aeri/api/prompts/__init__.py +100 -0
  300. aeri/api/prompts/client.py +550 -0
  301. aeri/api/prompts/raw_client.py +987 -0
  302. aeri/api/prompts/types/__init__.py +96 -0
  303. aeri/api/prompts/types/base_prompt.py +42 -0
  304. aeri/api/prompts/types/chat_message.py +17 -0
  305. aeri/api/prompts/types/chat_message_type.py +15 -0
  306. aeri/api/prompts/types/chat_message_with_placeholders.py +8 -0
  307. aeri/api/prompts/types/chat_prompt.py +15 -0
  308. aeri/api/prompts/types/create_chat_prompt_request.py +37 -0
  309. aeri/api/prompts/types/create_chat_prompt_type.py +15 -0
  310. aeri/api/prompts/types/create_prompt_request.py +8 -0
  311. aeri/api/prompts/types/create_text_prompt_request.py +36 -0
  312. aeri/api/prompts/types/create_text_prompt_type.py +15 -0
  313. aeri/api/prompts/types/placeholder_message.py +16 -0
  314. aeri/api/prompts/types/placeholder_message_type.py +15 -0
  315. aeri/api/prompts/types/prompt.py +58 -0
  316. aeri/api/prompts/types/prompt_meta.py +35 -0
  317. aeri/api/prompts/types/prompt_meta_list_response.py +17 -0
  318. aeri/api/prompts/types/prompt_type.py +20 -0
  319. aeri/api/prompts/types/text_prompt.py +14 -0
  320. aeri/api/scim/__init__.py +94 -0
  321. aeri/api/scim/client.py +686 -0
  322. aeri/api/scim/raw_client.py +1528 -0
  323. aeri/api/scim/types/__init__.py +92 -0
  324. aeri/api/scim/types/authentication_scheme.py +20 -0
  325. aeri/api/scim/types/bulk_config.py +22 -0
  326. aeri/api/scim/types/empty_response.py +16 -0
  327. aeri/api/scim/types/filter_config.py +17 -0
  328. aeri/api/scim/types/resource_meta.py +17 -0
  329. aeri/api/scim/types/resource_type.py +27 -0
  330. aeri/api/scim/types/resource_types_response.py +21 -0
  331. aeri/api/scim/types/schema_extension.py +17 -0
  332. aeri/api/scim/types/schema_resource.py +19 -0
  333. aeri/api/scim/types/schemas_response.py +21 -0
  334. aeri/api/scim/types/scim_email.py +16 -0
  335. aeri/api/scim/types/scim_feature_support.py +14 -0
  336. aeri/api/scim/types/scim_name.py +14 -0
  337. aeri/api/scim/types/scim_user.py +24 -0
  338. aeri/api/scim/types/scim_users_list_response.py +25 -0
  339. aeri/api/scim/types/service_provider_config.py +36 -0
  340. aeri/api/scim/types/user_meta.py +20 -0
  341. aeri/api/score_configs/__init__.py +44 -0
  342. aeri/api/score_configs/client.py +526 -0
  343. aeri/api/score_configs/raw_client.py +1012 -0
  344. aeri/api/score_configs/types/__init__.py +46 -0
  345. aeri/api/score_configs/types/create_score_config_request.py +46 -0
  346. aeri/api/score_configs/types/score_configs.py +17 -0
  347. aeri/api/score_configs/types/update_score_config_request.py +53 -0
  348. aeri/api/scores/__init__.py +76 -0
  349. aeri/api/scores/client.py +420 -0
  350. aeri/api/scores/raw_client.py +656 -0
  351. aeri/api/scores/types/__init__.py +76 -0
  352. aeri/api/scores/types/get_scores_response.py +17 -0
  353. aeri/api/scores/types/get_scores_response_data.py +211 -0
  354. aeri/api/scores/types/get_scores_response_data_boolean.py +15 -0
  355. aeri/api/scores/types/get_scores_response_data_categorical.py +15 -0
  356. aeri/api/scores/types/get_scores_response_data_correction.py +15 -0
  357. aeri/api/scores/types/get_scores_response_data_numeric.py +15 -0
  358. aeri/api/scores/types/get_scores_response_trace_data.py +38 -0
  359. aeri/api/sessions/__init__.py +40 -0
  360. aeri/api/sessions/client.py +262 -0
  361. aeri/api/sessions/raw_client.py +500 -0
  362. aeri/api/sessions/types/__init__.py +40 -0
  363. aeri/api/sessions/types/paginated_sessions.py +17 -0
  364. aeri/api/trace/__init__.py +44 -0
  365. aeri/api/trace/client.py +728 -0
  366. aeri/api/trace/raw_client.py +1208 -0
  367. aeri/api/trace/types/__init__.py +46 -0
  368. aeri/api/trace/types/delete_trace_response.py +14 -0
  369. aeri/api/trace/types/sort.py +14 -0
  370. aeri/api/trace/types/traces.py +17 -0
  371. aeri/api/utils/__init__.py +44 -0
  372. aeri/api/utils/pagination/__init__.py +40 -0
  373. aeri/api/utils/pagination/types/__init__.py +40 -0
  374. aeri/api/utils/pagination/types/meta_response.py +38 -0
  375. aeri/batch_evaluation.py +1643 -0
  376. aeri/experiment.py +1044 -0
  377. aeri/langchain/CallbackHandler.py +1377 -0
  378. aeri/langchain/__init__.py +5 -0
  379. aeri/langchain/utils.py +212 -0
  380. aeri/logger.py +28 -0
  381. aeri/media.py +352 -0
  382. aeri/model.py +477 -0
  383. aeri/openai.py +1124 -0
  384. aeri/py.typed +0 -0
  385. aeri/span_filter.py +17 -0
  386. aeri/types.py +79 -0
  387. aeri/version.py +3 -0
  388. aeri_python-4.0.0.dist-info/METADATA +51 -0
  389. aeri_python-4.0.0.dist-info/RECORD +391 -0
  390. aeri_python-4.0.0.dist-info/WHEEL +4 -0
  391. aeri_python-4.0.0.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,510 @@
1
+ """Tracer implementation for Aeri OpenTelemetry integration.
2
+
3
+ This module provides the AeriTracer class, a thread-safe singleton that manages OpenTelemetry
4
+ tracing infrastructure for Aeri. It handles tracer initialization, span processors,
5
+ API clients, and coordinates background tasks for efficient data processing and media handling.
6
+
7
+ Key features:
8
+ - Thread-safe OpenTelemetry tracer with Aeri-specific span processors and sampling
9
+ - Configurable batch processing of spans and scores with intelligent flushing behavior
10
+ - Asynchronous background media upload processing with dedicated worker threads
11
+ - Concurrent score ingestion with batching and retry mechanisms
12
+ - Automatic project ID discovery and caching
13
+ - Graceful shutdown handling with proper resource cleanup
14
+ - Fault tolerance with detailed error logging and recovery mechanisms
15
+ """
16
+
17
+ import atexit
18
+ import os
19
+ import threading
20
+ from datetime import datetime
21
+ from queue import Full, Queue
22
+ from typing import Any, Callable, Dict, List, Literal, Optional, Union, cast
23
+
24
+ import httpx
25
+ from opentelemetry import trace as otel_trace_api
26
+ from opentelemetry.sdk.resources import Resource
27
+ from opentelemetry.sdk.trace import ReadableSpan, TracerProvider
28
+ from opentelemetry.sdk.trace.sampling import Decision, TraceIdRatioBased
29
+ from opentelemetry.trace import Tracer
30
+ from pydantic import BaseModel, ConfigDict, ValidationError
31
+
32
+ from aeri._client.attributes import AeriOtelSpanAttributes
33
+ from aeri._client.constants import AERI_TRACER_NAME
34
+ from aeri._client.environment_variables import (
35
+ AERI_MEDIA_UPLOAD_ENABLED,
36
+ AERI_MEDIA_UPLOAD_THREAD_COUNT,
37
+ AERI_RELEASE,
38
+ AERI_TRACING_ENVIRONMENT,
39
+ )
40
+ from aeri._client.span_processor import AeriSpanProcessor
41
+ from aeri._task_manager.media_manager import MediaManager
42
+ from aeri._task_manager.media_upload_consumer import MediaUploadConsumer
43
+ from aeri._task_manager.task_manager import AsyncIngestionTaskManager
44
+ from aeri._utils.environment import get_common_release_envs
45
+ from aeri._utils.prompt_cache import PromptCache
46
+ from aeri.api import AsyncAeriAPI, AeriAPI
47
+ from aeri.api.ingestion.types.ingestion_event import (
48
+ IngestionEvent_ScoreCreate,
49
+ IngestionEvent_TraceCreate,
50
+ )
51
+ from aeri.api.ingestion.types.score_body import ScoreBody
52
+ from aeri.api.ingestion.types.trace_body import TraceBody
53
+ from aeri.logger import aeri_logger
54
+ from aeri.types import MaskFunction
55
+
56
+ from ..version import __version__ as aeri_version
57
+
58
+
59
+ class _StrictScoreQueueEvent(BaseModel):
60
+ model_config = ConfigDict(extra="forbid", strict=True)
61
+
62
+ id: str
63
+ type: Literal["score-create"]
64
+ timestamp: datetime
65
+ body: ScoreBody
66
+ metadata: Optional[Any] = None
67
+
68
+ def to_ingestion_event(self) -> IngestionEvent_ScoreCreate:
69
+ return IngestionEvent_ScoreCreate(
70
+ id=self.id,
71
+ type=self.type,
72
+ timestamp=self.timestamp.isoformat(),
73
+ body=self.body,
74
+ metadata=self.metadata,
75
+ )
76
+
77
+
78
+ class _StrictTraceQueueEvent(BaseModel):
79
+ model_config = ConfigDict(extra="forbid", strict=True)
80
+
81
+ id: str
82
+ type: Literal["trace-create"]
83
+ timestamp: datetime
84
+ body: TraceBody
85
+ metadata: Optional[Any] = None
86
+
87
+ def to_ingestion_event(self) -> IngestionEvent_TraceCreate:
88
+ return IngestionEvent_TraceCreate(
89
+ id=self.id,
90
+ type=self.type,
91
+ timestamp=self.timestamp.isoformat(),
92
+ body=self.body,
93
+ metadata=self.metadata,
94
+ )
95
+
96
+
97
+ IngestionQueueEvent = Union[IngestionEvent_ScoreCreate, IngestionEvent_TraceCreate]
98
+
99
+
100
+ class AeriResourceManager:
101
+ """Thread-safe singleton that provides access to the OpenTelemetry tracer and processors.
102
+
103
+ This class implements a thread-safe singleton pattern keyed by the public API key,
104
+ ensuring that only one tracer instance exists per API key combination. It manages
105
+ the lifecycle of the OpenTelemetry tracer provider, span processors, and resource
106
+ attributes, as well as background threads for media uploads and score ingestion.
107
+
108
+ The tracer is responsible for:
109
+ 1. Setting up the OpenTelemetry tracer with appropriate sampling and configuration
110
+ 2. Managing the span processor for exporting spans to the Aeri API
111
+ 3. Creating and managing Aeri API clients (both synchronous and asynchronous)
112
+ 4. Handling background media upload processing via dedicated worker threads
113
+ 5. Processing and batching score ingestion events with configurable flush settings
114
+ 6. Retrieving and caching project information for URL generation and media handling
115
+ 7. Coordinating graceful shutdown of all background processes with proper resource cleanup
116
+
117
+ This implementation follows best practices for resource management in long-running
118
+ applications, including thread-safe singleton pattern, bounded queues to prevent memory
119
+ exhaustion, proper resource cleanup on shutdown, and fault-tolerant error handling with
120
+ detailed logging.
121
+
122
+ Thread safety is ensured through the use of locks, thread-safe queues, and atomic operations,
123
+ making this implementation suitable for multi-threaded and asyncio applications.
124
+ """
125
+
126
+ _instances: Dict[str, "AeriResourceManager"] = {}
127
+ _lock = threading.RLock()
128
+
129
+ def __new__(
130
+ cls,
131
+ *,
132
+ public_key: str,
133
+ secret_key: str,
134
+ base_url: str,
135
+ environment: Optional[str] = None,
136
+ release: Optional[str] = None,
137
+ timeout: Optional[int] = None,
138
+ flush_at: Optional[int] = None,
139
+ flush_interval: Optional[float] = None,
140
+ httpx_client: Optional[httpx.Client] = None,
141
+ media_upload_thread_count: Optional[int] = None,
142
+ sample_rate: Optional[float] = None,
143
+ mask: Optional[MaskFunction] = None,
144
+ tracing_enabled: Optional[bool] = None,
145
+ blocked_instrumentation_scopes: Optional[List[str]] = None,
146
+ should_export_span: Optional[Callable[[ReadableSpan], bool]] = None,
147
+ additional_headers: Optional[Dict[str, str]] = None,
148
+ tracer_provider: Optional[TracerProvider] = None,
149
+ ) -> "AeriResourceManager":
150
+ if public_key in cls._instances:
151
+ return cls._instances[public_key]
152
+
153
+ with cls._lock:
154
+ if public_key not in cls._instances:
155
+ instance = super(AeriResourceManager, cls).__new__(cls)
156
+
157
+ # Initialize tracer (will be noop until init instance)
158
+ instance._otel_tracer = otel_trace_api.get_tracer(
159
+ AERI_TRACER_NAME,
160
+ aeri_version,
161
+ attributes={"public_key": public_key},
162
+ )
163
+
164
+ instance._initialize_instance(
165
+ public_key=public_key,
166
+ secret_key=secret_key,
167
+ base_url=base_url,
168
+ timeout=timeout,
169
+ environment=environment,
170
+ release=release,
171
+ flush_at=flush_at,
172
+ flush_interval=flush_interval,
173
+ httpx_client=httpx_client,
174
+ media_upload_thread_count=media_upload_thread_count,
175
+ sample_rate=sample_rate,
176
+ mask=mask,
177
+ tracing_enabled=tracing_enabled
178
+ if tracing_enabled is not None
179
+ else True,
180
+ blocked_instrumentation_scopes=blocked_instrumentation_scopes,
181
+ should_export_span=should_export_span,
182
+ additional_headers=additional_headers,
183
+ tracer_provider=tracer_provider,
184
+ )
185
+
186
+ cls._instances[public_key] = instance
187
+
188
+ return cls._instances[public_key]
189
+
190
+ def _initialize_instance(
191
+ self,
192
+ *,
193
+ public_key: str,
194
+ secret_key: str,
195
+ base_url: str,
196
+ environment: Optional[str] = None,
197
+ release: Optional[str] = None,
198
+ timeout: Optional[int] = None,
199
+ flush_at: Optional[int] = None,
200
+ flush_interval: Optional[float] = None,
201
+ media_upload_thread_count: Optional[int] = None,
202
+ httpx_client: Optional[httpx.Client] = None,
203
+ sample_rate: Optional[float] = None,
204
+ mask: Optional[MaskFunction] = None,
205
+ tracing_enabled: bool = True,
206
+ blocked_instrumentation_scopes: Optional[List[str]] = None,
207
+ should_export_span: Optional[Callable[[ReadableSpan], bool]] = None,
208
+ additional_headers: Optional[Dict[str, str]] = None,
209
+ tracer_provider: Optional[TracerProvider] = None,
210
+ ) -> None:
211
+ self.public_key = public_key
212
+ self.secret_key = secret_key
213
+ self.tracing_enabled = tracing_enabled
214
+ self.base_url = base_url
215
+ self.mask = mask
216
+ self.environment = environment
217
+
218
+ # Store additional client settings for get_client() to use
219
+ self.timeout = timeout
220
+ self.flush_at = flush_at
221
+ self.flush_interval = flush_interval
222
+ self.release = release
223
+ self.media_upload_thread_count = media_upload_thread_count
224
+ self.sample_rate = sample_rate
225
+ self.blocked_instrumentation_scopes = blocked_instrumentation_scopes
226
+ self.should_export_span = should_export_span
227
+ self.additional_headers = additional_headers
228
+ self.tracer_provider: Optional[TracerProvider] = None
229
+
230
+ # OTEL Tracer
231
+ if tracing_enabled:
232
+ tracer_provider = tracer_provider or _init_tracer_provider(
233
+ environment=environment, release=release, sample_rate=sample_rate
234
+ )
235
+ self.tracer_provider = tracer_provider
236
+
237
+ aeri_processor = AeriSpanProcessor(
238
+ public_key=self.public_key,
239
+ secret_key=secret_key,
240
+ base_url=base_url,
241
+ timeout=timeout,
242
+ flush_at=flush_at,
243
+ flush_interval=flush_interval,
244
+ blocked_instrumentation_scopes=blocked_instrumentation_scopes,
245
+ should_export_span=should_export_span,
246
+ additional_headers=additional_headers,
247
+ )
248
+ tracer_provider.add_span_processor(aeri_processor)
249
+
250
+ self._otel_tracer = tracer_provider.get_tracer(
251
+ AERI_TRACER_NAME,
252
+ aeri_version,
253
+ attributes={"public_key": self.public_key},
254
+ )
255
+
256
+ # API Clients
257
+
258
+ ## API clients must be singletons because the underlying HTTPX clients
259
+ ## use connection pools with limited capacity. Creating multiple instances
260
+ ## could exhaust the OS's maximum number of available TCP sockets (file descriptors),
261
+ ## leading to connection errors.
262
+ if httpx_client is not None:
263
+ self.httpx_client = httpx_client
264
+ else:
265
+ # Create a new httpx client with additional_headers if provided
266
+ client_headers = additional_headers if additional_headers else {}
267
+ self.httpx_client = httpx.Client(timeout=timeout, headers=client_headers)
268
+
269
+ self.api = AeriAPI(
270
+ base_url=base_url,
271
+ username=self.public_key,
272
+ password=secret_key,
273
+ x_aeri_sdk_name="python",
274
+ x_aeri_sdk_version=aeri_version,
275
+ x_aeri_public_key=self.public_key,
276
+ httpx_client=self.httpx_client,
277
+ timeout=timeout,
278
+ )
279
+ self.async_api = AsyncAeriAPI(
280
+ base_url=base_url,
281
+ username=self.public_key,
282
+ password=secret_key,
283
+ x_aeri_sdk_name="python",
284
+ x_aeri_sdk_version=aeri_version,
285
+ x_aeri_public_key=self.public_key,
286
+ timeout=timeout,
287
+ )
288
+ # Media
289
+ self._media_upload_enabled = os.environ.get(
290
+ AERI_MEDIA_UPLOAD_ENABLED, "True"
291
+ ).lower() not in ("false", "0")
292
+
293
+ self._media_upload_queue: Queue[Any] = Queue(100_000)
294
+ self._media_manager = MediaManager(
295
+ api_client=self.api,
296
+ httpx_client=self.httpx_client,
297
+ media_upload_queue=self._media_upload_queue,
298
+ max_retries=3,
299
+ )
300
+ self._media_upload_consumers = []
301
+
302
+ media_upload_thread_count = media_upload_thread_count or max(
303
+ int(os.getenv(AERI_MEDIA_UPLOAD_THREAD_COUNT, 1)), 1
304
+ )
305
+
306
+ if self._media_upload_enabled:
307
+ for i in range(media_upload_thread_count):
308
+ media_upload_consumer = MediaUploadConsumer(
309
+ identifier=i,
310
+ media_manager=self._media_manager,
311
+ )
312
+ media_upload_consumer.start()
313
+ self._media_upload_consumers.append(media_upload_consumer)
314
+
315
+ # Prompt cache
316
+ self.prompt_cache = PromptCache()
317
+
318
+ # Score / trace ingestion — fully async task manager replaces thread-based consumer
319
+ self._task_manager = AsyncIngestionTaskManager(
320
+ async_client=self.async_api,
321
+ public_key=self.public_key,
322
+ flush_at=flush_at,
323
+ flush_interval=flush_interval,
324
+ max_retries=3,
325
+ )
326
+
327
+ # Register shutdown handler
328
+ atexit.register(self.shutdown)
329
+
330
+ aeri_logger.info(
331
+ f"Startup: Aeri tracer successfully initialized | "
332
+ f"public_key={self.public_key} | "
333
+ f"base_url={base_url} | "
334
+ f"environment={environment or 'default'} | "
335
+ f"sample_rate={sample_rate if sample_rate is not None else 1.0} | "
336
+ f"media_threads={media_upload_thread_count or 1}"
337
+ )
338
+
339
+ @classmethod
340
+ def reset(cls) -> None:
341
+ with cls._lock:
342
+ for key in cls._instances:
343
+ cls._instances[key].shutdown()
344
+
345
+ cls._instances.clear()
346
+
347
+ def add_score_task(self, event: Dict[str, Any], *, force_sample: bool = False) -> None:
348
+ try:
349
+ validated_event = _StrictScoreQueueEvent.model_validate(event)
350
+ trace_id = validated_event.body.trace_id
351
+
352
+ # Sample scores with the same sampler that is used for tracing
353
+ tracer_provider = cast(TracerProvider, otel_trace_api.get_tracer_provider())
354
+ should_sample = (
355
+ force_sample
356
+ or isinstance(
357
+ tracer_provider, otel_trace_api.ProxyTracerProvider
358
+ ) # default to in-sample if otel sampler is not available
359
+ or (
360
+ (
361
+ tracer_provider.sampler.should_sample(
362
+ parent_context=None,
363
+ trace_id=int(trace_id, 16),
364
+ name="score",
365
+ ).decision
366
+ == Decision.RECORD_AND_SAMPLE
367
+ if trace_id is not None
368
+ else True
369
+ )
370
+ if trace_id
371
+ is not None # do not sample out session / dataset run scores
372
+ else True
373
+ )
374
+ )
375
+
376
+ if should_sample:
377
+ aeri_logger.debug(
378
+ f"Score: Enqueuing event type={validated_event.type} for trace_id={trace_id} name={validated_event.body.name} value={validated_event.body.value}"
379
+ )
380
+ self._task_manager.submit_event(event)
381
+
382
+ except ValidationError as e:
383
+ aeri_logger.error(
384
+ f"Validation error: Failed to enqueue score event due to invalid payload. Error details: {e}"
385
+ )
386
+
387
+ return
388
+ except Exception as e:
389
+ aeri_logger.error(
390
+ f"Unexpected error: Failed to process score event. The score will be dropped. Error details: {e}"
391
+ )
392
+
393
+ return
394
+
395
+ def add_trace_task(
396
+ self,
397
+ event: Dict[str, Any],
398
+ ) -> None:
399
+ try:
400
+ validated_event = _StrictTraceQueueEvent.model_validate(event)
401
+
402
+ aeri_logger.debug(
403
+ f"Trace: Enqueuing event type={validated_event.type} for trace_id={validated_event.body.id}"
404
+ )
405
+ self._task_manager.submit_event(event)
406
+
407
+ except Full:
408
+ aeri_logger.warning(
409
+ "System overload: Trace ingestion queue has reached capacity (100,000 items). Trace update will be dropped. Consider increasing flush frequency or decreasing event volume."
410
+ )
411
+
412
+ return
413
+ except ValidationError as e:
414
+ aeri_logger.error(
415
+ f"Validation error: Failed to enqueue trace event due to invalid payload. Error details: {e}"
416
+ )
417
+
418
+ return
419
+ except Exception as e:
420
+ aeri_logger.error(
421
+ f"Unexpected error: Failed to process trace event. The trace update will be dropped. Error details: {e}"
422
+ )
423
+
424
+ return
425
+
426
+ @property
427
+ def tracer(self) -> Optional[Tracer]:
428
+ return self._otel_tracer
429
+
430
+ @staticmethod
431
+ def get_current_span() -> Any:
432
+ return otel_trace_api.get_current_span()
433
+
434
+ def _stop_and_join_consumer_threads(self) -> None:
435
+ """Drain and stop all background workers."""
436
+ aeri_logger.debug(
437
+ f"Shutdown: Waiting for {len(self._media_upload_consumers)} media upload thread(s) to complete processing"
438
+ )
439
+ for media_upload_consumer in self._media_upload_consumers:
440
+ media_upload_consumer.pause()
441
+
442
+ for media_upload_consumer in self._media_upload_consumers:
443
+ try:
444
+ media_upload_consumer.join()
445
+ except RuntimeError:
446
+ pass
447
+
448
+ aeri_logger.debug(
449
+ f"Shutdown: Media upload thread #{media_upload_consumer._identifier} successfully terminated"
450
+ )
451
+
452
+ aeri_logger.debug("Shutdown: Stopping async ingestion task manager …")
453
+ self._task_manager.shutdown()
454
+ aeri_logger.debug("Shutdown: Async ingestion task manager stopped.")
455
+
456
+ def flush(self) -> None:
457
+ if self.tracer_provider is not None and not isinstance(
458
+ self.tracer_provider, otel_trace_api.ProxyTracerProvider
459
+ ):
460
+ self.tracer_provider.force_flush()
461
+ aeri_logger.debug("Successfully flushed OTEL tracer provider")
462
+
463
+ self._task_manager.flush()
464
+ aeri_logger.debug("Successfully flushed async ingestion task manager")
465
+
466
+ self._media_upload_queue.join()
467
+ aeri_logger.debug("Successfully flushed media upload queue")
468
+
469
+ def shutdown(self) -> None:
470
+ # Unregister the atexit handler first
471
+ atexit.unregister(self.shutdown)
472
+
473
+ self.flush()
474
+ self._stop_and_join_consumer_threads()
475
+
476
+
477
+ def _init_tracer_provider(
478
+ *,
479
+ environment: Optional[str] = None,
480
+ release: Optional[str] = None,
481
+ sample_rate: Optional[float] = None,
482
+ ) -> TracerProvider:
483
+ environment = environment or os.environ.get(AERI_TRACING_ENVIRONMENT)
484
+ release = release or os.environ.get(AERI_RELEASE) or get_common_release_envs()
485
+
486
+ resource_attributes = {
487
+ AeriOtelSpanAttributes.ENVIRONMENT: environment,
488
+ AeriOtelSpanAttributes.RELEASE: release,
489
+ }
490
+
491
+ resource = Resource.create(
492
+ {k: v for k, v in resource_attributes.items() if v is not None}
493
+ )
494
+
495
+ provider = None
496
+ default_provider = cast(TracerProvider, otel_trace_api.get_tracer_provider())
497
+
498
+ if isinstance(default_provider, otel_trace_api.ProxyTracerProvider):
499
+ provider = TracerProvider(
500
+ resource=resource,
501
+ sampler=TraceIdRatioBased(sample_rate)
502
+ if sample_rate is not None and sample_rate < 1
503
+ else None,
504
+ )
505
+ otel_trace_api.set_tracer_provider(provider)
506
+
507
+ else:
508
+ provider = default_provider
509
+
510
+ return provider