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.
- aeri/__init__.py +72 -0
- aeri/_client/_validation.py +204 -0
- aeri/_client/attributes.py +188 -0
- aeri/_client/client.py +3761 -0
- aeri/_client/constants.py +65 -0
- aeri/_client/datasets.py +302 -0
- aeri/_client/environment_variables.py +158 -0
- aeri/_client/get_client.py +149 -0
- aeri/_client/observe.py +661 -0
- aeri/_client/propagation.py +475 -0
- aeri/_client/resource_manager.py +510 -0
- aeri/_client/span.py +1519 -0
- aeri/_client/span_filter.py +76 -0
- aeri/_client/span_processor.py +206 -0
- aeri/_client/utils.py +132 -0
- aeri/_task_manager/media_manager.py +331 -0
- aeri/_task_manager/media_upload_consumer.py +44 -0
- aeri/_task_manager/media_upload_queue.py +12 -0
- aeri/_task_manager/score_ingestion_consumer.py +208 -0
- aeri/_task_manager/task_manager.py +475 -0
- aeri/_utils/__init__.py +19 -0
- aeri/_utils/environment.py +34 -0
- aeri/_utils/error_logging.py +47 -0
- aeri/_utils/parse_error.py +99 -0
- aeri/_utils/prompt_cache.py +188 -0
- aeri/_utils/request.py +137 -0
- aeri/_utils/serializer.py +205 -0
- aeri/api/.fern/metadata.json +14 -0
- aeri/api/__init__.py +836 -0
- aeri/api/annotation_queues/__init__.py +82 -0
- aeri/api/annotation_queues/client.py +1111 -0
- aeri/api/annotation_queues/raw_client.py +2288 -0
- aeri/api/annotation_queues/types/__init__.py +84 -0
- aeri/api/annotation_queues/types/annotation_queue.py +28 -0
- aeri/api/annotation_queues/types/annotation_queue_assignment_request.py +16 -0
- aeri/api/annotation_queues/types/annotation_queue_item.py +34 -0
- aeri/api/annotation_queues/types/annotation_queue_object_type.py +26 -0
- aeri/api/annotation_queues/types/annotation_queue_status.py +22 -0
- aeri/api/annotation_queues/types/create_annotation_queue_assignment_response.py +18 -0
- aeri/api/annotation_queues/types/create_annotation_queue_item_request.py +25 -0
- aeri/api/annotation_queues/types/create_annotation_queue_request.py +20 -0
- aeri/api/annotation_queues/types/delete_annotation_queue_assignment_response.py +14 -0
- aeri/api/annotation_queues/types/delete_annotation_queue_item_response.py +15 -0
- aeri/api/annotation_queues/types/paginated_annotation_queue_items.py +17 -0
- aeri/api/annotation_queues/types/paginated_annotation_queues.py +17 -0
- aeri/api/annotation_queues/types/update_annotation_queue_item_request.py +15 -0
- aeri/api/blob_storage_integrations/__init__.py +73 -0
- aeri/api/blob_storage_integrations/client.py +550 -0
- aeri/api/blob_storage_integrations/raw_client.py +976 -0
- aeri/api/blob_storage_integrations/types/__init__.py +77 -0
- aeri/api/blob_storage_integrations/types/blob_storage_export_frequency.py +26 -0
- aeri/api/blob_storage_integrations/types/blob_storage_export_mode.py +26 -0
- aeri/api/blob_storage_integrations/types/blob_storage_integration_deletion_response.py +14 -0
- aeri/api/blob_storage_integrations/types/blob_storage_integration_file_type.py +26 -0
- aeri/api/blob_storage_integrations/types/blob_storage_integration_response.py +64 -0
- aeri/api/blob_storage_integrations/types/blob_storage_integration_status_response.py +50 -0
- aeri/api/blob_storage_integrations/types/blob_storage_integration_type.py +26 -0
- aeri/api/blob_storage_integrations/types/blob_storage_integrations_response.py +15 -0
- aeri/api/blob_storage_integrations/types/blob_storage_sync_status.py +47 -0
- aeri/api/blob_storage_integrations/types/create_blob_storage_integration_request.py +91 -0
- aeri/api/client.py +679 -0
- aeri/api/comments/__init__.py +44 -0
- aeri/api/comments/client.py +407 -0
- aeri/api/comments/raw_client.py +750 -0
- aeri/api/comments/types/__init__.py +46 -0
- aeri/api/comments/types/create_comment_request.py +47 -0
- aeri/api/comments/types/create_comment_response.py +17 -0
- aeri/api/comments/types/get_comments_response.py +17 -0
- aeri/api/commons/__init__.py +210 -0
- aeri/api/commons/errors/__init__.py +56 -0
- aeri/api/commons/errors/access_denied_error.py +12 -0
- aeri/api/commons/errors/error.py +12 -0
- aeri/api/commons/errors/method_not_allowed_error.py +12 -0
- aeri/api/commons/errors/not_found_error.py +12 -0
- aeri/api/commons/errors/unauthorized_error.py +12 -0
- aeri/api/commons/types/__init__.py +190 -0
- aeri/api/commons/types/base_score.py +90 -0
- aeri/api/commons/types/base_score_v1.py +70 -0
- aeri/api/commons/types/boolean_score.py +26 -0
- aeri/api/commons/types/boolean_score_v1.py +26 -0
- aeri/api/commons/types/categorical_score.py +26 -0
- aeri/api/commons/types/categorical_score_v1.py +26 -0
- aeri/api/commons/types/comment.py +36 -0
- aeri/api/commons/types/comment_object_type.py +30 -0
- aeri/api/commons/types/config_category.py +15 -0
- aeri/api/commons/types/correction_score.py +26 -0
- aeri/api/commons/types/create_score_value.py +5 -0
- aeri/api/commons/types/dataset.py +49 -0
- aeri/api/commons/types/dataset_item.py +58 -0
- aeri/api/commons/types/dataset_run.py +63 -0
- aeri/api/commons/types/dataset_run_item.py +40 -0
- aeri/api/commons/types/dataset_run_with_items.py +19 -0
- aeri/api/commons/types/dataset_status.py +22 -0
- aeri/api/commons/types/map_value.py +11 -0
- aeri/api/commons/types/model.py +125 -0
- aeri/api/commons/types/model_price.py +14 -0
- aeri/api/commons/types/model_usage_unit.py +42 -0
- aeri/api/commons/types/numeric_score.py +17 -0
- aeri/api/commons/types/numeric_score_v1.py +17 -0
- aeri/api/commons/types/observation.py +142 -0
- aeri/api/commons/types/observation_level.py +30 -0
- aeri/api/commons/types/observation_v2.py +235 -0
- aeri/api/commons/types/observations_view.py +89 -0
- aeri/api/commons/types/pricing_tier.py +91 -0
- aeri/api/commons/types/pricing_tier_condition.py +68 -0
- aeri/api/commons/types/pricing_tier_input.py +76 -0
- aeri/api/commons/types/pricing_tier_operator.py +42 -0
- aeri/api/commons/types/score.py +201 -0
- aeri/api/commons/types/score_config.py +66 -0
- aeri/api/commons/types/score_config_data_type.py +26 -0
- aeri/api/commons/types/score_data_type.py +30 -0
- aeri/api/commons/types/score_source.py +26 -0
- aeri/api/commons/types/score_v1.py +131 -0
- aeri/api/commons/types/session.py +25 -0
- aeri/api/commons/types/session_with_traces.py +15 -0
- aeri/api/commons/types/trace.py +84 -0
- aeri/api/commons/types/trace_with_details.py +43 -0
- aeri/api/commons/types/trace_with_full_details.py +45 -0
- aeri/api/commons/types/usage.py +59 -0
- aeri/api/core/__init__.py +111 -0
- aeri/api/core/api_error.py +23 -0
- aeri/api/core/client_wrapper.py +141 -0
- aeri/api/core/datetime_utils.py +30 -0
- aeri/api/core/enum.py +20 -0
- aeri/api/core/file.py +70 -0
- aeri/api/core/force_multipart.py +18 -0
- aeri/api/core/http_client.py +711 -0
- aeri/api/core/http_response.py +55 -0
- aeri/api/core/http_sse/__init__.py +48 -0
- aeri/api/core/http_sse/_api.py +114 -0
- aeri/api/core/http_sse/_decoders.py +66 -0
- aeri/api/core/http_sse/_exceptions.py +7 -0
- aeri/api/core/http_sse/_models.py +17 -0
- aeri/api/core/jsonable_encoder.py +102 -0
- aeri/api/core/pydantic_utilities.py +310 -0
- aeri/api/core/query_encoder.py +60 -0
- aeri/api/core/remove_none_from_dict.py +11 -0
- aeri/api/core/request_options.py +35 -0
- aeri/api/core/serialization.py +282 -0
- aeri/api/dataset_items/__init__.py +52 -0
- aeri/api/dataset_items/client.py +499 -0
- aeri/api/dataset_items/raw_client.py +973 -0
- aeri/api/dataset_items/types/__init__.py +50 -0
- aeri/api/dataset_items/types/create_dataset_item_request.py +37 -0
- aeri/api/dataset_items/types/delete_dataset_item_response.py +17 -0
- aeri/api/dataset_items/types/paginated_dataset_items.py +17 -0
- aeri/api/dataset_run_items/__init__.py +43 -0
- aeri/api/dataset_run_items/client.py +323 -0
- aeri/api/dataset_run_items/raw_client.py +547 -0
- aeri/api/dataset_run_items/types/__init__.py +44 -0
- aeri/api/dataset_run_items/types/create_dataset_run_item_request.py +51 -0
- aeri/api/dataset_run_items/types/paginated_dataset_run_items.py +17 -0
- aeri/api/datasets/__init__.py +55 -0
- aeri/api/datasets/client.py +661 -0
- aeri/api/datasets/raw_client.py +1368 -0
- aeri/api/datasets/types/__init__.py +53 -0
- aeri/api/datasets/types/create_dataset_request.py +31 -0
- aeri/api/datasets/types/delete_dataset_run_response.py +14 -0
- aeri/api/datasets/types/paginated_dataset_runs.py +17 -0
- aeri/api/datasets/types/paginated_datasets.py +17 -0
- aeri/api/health/__init__.py +44 -0
- aeri/api/health/client.py +112 -0
- aeri/api/health/errors/__init__.py +42 -0
- aeri/api/health/errors/service_unavailable_error.py +13 -0
- aeri/api/health/raw_client.py +227 -0
- aeri/api/health/types/__init__.py +40 -0
- aeri/api/health/types/health_response.py +30 -0
- aeri/api/ingestion/__init__.py +169 -0
- aeri/api/ingestion/client.py +221 -0
- aeri/api/ingestion/raw_client.py +293 -0
- aeri/api/ingestion/types/__init__.py +169 -0
- aeri/api/ingestion/types/base_event.py +27 -0
- aeri/api/ingestion/types/create_event_body.py +14 -0
- aeri/api/ingestion/types/create_event_event.py +15 -0
- aeri/api/ingestion/types/create_generation_body.py +40 -0
- aeri/api/ingestion/types/create_generation_event.py +15 -0
- aeri/api/ingestion/types/create_observation_event.py +15 -0
- aeri/api/ingestion/types/create_span_body.py +19 -0
- aeri/api/ingestion/types/create_span_event.py +15 -0
- aeri/api/ingestion/types/ingestion_error.py +17 -0
- aeri/api/ingestion/types/ingestion_event.py +155 -0
- aeri/api/ingestion/types/ingestion_response.py +17 -0
- aeri/api/ingestion/types/ingestion_success.py +15 -0
- aeri/api/ingestion/types/ingestion_usage.py +8 -0
- aeri/api/ingestion/types/observation_body.py +53 -0
- aeri/api/ingestion/types/observation_type.py +54 -0
- aeri/api/ingestion/types/open_ai_completion_usage_schema.py +26 -0
- aeri/api/ingestion/types/open_ai_response_usage_schema.py +24 -0
- aeri/api/ingestion/types/open_ai_usage.py +28 -0
- aeri/api/ingestion/types/optional_observation_body.py +36 -0
- aeri/api/ingestion/types/score_body.py +75 -0
- aeri/api/ingestion/types/score_event.py +15 -0
- aeri/api/ingestion/types/sdk_log_body.py +14 -0
- aeri/api/ingestion/types/sdk_log_event.py +15 -0
- aeri/api/ingestion/types/trace_body.py +36 -0
- aeri/api/ingestion/types/trace_event.py +15 -0
- aeri/api/ingestion/types/update_event_body.py +14 -0
- aeri/api/ingestion/types/update_generation_body.py +40 -0
- aeri/api/ingestion/types/update_generation_event.py +15 -0
- aeri/api/ingestion/types/update_observation_event.py +15 -0
- aeri/api/ingestion/types/update_span_body.py +19 -0
- aeri/api/ingestion/types/update_span_event.py +15 -0
- aeri/api/ingestion/types/usage_details.py +10 -0
- aeri/api/legacy/__init__.py +61 -0
- aeri/api/legacy/client.py +105 -0
- aeri/api/legacy/metrics_v1/__init__.py +40 -0
- aeri/api/legacy/metrics_v1/client.py +214 -0
- aeri/api/legacy/metrics_v1/raw_client.py +322 -0
- aeri/api/legacy/metrics_v1/types/__init__.py +40 -0
- aeri/api/legacy/metrics_v1/types/metrics_response.py +19 -0
- aeri/api/legacy/observations_v1/__init__.py +43 -0
- aeri/api/legacy/observations_v1/client.py +523 -0
- aeri/api/legacy/observations_v1/raw_client.py +759 -0
- aeri/api/legacy/observations_v1/types/__init__.py +44 -0
- aeri/api/legacy/observations_v1/types/observations.py +17 -0
- aeri/api/legacy/observations_v1/types/observations_views.py +17 -0
- aeri/api/legacy/raw_client.py +13 -0
- aeri/api/legacy/score_v1/__init__.py +43 -0
- aeri/api/legacy/score_v1/client.py +329 -0
- aeri/api/legacy/score_v1/raw_client.py +545 -0
- aeri/api/legacy/score_v1/types/__init__.py +44 -0
- aeri/api/legacy/score_v1/types/create_score_request.py +75 -0
- aeri/api/legacy/score_v1/types/create_score_response.py +17 -0
- aeri/api/llm_connections/__init__.py +55 -0
- aeri/api/llm_connections/client.py +311 -0
- aeri/api/llm_connections/raw_client.py +541 -0
- aeri/api/llm_connections/types/__init__.py +53 -0
- aeri/api/llm_connections/types/llm_adapter.py +38 -0
- aeri/api/llm_connections/types/llm_connection.py +77 -0
- aeri/api/llm_connections/types/paginated_llm_connections.py +17 -0
- aeri/api/llm_connections/types/upsert_llm_connection_request.py +69 -0
- aeri/api/media/__init__.py +58 -0
- aeri/api/media/client.py +427 -0
- aeri/api/media/raw_client.py +739 -0
- aeri/api/media/types/__init__.py +56 -0
- aeri/api/media/types/get_media_response.py +55 -0
- aeri/api/media/types/get_media_upload_url_request.py +51 -0
- aeri/api/media/types/get_media_upload_url_response.py +28 -0
- aeri/api/media/types/media_content_type.py +232 -0
- aeri/api/media/types/patch_media_body.py +43 -0
- aeri/api/metrics/__init__.py +40 -0
- aeri/api/metrics/client.py +422 -0
- aeri/api/metrics/raw_client.py +530 -0
- aeri/api/metrics/types/__init__.py +40 -0
- aeri/api/metrics/types/metrics_v2response.py +19 -0
- aeri/api/models/__init__.py +43 -0
- aeri/api/models/client.py +523 -0
- aeri/api/models/raw_client.py +993 -0
- aeri/api/models/types/__init__.py +44 -0
- aeri/api/models/types/create_model_request.py +103 -0
- aeri/api/models/types/paginated_models.py +17 -0
- aeri/api/observations/__init__.py +43 -0
- aeri/api/observations/client.py +522 -0
- aeri/api/observations/raw_client.py +641 -0
- aeri/api/observations/types/__init__.py +44 -0
- aeri/api/observations/types/observations_v2meta.py +21 -0
- aeri/api/observations/types/observations_v2response.py +28 -0
- aeri/api/opentelemetry/__init__.py +67 -0
- aeri/api/opentelemetry/client.py +276 -0
- aeri/api/opentelemetry/raw_client.py +291 -0
- aeri/api/opentelemetry/types/__init__.py +65 -0
- aeri/api/opentelemetry/types/otel_attribute.py +27 -0
- aeri/api/opentelemetry/types/otel_attribute_value.py +46 -0
- aeri/api/opentelemetry/types/otel_resource.py +24 -0
- aeri/api/opentelemetry/types/otel_resource_span.py +32 -0
- aeri/api/opentelemetry/types/otel_scope.py +34 -0
- aeri/api/opentelemetry/types/otel_scope_span.py +28 -0
- aeri/api/opentelemetry/types/otel_span.py +76 -0
- aeri/api/opentelemetry/types/otel_trace_response.py +16 -0
- aeri/api/organizations/__init__.py +73 -0
- aeri/api/organizations/client.py +756 -0
- aeri/api/organizations/raw_client.py +1707 -0
- aeri/api/organizations/types/__init__.py +71 -0
- aeri/api/organizations/types/delete_membership_request.py +16 -0
- aeri/api/organizations/types/membership_deletion_response.py +17 -0
- aeri/api/organizations/types/membership_request.py +18 -0
- aeri/api/organizations/types/membership_response.py +20 -0
- aeri/api/organizations/types/membership_role.py +30 -0
- aeri/api/organizations/types/memberships_response.py +15 -0
- aeri/api/organizations/types/organization_api_key.py +31 -0
- aeri/api/organizations/types/organization_api_keys_response.py +19 -0
- aeri/api/organizations/types/organization_project.py +25 -0
- aeri/api/organizations/types/organization_projects_response.py +15 -0
- aeri/api/projects/__init__.py +67 -0
- aeri/api/projects/client.py +760 -0
- aeri/api/projects/raw_client.py +1577 -0
- aeri/api/projects/types/__init__.py +65 -0
- aeri/api/projects/types/api_key_deletion_response.py +18 -0
- aeri/api/projects/types/api_key_list.py +23 -0
- aeri/api/projects/types/api_key_response.py +30 -0
- aeri/api/projects/types/api_key_summary.py +35 -0
- aeri/api/projects/types/organization.py +22 -0
- aeri/api/projects/types/project.py +34 -0
- aeri/api/projects/types/project_deletion_response.py +15 -0
- aeri/api/projects/types/projects.py +15 -0
- aeri/api/prompt_version/__init__.py +4 -0
- aeri/api/prompt_version/client.py +157 -0
- aeri/api/prompt_version/raw_client.py +264 -0
- aeri/api/prompts/__init__.py +100 -0
- aeri/api/prompts/client.py +550 -0
- aeri/api/prompts/raw_client.py +987 -0
- aeri/api/prompts/types/__init__.py +96 -0
- aeri/api/prompts/types/base_prompt.py +42 -0
- aeri/api/prompts/types/chat_message.py +17 -0
- aeri/api/prompts/types/chat_message_type.py +15 -0
- aeri/api/prompts/types/chat_message_with_placeholders.py +8 -0
- aeri/api/prompts/types/chat_prompt.py +15 -0
- aeri/api/prompts/types/create_chat_prompt_request.py +37 -0
- aeri/api/prompts/types/create_chat_prompt_type.py +15 -0
- aeri/api/prompts/types/create_prompt_request.py +8 -0
- aeri/api/prompts/types/create_text_prompt_request.py +36 -0
- aeri/api/prompts/types/create_text_prompt_type.py +15 -0
- aeri/api/prompts/types/placeholder_message.py +16 -0
- aeri/api/prompts/types/placeholder_message_type.py +15 -0
- aeri/api/prompts/types/prompt.py +58 -0
- aeri/api/prompts/types/prompt_meta.py +35 -0
- aeri/api/prompts/types/prompt_meta_list_response.py +17 -0
- aeri/api/prompts/types/prompt_type.py +20 -0
- aeri/api/prompts/types/text_prompt.py +14 -0
- aeri/api/scim/__init__.py +94 -0
- aeri/api/scim/client.py +686 -0
- aeri/api/scim/raw_client.py +1528 -0
- aeri/api/scim/types/__init__.py +92 -0
- aeri/api/scim/types/authentication_scheme.py +20 -0
- aeri/api/scim/types/bulk_config.py +22 -0
- aeri/api/scim/types/empty_response.py +16 -0
- aeri/api/scim/types/filter_config.py +17 -0
- aeri/api/scim/types/resource_meta.py +17 -0
- aeri/api/scim/types/resource_type.py +27 -0
- aeri/api/scim/types/resource_types_response.py +21 -0
- aeri/api/scim/types/schema_extension.py +17 -0
- aeri/api/scim/types/schema_resource.py +19 -0
- aeri/api/scim/types/schemas_response.py +21 -0
- aeri/api/scim/types/scim_email.py +16 -0
- aeri/api/scim/types/scim_feature_support.py +14 -0
- aeri/api/scim/types/scim_name.py +14 -0
- aeri/api/scim/types/scim_user.py +24 -0
- aeri/api/scim/types/scim_users_list_response.py +25 -0
- aeri/api/scim/types/service_provider_config.py +36 -0
- aeri/api/scim/types/user_meta.py +20 -0
- aeri/api/score_configs/__init__.py +44 -0
- aeri/api/score_configs/client.py +526 -0
- aeri/api/score_configs/raw_client.py +1012 -0
- aeri/api/score_configs/types/__init__.py +46 -0
- aeri/api/score_configs/types/create_score_config_request.py +46 -0
- aeri/api/score_configs/types/score_configs.py +17 -0
- aeri/api/score_configs/types/update_score_config_request.py +53 -0
- aeri/api/scores/__init__.py +76 -0
- aeri/api/scores/client.py +420 -0
- aeri/api/scores/raw_client.py +656 -0
- aeri/api/scores/types/__init__.py +76 -0
- aeri/api/scores/types/get_scores_response.py +17 -0
- aeri/api/scores/types/get_scores_response_data.py +211 -0
- aeri/api/scores/types/get_scores_response_data_boolean.py +15 -0
- aeri/api/scores/types/get_scores_response_data_categorical.py +15 -0
- aeri/api/scores/types/get_scores_response_data_correction.py +15 -0
- aeri/api/scores/types/get_scores_response_data_numeric.py +15 -0
- aeri/api/scores/types/get_scores_response_trace_data.py +38 -0
- aeri/api/sessions/__init__.py +40 -0
- aeri/api/sessions/client.py +262 -0
- aeri/api/sessions/raw_client.py +500 -0
- aeri/api/sessions/types/__init__.py +40 -0
- aeri/api/sessions/types/paginated_sessions.py +17 -0
- aeri/api/trace/__init__.py +44 -0
- aeri/api/trace/client.py +728 -0
- aeri/api/trace/raw_client.py +1208 -0
- aeri/api/trace/types/__init__.py +46 -0
- aeri/api/trace/types/delete_trace_response.py +14 -0
- aeri/api/trace/types/sort.py +14 -0
- aeri/api/trace/types/traces.py +17 -0
- aeri/api/utils/__init__.py +44 -0
- aeri/api/utils/pagination/__init__.py +40 -0
- aeri/api/utils/pagination/types/__init__.py +40 -0
- aeri/api/utils/pagination/types/meta_response.py +38 -0
- aeri/batch_evaluation.py +1643 -0
- aeri/experiment.py +1044 -0
- aeri/langchain/CallbackHandler.py +1377 -0
- aeri/langchain/__init__.py +5 -0
- aeri/langchain/utils.py +212 -0
- aeri/logger.py +28 -0
- aeri/media.py +352 -0
- aeri/model.py +477 -0
- aeri/openai.py +1124 -0
- aeri/py.typed +0 -0
- aeri/span_filter.py +17 -0
- aeri/types.py +79 -0
- aeri/version.py +3 -0
- aeri_python-4.0.0.dist-info/METADATA +51 -0
- aeri_python-4.0.0.dist-info/RECORD +391 -0
- aeri_python-4.0.0.dist-info/WHEEL +4 -0
- aeri_python-4.0.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"""Constants used by the Aeri OpenTelemetry integration.
|
|
2
|
+
|
|
3
|
+
This module defines constants used throughout the Aeri OpenTelemetry integration.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from typing import Any, List, Literal, Union, get_args
|
|
7
|
+
|
|
8
|
+
from typing_extensions import TypeAlias
|
|
9
|
+
|
|
10
|
+
AERI_TRACER_NAME = "aeri-sdk"
|
|
11
|
+
|
|
12
|
+
AERI_SDK_EXPERIMENT_ENVIRONMENT = "sdk-experiment"
|
|
13
|
+
|
|
14
|
+
"""Note: this type is used with .__args__ / get_args in some cases and therefore must remain flat"""
|
|
15
|
+
ObservationTypeGenerationLike: TypeAlias = Literal[
|
|
16
|
+
"generation",
|
|
17
|
+
"embedding",
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
ObservationTypeSpanLike: TypeAlias = Literal[
|
|
21
|
+
"span",
|
|
22
|
+
"agent",
|
|
23
|
+
"tool",
|
|
24
|
+
"chain",
|
|
25
|
+
"retriever",
|
|
26
|
+
"evaluator",
|
|
27
|
+
"guardrail",
|
|
28
|
+
]
|
|
29
|
+
|
|
30
|
+
ObservationTypeLiteralNoEvent: TypeAlias = Union[
|
|
31
|
+
ObservationTypeGenerationLike,
|
|
32
|
+
ObservationTypeSpanLike,
|
|
33
|
+
]
|
|
34
|
+
|
|
35
|
+
"""Enumeration of valid observation types for Aeri tracing.
|
|
36
|
+
|
|
37
|
+
This Literal defines all available observation types that can be used with the @observe
|
|
38
|
+
decorator and other Aeri SDK methods.
|
|
39
|
+
"""
|
|
40
|
+
ObservationTypeLiteral: TypeAlias = Union[
|
|
41
|
+
ObservationTypeLiteralNoEvent, Literal["event"]
|
|
42
|
+
]
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def get_observation_types_list(
|
|
46
|
+
literal_type: Any,
|
|
47
|
+
) -> List[str]:
|
|
48
|
+
"""Flattens the Literal type to provide a list of strings.
|
|
49
|
+
|
|
50
|
+
Args:
|
|
51
|
+
literal_type: A Literal type, TypeAlias, or union of Literals to flatten
|
|
52
|
+
|
|
53
|
+
Returns:
|
|
54
|
+
Flat list of all string values contained in the Literal type
|
|
55
|
+
"""
|
|
56
|
+
result = []
|
|
57
|
+
args = get_args(literal_type)
|
|
58
|
+
|
|
59
|
+
for arg in args:
|
|
60
|
+
if hasattr(arg, "__args__"):
|
|
61
|
+
result.extend(get_observation_types_list(arg))
|
|
62
|
+
else:
|
|
63
|
+
result.append(arg)
|
|
64
|
+
|
|
65
|
+
return result
|
aeri/_client/datasets.py
ADDED
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
import datetime as dt
|
|
2
|
+
from typing import TYPE_CHECKING, Any, Dict, List, Optional
|
|
3
|
+
|
|
4
|
+
from aeri.api import (
|
|
5
|
+
Dataset,
|
|
6
|
+
DatasetItem,
|
|
7
|
+
)
|
|
8
|
+
from aeri.batch_evaluation import CompositeEvaluatorFunction
|
|
9
|
+
from aeri.experiment import (
|
|
10
|
+
EvaluatorFunction,
|
|
11
|
+
ExperimentResult,
|
|
12
|
+
RunEvaluatorFunction,
|
|
13
|
+
TaskFunction,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
if TYPE_CHECKING:
|
|
17
|
+
from aeri._client.client import Aeri
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class DatasetClient:
|
|
21
|
+
"""Class for managing datasets in Aeri.
|
|
22
|
+
|
|
23
|
+
Attributes:
|
|
24
|
+
id (str): Unique identifier of the dataset.
|
|
25
|
+
name (str): Name of the dataset.
|
|
26
|
+
description (Optional[str]): Description of the dataset.
|
|
27
|
+
metadata (Optional[typing.Any]): Additional metadata of the dataset.
|
|
28
|
+
project_id (str): Identifier of the project to which the dataset belongs.
|
|
29
|
+
created_at (datetime): Timestamp of dataset creation.
|
|
30
|
+
updated_at (datetime): Timestamp of the last update to the dataset.
|
|
31
|
+
items (List[DatasetItem]): List of dataset items associated with the dataset.
|
|
32
|
+
version (Optional[datetime]): Timestamp of the dataset version.
|
|
33
|
+
Example:
|
|
34
|
+
Print the input of each dataset item in a dataset.
|
|
35
|
+
```python
|
|
36
|
+
from aeri import Aeri
|
|
37
|
+
|
|
38
|
+
aeri = Aeri()
|
|
39
|
+
|
|
40
|
+
dataset = aeri.get_dataset("<dataset_name>")
|
|
41
|
+
|
|
42
|
+
for item in dataset.items:
|
|
43
|
+
print(item.input)
|
|
44
|
+
```
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
id: str
|
|
48
|
+
name: str
|
|
49
|
+
description: Optional[str]
|
|
50
|
+
project_id: str
|
|
51
|
+
metadata: Optional[Any]
|
|
52
|
+
created_at: dt.datetime
|
|
53
|
+
updated_at: dt.datetime
|
|
54
|
+
input_schema: Optional[Any]
|
|
55
|
+
expected_output_schema: Optional[Any]
|
|
56
|
+
items: List[DatasetItem]
|
|
57
|
+
version: Optional[dt.datetime]
|
|
58
|
+
|
|
59
|
+
def __init__(
|
|
60
|
+
self,
|
|
61
|
+
*,
|
|
62
|
+
dataset: Dataset,
|
|
63
|
+
items: List[DatasetItem],
|
|
64
|
+
aeri_client: "Aeri",
|
|
65
|
+
version: Optional[dt.datetime] = None,
|
|
66
|
+
):
|
|
67
|
+
"""Initialize the DatasetClient."""
|
|
68
|
+
self.id = dataset.id
|
|
69
|
+
self.name = dataset.name
|
|
70
|
+
self.description = dataset.description
|
|
71
|
+
self.project_id = dataset.project_id
|
|
72
|
+
self.metadata = dataset.metadata
|
|
73
|
+
self.created_at = dataset.created_at
|
|
74
|
+
self.updated_at = dataset.updated_at
|
|
75
|
+
self.input_schema = dataset.input_schema
|
|
76
|
+
self.expected_output_schema = dataset.expected_output_schema
|
|
77
|
+
self.items = items
|
|
78
|
+
self.version = version
|
|
79
|
+
self._aeri_client: "Aeri" = aeri_client
|
|
80
|
+
|
|
81
|
+
def run_experiment(
|
|
82
|
+
self,
|
|
83
|
+
*,
|
|
84
|
+
name: str,
|
|
85
|
+
run_name: Optional[str] = None,
|
|
86
|
+
description: Optional[str] = None,
|
|
87
|
+
task: TaskFunction,
|
|
88
|
+
evaluators: List[EvaluatorFunction] = [],
|
|
89
|
+
composite_evaluator: Optional[CompositeEvaluatorFunction] = None,
|
|
90
|
+
run_evaluators: List[RunEvaluatorFunction] = [],
|
|
91
|
+
max_concurrency: int = 50,
|
|
92
|
+
metadata: Optional[Dict[str, Any]] = None,
|
|
93
|
+
) -> ExperimentResult:
|
|
94
|
+
"""Run an experiment on this Aeri dataset with automatic tracking.
|
|
95
|
+
|
|
96
|
+
This is a convenience method that runs an experiment using all items in this
|
|
97
|
+
dataset. It automatically creates a dataset run in Aeri for tracking and
|
|
98
|
+
comparison purposes, linking all experiment results to the dataset.
|
|
99
|
+
|
|
100
|
+
Key benefits of using dataset.run_experiment():
|
|
101
|
+
- Automatic dataset run creation and linking in Aeri UI
|
|
102
|
+
- Built-in experiment tracking and versioning
|
|
103
|
+
- Easy comparison between different experiment runs
|
|
104
|
+
- Direct access to dataset items with their metadata and expected outputs
|
|
105
|
+
- Automatic URL generation for viewing results in Aeri dashboard
|
|
106
|
+
|
|
107
|
+
Args:
|
|
108
|
+
name: Human-readable name for the experiment run. This will be used as
|
|
109
|
+
the dataset run name in Aeri for tracking and identification.
|
|
110
|
+
run_name: Optional exact name for the dataset run. If provided, this will be
|
|
111
|
+
used as the exact dataset run name in Aeri. If not provided, this will
|
|
112
|
+
default to the experiment name appended with an ISO timestamp.
|
|
113
|
+
description: Optional description of the experiment's purpose, methodology,
|
|
114
|
+
or what you're testing. Appears in the Aeri UI for context.
|
|
115
|
+
task: Function that processes each dataset item and returns output.
|
|
116
|
+
The function will receive DatasetItem objects with .input, .expected_output,
|
|
117
|
+
.metadata attributes. Signature should be: task(*, item, **kwargs) -> Any
|
|
118
|
+
evaluators: List of functions to evaluate each item's output individually.
|
|
119
|
+
These will have access to the item's expected_output for comparison.
|
|
120
|
+
composite_evaluator: Optional function that creates composite scores from item-level evaluations.
|
|
121
|
+
Receives the same inputs as item-level evaluators (input, output, expected_output, metadata)
|
|
122
|
+
plus the list of evaluations from item-level evaluators. Useful for weighted averages,
|
|
123
|
+
pass/fail decisions based on multiple criteria, or custom scoring logic combining multiple metrics.
|
|
124
|
+
run_evaluators: List of functions to evaluate the entire experiment run.
|
|
125
|
+
Useful for computing aggregate statistics across all dataset items.
|
|
126
|
+
max_concurrency: Maximum number of concurrent task executions (default: 50).
|
|
127
|
+
Adjust based on API rate limits and system resources.
|
|
128
|
+
metadata: Optional metadata to attach to the experiment run and all traces.
|
|
129
|
+
Will be combined with individual item metadata.
|
|
130
|
+
|
|
131
|
+
Returns:
|
|
132
|
+
ExperimentResult object containing:
|
|
133
|
+
- name: The experiment name.
|
|
134
|
+
- run_name: The experiment run name (equivalent to the dataset run name).
|
|
135
|
+
- description: Optional experiment description.
|
|
136
|
+
- item_results: Results for each dataset item with outputs and evaluations.
|
|
137
|
+
- run_evaluations: Aggregate evaluation results for the entire run.
|
|
138
|
+
- dataset_run_id: ID of the created dataset run in Aeri.
|
|
139
|
+
- dataset_run_url: Direct URL to view the experiment results in Aeri UI.
|
|
140
|
+
|
|
141
|
+
The result object provides a format() method for human-readable output:
|
|
142
|
+
```python
|
|
143
|
+
result = dataset.run_experiment(...)
|
|
144
|
+
print(result.format()) # Summary view
|
|
145
|
+
print(result.format(include_item_results=True)) # Detailed view
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
Raises:
|
|
149
|
+
ValueError: If the dataset has no items or no Aeri client is available.
|
|
150
|
+
|
|
151
|
+
Examples:
|
|
152
|
+
Basic dataset experiment:
|
|
153
|
+
```python
|
|
154
|
+
dataset = aeri.get_dataset("qa-evaluation-set")
|
|
155
|
+
|
|
156
|
+
def answer_questions(*, item, **kwargs):
|
|
157
|
+
# item is a DatasetItem with .input, .expected_output, .metadata
|
|
158
|
+
question = item.input
|
|
159
|
+
return my_qa_system.answer(question)
|
|
160
|
+
|
|
161
|
+
def accuracy_evaluator(*, input, output, expected_output=None, **kwargs):
|
|
162
|
+
if not expected_output:
|
|
163
|
+
return {"name": "accuracy", "value": 0, "comment": "No expected output"}
|
|
164
|
+
|
|
165
|
+
is_correct = output.strip().lower() == expected_output.strip().lower()
|
|
166
|
+
return {
|
|
167
|
+
"name": "accuracy",
|
|
168
|
+
"value": 1.0 if is_correct else 0.0,
|
|
169
|
+
"comment": "Correct" if is_correct else "Incorrect"
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
result = dataset.run_experiment(
|
|
173
|
+
name="QA System v2.0 Evaluation",
|
|
174
|
+
description="Testing improved QA system on curated question set",
|
|
175
|
+
task=answer_questions,
|
|
176
|
+
evaluators=[accuracy_evaluator]
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
print(f"Evaluated {len(result['item_results'])} questions")
|
|
180
|
+
print(f"View detailed results: {result['dataset_run_url']}")
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
Advanced experiment with multiple evaluators and run-level analysis:
|
|
184
|
+
```python
|
|
185
|
+
dataset = aeri.get_dataset("content-generation-benchmark")
|
|
186
|
+
|
|
187
|
+
async def generate_content(*, item, **kwargs):
|
|
188
|
+
prompt = item.input
|
|
189
|
+
response = await openai_client.chat.completions.create(
|
|
190
|
+
model="gpt-4",
|
|
191
|
+
messages=[{"role": "user", "content": prompt}],
|
|
192
|
+
temperature=0.7
|
|
193
|
+
)
|
|
194
|
+
return response.choices[0].message.content
|
|
195
|
+
|
|
196
|
+
def quality_evaluator(*, input, output, expected_output=None, metadata=None, **kwargs):
|
|
197
|
+
# Use metadata for context-aware evaluation
|
|
198
|
+
content_type = metadata.get("type", "general") if metadata else "general"
|
|
199
|
+
|
|
200
|
+
# Basic quality checks
|
|
201
|
+
word_count = len(output.split())
|
|
202
|
+
min_words = {"blog": 300, "tweet": 10, "summary": 100}.get(content_type, 50)
|
|
203
|
+
|
|
204
|
+
return [
|
|
205
|
+
{
|
|
206
|
+
"name": "word_count",
|
|
207
|
+
"value": word_count,
|
|
208
|
+
"comment": f"Generated {word_count} words"
|
|
209
|
+
},
|
|
210
|
+
{
|
|
211
|
+
"name": "meets_length_requirement",
|
|
212
|
+
"value": word_count >= min_words,
|
|
213
|
+
"comment": f"{'Meets' if word_count >= min_words else 'Below'} minimum {min_words} words for {content_type}"
|
|
214
|
+
}
|
|
215
|
+
]
|
|
216
|
+
|
|
217
|
+
def content_diversity(*, item_results, **kwargs):
|
|
218
|
+
# Analyze diversity across all generated content
|
|
219
|
+
all_outputs = [result["output"] for result in item_results]
|
|
220
|
+
unique_words = set()
|
|
221
|
+
total_words = 0
|
|
222
|
+
|
|
223
|
+
for output in all_outputs:
|
|
224
|
+
words = output.lower().split()
|
|
225
|
+
unique_words.update(words)
|
|
226
|
+
total_words += len(words)
|
|
227
|
+
|
|
228
|
+
diversity_ratio = len(unique_words) / total_words if total_words > 0 else 0
|
|
229
|
+
|
|
230
|
+
return {
|
|
231
|
+
"name": "vocabulary_diversity",
|
|
232
|
+
"value": diversity_ratio,
|
|
233
|
+
"comment": f"Used {len(unique_words)} unique words out of {total_words} total ({diversity_ratio:.2%} diversity)"
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
result = dataset.run_experiment(
|
|
237
|
+
name="Content Generation Diversity Test",
|
|
238
|
+
description="Evaluating content quality and vocabulary diversity across different content types",
|
|
239
|
+
task=generate_content,
|
|
240
|
+
evaluators=[quality_evaluator],
|
|
241
|
+
run_evaluators=[content_diversity],
|
|
242
|
+
max_concurrency=3, # Limit API calls
|
|
243
|
+
metadata={"model": "gpt-4", "temperature": 0.7}
|
|
244
|
+
)
|
|
245
|
+
|
|
246
|
+
# Results are automatically linked to dataset in Aeri
|
|
247
|
+
print(f"Experiment completed! View in Aeri: {result['dataset_run_url']}")
|
|
248
|
+
|
|
249
|
+
# Access individual results
|
|
250
|
+
for i, item_result in enumerate(result["item_results"]):
|
|
251
|
+
print(f"Item {i+1}: {item_result['evaluations']}")
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
Comparing different model versions:
|
|
255
|
+
```python
|
|
256
|
+
# Run multiple experiments on the same dataset for comparison
|
|
257
|
+
dataset = aeri.get_dataset("model-benchmark")
|
|
258
|
+
|
|
259
|
+
# Experiment 1: GPT-4
|
|
260
|
+
result_gpt4 = dataset.run_experiment(
|
|
261
|
+
name="GPT-4 Baseline",
|
|
262
|
+
description="Baseline performance with GPT-4",
|
|
263
|
+
task=lambda *, item, **kwargs: gpt4_model.generate(item.input),
|
|
264
|
+
evaluators=[accuracy_evaluator, fluency_evaluator]
|
|
265
|
+
)
|
|
266
|
+
|
|
267
|
+
# Experiment 2: Custom model
|
|
268
|
+
result_custom = dataset.run_experiment(
|
|
269
|
+
name="Custom Model v1.2",
|
|
270
|
+
description="Testing our fine-tuned model",
|
|
271
|
+
task=lambda *, item, **kwargs: custom_model.generate(item.input),
|
|
272
|
+
evaluators=[accuracy_evaluator, fluency_evaluator]
|
|
273
|
+
)
|
|
274
|
+
|
|
275
|
+
# Both experiments are now visible in Aeri for easy comparison
|
|
276
|
+
print("Compare results in Aeri:")
|
|
277
|
+
print(f"GPT-4: {result_gpt4.dataset_run_url}")
|
|
278
|
+
print(f"Custom: {result_custom.dataset_run_url}")
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
Note:
|
|
282
|
+
- All experiment results are automatically tracked in Aeri as dataset runs
|
|
283
|
+
- Dataset items provide .input, .expected_output, and .metadata attributes
|
|
284
|
+
- Results can be easily compared across different experiment runs in the UI
|
|
285
|
+
- The dataset_run_url provides direct access to detailed results and analysis
|
|
286
|
+
- Failed items are handled gracefully and logged without stopping the experiment
|
|
287
|
+
- This method works in both sync and async contexts (Jupyter notebooks, web apps, etc.)
|
|
288
|
+
- Async execution is handled automatically with smart event loop detection
|
|
289
|
+
"""
|
|
290
|
+
return self._aeri_client.run_experiment(
|
|
291
|
+
name=name,
|
|
292
|
+
run_name=run_name,
|
|
293
|
+
description=description,
|
|
294
|
+
data=self.items,
|
|
295
|
+
task=task,
|
|
296
|
+
evaluators=evaluators,
|
|
297
|
+
composite_evaluator=composite_evaluator,
|
|
298
|
+
run_evaluators=run_evaluators,
|
|
299
|
+
max_concurrency=max_concurrency,
|
|
300
|
+
metadata=metadata,
|
|
301
|
+
_dataset_version=self.version,
|
|
302
|
+
)
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
"""Environment variable definitions for Aeri OpenTelemetry integration.
|
|
2
|
+
|
|
3
|
+
This module defines environment variables used to configure the Aeri OpenTelemetry integration.
|
|
4
|
+
Each environment variable includes documentation on its purpose, expected values, and defaults.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
AERI_TRACING_ENVIRONMENT = "AERI_TRACING_ENVIRONMENT"
|
|
8
|
+
"""
|
|
9
|
+
.. envvar:: AERI_TRACING_ENVIRONMENT
|
|
10
|
+
|
|
11
|
+
The tracing environment. Can be any lowercase alphanumeric string with hyphens and underscores that does not start with 'aeri'.
|
|
12
|
+
|
|
13
|
+
**Default value:** ``"default"``
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
AERI_RELEASE = "AERI_RELEASE"
|
|
17
|
+
"""
|
|
18
|
+
.. envvar:: AERI_RELEASE
|
|
19
|
+
|
|
20
|
+
Release number/hash of the application to provide analytics grouped by release.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
AERI_PUBLIC_KEY = "AERI_PUBLIC_KEY"
|
|
25
|
+
"""
|
|
26
|
+
.. envvar:: AERI_PUBLIC_KEY
|
|
27
|
+
|
|
28
|
+
Public API key of Aeri project
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
AERI_SECRET_KEY = "AERI_SECRET_KEY"
|
|
32
|
+
"""
|
|
33
|
+
.. envvar:: AERI_SECRET_KEY
|
|
34
|
+
|
|
35
|
+
Secret API key of Aeri project
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
AERI_BASE_URL = "AERI_BASE_URL"
|
|
39
|
+
"""
|
|
40
|
+
.. envvar:: AERI_BASE_URL
|
|
41
|
+
|
|
42
|
+
Base URL of Aeri API. Can be set via `AERI_BASE_URL` environment variable.
|
|
43
|
+
|
|
44
|
+
**Default value:** ``"https://cloud.aeri.com"``
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
AERI_HOST = "AERI_HOST"
|
|
48
|
+
"""
|
|
49
|
+
.. envvar:: AERI_HOST
|
|
50
|
+
|
|
51
|
+
Deprecated. Use AERI_BASE_URL instead. Host of Aeri API. Can be set via `AERI_HOST` environment variable.
|
|
52
|
+
|
|
53
|
+
**Default value:** ``"https://cloud.aeri.com"``
|
|
54
|
+
"""
|
|
55
|
+
|
|
56
|
+
AERI_OTEL_TRACES_EXPORT_PATH = "AERI_OTEL_TRACES_EXPORT_PATH"
|
|
57
|
+
"""
|
|
58
|
+
.. envvar:: AERI_OTEL_TRACES_EXPORT_PATH
|
|
59
|
+
|
|
60
|
+
URL path on the configured host to export traces to.
|
|
61
|
+
|
|
62
|
+
**Default value:** ``/api/public/otel/v1/traces``
|
|
63
|
+
"""
|
|
64
|
+
|
|
65
|
+
AERI_DEBUG = "AERI_DEBUG"
|
|
66
|
+
"""
|
|
67
|
+
.. envvar:: AERI_DEBUG
|
|
68
|
+
|
|
69
|
+
Enables debug mode for more verbose logging.
|
|
70
|
+
|
|
71
|
+
**Default value:** ``"False"``
|
|
72
|
+
"""
|
|
73
|
+
|
|
74
|
+
AERI_TRACING_ENABLED = "AERI_TRACING_ENABLED"
|
|
75
|
+
"""
|
|
76
|
+
.. envvar:: AERI_TRACING_ENABLED
|
|
77
|
+
|
|
78
|
+
Enables or disables the Aeri client. If disabled, all observability calls to the backend will be no-ops. Default is True. Set to `False` to disable tracing.
|
|
79
|
+
|
|
80
|
+
**Default value:** ``"True"``
|
|
81
|
+
"""
|
|
82
|
+
|
|
83
|
+
AERI_MEDIA_UPLOAD_THREAD_COUNT = "AERI_MEDIA_UPLOAD_THREAD_COUNT"
|
|
84
|
+
"""
|
|
85
|
+
.. envvar:: AERI_MEDIA_UPLOAD_THREAD_COUNT
|
|
86
|
+
|
|
87
|
+
Number of background threads to handle media uploads from trace ingestion.
|
|
88
|
+
|
|
89
|
+
**Default value:** ``1``
|
|
90
|
+
"""
|
|
91
|
+
|
|
92
|
+
AERI_FLUSH_AT = "AERI_FLUSH_AT"
|
|
93
|
+
"""
|
|
94
|
+
.. envvar:: AERI_FLUSH_AT
|
|
95
|
+
|
|
96
|
+
Max batch size until a new ingestion batch is sent to the API.
|
|
97
|
+
**Default value:** same as OTEL ``OTEL_BSP_MAX_EXPORT_BATCH_SIZE``
|
|
98
|
+
"""
|
|
99
|
+
|
|
100
|
+
AERI_FLUSH_INTERVAL = "AERI_FLUSH_INTERVAL"
|
|
101
|
+
"""
|
|
102
|
+
.. envvar:: AERI_FLUSH_INTERVAL
|
|
103
|
+
|
|
104
|
+
Max delay in seconds until a new ingestion batch is sent to the API.
|
|
105
|
+
**Default value:** same as OTEL ``OTEL_BSP_SCHEDULE_DELAY``
|
|
106
|
+
"""
|
|
107
|
+
|
|
108
|
+
AERI_SAMPLE_RATE = "AERI_SAMPLE_RATE"
|
|
109
|
+
"""
|
|
110
|
+
.. envvar: AERI_SAMPLE_RATE
|
|
111
|
+
|
|
112
|
+
Float between 0 and 1 indicating the sample rate of traces to bet sent to Aeri servers.
|
|
113
|
+
|
|
114
|
+
**Default value**: ``1.0``
|
|
115
|
+
|
|
116
|
+
"""
|
|
117
|
+
AERI_OBSERVE_DECORATOR_IO_CAPTURE_ENABLED = (
|
|
118
|
+
"AERI_OBSERVE_DECORATOR_IO_CAPTURE_ENABLED"
|
|
119
|
+
)
|
|
120
|
+
"""
|
|
121
|
+
.. envvar: AERI_OBSERVE_DECORATOR_IO_CAPTURE_ENABLED
|
|
122
|
+
|
|
123
|
+
Default capture of function args, kwargs and return value when using the @observe decorator.
|
|
124
|
+
|
|
125
|
+
Having default IO capture enabled for observe decorated function may have a performance impact on your application
|
|
126
|
+
if large or deeply nested objects are attempted to be serialized. Set this value to `False` and use manual
|
|
127
|
+
input/output setting on your observation to avoid this.
|
|
128
|
+
|
|
129
|
+
**Default value**: ``True``
|
|
130
|
+
"""
|
|
131
|
+
|
|
132
|
+
AERI_MEDIA_UPLOAD_ENABLED = "AERI_MEDIA_UPLOAD_ENABLED"
|
|
133
|
+
"""
|
|
134
|
+
.. envvar: AERI_MEDIA_UPLOAD_ENABLED
|
|
135
|
+
|
|
136
|
+
Controls whether media detection and upload is attempted by the SDK.
|
|
137
|
+
|
|
138
|
+
**Default value**: ``True``
|
|
139
|
+
"""
|
|
140
|
+
|
|
141
|
+
AERI_TIMEOUT = "AERI_TIMEOUT"
|
|
142
|
+
"""
|
|
143
|
+
.. envvar: AERI_TIMEOUT
|
|
144
|
+
|
|
145
|
+
Controls the timeout for all API requests in seconds
|
|
146
|
+
|
|
147
|
+
**Default value**: ``5``
|
|
148
|
+
"""
|
|
149
|
+
|
|
150
|
+
AERI_PROMPT_CACHE_DEFAULT_TTL_SECONDS = "AERI_PROMPT_CACHE_DEFAULT_TTL_SECONDS"
|
|
151
|
+
"""
|
|
152
|
+
.. envvar: AERI_PROMPT_CACHE_DEFAULT_TTL_SECONDS
|
|
153
|
+
|
|
154
|
+
Controls the default time-to-live (TTL) in seconds for cached prompts.
|
|
155
|
+
This setting determines how long prompt responses are cached before they expire.
|
|
156
|
+
|
|
157
|
+
**Default value**: ``60``
|
|
158
|
+
"""
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
from contextlib import contextmanager
|
|
2
|
+
from contextvars import ContextVar
|
|
3
|
+
from typing import Iterator, Optional
|
|
4
|
+
|
|
5
|
+
from aeri._client.client import Aeri
|
|
6
|
+
from aeri._client.resource_manager import AeriResourceManager
|
|
7
|
+
from aeri.logger import aeri_logger
|
|
8
|
+
|
|
9
|
+
# Context variable to track the current aeri_public_key in execution context
|
|
10
|
+
_current_public_key: ContextVar[Optional[str]] = ContextVar(
|
|
11
|
+
"aeri_public_key", default=None
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@contextmanager
|
|
16
|
+
def _set_current_public_key(public_key: Optional[str]) -> Iterator[None]:
|
|
17
|
+
"""Context manager to set and restore the current public key in execution context.
|
|
18
|
+
|
|
19
|
+
Args:
|
|
20
|
+
public_key: The public key to set in context. If None, context is not modified.
|
|
21
|
+
|
|
22
|
+
Yields:
|
|
23
|
+
None
|
|
24
|
+
"""
|
|
25
|
+
if public_key is None:
|
|
26
|
+
yield # Don't modify context if no key provided
|
|
27
|
+
return
|
|
28
|
+
|
|
29
|
+
token = _current_public_key.set(public_key)
|
|
30
|
+
try:
|
|
31
|
+
yield
|
|
32
|
+
finally:
|
|
33
|
+
_current_public_key.reset(token)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def _create_client_from_instance(
|
|
37
|
+
instance: "AeriResourceManager", public_key: Optional[str] = None
|
|
38
|
+
) -> Aeri:
|
|
39
|
+
"""Create a Aeri client from a resource manager instance with all settings preserved."""
|
|
40
|
+
return Aeri(
|
|
41
|
+
public_key=public_key or instance.public_key,
|
|
42
|
+
secret_key=instance.secret_key,
|
|
43
|
+
base_url=instance.base_url,
|
|
44
|
+
tracing_enabled=instance.tracing_enabled,
|
|
45
|
+
environment=instance.environment,
|
|
46
|
+
timeout=instance.timeout,
|
|
47
|
+
flush_at=instance.flush_at,
|
|
48
|
+
flush_interval=instance.flush_interval,
|
|
49
|
+
release=instance.release,
|
|
50
|
+
media_upload_thread_count=instance.media_upload_thread_count,
|
|
51
|
+
sample_rate=instance.sample_rate,
|
|
52
|
+
mask=instance.mask,
|
|
53
|
+
blocked_instrumentation_scopes=instance.blocked_instrumentation_scopes,
|
|
54
|
+
should_export_span=instance.should_export_span,
|
|
55
|
+
additional_headers=instance.additional_headers,
|
|
56
|
+
tracer_provider=instance.tracer_provider,
|
|
57
|
+
httpx_client=instance.httpx_client,
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def get_client(*, public_key: Optional[str] = None) -> Aeri:
|
|
62
|
+
"""Get or create a Aeri client instance.
|
|
63
|
+
|
|
64
|
+
Returns an existing Aeri client or creates a new one if none exists. In multi-project setups,
|
|
65
|
+
providing a public_key is required. Multi-project support is experimental - see Aeri docs.
|
|
66
|
+
|
|
67
|
+
Behavior:
|
|
68
|
+
- Single project: Returns existing client or creates new one
|
|
69
|
+
- Multi-project: Requires public_key to return specific client
|
|
70
|
+
- No public_key in multi-project: Returns disabled client to prevent data leakage
|
|
71
|
+
|
|
72
|
+
The function uses a singleton pattern per public_key to conserve resources and maintain state.
|
|
73
|
+
|
|
74
|
+
Args:
|
|
75
|
+
public_key (Optional[str]): Project identifier
|
|
76
|
+
- With key: Returns client for that project
|
|
77
|
+
- Without key: Returns single client or disabled client if multiple exist
|
|
78
|
+
|
|
79
|
+
Returns:
|
|
80
|
+
Aeri: Client instance in one of three states:
|
|
81
|
+
1. Client for specified public_key
|
|
82
|
+
2. Default client for single-project setup
|
|
83
|
+
3. Disabled client when multiple projects exist without key
|
|
84
|
+
|
|
85
|
+
Security:
|
|
86
|
+
Disables tracing when multiple projects exist without explicit key to prevent
|
|
87
|
+
cross-project data leakage. Multi-project setups are experimental.
|
|
88
|
+
|
|
89
|
+
Example:
|
|
90
|
+
```python
|
|
91
|
+
# Single project
|
|
92
|
+
client = get_client() # Default client
|
|
93
|
+
|
|
94
|
+
# In multi-project usage:
|
|
95
|
+
client_a = get_client(public_key="project_a_key") # Returns project A's client
|
|
96
|
+
client_b = get_client(public_key="project_b_key") # Returns project B's client
|
|
97
|
+
|
|
98
|
+
# Without specific key in multi-project setup:
|
|
99
|
+
client = get_client() # Returns disabled client for safety
|
|
100
|
+
```
|
|
101
|
+
"""
|
|
102
|
+
with AeriResourceManager._lock:
|
|
103
|
+
active_instances = AeriResourceManager._instances
|
|
104
|
+
|
|
105
|
+
# If no explicit public_key provided, check execution context
|
|
106
|
+
if not public_key:
|
|
107
|
+
public_key = _current_public_key.get(None)
|
|
108
|
+
|
|
109
|
+
if not public_key:
|
|
110
|
+
if len(active_instances) == 0:
|
|
111
|
+
# No clients initialized yet, create default instance
|
|
112
|
+
return Aeri()
|
|
113
|
+
|
|
114
|
+
if len(active_instances) == 1:
|
|
115
|
+
# Only one client exists, safe to use without specifying key
|
|
116
|
+
instance = list(active_instances.values())[0]
|
|
117
|
+
|
|
118
|
+
# Initialize with the credentials bound to the instance
|
|
119
|
+
# This is important if the original instance was instantiated
|
|
120
|
+
# via constructor arguments
|
|
121
|
+
return _create_client_from_instance(instance)
|
|
122
|
+
|
|
123
|
+
else:
|
|
124
|
+
# Multiple clients exist but no key specified - disable tracing
|
|
125
|
+
# to prevent cross-project data leakage
|
|
126
|
+
aeri_logger.warning(
|
|
127
|
+
"No 'aeri_public_key' passed to decorated function, but multiple aeri clients are instantiated in current process. Skipping tracing for this function to avoid cross-project leakage."
|
|
128
|
+
)
|
|
129
|
+
return Aeri(
|
|
130
|
+
tracing_enabled=False, public_key="fake", secret_key="fake"
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
else:
|
|
134
|
+
# Specific key provided, look up existing instance
|
|
135
|
+
target_instance: Optional[AeriResourceManager] = active_instances.get(
|
|
136
|
+
public_key, None
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
if target_instance is None:
|
|
140
|
+
# No instance found with this key - client not initialized properly
|
|
141
|
+
aeri_logger.warning(
|
|
142
|
+
f"No Aeri client with public key {public_key} has been initialized. Skipping tracing for decorated function."
|
|
143
|
+
)
|
|
144
|
+
return Aeri(
|
|
145
|
+
tracing_enabled=False, public_key="fake", secret_key="fake"
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
# target_instance is guaranteed to be not None at this point
|
|
149
|
+
return _create_client_from_instance(target_instance, public_key)
|