mirascope 1.0.5__py3-none-any.whl → 2.1.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- mirascope/__init__.py +6 -6
- mirascope/_stubs.py +384 -0
- mirascope/_utils.py +34 -0
- mirascope/api/__init__.py +14 -0
- mirascope/api/_generated/README.md +207 -0
- mirascope/api/_generated/__init__.py +444 -0
- mirascope/api/_generated/annotations/__init__.py +33 -0
- mirascope/api/_generated/annotations/client.py +506 -0
- mirascope/api/_generated/annotations/raw_client.py +1414 -0
- mirascope/api/_generated/annotations/types/__init__.py +31 -0
- mirascope/api/_generated/annotations/types/annotations_create_request_label.py +5 -0
- mirascope/api/_generated/annotations/types/annotations_create_response.py +48 -0
- mirascope/api/_generated/annotations/types/annotations_create_response_label.py +5 -0
- mirascope/api/_generated/annotations/types/annotations_get_response.py +48 -0
- mirascope/api/_generated/annotations/types/annotations_get_response_label.py +5 -0
- mirascope/api/_generated/annotations/types/annotations_list_request_label.py +5 -0
- mirascope/api/_generated/annotations/types/annotations_list_response.py +21 -0
- mirascope/api/_generated/annotations/types/annotations_list_response_annotations_item.py +50 -0
- mirascope/api/_generated/annotations/types/annotations_list_response_annotations_item_label.py +5 -0
- mirascope/api/_generated/annotations/types/annotations_update_request_label.py +5 -0
- mirascope/api/_generated/annotations/types/annotations_update_response.py +48 -0
- mirascope/api/_generated/annotations/types/annotations_update_response_label.py +5 -0
- mirascope/api/_generated/api_keys/__init__.py +17 -0
- mirascope/api/_generated/api_keys/client.py +530 -0
- mirascope/api/_generated/api_keys/raw_client.py +1236 -0
- mirascope/api/_generated/api_keys/types/__init__.py +15 -0
- mirascope/api/_generated/api_keys/types/api_keys_create_response.py +28 -0
- mirascope/api/_generated/api_keys/types/api_keys_get_response.py +27 -0
- mirascope/api/_generated/api_keys/types/api_keys_list_all_for_org_response_item.py +40 -0
- mirascope/api/_generated/api_keys/types/api_keys_list_response_item.py +27 -0
- mirascope/api/_generated/client.py +211 -0
- mirascope/api/_generated/core/__init__.py +52 -0
- mirascope/api/_generated/core/api_error.py +23 -0
- mirascope/api/_generated/core/client_wrapper.py +46 -0
- mirascope/api/_generated/core/datetime_utils.py +28 -0
- mirascope/api/_generated/core/file.py +67 -0
- mirascope/api/_generated/core/force_multipart.py +16 -0
- mirascope/api/_generated/core/http_client.py +543 -0
- mirascope/api/_generated/core/http_response.py +55 -0
- mirascope/api/_generated/core/jsonable_encoder.py +100 -0
- mirascope/api/_generated/core/pydantic_utilities.py +255 -0
- mirascope/api/_generated/core/query_encoder.py +58 -0
- mirascope/api/_generated/core/remove_none_from_dict.py +11 -0
- mirascope/api/_generated/core/request_options.py +35 -0
- mirascope/api/_generated/core/serialization.py +276 -0
- mirascope/api/_generated/docs/__init__.py +4 -0
- mirascope/api/_generated/docs/client.py +91 -0
- mirascope/api/_generated/docs/raw_client.py +178 -0
- mirascope/api/_generated/environment.py +9 -0
- mirascope/api/_generated/environments/__init__.py +23 -0
- mirascope/api/_generated/environments/client.py +649 -0
- mirascope/api/_generated/environments/raw_client.py +1567 -0
- mirascope/api/_generated/environments/types/__init__.py +25 -0
- mirascope/api/_generated/environments/types/environments_create_response.py +24 -0
- mirascope/api/_generated/environments/types/environments_get_analytics_response.py +60 -0
- mirascope/api/_generated/environments/types/environments_get_analytics_response_top_functions_item.py +24 -0
- mirascope/api/_generated/environments/types/environments_get_analytics_response_top_models_item.py +22 -0
- mirascope/api/_generated/environments/types/environments_get_response.py +24 -0
- mirascope/api/_generated/environments/types/environments_list_response_item.py +24 -0
- mirascope/api/_generated/environments/types/environments_update_response.py +24 -0
- mirascope/api/_generated/errors/__init__.py +25 -0
- mirascope/api/_generated/errors/bad_request_error.py +14 -0
- mirascope/api/_generated/errors/conflict_error.py +14 -0
- mirascope/api/_generated/errors/forbidden_error.py +11 -0
- mirascope/api/_generated/errors/internal_server_error.py +10 -0
- mirascope/api/_generated/errors/not_found_error.py +11 -0
- mirascope/api/_generated/errors/payment_required_error.py +15 -0
- mirascope/api/_generated/errors/service_unavailable_error.py +14 -0
- mirascope/api/_generated/errors/too_many_requests_error.py +15 -0
- mirascope/api/_generated/errors/unauthorized_error.py +11 -0
- mirascope/api/_generated/functions/__init__.py +39 -0
- mirascope/api/_generated/functions/client.py +647 -0
- mirascope/api/_generated/functions/raw_client.py +1890 -0
- mirascope/api/_generated/functions/types/__init__.py +53 -0
- mirascope/api/_generated/functions/types/functions_create_request_dependencies_value.py +20 -0
- mirascope/api/_generated/functions/types/functions_create_response.py +37 -0
- mirascope/api/_generated/functions/types/functions_create_response_dependencies_value.py +20 -0
- mirascope/api/_generated/functions/types/functions_find_by_hash_response.py +39 -0
- mirascope/api/_generated/functions/types/functions_find_by_hash_response_dependencies_value.py +20 -0
- mirascope/api/_generated/functions/types/functions_get_by_env_response.py +53 -0
- mirascope/api/_generated/functions/types/functions_get_by_env_response_dependencies_value.py +22 -0
- mirascope/api/_generated/functions/types/functions_get_response.py +37 -0
- mirascope/api/_generated/functions/types/functions_get_response_dependencies_value.py +20 -0
- mirascope/api/_generated/functions/types/functions_list_by_env_response.py +25 -0
- mirascope/api/_generated/functions/types/functions_list_by_env_response_functions_item.py +56 -0
- mirascope/api/_generated/functions/types/functions_list_by_env_response_functions_item_dependencies_value.py +22 -0
- mirascope/api/_generated/functions/types/functions_list_response.py +21 -0
- mirascope/api/_generated/functions/types/functions_list_response_functions_item.py +41 -0
- mirascope/api/_generated/functions/types/functions_list_response_functions_item_dependencies_value.py +20 -0
- mirascope/api/_generated/health/__init__.py +7 -0
- mirascope/api/_generated/health/client.py +92 -0
- mirascope/api/_generated/health/raw_client.py +175 -0
- mirascope/api/_generated/health/types/__init__.py +8 -0
- mirascope/api/_generated/health/types/health_check_response.py +22 -0
- mirascope/api/_generated/health/types/health_check_response_status.py +5 -0
- mirascope/api/_generated/organization_invitations/__init__.py +33 -0
- mirascope/api/_generated/organization_invitations/client.py +546 -0
- mirascope/api/_generated/organization_invitations/raw_client.py +1519 -0
- mirascope/api/_generated/organization_invitations/types/__init__.py +53 -0
- mirascope/api/_generated/organization_invitations/types/organization_invitations_accept_response.py +34 -0
- mirascope/api/_generated/organization_invitations/types/organization_invitations_accept_response_role.py +7 -0
- mirascope/api/_generated/organization_invitations/types/organization_invitations_create_request_role.py +7 -0
- mirascope/api/_generated/organization_invitations/types/organization_invitations_create_response.py +48 -0
- mirascope/api/_generated/organization_invitations/types/organization_invitations_create_response_role.py +7 -0
- mirascope/api/_generated/organization_invitations/types/organization_invitations_create_response_status.py +7 -0
- mirascope/api/_generated/organization_invitations/types/organization_invitations_get_response.py +48 -0
- mirascope/api/_generated/organization_invitations/types/organization_invitations_get_response_role.py +7 -0
- mirascope/api/_generated/organization_invitations/types/organization_invitations_get_response_status.py +7 -0
- mirascope/api/_generated/organization_invitations/types/organization_invitations_list_response_item.py +48 -0
- mirascope/api/_generated/organization_invitations/types/organization_invitations_list_response_item_role.py +7 -0
- mirascope/api/_generated/organization_invitations/types/organization_invitations_list_response_item_status.py +7 -0
- mirascope/api/_generated/organization_memberships/__init__.py +19 -0
- mirascope/api/_generated/organization_memberships/client.py +302 -0
- mirascope/api/_generated/organization_memberships/raw_client.py +736 -0
- mirascope/api/_generated/organization_memberships/types/__init__.py +27 -0
- mirascope/api/_generated/organization_memberships/types/organization_memberships_list_response_item.py +33 -0
- mirascope/api/_generated/organization_memberships/types/organization_memberships_list_response_item_role.py +7 -0
- mirascope/api/_generated/organization_memberships/types/organization_memberships_update_request_role.py +7 -0
- mirascope/api/_generated/organization_memberships/types/organization_memberships_update_response.py +31 -0
- mirascope/api/_generated/organization_memberships/types/organization_memberships_update_response_role.py +7 -0
- mirascope/api/_generated/organizations/__init__.py +51 -0
- mirascope/api/_generated/organizations/client.py +869 -0
- mirascope/api/_generated/organizations/raw_client.py +2593 -0
- mirascope/api/_generated/organizations/types/__init__.py +71 -0
- mirascope/api/_generated/organizations/types/organizations_create_payment_intent_response.py +24 -0
- mirascope/api/_generated/organizations/types/organizations_create_response.py +26 -0
- mirascope/api/_generated/organizations/types/organizations_create_response_role.py +5 -0
- mirascope/api/_generated/organizations/types/organizations_get_response.py +26 -0
- mirascope/api/_generated/organizations/types/organizations_get_response_role.py +5 -0
- mirascope/api/_generated/organizations/types/organizations_list_response_item.py +26 -0
- mirascope/api/_generated/organizations/types/organizations_list_response_item_role.py +5 -0
- mirascope/api/_generated/organizations/types/organizations_preview_subscription_change_request_target_plan.py +7 -0
- mirascope/api/_generated/organizations/types/organizations_preview_subscription_change_response.py +47 -0
- mirascope/api/_generated/organizations/types/organizations_preview_subscription_change_response_validation_errors_item.py +33 -0
- mirascope/api/_generated/organizations/types/organizations_preview_subscription_change_response_validation_errors_item_resource.py +7 -0
- mirascope/api/_generated/organizations/types/organizations_router_balance_response.py +24 -0
- mirascope/api/_generated/organizations/types/organizations_subscription_response.py +53 -0
- mirascope/api/_generated/organizations/types/organizations_subscription_response_current_plan.py +7 -0
- mirascope/api/_generated/organizations/types/organizations_subscription_response_payment_method.py +26 -0
- mirascope/api/_generated/organizations/types/organizations_subscription_response_scheduled_change.py +34 -0
- mirascope/api/_generated/organizations/types/organizations_subscription_response_scheduled_change_target_plan.py +7 -0
- mirascope/api/_generated/organizations/types/organizations_update_response.py +26 -0
- mirascope/api/_generated/organizations/types/organizations_update_response_role.py +5 -0
- mirascope/api/_generated/organizations/types/organizations_update_subscription_request_target_plan.py +7 -0
- mirascope/api/_generated/organizations/types/organizations_update_subscription_response.py +35 -0
- mirascope/api/_generated/project_memberships/__init__.py +29 -0
- mirascope/api/_generated/project_memberships/client.py +528 -0
- mirascope/api/_generated/project_memberships/raw_client.py +1278 -0
- mirascope/api/_generated/project_memberships/types/__init__.py +33 -0
- mirascope/api/_generated/project_memberships/types/project_memberships_create_request_role.py +7 -0
- mirascope/api/_generated/project_memberships/types/project_memberships_create_response.py +35 -0
- mirascope/api/_generated/project_memberships/types/project_memberships_create_response_role.py +7 -0
- mirascope/api/_generated/project_memberships/types/project_memberships_get_response.py +33 -0
- mirascope/api/_generated/project_memberships/types/project_memberships_get_response_role.py +7 -0
- mirascope/api/_generated/project_memberships/types/project_memberships_list_response_item.py +33 -0
- mirascope/api/_generated/project_memberships/types/project_memberships_list_response_item_role.py +7 -0
- mirascope/api/_generated/project_memberships/types/project_memberships_update_request_role.py +7 -0
- mirascope/api/_generated/project_memberships/types/project_memberships_update_response.py +35 -0
- mirascope/api/_generated/project_memberships/types/project_memberships_update_response_role.py +7 -0
- mirascope/api/_generated/projects/__init__.py +7 -0
- mirascope/api/_generated/projects/client.py +428 -0
- mirascope/api/_generated/projects/raw_client.py +1302 -0
- mirascope/api/_generated/projects/types/__init__.py +10 -0
- mirascope/api/_generated/projects/types/projects_create_response.py +25 -0
- mirascope/api/_generated/projects/types/projects_get_response.py +25 -0
- mirascope/api/_generated/projects/types/projects_list_response_item.py +25 -0
- mirascope/api/_generated/projects/types/projects_update_response.py +25 -0
- mirascope/api/_generated/reference.md +4987 -0
- mirascope/api/_generated/tags/__init__.py +19 -0
- mirascope/api/_generated/tags/client.py +504 -0
- mirascope/api/_generated/tags/raw_client.py +1288 -0
- mirascope/api/_generated/tags/types/__init__.py +17 -0
- mirascope/api/_generated/tags/types/tags_create_response.py +41 -0
- mirascope/api/_generated/tags/types/tags_get_response.py +41 -0
- mirascope/api/_generated/tags/types/tags_list_response.py +23 -0
- mirascope/api/_generated/tags/types/tags_list_response_tags_item.py +41 -0
- mirascope/api/_generated/tags/types/tags_update_response.py +41 -0
- mirascope/api/_generated/token_cost/__init__.py +7 -0
- mirascope/api/_generated/token_cost/client.py +160 -0
- mirascope/api/_generated/token_cost/raw_client.py +264 -0
- mirascope/api/_generated/token_cost/types/__init__.py +8 -0
- mirascope/api/_generated/token_cost/types/token_cost_calculate_request_usage.py +54 -0
- mirascope/api/_generated/token_cost/types/token_cost_calculate_response.py +52 -0
- mirascope/api/_generated/traces/__init__.py +97 -0
- mirascope/api/_generated/traces/client.py +1103 -0
- mirascope/api/_generated/traces/raw_client.py +2322 -0
- mirascope/api/_generated/traces/types/__init__.py +155 -0
- mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item.py +29 -0
- mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_resource.py +27 -0
- mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_resource_attributes_item.py +23 -0
- mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_resource_attributes_item_value.py +38 -0
- mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_resource_attributes_item_value_array_value.py +19 -0
- mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_resource_attributes_item_value_kvlist_value.py +22 -0
- mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_resource_attributes_item_value_kvlist_value_values_item.py +20 -0
- mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_scope_spans_item.py +29 -0
- mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_scope_spans_item_scope.py +31 -0
- mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_scope_spans_item_scope_attributes_item.py +23 -0
- mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_scope_spans_item_scope_attributes_item_value.py +38 -0
- mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_scope_spans_item_scope_attributes_item_value_array_value.py +19 -0
- mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_scope_spans_item_scope_attributes_item_value_kvlist_value.py +22 -0
- mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_scope_spans_item_scope_attributes_item_value_kvlist_value_values_item.py +22 -0
- mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_scope_spans_item_spans_item.py +48 -0
- mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_scope_spans_item_spans_item_attributes_item.py +23 -0
- mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_scope_spans_item_spans_item_attributes_item_value.py +38 -0
- mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_scope_spans_item_spans_item_attributes_item_value_array_value.py +19 -0
- mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_scope_spans_item_spans_item_attributes_item_value_kvlist_value.py +24 -0
- mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_scope_spans_item_spans_item_attributes_item_value_kvlist_value_values_item.py +22 -0
- mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_scope_spans_item_spans_item_status.py +20 -0
- mirascope/api/_generated/traces/types/traces_create_response.py +24 -0
- mirascope/api/_generated/traces/types/traces_create_response_partial_success.py +22 -0
- mirascope/api/_generated/traces/types/traces_get_analytics_summary_response.py +60 -0
- mirascope/api/_generated/traces/types/traces_get_analytics_summary_response_top_functions_item.py +24 -0
- mirascope/api/_generated/traces/types/traces_get_analytics_summary_response_top_models_item.py +22 -0
- mirascope/api/_generated/traces/types/traces_get_trace_detail_by_env_response.py +33 -0
- mirascope/api/_generated/traces/types/traces_get_trace_detail_by_env_response_spans_item.py +88 -0
- mirascope/api/_generated/traces/types/traces_get_trace_detail_response.py +33 -0
- mirascope/api/_generated/traces/types/traces_get_trace_detail_response_spans_item.py +88 -0
- mirascope/api/_generated/traces/types/traces_list_by_function_hash_response.py +25 -0
- mirascope/api/_generated/traces/types/traces_list_by_function_hash_response_traces_item.py +44 -0
- mirascope/api/_generated/traces/types/traces_search_by_env_request_attribute_filters_item.py +26 -0
- mirascope/api/_generated/traces/types/traces_search_by_env_request_attribute_filters_item_operator.py +7 -0
- mirascope/api/_generated/traces/types/traces_search_by_env_request_sort_by.py +7 -0
- mirascope/api/_generated/traces/types/traces_search_by_env_request_sort_order.py +7 -0
- mirascope/api/_generated/traces/types/traces_search_by_env_response.py +26 -0
- mirascope/api/_generated/traces/types/traces_search_by_env_response_spans_item.py +50 -0
- mirascope/api/_generated/traces/types/traces_search_request_attribute_filters_item.py +26 -0
- mirascope/api/_generated/traces/types/traces_search_request_attribute_filters_item_operator.py +7 -0
- mirascope/api/_generated/traces/types/traces_search_request_sort_by.py +7 -0
- mirascope/api/_generated/traces/types/traces_search_request_sort_order.py +5 -0
- mirascope/api/_generated/traces/types/traces_search_response.py +26 -0
- mirascope/api/_generated/traces/types/traces_search_response_spans_item.py +50 -0
- mirascope/api/_generated/types/__init__.py +85 -0
- mirascope/api/_generated/types/already_exists_error.py +22 -0
- mirascope/api/_generated/types/already_exists_error_tag.py +5 -0
- mirascope/api/_generated/types/bad_request_error_body.py +50 -0
- mirascope/api/_generated/types/click_house_error.py +22 -0
- mirascope/api/_generated/types/database_error.py +22 -0
- mirascope/api/_generated/types/database_error_tag.py +5 -0
- mirascope/api/_generated/types/date.py +3 -0
- mirascope/api/_generated/types/http_api_decode_error.py +27 -0
- mirascope/api/_generated/types/http_api_decode_error_tag.py +5 -0
- mirascope/api/_generated/types/immutable_resource_error.py +22 -0
- mirascope/api/_generated/types/internal_server_error_body.py +49 -0
- mirascope/api/_generated/types/issue.py +38 -0
- mirascope/api/_generated/types/issue_tag.py +10 -0
- mirascope/api/_generated/types/not_found_error_body.py +22 -0
- mirascope/api/_generated/types/not_found_error_tag.py +5 -0
- mirascope/api/_generated/types/number_from_string.py +3 -0
- mirascope/api/_generated/types/permission_denied_error.py +22 -0
- mirascope/api/_generated/types/permission_denied_error_tag.py +5 -0
- mirascope/api/_generated/types/plan_limit_exceeded_error.py +32 -0
- mirascope/api/_generated/types/plan_limit_exceeded_error_tag.py +7 -0
- mirascope/api/_generated/types/pricing_unavailable_error.py +23 -0
- mirascope/api/_generated/types/property_key.py +7 -0
- mirascope/api/_generated/types/property_key_key.py +25 -0
- mirascope/api/_generated/types/property_key_key_tag.py +5 -0
- mirascope/api/_generated/types/rate_limit_error.py +31 -0
- mirascope/api/_generated/types/rate_limit_error_tag.py +5 -0
- mirascope/api/_generated/types/service_unavailable_error_body.py +24 -0
- mirascope/api/_generated/types/service_unavailable_error_tag.py +7 -0
- mirascope/api/_generated/types/stripe_error.py +20 -0
- mirascope/api/_generated/types/subscription_past_due_error.py +31 -0
- mirascope/api/_generated/types/subscription_past_due_error_tag.py +7 -0
- mirascope/api/_generated/types/unauthorized_error_body.py +21 -0
- mirascope/api/_generated/types/unauthorized_error_tag.py +5 -0
- mirascope/api/client.py +255 -0
- mirascope/api/settings.py +99 -0
- mirascope/llm/__init__.py +316 -0
- mirascope/llm/calls/__init__.py +17 -0
- mirascope/llm/calls/calls.py +348 -0
- mirascope/llm/calls/decorator.py +268 -0
- mirascope/llm/content/__init__.py +71 -0
- mirascope/llm/content/audio.py +173 -0
- mirascope/llm/content/document.py +94 -0
- mirascope/llm/content/image.py +206 -0
- mirascope/llm/content/text.py +47 -0
- mirascope/llm/content/thought.py +58 -0
- mirascope/llm/content/tool_call.py +69 -0
- mirascope/llm/content/tool_output.py +43 -0
- mirascope/llm/context/__init__.py +6 -0
- mirascope/llm/context/_utils.py +41 -0
- mirascope/llm/context/context.py +24 -0
- mirascope/llm/exceptions.py +360 -0
- mirascope/llm/formatting/__init__.py +39 -0
- mirascope/llm/formatting/format.py +291 -0
- mirascope/llm/formatting/from_call_args.py +30 -0
- mirascope/llm/formatting/output_parser.py +178 -0
- mirascope/llm/formatting/partial.py +131 -0
- mirascope/llm/formatting/primitives.py +192 -0
- mirascope/llm/formatting/types.py +83 -0
- mirascope/llm/mcp/__init__.py +5 -0
- mirascope/llm/mcp/mcp_client.py +130 -0
- mirascope/llm/messages/__init__.py +35 -0
- mirascope/llm/messages/_utils.py +34 -0
- mirascope/llm/messages/message.py +190 -0
- mirascope/llm/models/__init__.py +21 -0
- mirascope/llm/models/models.py +1339 -0
- mirascope/llm/models/params.py +72 -0
- mirascope/llm/models/thinking_config.py +61 -0
- mirascope/llm/prompts/__init__.py +34 -0
- mirascope/llm/prompts/_utils.py +31 -0
- mirascope/llm/prompts/decorator.py +215 -0
- mirascope/llm/prompts/prompts.py +484 -0
- mirascope/llm/prompts/protocols.py +65 -0
- mirascope/llm/providers/__init__.py +65 -0
- mirascope/llm/providers/anthropic/__init__.py +11 -0
- mirascope/llm/providers/anthropic/_utils/__init__.py +27 -0
- mirascope/llm/providers/anthropic/_utils/beta_decode.py +297 -0
- mirascope/llm/providers/anthropic/_utils/beta_encode.py +272 -0
- mirascope/llm/providers/anthropic/_utils/decode.py +326 -0
- mirascope/llm/providers/anthropic/_utils/encode.py +431 -0
- mirascope/llm/providers/anthropic/_utils/errors.py +46 -0
- mirascope/llm/providers/anthropic/beta_provider.py +338 -0
- mirascope/llm/providers/anthropic/model_id.py +23 -0
- mirascope/llm/providers/anthropic/model_info.py +87 -0
- mirascope/llm/providers/anthropic/provider.py +440 -0
- mirascope/llm/providers/base/__init__.py +14 -0
- mirascope/llm/providers/base/_utils.py +248 -0
- mirascope/llm/providers/base/base_provider.py +1463 -0
- mirascope/llm/providers/base/kwargs.py +12 -0
- mirascope/llm/providers/google/__init__.py +6 -0
- mirascope/llm/providers/google/_utils/__init__.py +17 -0
- mirascope/llm/providers/google/_utils/decode.py +357 -0
- mirascope/llm/providers/google/_utils/encode.py +418 -0
- mirascope/llm/providers/google/_utils/errors.py +50 -0
- mirascope/llm/providers/google/message.py +7 -0
- mirascope/llm/providers/google/model_id.py +22 -0
- mirascope/llm/providers/google/model_info.py +63 -0
- mirascope/llm/providers/google/provider.py +456 -0
- mirascope/llm/providers/mirascope/__init__.py +5 -0
- mirascope/llm/providers/mirascope/_utils.py +73 -0
- mirascope/llm/providers/mirascope/provider.py +313 -0
- mirascope/llm/providers/mlx/__init__.py +9 -0
- mirascope/llm/providers/mlx/_utils.py +141 -0
- mirascope/llm/providers/mlx/encoding/__init__.py +8 -0
- mirascope/llm/providers/mlx/encoding/base.py +69 -0
- mirascope/llm/providers/mlx/encoding/transformers.py +146 -0
- mirascope/llm/providers/mlx/mlx.py +242 -0
- mirascope/llm/providers/mlx/model_id.py +17 -0
- mirascope/llm/providers/mlx/provider.py +416 -0
- mirascope/llm/providers/model_id.py +16 -0
- mirascope/llm/providers/ollama/__init__.py +7 -0
- mirascope/llm/providers/ollama/provider.py +71 -0
- mirascope/llm/providers/openai/__init__.py +15 -0
- mirascope/llm/providers/openai/_utils/__init__.py +5 -0
- mirascope/llm/providers/openai/_utils/errors.py +46 -0
- mirascope/llm/providers/openai/completions/__init__.py +7 -0
- mirascope/llm/providers/openai/completions/_utils/__init__.py +18 -0
- mirascope/llm/providers/openai/completions/_utils/decode.py +252 -0
- mirascope/llm/providers/openai/completions/_utils/encode.py +390 -0
- mirascope/llm/providers/openai/completions/_utils/feature_info.py +50 -0
- mirascope/llm/providers/openai/completions/base_provider.py +522 -0
- mirascope/llm/providers/openai/completions/provider.py +28 -0
- mirascope/llm/providers/openai/model_id.py +31 -0
- mirascope/llm/providers/openai/model_info.py +303 -0
- mirascope/llm/providers/openai/provider.py +405 -0
- mirascope/llm/providers/openai/responses/__init__.py +5 -0
- mirascope/llm/providers/openai/responses/_utils/__init__.py +15 -0
- mirascope/llm/providers/openai/responses/_utils/decode.py +289 -0
- mirascope/llm/providers/openai/responses/_utils/encode.py +399 -0
- mirascope/llm/providers/openai/responses/provider.py +472 -0
- mirascope/llm/providers/openrouter/__init__.py +5 -0
- mirascope/llm/providers/openrouter/provider.py +67 -0
- mirascope/llm/providers/provider_id.py +26 -0
- mirascope/llm/providers/provider_registry.py +305 -0
- mirascope/llm/providers/together/__init__.py +7 -0
- mirascope/llm/providers/together/provider.py +40 -0
- mirascope/llm/responses/__init__.py +66 -0
- mirascope/llm/responses/_utils.py +146 -0
- mirascope/llm/responses/base_response.py +103 -0
- mirascope/llm/responses/base_stream_response.py +824 -0
- mirascope/llm/responses/finish_reason.py +28 -0
- mirascope/llm/responses/response.py +362 -0
- mirascope/llm/responses/root_response.py +248 -0
- mirascope/llm/responses/stream_response.py +577 -0
- mirascope/llm/responses/streams.py +363 -0
- mirascope/llm/responses/usage.py +139 -0
- mirascope/llm/tools/__init__.py +71 -0
- mirascope/llm/tools/_utils.py +34 -0
- mirascope/llm/tools/decorator.py +184 -0
- mirascope/llm/tools/protocols.py +96 -0
- mirascope/llm/tools/provider_tools.py +18 -0
- mirascope/llm/tools/tool_schema.py +321 -0
- mirascope/llm/tools/toolkit.py +178 -0
- mirascope/llm/tools/tools.py +263 -0
- mirascope/llm/tools/types.py +112 -0
- mirascope/llm/tools/web_search_tool.py +32 -0
- mirascope/llm/types/__init__.py +22 -0
- mirascope/llm/types/dataclass.py +9 -0
- mirascope/llm/types/jsonable.py +44 -0
- mirascope/llm/types/type_vars.py +19 -0
- mirascope/ops/__init__.py +129 -0
- mirascope/ops/_internal/__init__.py +5 -0
- mirascope/ops/_internal/closure.py +1172 -0
- mirascope/ops/_internal/configuration.py +177 -0
- mirascope/ops/_internal/context.py +76 -0
- mirascope/ops/_internal/exporters/__init__.py +26 -0
- mirascope/ops/_internal/exporters/exporters.py +362 -0
- mirascope/ops/_internal/exporters/processors.py +104 -0
- mirascope/ops/_internal/exporters/types.py +165 -0
- mirascope/ops/_internal/exporters/utils.py +66 -0
- mirascope/ops/_internal/instrumentation/__init__.py +28 -0
- mirascope/ops/_internal/instrumentation/llm/__init__.py +8 -0
- mirascope/ops/_internal/instrumentation/llm/common.py +500 -0
- mirascope/ops/_internal/instrumentation/llm/cost.py +190 -0
- mirascope/ops/_internal/instrumentation/llm/encode.py +238 -0
- mirascope/ops/_internal/instrumentation/llm/gen_ai_types/__init__.py +38 -0
- mirascope/ops/_internal/instrumentation/llm/gen_ai_types/gen_ai_input_messages.py +31 -0
- mirascope/ops/_internal/instrumentation/llm/gen_ai_types/gen_ai_output_messages.py +38 -0
- mirascope/ops/_internal/instrumentation/llm/gen_ai_types/gen_ai_system_instructions.py +18 -0
- mirascope/ops/_internal/instrumentation/llm/gen_ai_types/shared.py +100 -0
- mirascope/ops/_internal/instrumentation/llm/llm.py +161 -0
- mirascope/ops/_internal/instrumentation/llm/model.py +1777 -0
- mirascope/ops/_internal/instrumentation/llm/response.py +521 -0
- mirascope/ops/_internal/instrumentation/llm/serialize.py +324 -0
- mirascope/ops/_internal/instrumentation/providers/__init__.py +29 -0
- mirascope/ops/_internal/instrumentation/providers/anthropic.py +78 -0
- mirascope/ops/_internal/instrumentation/providers/base.py +179 -0
- mirascope/ops/_internal/instrumentation/providers/google_genai.py +85 -0
- mirascope/ops/_internal/instrumentation/providers/openai.py +82 -0
- mirascope/ops/_internal/propagation.py +198 -0
- mirascope/ops/_internal/protocols.py +133 -0
- mirascope/ops/_internal/session.py +139 -0
- mirascope/ops/_internal/spans.py +232 -0
- mirascope/ops/_internal/traced_calls.py +389 -0
- mirascope/ops/_internal/traced_functions.py +528 -0
- mirascope/ops/_internal/tracing.py +353 -0
- mirascope/ops/_internal/types.py +13 -0
- mirascope/ops/_internal/utils.py +131 -0
- mirascope/ops/_internal/versioned_calls.py +512 -0
- mirascope/ops/_internal/versioned_functions.py +357 -0
- mirascope/ops/_internal/versioning.py +303 -0
- mirascope/ops/exceptions.py +21 -0
- mirascope-2.1.1.dist-info/METADATA +231 -0
- mirascope-2.1.1.dist-info/RECORD +437 -0
- {mirascope-1.0.5.dist-info → mirascope-2.1.1.dist-info}/WHEEL +1 -1
- {mirascope-1.0.5.dist-info → mirascope-2.1.1.dist-info}/licenses/LICENSE +1 -1
- mirascope/beta/__init__.py +0 -0
- mirascope/beta/openai/__init__.py +0 -5
- mirascope/beta/openai/parse.py +0 -129
- mirascope/beta/rag/__init__.py +0 -24
- mirascope/beta/rag/base/__init__.py +0 -22
- mirascope/beta/rag/base/chunkers/__init__.py +0 -2
- mirascope/beta/rag/base/chunkers/base_chunker.py +0 -37
- mirascope/beta/rag/base/chunkers/text_chunker.py +0 -33
- mirascope/beta/rag/base/config.py +0 -8
- mirascope/beta/rag/base/document.py +0 -11
- mirascope/beta/rag/base/embedders.py +0 -35
- mirascope/beta/rag/base/embedding_params.py +0 -18
- mirascope/beta/rag/base/embedding_response.py +0 -30
- mirascope/beta/rag/base/query_results.py +0 -7
- mirascope/beta/rag/base/vectorstore_params.py +0 -18
- mirascope/beta/rag/base/vectorstores.py +0 -37
- mirascope/beta/rag/chroma/__init__.py +0 -11
- mirascope/beta/rag/chroma/types.py +0 -57
- mirascope/beta/rag/chroma/vectorstores.py +0 -97
- mirascope/beta/rag/cohere/__init__.py +0 -11
- mirascope/beta/rag/cohere/embedders.py +0 -87
- mirascope/beta/rag/cohere/embedding_params.py +0 -29
- mirascope/beta/rag/cohere/embedding_response.py +0 -29
- mirascope/beta/rag/cohere/py.typed +0 -0
- mirascope/beta/rag/openai/__init__.py +0 -11
- mirascope/beta/rag/openai/embedders.py +0 -144
- mirascope/beta/rag/openai/embedding_params.py +0 -18
- mirascope/beta/rag/openai/embedding_response.py +0 -14
- mirascope/beta/rag/openai/py.typed +0 -0
- mirascope/beta/rag/pinecone/__init__.py +0 -19
- mirascope/beta/rag/pinecone/types.py +0 -143
- mirascope/beta/rag/pinecone/vectorstores.py +0 -148
- mirascope/beta/rag/weaviate/__init__.py +0 -6
- mirascope/beta/rag/weaviate/types.py +0 -92
- mirascope/beta/rag/weaviate/vectorstores.py +0 -103
- mirascope/core/__init__.py +0 -55
- mirascope/core/anthropic/__init__.py +0 -21
- mirascope/core/anthropic/_call.py +0 -71
- mirascope/core/anthropic/_utils/__init__.py +0 -16
- mirascope/core/anthropic/_utils/_calculate_cost.py +0 -63
- mirascope/core/anthropic/_utils/_convert_message_params.py +0 -54
- mirascope/core/anthropic/_utils/_get_json_output.py +0 -34
- mirascope/core/anthropic/_utils/_handle_stream.py +0 -89
- mirascope/core/anthropic/_utils/_setup_call.py +0 -76
- mirascope/core/anthropic/call_params.py +0 -36
- mirascope/core/anthropic/call_response.py +0 -158
- mirascope/core/anthropic/call_response_chunk.py +0 -104
- mirascope/core/anthropic/dynamic_config.py +0 -26
- mirascope/core/anthropic/py.typed +0 -0
- mirascope/core/anthropic/stream.py +0 -140
- mirascope/core/anthropic/tool.py +0 -77
- mirascope/core/base/__init__.py +0 -40
- mirascope/core/base/_call_factory.py +0 -323
- mirascope/core/base/_create.py +0 -167
- mirascope/core/base/_extract.py +0 -139
- mirascope/core/base/_partial.py +0 -63
- mirascope/core/base/_utils/__init__.py +0 -64
- mirascope/core/base/_utils/_base_type.py +0 -17
- mirascope/core/base/_utils/_convert_base_model_to_base_tool.py +0 -45
- mirascope/core/base/_utils/_convert_base_type_to_base_tool.py +0 -24
- mirascope/core/base/_utils/_convert_function_to_base_tool.py +0 -126
- mirascope/core/base/_utils/_default_tool_docstring.py +0 -6
- mirascope/core/base/_utils/_extract_tool_return.py +0 -36
- mirascope/core/base/_utils/_format_template.py +0 -29
- mirascope/core/base/_utils/_get_audio_type.py +0 -18
- mirascope/core/base/_utils/_get_fn_args.py +0 -14
- mirascope/core/base/_utils/_get_image_type.py +0 -26
- mirascope/core/base/_utils/_get_metadata.py +0 -17
- mirascope/core/base/_utils/_get_possible_user_message_param.py +0 -21
- mirascope/core/base/_utils/_get_prompt_template.py +0 -25
- mirascope/core/base/_utils/_get_template_values.py +0 -52
- mirascope/core/base/_utils/_get_template_variables.py +0 -38
- mirascope/core/base/_utils/_json_mode_content.py +0 -15
- mirascope/core/base/_utils/_parse_content_template.py +0 -157
- mirascope/core/base/_utils/_parse_prompt_messages.py +0 -51
- mirascope/core/base/_utils/_protocols.py +0 -215
- mirascope/core/base/_utils/_setup_call.py +0 -64
- mirascope/core/base/_utils/_setup_extract_tool.py +0 -24
- mirascope/core/base/call_params.py +0 -6
- mirascope/core/base/call_response.py +0 -189
- mirascope/core/base/call_response_chunk.py +0 -91
- mirascope/core/base/dynamic_config.py +0 -55
- mirascope/core/base/message_param.py +0 -61
- mirascope/core/base/metadata.py +0 -13
- mirascope/core/base/prompt.py +0 -415
- mirascope/core/base/stream.py +0 -365
- mirascope/core/base/structured_stream.py +0 -251
- mirascope/core/base/tool.py +0 -126
- mirascope/core/base/toolkit.py +0 -146
- mirascope/core/cohere/__init__.py +0 -21
- mirascope/core/cohere/_call.py +0 -71
- mirascope/core/cohere/_utils/__init__.py +0 -16
- mirascope/core/cohere/_utils/_calculate_cost.py +0 -39
- mirascope/core/cohere/_utils/_convert_message_params.py +0 -31
- mirascope/core/cohere/_utils/_get_json_output.py +0 -31
- mirascope/core/cohere/_utils/_handle_stream.py +0 -33
- mirascope/core/cohere/_utils/_setup_call.py +0 -89
- mirascope/core/cohere/call_params.py +0 -57
- mirascope/core/cohere/call_response.py +0 -167
- mirascope/core/cohere/call_response_chunk.py +0 -101
- mirascope/core/cohere/dynamic_config.py +0 -24
- mirascope/core/cohere/py.typed +0 -0
- mirascope/core/cohere/stream.py +0 -113
- mirascope/core/cohere/tool.py +0 -92
- mirascope/core/gemini/__init__.py +0 -21
- mirascope/core/gemini/_call.py +0 -71
- mirascope/core/gemini/_utils/__init__.py +0 -16
- mirascope/core/gemini/_utils/_calculate_cost.py +0 -8
- mirascope/core/gemini/_utils/_convert_message_params.py +0 -74
- mirascope/core/gemini/_utils/_get_json_output.py +0 -33
- mirascope/core/gemini/_utils/_handle_stream.py +0 -33
- mirascope/core/gemini/_utils/_setup_call.py +0 -68
- mirascope/core/gemini/call_params.py +0 -28
- mirascope/core/gemini/call_response.py +0 -173
- mirascope/core/gemini/call_response_chunk.py +0 -85
- mirascope/core/gemini/dynamic_config.py +0 -26
- mirascope/core/gemini/stream.py +0 -121
- mirascope/core/gemini/tool.py +0 -104
- mirascope/core/groq/__init__.py +0 -21
- mirascope/core/groq/_call.py +0 -71
- mirascope/core/groq/_utils/__init__.py +0 -16
- mirascope/core/groq/_utils/_calculate_cost.py +0 -68
- mirascope/core/groq/_utils/_convert_message_params.py +0 -23
- mirascope/core/groq/_utils/_get_json_output.py +0 -27
- mirascope/core/groq/_utils/_handle_stream.py +0 -121
- mirascope/core/groq/_utils/_setup_call.py +0 -67
- mirascope/core/groq/call_params.py +0 -51
- mirascope/core/groq/call_response.py +0 -160
- mirascope/core/groq/call_response_chunk.py +0 -89
- mirascope/core/groq/dynamic_config.py +0 -26
- mirascope/core/groq/py.typed +0 -0
- mirascope/core/groq/stream.py +0 -136
- mirascope/core/groq/tool.py +0 -79
- mirascope/core/litellm/__init__.py +0 -6
- mirascope/core/litellm/_call.py +0 -73
- mirascope/core/litellm/_utils/__init__.py +0 -5
- mirascope/core/litellm/_utils/_setup_call.py +0 -46
- mirascope/core/litellm/py.typed +0 -0
- mirascope/core/mistral/__init__.py +0 -21
- mirascope/core/mistral/_call.py +0 -69
- mirascope/core/mistral/_utils/__init__.py +0 -16
- mirascope/core/mistral/_utils/_calculate_cost.py +0 -47
- mirascope/core/mistral/_utils/_convert_message_params.py +0 -23
- mirascope/core/mistral/_utils/_get_json_output.py +0 -28
- mirascope/core/mistral/_utils/_handle_stream.py +0 -121
- mirascope/core/mistral/_utils/_setup_call.py +0 -86
- mirascope/core/mistral/call_params.py +0 -36
- mirascope/core/mistral/call_response.py +0 -156
- mirascope/core/mistral/call_response_chunk.py +0 -84
- mirascope/core/mistral/dynamic_config.py +0 -24
- mirascope/core/mistral/py.typed +0 -0
- mirascope/core/mistral/stream.py +0 -117
- mirascope/core/mistral/tool.py +0 -77
- mirascope/core/openai/__init__.py +0 -21
- mirascope/core/openai/_call.py +0 -71
- mirascope/core/openai/_utils/__init__.py +0 -16
- mirascope/core/openai/_utils/_calculate_cost.py +0 -110
- mirascope/core/openai/_utils/_convert_message_params.py +0 -53
- mirascope/core/openai/_utils/_get_json_output.py +0 -27
- mirascope/core/openai/_utils/_handle_stream.py +0 -125
- mirascope/core/openai/_utils/_setup_call.py +0 -62
- mirascope/core/openai/call_params.py +0 -54
- mirascope/core/openai/call_response.py +0 -162
- mirascope/core/openai/call_response_chunk.py +0 -90
- mirascope/core/openai/dynamic_config.py +0 -26
- mirascope/core/openai/py.typed +0 -0
- mirascope/core/openai/stream.py +0 -148
- mirascope/core/openai/tool.py +0 -79
- mirascope/core/py.typed +0 -0
- mirascope/integrations/__init__.py +0 -20
- mirascope/integrations/_middleware_factory.py +0 -277
- mirascope/integrations/langfuse/__init__.py +0 -3
- mirascope/integrations/langfuse/_utils.py +0 -114
- mirascope/integrations/langfuse/_with_langfuse.py +0 -71
- mirascope/integrations/logfire/__init__.py +0 -3
- mirascope/integrations/logfire/_utils.py +0 -188
- mirascope/integrations/logfire/_with_logfire.py +0 -60
- mirascope/integrations/otel/__init__.py +0 -5
- mirascope/integrations/otel/_utils.py +0 -268
- mirascope/integrations/otel/_with_hyperdx.py +0 -61
- mirascope/integrations/otel/_with_otel.py +0 -60
- mirascope/integrations/tenacity.py +0 -50
- mirascope/py.typed +0 -0
- mirascope/v0/__init__.py +0 -43
- mirascope/v0/anthropic.py +0 -54
- mirascope/v0/base/__init__.py +0 -12
- mirascope/v0/base/calls.py +0 -118
- mirascope/v0/base/extractors.py +0 -122
- mirascope/v0/base/ops_utils.py +0 -207
- mirascope/v0/base/prompts.py +0 -48
- mirascope/v0/base/types.py +0 -14
- mirascope/v0/base/utils.py +0 -21
- mirascope/v0/openai.py +0 -54
- mirascope-1.0.5.dist-info/METADATA +0 -519
- mirascope-1.0.5.dist-info/RECORD +0 -198
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
"""Mirascope-specific serialization for span attributes."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
from collections.abc import Sequence
|
|
7
|
+
from typing import Any, Protocol
|
|
8
|
+
|
|
9
|
+
from opentelemetry.util.types import AttributeValue
|
|
10
|
+
|
|
11
|
+
from .....llm import (
|
|
12
|
+
AnyToolSchema,
|
|
13
|
+
AssistantMessage,
|
|
14
|
+
Audio,
|
|
15
|
+
Base64ImageSource,
|
|
16
|
+
Document,
|
|
17
|
+
Image,
|
|
18
|
+
Jsonable,
|
|
19
|
+
Message,
|
|
20
|
+
ProviderTool,
|
|
21
|
+
RootResponse,
|
|
22
|
+
SystemMessage,
|
|
23
|
+
Text,
|
|
24
|
+
Thought,
|
|
25
|
+
ToolCall,
|
|
26
|
+
ToolOutput,
|
|
27
|
+
Usage,
|
|
28
|
+
UserMessage,
|
|
29
|
+
)
|
|
30
|
+
from .....llm.content.document import Base64DocumentSource, TextDocumentSource
|
|
31
|
+
from ...utils import json_dumps
|
|
32
|
+
from .cost import calculate_cost_async, calculate_cost_sync
|
|
33
|
+
|
|
34
|
+
logger = logging.getLogger(__name__)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class SpanProtocol(Protocol):
|
|
38
|
+
"""Protocol for span objects that support setting attributes."""
|
|
39
|
+
|
|
40
|
+
def set(self, **attributes: AttributeValue) -> None:
|
|
41
|
+
"""Set attributes on the span."""
|
|
42
|
+
...
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def _serialize_content_part(
|
|
46
|
+
part: Text | ToolCall | Thought | Image | Audio | Document | ToolOutput[Jsonable],
|
|
47
|
+
) -> dict[str, Jsonable]:
|
|
48
|
+
"""Serialize a single content part to a dict matching the Mirascope dataclass structure."""
|
|
49
|
+
if isinstance(part, Text):
|
|
50
|
+
return {"type": "text", "text": part.text}
|
|
51
|
+
elif isinstance(part, ToolCall):
|
|
52
|
+
return {
|
|
53
|
+
"type": "tool_call",
|
|
54
|
+
"id": part.id,
|
|
55
|
+
"name": part.name,
|
|
56
|
+
"args": part.args,
|
|
57
|
+
}
|
|
58
|
+
elif isinstance(part, Thought):
|
|
59
|
+
return {"type": "thought", "thought": part.thought}
|
|
60
|
+
elif isinstance(part, ToolOutput):
|
|
61
|
+
return {
|
|
62
|
+
"type": "tool_output",
|
|
63
|
+
"id": part.id,
|
|
64
|
+
"name": part.name,
|
|
65
|
+
"result": part.result,
|
|
66
|
+
}
|
|
67
|
+
elif isinstance(part, Image):
|
|
68
|
+
if isinstance(part.source, Base64ImageSource):
|
|
69
|
+
return {
|
|
70
|
+
"type": "image",
|
|
71
|
+
"source": {
|
|
72
|
+
"type": "base64_image_source",
|
|
73
|
+
"mime_type": part.source.mime_type,
|
|
74
|
+
"data": part.source.data,
|
|
75
|
+
},
|
|
76
|
+
}
|
|
77
|
+
else: # URLImageSource
|
|
78
|
+
return {
|
|
79
|
+
"type": "image",
|
|
80
|
+
"source": {"type": "url_image_source", "url": part.source.url},
|
|
81
|
+
}
|
|
82
|
+
elif isinstance(part, Audio):
|
|
83
|
+
return {
|
|
84
|
+
"type": "audio",
|
|
85
|
+
"source": {
|
|
86
|
+
"type": "base64_audio_source",
|
|
87
|
+
"mime_type": part.source.mime_type,
|
|
88
|
+
"data": part.source.data,
|
|
89
|
+
},
|
|
90
|
+
}
|
|
91
|
+
elif isinstance(part, Document):
|
|
92
|
+
# Document has multiple source types - serialize based on actual type
|
|
93
|
+
if isinstance(part.source, Base64DocumentSource):
|
|
94
|
+
return {
|
|
95
|
+
"type": "document",
|
|
96
|
+
"source": {
|
|
97
|
+
"type": "base64_document_source",
|
|
98
|
+
"data": part.source.data,
|
|
99
|
+
"media_type": part.source.media_type,
|
|
100
|
+
},
|
|
101
|
+
}
|
|
102
|
+
elif isinstance(part.source, TextDocumentSource):
|
|
103
|
+
return {
|
|
104
|
+
"type": "document",
|
|
105
|
+
"source": {
|
|
106
|
+
"type": "text_document_source",
|
|
107
|
+
"data": part.source.data,
|
|
108
|
+
"media_type": part.source.media_type,
|
|
109
|
+
},
|
|
110
|
+
}
|
|
111
|
+
else: # URLDocumentSource
|
|
112
|
+
return {
|
|
113
|
+
"type": "document",
|
|
114
|
+
"source": {
|
|
115
|
+
"type": "url_document_source",
|
|
116
|
+
"url": part.source.url,
|
|
117
|
+
},
|
|
118
|
+
}
|
|
119
|
+
return {"type": "unknown"} # pragma: no cover
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def _serialize_message(message: Message) -> dict[str, Jsonable]:
|
|
123
|
+
"""Serialize a Message to a dict matching the Mirascope dataclass structure."""
|
|
124
|
+
if isinstance(message, SystemMessage):
|
|
125
|
+
return {
|
|
126
|
+
"role": "system",
|
|
127
|
+
"content": _serialize_content_part(message.content),
|
|
128
|
+
}
|
|
129
|
+
elif isinstance(message, UserMessage):
|
|
130
|
+
return {
|
|
131
|
+
"role": "user",
|
|
132
|
+
"content": [_serialize_content_part(p) for p in message.content],
|
|
133
|
+
"name": message.name,
|
|
134
|
+
}
|
|
135
|
+
elif isinstance(message, AssistantMessage):
|
|
136
|
+
return {
|
|
137
|
+
"role": "assistant",
|
|
138
|
+
"content": [_serialize_content_part(p) for p in message.content],
|
|
139
|
+
"name": message.name,
|
|
140
|
+
}
|
|
141
|
+
return {"role": "unknown"} # pragma: no cover
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def serialize_mirascope_messages(messages: Sequence[Message]) -> str:
|
|
145
|
+
"""Serialize input messages to JSON for span attributes."""
|
|
146
|
+
return json_dumps([_serialize_message(m) for m in messages])
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
def serialize_mirascope_content(
|
|
150
|
+
content: Sequence[Text | ToolCall | Thought],
|
|
151
|
+
) -> str:
|
|
152
|
+
"""Serialize response content to JSON for span attributes."""
|
|
153
|
+
return json_dumps([_serialize_content_part(p) for p in content])
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def serialize_mirascope_usage(usage: Usage | None) -> AttributeValue | None:
|
|
157
|
+
"""Serialize response usage to JSON for span attributes. Returns None if usage is None."""
|
|
158
|
+
if usage is None:
|
|
159
|
+
return None
|
|
160
|
+
return json_dumps(
|
|
161
|
+
{
|
|
162
|
+
"input_tokens": usage.input_tokens,
|
|
163
|
+
"output_tokens": usage.output_tokens,
|
|
164
|
+
"cache_read_tokens": usage.cache_read_tokens,
|
|
165
|
+
"cache_write_tokens": usage.cache_write_tokens,
|
|
166
|
+
"reasoning_tokens": usage.reasoning_tokens,
|
|
167
|
+
"total_tokens": usage.total_tokens,
|
|
168
|
+
}
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def _serialize_tool(tool: AnyToolSchema | ProviderTool) -> dict[str, Jsonable]:
|
|
173
|
+
if isinstance(tool, ProviderTool):
|
|
174
|
+
return {"name": tool.name, "type": "extension"}
|
|
175
|
+
result: dict[str, Jsonable] = {
|
|
176
|
+
"name": tool.name,
|
|
177
|
+
"description": tool.description,
|
|
178
|
+
"type": "function",
|
|
179
|
+
"parameters": tool.parameters.model_dump(by_alias=True, mode="json"),
|
|
180
|
+
}
|
|
181
|
+
if tool.strict is not None:
|
|
182
|
+
result["strict"] = tool.strict
|
|
183
|
+
return result
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
def serialize_tools(tools: Sequence[AnyToolSchema | ProviderTool]) -> str | None:
|
|
187
|
+
"""Serialize a sequence of Mirascope tools"""
|
|
188
|
+
if not tools:
|
|
189
|
+
return None
|
|
190
|
+
return json_dumps([_serialize_tool(t) for t in tools])
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
def serialize_mirascope_cost(
|
|
194
|
+
input_cost: float,
|
|
195
|
+
output_cost: float,
|
|
196
|
+
total_cost: float,
|
|
197
|
+
cache_read_cost: float | None = None,
|
|
198
|
+
cache_write_cost: float | None = None,
|
|
199
|
+
) -> str:
|
|
200
|
+
"""Serialize cost to JSON for span attributes.
|
|
201
|
+
|
|
202
|
+
All costs are in centicents (1 centicent = $0.0001).
|
|
203
|
+
Consumers can divide by 10,000 to get dollar amounts.
|
|
204
|
+
"""
|
|
205
|
+
return json_dumps(
|
|
206
|
+
{
|
|
207
|
+
"input_cost": input_cost,
|
|
208
|
+
"output_cost": output_cost,
|
|
209
|
+
"cache_read_cost": cache_read_cost,
|
|
210
|
+
"cache_write_cost": cache_write_cost,
|
|
211
|
+
"total_cost": total_cost,
|
|
212
|
+
}
|
|
213
|
+
)
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
def attach_mirascope_response(
|
|
217
|
+
span: SpanProtocol, response: RootResponse[Any, Any]
|
|
218
|
+
) -> None:
|
|
219
|
+
"""Attach Mirascope-specific response attributes to a span.
|
|
220
|
+
|
|
221
|
+
Sets the following attributes:
|
|
222
|
+
- mirascope.trace.output: Pretty-printed response
|
|
223
|
+
- mirascope.response.messages: Serialized input messages (excluding final assistant message)
|
|
224
|
+
- mirascope.response.content: Serialized response content
|
|
225
|
+
- mirascope.response.usage: Serialized usage (if available)
|
|
226
|
+
- mirascope.response.cost: Serialized cost (if available)
|
|
227
|
+
"""
|
|
228
|
+
span.set(
|
|
229
|
+
**{
|
|
230
|
+
"mirascope.response.provider_id": response.provider_id,
|
|
231
|
+
"mirascope.response.model_id": response.model_id,
|
|
232
|
+
"mirascope.trace.output": response.pretty(),
|
|
233
|
+
"mirascope.response.messages": serialize_mirascope_messages(
|
|
234
|
+
response.messages[:-1]
|
|
235
|
+
),
|
|
236
|
+
"mirascope.response.content": serialize_mirascope_content(response.content),
|
|
237
|
+
}
|
|
238
|
+
)
|
|
239
|
+
if (usage_json := serialize_mirascope_usage(response.usage)) is not None:
|
|
240
|
+
span.set(**{"mirascope.response.usage": usage_json})
|
|
241
|
+
logger.debug("Attached usage to span")
|
|
242
|
+
else:
|
|
243
|
+
logger.debug("No usage available, skipping cost calculation")
|
|
244
|
+
|
|
245
|
+
# Calculate and attach cost if usage is available
|
|
246
|
+
if response.usage is not None:
|
|
247
|
+
logger.debug("Attempting cost calculation (sync)")
|
|
248
|
+
cost = calculate_cost_sync(
|
|
249
|
+
response.provider_id, response.model_id, response.usage
|
|
250
|
+
)
|
|
251
|
+
if cost is not None:
|
|
252
|
+
span.set(
|
|
253
|
+
**{
|
|
254
|
+
"mirascope.response.cost": serialize_mirascope_cost(
|
|
255
|
+
input_cost=cost.input_cost_centicents,
|
|
256
|
+
output_cost=cost.output_cost_centicents,
|
|
257
|
+
total_cost=cost.total_cost_centicents,
|
|
258
|
+
cache_read_cost=cost.cache_read_cost_centicents,
|
|
259
|
+
cache_write_cost=cost.cache_write_cost_centicents,
|
|
260
|
+
)
|
|
261
|
+
}
|
|
262
|
+
)
|
|
263
|
+
logger.debug(
|
|
264
|
+
"Attached cost to span: total=%s centicents", cost.total_cost_centicents
|
|
265
|
+
)
|
|
266
|
+
else:
|
|
267
|
+
logger.debug("Cost calculation returned None, not attaching cost to span")
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
async def attach_mirascope_response_async(
|
|
271
|
+
span: SpanProtocol, response: RootResponse[Any, Any]
|
|
272
|
+
) -> None:
|
|
273
|
+
"""Attach Mirascope-specific response attributes to a span (async version).
|
|
274
|
+
|
|
275
|
+
Sets the following attributes:
|
|
276
|
+
- mirascope.trace.output: Pretty-printed response
|
|
277
|
+
- mirascope.response.messages: Serialized input messages (excluding final assistant message)
|
|
278
|
+
- mirascope.response.content: Serialized response content
|
|
279
|
+
- mirascope.response.usage: Serialized usage (if available)
|
|
280
|
+
- mirascope.response.cost: Serialized cost (if available)
|
|
281
|
+
"""
|
|
282
|
+
span.set(
|
|
283
|
+
**{
|
|
284
|
+
"mirascope.response.provider_id": response.provider_id,
|
|
285
|
+
"mirascope.response.model_id": response.model_id,
|
|
286
|
+
"mirascope.trace.output": response.pretty(),
|
|
287
|
+
"mirascope.response.messages": serialize_mirascope_messages(
|
|
288
|
+
response.messages[:-1]
|
|
289
|
+
),
|
|
290
|
+
"mirascope.response.content": serialize_mirascope_content(response.content),
|
|
291
|
+
}
|
|
292
|
+
)
|
|
293
|
+
if (usage_json := serialize_mirascope_usage(response.usage)) is not None:
|
|
294
|
+
span.set(**{"mirascope.response.usage": usage_json})
|
|
295
|
+
logger.debug("Attached usage to span (async)")
|
|
296
|
+
else:
|
|
297
|
+
logger.debug("No usage available, skipping cost calculation (async)")
|
|
298
|
+
|
|
299
|
+
# Calculate and attach cost if usage is available (async)
|
|
300
|
+
if response.usage is not None:
|
|
301
|
+
logger.debug("Attempting cost calculation (async)")
|
|
302
|
+
cost = await calculate_cost_async(
|
|
303
|
+
response.provider_id, response.model_id, response.usage
|
|
304
|
+
)
|
|
305
|
+
if cost is not None:
|
|
306
|
+
span.set(
|
|
307
|
+
**{
|
|
308
|
+
"mirascope.response.cost": serialize_mirascope_cost(
|
|
309
|
+
input_cost=cost.input_cost_centicents,
|
|
310
|
+
output_cost=cost.output_cost_centicents,
|
|
311
|
+
total_cost=cost.total_cost_centicents,
|
|
312
|
+
cache_read_cost=cost.cache_read_cost_centicents,
|
|
313
|
+
cache_write_cost=cost.cache_write_cost_centicents,
|
|
314
|
+
)
|
|
315
|
+
}
|
|
316
|
+
)
|
|
317
|
+
logger.debug(
|
|
318
|
+
"Attached cost to span (async): total=%s centicents",
|
|
319
|
+
cost.total_cost_centicents,
|
|
320
|
+
)
|
|
321
|
+
else:
|
|
322
|
+
logger.debug(
|
|
323
|
+
"Cost calculation returned None, not attaching cost to span (async)"
|
|
324
|
+
)
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"""Provider instrumentation modules."""
|
|
2
|
+
|
|
3
|
+
from .anthropic import (
|
|
4
|
+
instrument_anthropic,
|
|
5
|
+
is_anthropic_instrumented,
|
|
6
|
+
uninstrument_anthropic,
|
|
7
|
+
)
|
|
8
|
+
from .google_genai import (
|
|
9
|
+
instrument_google_genai,
|
|
10
|
+
is_google_genai_instrumented,
|
|
11
|
+
uninstrument_google_genai,
|
|
12
|
+
)
|
|
13
|
+
from .openai import (
|
|
14
|
+
instrument_openai,
|
|
15
|
+
is_openai_instrumented,
|
|
16
|
+
uninstrument_openai,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
__all__ = [
|
|
20
|
+
"instrument_anthropic",
|
|
21
|
+
"instrument_google_genai",
|
|
22
|
+
"instrument_openai",
|
|
23
|
+
"is_anthropic_instrumented",
|
|
24
|
+
"is_google_genai_instrumented",
|
|
25
|
+
"is_openai_instrumented",
|
|
26
|
+
"uninstrument_anthropic",
|
|
27
|
+
"uninstrument_google_genai",
|
|
28
|
+
"uninstrument_openai",
|
|
29
|
+
]
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"""OpenTelemetry instrumentation for Anthropic SDK."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import TYPE_CHECKING
|
|
6
|
+
|
|
7
|
+
from opentelemetry.instrumentation.anthropic import AnthropicInstrumentor
|
|
8
|
+
|
|
9
|
+
from .base import BaseInstrumentation, ContentCaptureMode
|
|
10
|
+
|
|
11
|
+
if TYPE_CHECKING:
|
|
12
|
+
from opentelemetry.trace import TracerProvider
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class AnthropicInstrumentation(BaseInstrumentation[AnthropicInstrumentor]):
|
|
16
|
+
"""Manages OpenTelemetry instrumentation lifecycle for the Anthropic SDK."""
|
|
17
|
+
|
|
18
|
+
def _create_instrumentor(self) -> AnthropicInstrumentor:
|
|
19
|
+
"""Create a new Anthropic instrumentor instance."""
|
|
20
|
+
return AnthropicInstrumentor()
|
|
21
|
+
|
|
22
|
+
def _configure_capture_content(self, capture_content: ContentCaptureMode) -> None:
|
|
23
|
+
"""Configure environment variables for Anthropic content capture."""
|
|
24
|
+
if capture_content == "enabled":
|
|
25
|
+
self._set_env_var("TRACELOOP_TRACE_CONTENT", "true")
|
|
26
|
+
elif capture_content == "disabled":
|
|
27
|
+
self._set_env_var("TRACELOOP_TRACE_CONTENT", "false")
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def instrument_anthropic(
|
|
31
|
+
*,
|
|
32
|
+
tracer_provider: TracerProvider | None = None,
|
|
33
|
+
capture_content: ContentCaptureMode = "default",
|
|
34
|
+
) -> None:
|
|
35
|
+
"""Enable OpenTelemetry instrumentation for the Anthropic Python SDK.
|
|
36
|
+
|
|
37
|
+
Uses the provided tracer_provider or the global OpenTelemetry tracer provider.
|
|
38
|
+
|
|
39
|
+
Args:
|
|
40
|
+
tracer_provider: Optional tracer provider to use. If not provided,
|
|
41
|
+
uses the global OpenTelemetry tracer provider.
|
|
42
|
+
capture_content: Controls whether to capture message content in spans.
|
|
43
|
+
- "enabled": Capture message content
|
|
44
|
+
- "disabled": Do not capture message content
|
|
45
|
+
- "default": Use the library's default behavior
|
|
46
|
+
|
|
47
|
+
Example:
|
|
48
|
+
|
|
49
|
+
Enable instrumentation with Mirascope Cloud:
|
|
50
|
+
```python
|
|
51
|
+
from mirascope import ops
|
|
52
|
+
|
|
53
|
+
ops.configure()
|
|
54
|
+
ops.instrument_anthropic()
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Enable instrumentation with content capture:
|
|
58
|
+
```python
|
|
59
|
+
from mirascope import ops
|
|
60
|
+
|
|
61
|
+
ops.configure()
|
|
62
|
+
ops.instrument_anthropic(capture_content="enabled")
|
|
63
|
+
```
|
|
64
|
+
"""
|
|
65
|
+
AnthropicInstrumentation().instrument(
|
|
66
|
+
tracer_provider=tracer_provider,
|
|
67
|
+
capture_content=capture_content,
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def uninstrument_anthropic() -> None:
|
|
72
|
+
"""Disable previously configured Anthropic instrumentation."""
|
|
73
|
+
AnthropicInstrumentation().uninstrument()
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def is_anthropic_instrumented() -> bool:
|
|
77
|
+
"""Return whether Anthropic instrumentation is currently active."""
|
|
78
|
+
return AnthropicInstrumentation().is_instrumented
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
"""Base class for OpenTelemetry SDK instrumentation."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import os
|
|
6
|
+
import threading
|
|
7
|
+
from abc import ABC, abstractmethod
|
|
8
|
+
from typing import TYPE_CHECKING, Generic, Literal, Protocol, TypeVar
|
|
9
|
+
|
|
10
|
+
if TYPE_CHECKING:
|
|
11
|
+
from opentelemetry.trace import TracerProvider
|
|
12
|
+
|
|
13
|
+
ContentCaptureMode = Literal["enabled", "disabled", "default"]
|
|
14
|
+
|
|
15
|
+
# OpenTelemetry semantic conventions environment variable
|
|
16
|
+
OTEL_SEMCONV_STABILITY_OPT_IN = "OTEL_SEMCONV_STABILITY_OPT_IN"
|
|
17
|
+
OTEL_SEMCONV_STABILITY_VALUE = "gen_ai_latest_experimental"
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class Instrumentor(Protocol):
|
|
21
|
+
"""Protocol for OpenTelemetry instrumentors."""
|
|
22
|
+
|
|
23
|
+
def instrument(self, *, tracer_provider: TracerProvider | None = None) -> None:
|
|
24
|
+
"""Enable instrumentation."""
|
|
25
|
+
...
|
|
26
|
+
|
|
27
|
+
def uninstrument(self) -> None:
|
|
28
|
+
"""Disable instrumentation."""
|
|
29
|
+
...
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
InstrumentorT = TypeVar("InstrumentorT", bound=Instrumentor)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class BaseInstrumentation(ABC, Generic[InstrumentorT]):
|
|
36
|
+
"""Base class for managing OpenTelemetry instrumentation lifecycle.
|
|
37
|
+
|
|
38
|
+
This class provides a thread-safe singleton pattern for SDK instrumentation.
|
|
39
|
+
Subclasses must implement `_create_instrumentor()` and `_configure_capture_content()`.
|
|
40
|
+
|
|
41
|
+
Each subclass gets its own `_instance` and `_instance_lock` via `__init_subclass__`,
|
|
42
|
+
ensuring that different providers can be initialized independently without blocking.
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
_instance: BaseInstrumentation[InstrumentorT] | None = None
|
|
46
|
+
_instance_lock: threading.Lock
|
|
47
|
+
_lock: threading.Lock
|
|
48
|
+
_instrumentor: InstrumentorT | None
|
|
49
|
+
_original_env: dict[str, str | None]
|
|
50
|
+
|
|
51
|
+
def __init_subclass__(cls, **kwargs: object) -> None:
|
|
52
|
+
"""Initialize subclass with its own singleton instance and lock."""
|
|
53
|
+
super().__init_subclass__(**kwargs)
|
|
54
|
+
cls._instance = None
|
|
55
|
+
cls._instance_lock = threading.Lock()
|
|
56
|
+
|
|
57
|
+
def __new__(cls) -> BaseInstrumentation[InstrumentorT]:
|
|
58
|
+
"""Create or return the singleton instance."""
|
|
59
|
+
if cls._instance is None:
|
|
60
|
+
with cls._instance_lock:
|
|
61
|
+
if cls._instance is None:
|
|
62
|
+
instance = super().__new__(cls)
|
|
63
|
+
instance._lock = threading.Lock()
|
|
64
|
+
instance._instrumentor = None
|
|
65
|
+
instance._original_env = {}
|
|
66
|
+
cls._instance = instance
|
|
67
|
+
return cls._instance
|
|
68
|
+
|
|
69
|
+
@property
|
|
70
|
+
def is_instrumented(self) -> bool:
|
|
71
|
+
"""Return whether instrumentation is currently active."""
|
|
72
|
+
return self._instrumentor is not None
|
|
73
|
+
|
|
74
|
+
@abstractmethod
|
|
75
|
+
def _create_instrumentor(self) -> InstrumentorT:
|
|
76
|
+
"""Create and return a new instrumentor instance.
|
|
77
|
+
|
|
78
|
+
Returns:
|
|
79
|
+
A new instance of the SDK-specific instrumentor.
|
|
80
|
+
"""
|
|
81
|
+
...
|
|
82
|
+
|
|
83
|
+
@abstractmethod
|
|
84
|
+
def _configure_capture_content(self, capture_content: ContentCaptureMode) -> None:
|
|
85
|
+
"""Configure environment variables for content capture.
|
|
86
|
+
|
|
87
|
+
Args:
|
|
88
|
+
capture_content: The capture content mode to configure.
|
|
89
|
+
"""
|
|
90
|
+
...
|
|
91
|
+
|
|
92
|
+
def instrument(
|
|
93
|
+
self,
|
|
94
|
+
*,
|
|
95
|
+
tracer_provider: TracerProvider | None = None,
|
|
96
|
+
capture_content: ContentCaptureMode = "default",
|
|
97
|
+
) -> None:
|
|
98
|
+
"""Enable OpenTelemetry instrumentation for the SDK.
|
|
99
|
+
|
|
100
|
+
Args:
|
|
101
|
+
tracer_provider: Optional tracer provider to use. If not provided,
|
|
102
|
+
uses the global OpenTelemetry tracer provider.
|
|
103
|
+
capture_content: Controls whether to capture message content in spans.
|
|
104
|
+
"""
|
|
105
|
+
with self._lock:
|
|
106
|
+
if self.is_instrumented:
|
|
107
|
+
return
|
|
108
|
+
|
|
109
|
+
self._set_env_var(
|
|
110
|
+
OTEL_SEMCONV_STABILITY_OPT_IN,
|
|
111
|
+
OTEL_SEMCONV_STABILITY_VALUE,
|
|
112
|
+
use_setdefault=True,
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
self._configure_capture_content(capture_content)
|
|
116
|
+
|
|
117
|
+
instrumentor = self._create_instrumentor()
|
|
118
|
+
try:
|
|
119
|
+
if tracer_provider is not None:
|
|
120
|
+
instrumentor.instrument(tracer_provider=tracer_provider)
|
|
121
|
+
else:
|
|
122
|
+
instrumentor.instrument()
|
|
123
|
+
except Exception:
|
|
124
|
+
self._restore_env_vars()
|
|
125
|
+
raise
|
|
126
|
+
|
|
127
|
+
self._instrumentor = instrumentor
|
|
128
|
+
|
|
129
|
+
def uninstrument(self) -> None:
|
|
130
|
+
"""Disable previously configured instrumentation."""
|
|
131
|
+
with self._lock:
|
|
132
|
+
if self._instrumentor is None:
|
|
133
|
+
return
|
|
134
|
+
|
|
135
|
+
self._instrumentor.uninstrument()
|
|
136
|
+
self._instrumentor = None
|
|
137
|
+
self._restore_env_vars()
|
|
138
|
+
|
|
139
|
+
def _set_env_var(
|
|
140
|
+
self, key: str, value: str, *, use_setdefault: bool = False
|
|
141
|
+
) -> None:
|
|
142
|
+
"""Set an environment variable and track the original value.
|
|
143
|
+
|
|
144
|
+
Args:
|
|
145
|
+
key: The environment variable name.
|
|
146
|
+
value: The value to set.
|
|
147
|
+
use_setdefault: If True, only set if not already present.
|
|
148
|
+
"""
|
|
149
|
+
if key not in self._original_env:
|
|
150
|
+
self._original_env[key] = os.environ.get(key)
|
|
151
|
+
|
|
152
|
+
if use_setdefault:
|
|
153
|
+
os.environ.setdefault(key, value)
|
|
154
|
+
else:
|
|
155
|
+
os.environ[key] = value
|
|
156
|
+
|
|
157
|
+
def _restore_env_vars(self) -> None:
|
|
158
|
+
"""Restore all environment variables to their original values."""
|
|
159
|
+
for key, value in self._original_env.items():
|
|
160
|
+
if value is None:
|
|
161
|
+
os.environ.pop(key, None)
|
|
162
|
+
else:
|
|
163
|
+
os.environ[key] = value
|
|
164
|
+
self._original_env.clear()
|
|
165
|
+
|
|
166
|
+
@classmethod
|
|
167
|
+
def reset_for_testing(cls) -> None:
|
|
168
|
+
"""Reset singleton instance for testing.
|
|
169
|
+
|
|
170
|
+
This properly cleans up the instrumentor and restores environment variables
|
|
171
|
+
before resetting the instance.
|
|
172
|
+
"""
|
|
173
|
+
with cls._instance_lock:
|
|
174
|
+
if cls._instance is not None:
|
|
175
|
+
if cls._instance._instrumentor is not None:
|
|
176
|
+
cls._instance._instrumentor.uninstrument()
|
|
177
|
+
cls._instance._instrumentor = None
|
|
178
|
+
cls._instance._restore_env_vars()
|
|
179
|
+
cls._instance = None
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"""OpenTelemetry instrumentation for Google GenAI SDK."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import TYPE_CHECKING
|
|
6
|
+
|
|
7
|
+
from opentelemetry.instrumentation.google_genai import GoogleGenAiSdkInstrumentor
|
|
8
|
+
|
|
9
|
+
from .base import BaseInstrumentation, ContentCaptureMode
|
|
10
|
+
|
|
11
|
+
if TYPE_CHECKING:
|
|
12
|
+
from opentelemetry.trace import TracerProvider
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class GoogleGenAIInstrumentation(BaseInstrumentation[GoogleGenAiSdkInstrumentor]):
|
|
16
|
+
"""Manages OpenTelemetry instrumentation lifecycle for the Google GenAI SDK."""
|
|
17
|
+
|
|
18
|
+
def _create_instrumentor(self) -> GoogleGenAiSdkInstrumentor:
|
|
19
|
+
"""Create a new Google GenAI instrumentor instance."""
|
|
20
|
+
return GoogleGenAiSdkInstrumentor()
|
|
21
|
+
|
|
22
|
+
def _configure_capture_content(self, capture_content: ContentCaptureMode) -> None:
|
|
23
|
+
"""Configure environment variables for Google GenAI content capture."""
|
|
24
|
+
# Google GenAI uses ContentCapturingMode enum instead of true/false.
|
|
25
|
+
# Valid values: NO_CONTENT, SPAN_ONLY, EVENT_ONLY, SPAN_AND_EVENT
|
|
26
|
+
if capture_content == "enabled":
|
|
27
|
+
self._set_env_var(
|
|
28
|
+
"OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT",
|
|
29
|
+
"SPAN_AND_EVENT",
|
|
30
|
+
)
|
|
31
|
+
elif capture_content == "disabled":
|
|
32
|
+
self._set_env_var(
|
|
33
|
+
"OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT", "NO_CONTENT"
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def instrument_google_genai(
|
|
38
|
+
*,
|
|
39
|
+
tracer_provider: TracerProvider | None = None,
|
|
40
|
+
capture_content: ContentCaptureMode = "default",
|
|
41
|
+
) -> None:
|
|
42
|
+
"""Enable OpenTelemetry instrumentation for the Google GenAI Python SDK.
|
|
43
|
+
|
|
44
|
+
Uses the provided tracer_provider or the global OpenTelemetry tracer provider.
|
|
45
|
+
|
|
46
|
+
Args:
|
|
47
|
+
tracer_provider: Optional tracer provider to use. If not provided,
|
|
48
|
+
uses the global OpenTelemetry tracer provider.
|
|
49
|
+
capture_content: Controls whether to capture message content in spans.
|
|
50
|
+
- "enabled": Capture message content
|
|
51
|
+
- "disabled": Do not capture message content
|
|
52
|
+
- "default": Use the library's default behavior
|
|
53
|
+
|
|
54
|
+
Example:
|
|
55
|
+
|
|
56
|
+
Enable instrumentation with Mirascope Cloud:
|
|
57
|
+
```python
|
|
58
|
+
from mirascope import ops
|
|
59
|
+
|
|
60
|
+
ops.configure()
|
|
61
|
+
ops.instrument_google_genai()
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Enable instrumentation with content capture:
|
|
65
|
+
```python
|
|
66
|
+
from mirascope import ops
|
|
67
|
+
|
|
68
|
+
ops.configure()
|
|
69
|
+
ops.instrument_google_genai(capture_content="enabled")
|
|
70
|
+
```
|
|
71
|
+
"""
|
|
72
|
+
GoogleGenAIInstrumentation().instrument(
|
|
73
|
+
tracer_provider=tracer_provider,
|
|
74
|
+
capture_content=capture_content,
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def uninstrument_google_genai() -> None:
|
|
79
|
+
"""Disable previously configured Google GenAI instrumentation."""
|
|
80
|
+
GoogleGenAIInstrumentation().uninstrument()
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def is_google_genai_instrumented() -> bool:
|
|
84
|
+
"""Return whether Google GenAI instrumentation is currently active."""
|
|
85
|
+
return GoogleGenAIInstrumentation().is_instrumented
|