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,76 @@
1
+ """Span filter predicates for controlling OpenTelemetry span export.
2
+
3
+ This module provides composable filter functions that determine which spans
4
+ the AeriSpanProcessor forwards to the Aeri backend.
5
+ """
6
+
7
+ from opentelemetry.sdk.trace import ReadableSpan
8
+
9
+ from aeri._client.constants import AERI_TRACER_NAME
10
+
11
+ KNOWN_LLM_INSTRUMENTATION_SCOPE_PREFIXES = frozenset(
12
+ {
13
+ AERI_TRACER_NAME,
14
+ "agent_framework",
15
+ "ai",
16
+ "haystack",
17
+ "langsmith",
18
+ "litellm",
19
+ "openinference",
20
+ "opentelemetry.instrumentation.anthropic",
21
+ "strands-agents",
22
+ "vllm",
23
+ }
24
+ )
25
+ """Known instrumentation scope namespace prefixes.
26
+
27
+ Prefix matching is boundary-aware:
28
+ - exact match (``scope == prefix``)
29
+ - direct descendant scopes (``scope.startswith(prefix + ".")``)
30
+
31
+ Please create a Github issue in https://github.com/aeri/aeri if you'd like to expand this default allow list.
32
+ """
33
+
34
+
35
+ def is_aeri_span(span: ReadableSpan) -> bool:
36
+ """Return whether the span was created by the Aeri SDK tracer."""
37
+ return (
38
+ span.instrumentation_scope is not None
39
+ and span.instrumentation_scope.name == AERI_TRACER_NAME
40
+ )
41
+
42
+
43
+ def is_genai_span(span: ReadableSpan) -> bool:
44
+ """Return whether the span has any ``gen_ai.*`` semantic convention attribute."""
45
+ if span.attributes is None:
46
+ return False
47
+
48
+ return any(
49
+ isinstance(key, str) and key.startswith("gen_ai")
50
+ for key in span.attributes.keys()
51
+ )
52
+
53
+
54
+ def _matches_scope_prefix(scope_name: str, prefix: str) -> bool:
55
+ """Return whether a scope matches a prefix using namespace boundaries."""
56
+ return scope_name == prefix or scope_name.startswith(f"{prefix}.")
57
+
58
+
59
+ def is_known_llm_instrumentor(span: ReadableSpan) -> bool:
60
+ """Return whether the span comes from a known LLM instrumentation scope."""
61
+ if span.instrumentation_scope is None:
62
+ return False
63
+
64
+ scope_name = span.instrumentation_scope.name
65
+
66
+ return any(
67
+ _matches_scope_prefix(scope_name, prefix)
68
+ for prefix in KNOWN_LLM_INSTRUMENTATION_SCOPE_PREFIXES
69
+ )
70
+
71
+
72
+ def is_default_export_span(span: ReadableSpan) -> bool:
73
+ """Return whether a span should be exported by default."""
74
+ return (
75
+ is_aeri_span(span) or is_genai_span(span) or is_known_llm_instrumentor(span)
76
+ )
@@ -0,0 +1,206 @@
1
+ """Span processor for Aeri OpenTelemetry integration.
2
+
3
+ This module defines the AeriSpanProcessor class, which extends OpenTelemetry's
4
+ BatchSpanProcessor with Aeri-specific functionality. It handles exporting
5
+ spans to the Aeri API with proper authentication and filtering.
6
+
7
+ Key features:
8
+ - HTTP-based span export to Aeri API
9
+ - Basic authentication with Aeri API keys
10
+ - Configurable batch processing behavior
11
+ - Project-scoped span filtering to prevent cross-project data leakage
12
+ """
13
+
14
+ import base64
15
+ import os
16
+ from typing import Callable, Dict, List, Optional
17
+
18
+ from opentelemetry import context as context_api
19
+ from opentelemetry.context import Context
20
+ from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
21
+ from opentelemetry.sdk.trace import ReadableSpan, Span
22
+ from opentelemetry.sdk.trace.export import BatchSpanProcessor
23
+ from opentelemetry.trace import format_span_id
24
+
25
+ from aeri._client.environment_variables import (
26
+ AERI_FLUSH_AT,
27
+ AERI_FLUSH_INTERVAL,
28
+ AERI_OTEL_TRACES_EXPORT_PATH,
29
+ )
30
+ from aeri._client.propagation import _get_propagated_attributes_from_context
31
+ from aeri._client.span_filter import is_default_export_span, is_aeri_span
32
+ from aeri._client.utils import span_formatter
33
+ from aeri.logger import aeri_logger
34
+ from aeri.version import __version__ as aeri_version
35
+
36
+
37
+ class AeriSpanProcessor(BatchSpanProcessor):
38
+ """OpenTelemetry span processor that exports spans to the Aeri API.
39
+
40
+ This processor extends OpenTelemetry's BatchSpanProcessor with Aeri-specific functionality:
41
+ 1. Project-scoped span filtering to prevent cross-project data leakage
42
+ 2. Instrumentation scope filtering to block spans from specific libraries/frameworks
43
+ 3. Configurable batch processing parameters for optimal performance
44
+ 4. HTTP-based span export to the Aeri OTLP endpoint
45
+ 5. Debug logging for span processing operations
46
+ 6. Authentication with Aeri API using Basic Auth
47
+
48
+ The processor is designed to efficiently handle large volumes of spans with
49
+ minimal overhead, while ensuring spans are only sent to the correct project.
50
+ It integrates with OpenTelemetry's standard span lifecycle, adding Aeri-specific
51
+ filtering and export capabilities.
52
+ """
53
+
54
+ def __init__(
55
+ self,
56
+ *,
57
+ public_key: str,
58
+ secret_key: str,
59
+ base_url: str,
60
+ timeout: Optional[int] = None,
61
+ flush_at: Optional[int] = None,
62
+ flush_interval: Optional[float] = None,
63
+ blocked_instrumentation_scopes: Optional[List[str]] = None,
64
+ should_export_span: Optional[Callable[[ReadableSpan], bool]] = None,
65
+ additional_headers: Optional[Dict[str, str]] = None,
66
+ ):
67
+ self.public_key = public_key
68
+ self.blocked_instrumentation_scopes = (
69
+ blocked_instrumentation_scopes
70
+ if blocked_instrumentation_scopes is not None
71
+ else []
72
+ )
73
+ self._should_export_span = should_export_span or is_default_export_span
74
+
75
+ env_flush_at = os.environ.get(AERI_FLUSH_AT, None)
76
+ flush_at = flush_at or int(env_flush_at) if env_flush_at is not None else None
77
+
78
+ env_flush_interval = os.environ.get(AERI_FLUSH_INTERVAL, None)
79
+ flush_interval = (
80
+ flush_interval or float(env_flush_interval)
81
+ if env_flush_interval is not None
82
+ else None
83
+ )
84
+
85
+ basic_auth_header = "Basic " + base64.b64encode(
86
+ f"{public_key}:{secret_key}".encode("utf-8")
87
+ ).decode("ascii")
88
+
89
+ # Prepare default headers
90
+ default_headers = {
91
+ "Authorization": basic_auth_header,
92
+ "x-aeri-sdk-name": "python",
93
+ "x-aeri-sdk-version": aeri_version,
94
+ "x-aeri-public-key": public_key,
95
+ }
96
+
97
+ # Merge additional headers if provided
98
+ headers = {**default_headers, **(additional_headers or {})}
99
+
100
+ traces_export_path = os.environ.get(AERI_OTEL_TRACES_EXPORT_PATH, None)
101
+
102
+ endpoint = (
103
+ f"{base_url}/{traces_export_path}"
104
+ if traces_export_path
105
+ else f"{base_url}/api/public/otel/v1/traces"
106
+ )
107
+
108
+ aeri_span_exporter = OTLPSpanExporter(
109
+ endpoint=endpoint,
110
+ headers=headers,
111
+ timeout=timeout,
112
+ )
113
+
114
+ super().__init__(
115
+ span_exporter=aeri_span_exporter,
116
+ export_timeout_millis=timeout * 1_000 if timeout else None,
117
+ max_export_batch_size=flush_at,
118
+ schedule_delay_millis=flush_interval * 1_000
119
+ if flush_interval is not None
120
+ else None,
121
+ )
122
+
123
+ def on_start(self, span: Span, parent_context: Optional[Context] = None) -> None:
124
+ context = parent_context or context_api.get_current()
125
+ propagated_attributes = _get_propagated_attributes_from_context(context)
126
+
127
+ if propagated_attributes:
128
+ span.set_attributes(propagated_attributes)
129
+
130
+ aeri_logger.debug(
131
+ f"Propagated {len(propagated_attributes)} attributes to span '{format_span_id(span.context.span_id)}': {propagated_attributes}"
132
+ )
133
+
134
+ return super().on_start(span, parent_context)
135
+
136
+ def on_end(self, span: ReadableSpan) -> None:
137
+ # Only export spans that belong to the scoped project
138
+ # This is important to not send spans to wrong project in multi-project setups
139
+ if is_aeri_span(span) and not self._is_aeri_project_span(span):
140
+ aeri_logger.debug(
141
+ f"Security: Span rejected - belongs to project '{span.instrumentation_scope.attributes.get('public_key') if span.instrumentation_scope and span.instrumentation_scope.attributes else None}' but processor is for '{self.public_key}'. "
142
+ f"This prevents cross-project data leakage in multi-project environments."
143
+ )
144
+ return
145
+
146
+ # Do not export spans from blocked instrumentation scopes
147
+ if self._is_blocked_instrumentation_scope(span):
148
+ aeri_logger.debug(
149
+ "Trace: Dropping span due to blocked instrumentation scope | "
150
+ f"span_name='{span.name}' | "
151
+ f"instrumentation_scope='{self._get_scope_name(span)}'"
152
+ )
153
+ return
154
+
155
+ # Apply custom or default span filter
156
+ try:
157
+ should_export = self._should_export_span(span)
158
+ except Exception as error:
159
+ aeri_logger.error(
160
+ "Trace: should_export_span callback raised an error. "
161
+ f"Dropping span name='{span.name}' scope='{self._get_scope_name(span)}'. "
162
+ f"Error: {error}"
163
+ )
164
+ return
165
+
166
+ if not should_export:
167
+ aeri_logger.debug(
168
+ "Trace: Dropping span due to should_export_span filter | "
169
+ f"span_name='{span.name}' | "
170
+ f"instrumentation_scope='{self._get_scope_name(span)}'"
171
+ )
172
+ return
173
+
174
+ aeri_logger.debug(
175
+ f"Trace: Processing span name='{span._name}' | Full details:\n{span_formatter(span)}"
176
+ )
177
+
178
+ super().on_end(span)
179
+
180
+ def _is_blocked_instrumentation_scope(self, span: ReadableSpan) -> bool:
181
+ return (
182
+ span.instrumentation_scope is not None
183
+ and span.instrumentation_scope.name in self.blocked_instrumentation_scopes
184
+ )
185
+
186
+ def _is_aeri_project_span(self, span: ReadableSpan) -> bool:
187
+ if not is_aeri_span(span):
188
+ return False
189
+
190
+ if span.instrumentation_scope is not None:
191
+ public_key_on_span = (
192
+ span.instrumentation_scope.attributes.get("public_key", None)
193
+ if span.instrumentation_scope.attributes
194
+ else None
195
+ )
196
+
197
+ return public_key_on_span == self.public_key
198
+
199
+ return False
200
+
201
+ @staticmethod
202
+ def _get_scope_name(span: ReadableSpan) -> Optional[str]:
203
+ if span.instrumentation_scope is None:
204
+ return None
205
+
206
+ return span.instrumentation_scope.name
aeri/_client/utils.py ADDED
@@ -0,0 +1,132 @@
1
+ """Utility functions for Aeri OpenTelemetry integration.
2
+
3
+ This module provides utility functions for working with OpenTelemetry spans,
4
+ including formatting and serialization of span data, and async execution helpers.
5
+ """
6
+
7
+ import asyncio
8
+ import json
9
+ import threading
10
+ from hashlib import sha256
11
+ from typing import Any, Coroutine
12
+
13
+ from opentelemetry import trace as otel_trace_api
14
+ from opentelemetry.sdk import util
15
+ from opentelemetry.sdk.trace import ReadableSpan
16
+
17
+
18
+ def span_formatter(span: ReadableSpan) -> str:
19
+ parent_id = (
20
+ otel_trace_api.format_span_id(span.parent.span_id) if span.parent else None
21
+ )
22
+ start_time = util.ns_to_iso_str(span._start_time) if span._start_time else None
23
+ end_time = util.ns_to_iso_str(span._end_time) if span._end_time else None
24
+ status = {
25
+ "status_code": str(span._status.status_code.name),
26
+ }
27
+
28
+ if span._status.description:
29
+ status["description"] = span._status.description
30
+
31
+ context = (
32
+ {
33
+ "trace_id": otel_trace_api.format_trace_id(span._context.trace_id),
34
+ "span_id": otel_trace_api.format_span_id(span._context.span_id),
35
+ "trace_state": repr(span._context.trace_state),
36
+ }
37
+ if span._context
38
+ else None
39
+ )
40
+
41
+ instrumentationScope = json.loads(
42
+ span._instrumentation_scope.to_json() if span._instrumentation_scope else "{}"
43
+ )
44
+
45
+ return (
46
+ json.dumps(
47
+ {
48
+ "name": span._name,
49
+ "context": context,
50
+ "kind": str(span.kind),
51
+ "parent_id": parent_id,
52
+ "start_time": start_time,
53
+ "end_time": end_time,
54
+ "status": status,
55
+ "attributes": span._format_attributes(span._attributes),
56
+ "events": span._format_events(span._events),
57
+ "links": span._format_links(span._links),
58
+ "resource": json.loads(span.resource.to_json()),
59
+ "instrumentationScope": instrumentationScope,
60
+ },
61
+ indent=2,
62
+ )
63
+ + "\n"
64
+ )
65
+
66
+
67
+ class _RunAsyncThread(threading.Thread):
68
+ """Helper thread class for running async coroutines in a separate thread."""
69
+
70
+ def __init__(self, coro: Coroutine[Any, Any, Any]) -> None:
71
+ self.coro = coro
72
+ self.result: Any = None
73
+ self.exception: Exception | None = None
74
+ super().__init__()
75
+
76
+ def run(self) -> None:
77
+ try:
78
+ self.result = asyncio.run(self.coro)
79
+ except Exception as e:
80
+ self.exception = e
81
+
82
+
83
+ def run_async_safely(coro: Coroutine[Any, Any, Any]) -> Any:
84
+ """Safely run an async coroutine, handling existing event loops.
85
+
86
+ This function detects if there's already a running event loop and uses
87
+ a separate thread if needed to avoid the "asyncio.run() cannot be called
88
+ from a running event loop" error. This is particularly useful in environments
89
+ like Jupyter notebooks, FastAPI applications, or other async frameworks.
90
+
91
+ Args:
92
+ coro: The coroutine to run
93
+
94
+ Returns:
95
+ The result of the coroutine
96
+
97
+ Raises:
98
+ Any exception raised by the coroutine
99
+
100
+ Example:
101
+ ```python
102
+ # Works in both sync and async contexts
103
+ async def my_async_function():
104
+ await asyncio.sleep(1)
105
+ return "done"
106
+
107
+ result = run_async_safely(my_async_function())
108
+ ```
109
+ """
110
+ try:
111
+ # Check if there's already a running event loop
112
+ loop = asyncio.get_running_loop()
113
+ except RuntimeError:
114
+ # No running loop, safe to use asyncio.run()
115
+ return asyncio.run(coro)
116
+
117
+ if loop and loop.is_running():
118
+ # There's a running loop, use a separate thread
119
+ thread = _RunAsyncThread(coro)
120
+ thread.start()
121
+ thread.join()
122
+
123
+ if thread.exception:
124
+ raise thread.exception
125
+ return thread.result
126
+ else:
127
+ # Loop exists but not running, safe to use asyncio.run()
128
+ return asyncio.run(coro)
129
+
130
+
131
+ def get_sha256_hash_hex(value: Any) -> str:
132
+ return sha256(value.encode("utf-8")).digest().hex()