mirascope 2.0.0__py3-none-any.whl → 2.0.0a1__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 +2 -11
- mirascope/graphs/__init__.py +22 -0
- mirascope/graphs/finite_state_machine.py +625 -0
- mirascope/llm/__init__.py +15 -96
- mirascope/llm/agents/__init__.py +15 -0
- mirascope/llm/agents/agent.py +97 -0
- mirascope/llm/agents/agent_template.py +45 -0
- mirascope/llm/agents/decorator.py +176 -0
- mirascope/llm/calls/__init__.py +1 -2
- mirascope/llm/calls/base_call.py +33 -0
- mirascope/llm/calls/calls.py +58 -84
- mirascope/llm/calls/decorator.py +120 -140
- mirascope/llm/clients/__init__.py +34 -0
- mirascope/llm/clients/_missing_import_stubs.py +47 -0
- mirascope/llm/clients/anthropic/__init__.py +25 -0
- mirascope/llm/{providers/openai/completions → clients/anthropic}/_utils/__init__.py +0 -2
- mirascope/llm/{providers → clients}/anthropic/_utils/decode.py +22 -66
- mirascope/llm/clients/anthropic/_utils/encode.py +243 -0
- mirascope/llm/clients/anthropic/clients.py +819 -0
- mirascope/llm/clients/anthropic/model_ids.py +8 -0
- mirascope/llm/{providers → clients}/base/__init__.py +5 -4
- mirascope/llm/{providers → clients}/base/_utils.py +17 -78
- mirascope/llm/{providers/base/base_provider.py → clients/base/client.py} +145 -468
- mirascope/llm/{models → clients/base}/params.py +37 -16
- mirascope/llm/clients/google/__init__.py +20 -0
- mirascope/llm/{providers/openai/responses → clients/google}/_utils/__init__.py +0 -2
- mirascope/llm/{providers → clients}/google/_utils/decode.py +22 -98
- mirascope/llm/{providers → clients}/google/_utils/encode.py +46 -168
- mirascope/llm/clients/google/clients.py +853 -0
- mirascope/llm/clients/google/model_ids.py +15 -0
- mirascope/llm/clients/openai/__init__.py +25 -0
- mirascope/llm/clients/openai/completions/__init__.py +28 -0
- mirascope/llm/{providers/google → clients/openai/completions}/_utils/__init__.py +0 -4
- mirascope/llm/{providers → clients}/openai/completions/_utils/decode.py +9 -74
- mirascope/llm/{providers → clients}/openai/completions/_utils/encode.py +52 -70
- mirascope/llm/clients/openai/completions/_utils/model_features.py +81 -0
- mirascope/llm/clients/openai/completions/clients.py +833 -0
- mirascope/llm/clients/openai/completions/model_ids.py +8 -0
- mirascope/llm/clients/openai/responses/__init__.py +26 -0
- mirascope/llm/clients/openai/responses/_utils/__init__.py +13 -0
- mirascope/llm/{providers → clients}/openai/responses/_utils/decode.py +14 -80
- mirascope/llm/{providers → clients}/openai/responses/_utils/encode.py +41 -92
- mirascope/llm/clients/openai/responses/_utils/model_features.py +87 -0
- mirascope/llm/clients/openai/responses/clients.py +832 -0
- mirascope/llm/clients/openai/responses/model_ids.py +8 -0
- mirascope/llm/clients/openai/shared/__init__.py +7 -0
- mirascope/llm/clients/openai/shared/_utils.py +55 -0
- mirascope/llm/clients/providers.py +175 -0
- mirascope/llm/content/__init__.py +2 -3
- mirascope/llm/content/tool_call.py +0 -6
- mirascope/llm/content/tool_output.py +5 -22
- mirascope/llm/context/_utils.py +6 -19
- mirascope/llm/exceptions.py +43 -298
- mirascope/llm/formatting/__init__.py +2 -19
- mirascope/llm/formatting/_utils.py +74 -0
- mirascope/llm/formatting/format.py +30 -219
- mirascope/llm/formatting/from_call_args.py +2 -2
- mirascope/llm/formatting/partial.py +7 -80
- mirascope/llm/formatting/types.py +64 -21
- mirascope/llm/mcp/__init__.py +2 -2
- mirascope/llm/mcp/client.py +118 -0
- mirascope/llm/messages/__init__.py +0 -3
- mirascope/llm/messages/message.py +5 -13
- mirascope/llm/models/__init__.py +2 -7
- mirascope/llm/models/models.py +139 -315
- mirascope/llm/prompts/__init__.py +12 -13
- mirascope/llm/prompts/_utils.py +43 -14
- mirascope/llm/prompts/decorator.py +204 -144
- mirascope/llm/prompts/protocols.py +59 -25
- mirascope/llm/responses/__init__.py +1 -9
- mirascope/llm/responses/_utils.py +12 -102
- mirascope/llm/responses/base_response.py +6 -18
- mirascope/llm/responses/base_stream_response.py +50 -173
- mirascope/llm/responses/finish_reason.py +0 -1
- mirascope/llm/responses/response.py +13 -34
- mirascope/llm/responses/root_response.py +29 -100
- mirascope/llm/responses/stream_response.py +31 -40
- mirascope/llm/tools/__init__.py +2 -9
- mirascope/llm/tools/_utils.py +3 -12
- mirascope/llm/tools/decorator.py +16 -25
- mirascope/llm/tools/protocols.py +4 -4
- mirascope/llm/tools/tool_schema.py +19 -87
- mirascope/llm/tools/toolkit.py +27 -35
- mirascope/llm/tools/tools.py +41 -135
- {mirascope-2.0.0.dist-info → mirascope-2.0.0a1.dist-info}/METADATA +13 -90
- mirascope-2.0.0a1.dist-info/RECORD +102 -0
- {mirascope-2.0.0.dist-info → mirascope-2.0.0a1.dist-info}/WHEEL +1 -1
- {mirascope-2.0.0.dist-info → mirascope-2.0.0a1.dist-info}/licenses/LICENSE +1 -1
- mirascope/_stubs.py +0 -363
- mirascope/api/__init__.py +0 -14
- mirascope/api/_generated/README.md +0 -207
- mirascope/api/_generated/__init__.py +0 -440
- mirascope/api/_generated/annotations/__init__.py +0 -33
- mirascope/api/_generated/annotations/client.py +0 -506
- mirascope/api/_generated/annotations/raw_client.py +0 -1414
- mirascope/api/_generated/annotations/types/__init__.py +0 -31
- mirascope/api/_generated/annotations/types/annotations_create_request_label.py +0 -5
- mirascope/api/_generated/annotations/types/annotations_create_response.py +0 -48
- mirascope/api/_generated/annotations/types/annotations_create_response_label.py +0 -5
- mirascope/api/_generated/annotations/types/annotations_get_response.py +0 -48
- mirascope/api/_generated/annotations/types/annotations_get_response_label.py +0 -5
- mirascope/api/_generated/annotations/types/annotations_list_request_label.py +0 -5
- mirascope/api/_generated/annotations/types/annotations_list_response.py +0 -21
- mirascope/api/_generated/annotations/types/annotations_list_response_annotations_item.py +0 -50
- mirascope/api/_generated/annotations/types/annotations_list_response_annotations_item_label.py +0 -5
- mirascope/api/_generated/annotations/types/annotations_update_request_label.py +0 -5
- mirascope/api/_generated/annotations/types/annotations_update_response.py +0 -48
- mirascope/api/_generated/annotations/types/annotations_update_response_label.py +0 -5
- mirascope/api/_generated/api_keys/__init__.py +0 -17
- mirascope/api/_generated/api_keys/client.py +0 -530
- mirascope/api/_generated/api_keys/raw_client.py +0 -1236
- mirascope/api/_generated/api_keys/types/__init__.py +0 -15
- mirascope/api/_generated/api_keys/types/api_keys_create_response.py +0 -28
- mirascope/api/_generated/api_keys/types/api_keys_get_response.py +0 -27
- mirascope/api/_generated/api_keys/types/api_keys_list_all_for_org_response_item.py +0 -40
- mirascope/api/_generated/api_keys/types/api_keys_list_response_item.py +0 -27
- mirascope/api/_generated/client.py +0 -211
- mirascope/api/_generated/core/__init__.py +0 -52
- mirascope/api/_generated/core/api_error.py +0 -23
- mirascope/api/_generated/core/client_wrapper.py +0 -46
- mirascope/api/_generated/core/datetime_utils.py +0 -28
- mirascope/api/_generated/core/file.py +0 -67
- mirascope/api/_generated/core/force_multipart.py +0 -16
- mirascope/api/_generated/core/http_client.py +0 -543
- mirascope/api/_generated/core/http_response.py +0 -55
- mirascope/api/_generated/core/jsonable_encoder.py +0 -100
- mirascope/api/_generated/core/pydantic_utilities.py +0 -255
- mirascope/api/_generated/core/query_encoder.py +0 -58
- mirascope/api/_generated/core/remove_none_from_dict.py +0 -11
- mirascope/api/_generated/core/request_options.py +0 -35
- mirascope/api/_generated/core/serialization.py +0 -276
- mirascope/api/_generated/docs/__init__.py +0 -4
- mirascope/api/_generated/docs/client.py +0 -91
- mirascope/api/_generated/docs/raw_client.py +0 -178
- mirascope/api/_generated/environment.py +0 -9
- mirascope/api/_generated/environments/__init__.py +0 -23
- mirascope/api/_generated/environments/client.py +0 -649
- mirascope/api/_generated/environments/raw_client.py +0 -1567
- mirascope/api/_generated/environments/types/__init__.py +0 -25
- mirascope/api/_generated/environments/types/environments_create_response.py +0 -24
- mirascope/api/_generated/environments/types/environments_get_analytics_response.py +0 -60
- mirascope/api/_generated/environments/types/environments_get_analytics_response_top_functions_item.py +0 -24
- mirascope/api/_generated/environments/types/environments_get_analytics_response_top_models_item.py +0 -22
- mirascope/api/_generated/environments/types/environments_get_response.py +0 -24
- mirascope/api/_generated/environments/types/environments_list_response_item.py +0 -24
- mirascope/api/_generated/environments/types/environments_update_response.py +0 -24
- mirascope/api/_generated/errors/__init__.py +0 -25
- mirascope/api/_generated/errors/bad_request_error.py +0 -14
- mirascope/api/_generated/errors/conflict_error.py +0 -14
- mirascope/api/_generated/errors/forbidden_error.py +0 -11
- mirascope/api/_generated/errors/internal_server_error.py +0 -10
- mirascope/api/_generated/errors/not_found_error.py +0 -11
- mirascope/api/_generated/errors/payment_required_error.py +0 -15
- mirascope/api/_generated/errors/service_unavailable_error.py +0 -14
- mirascope/api/_generated/errors/too_many_requests_error.py +0 -15
- mirascope/api/_generated/errors/unauthorized_error.py +0 -11
- mirascope/api/_generated/functions/__init__.py +0 -39
- mirascope/api/_generated/functions/client.py +0 -647
- mirascope/api/_generated/functions/raw_client.py +0 -1890
- mirascope/api/_generated/functions/types/__init__.py +0 -53
- mirascope/api/_generated/functions/types/functions_create_request_dependencies_value.py +0 -20
- mirascope/api/_generated/functions/types/functions_create_response.py +0 -37
- mirascope/api/_generated/functions/types/functions_create_response_dependencies_value.py +0 -20
- mirascope/api/_generated/functions/types/functions_find_by_hash_response.py +0 -39
- mirascope/api/_generated/functions/types/functions_find_by_hash_response_dependencies_value.py +0 -20
- mirascope/api/_generated/functions/types/functions_get_by_env_response.py +0 -53
- mirascope/api/_generated/functions/types/functions_get_by_env_response_dependencies_value.py +0 -22
- mirascope/api/_generated/functions/types/functions_get_response.py +0 -37
- mirascope/api/_generated/functions/types/functions_get_response_dependencies_value.py +0 -20
- mirascope/api/_generated/functions/types/functions_list_by_env_response.py +0 -25
- mirascope/api/_generated/functions/types/functions_list_by_env_response_functions_item.py +0 -56
- mirascope/api/_generated/functions/types/functions_list_by_env_response_functions_item_dependencies_value.py +0 -22
- mirascope/api/_generated/functions/types/functions_list_response.py +0 -21
- mirascope/api/_generated/functions/types/functions_list_response_functions_item.py +0 -41
- mirascope/api/_generated/functions/types/functions_list_response_functions_item_dependencies_value.py +0 -20
- mirascope/api/_generated/health/__init__.py +0 -7
- mirascope/api/_generated/health/client.py +0 -92
- mirascope/api/_generated/health/raw_client.py +0 -175
- mirascope/api/_generated/health/types/__init__.py +0 -8
- mirascope/api/_generated/health/types/health_check_response.py +0 -22
- mirascope/api/_generated/health/types/health_check_response_status.py +0 -5
- mirascope/api/_generated/organization_invitations/__init__.py +0 -33
- mirascope/api/_generated/organization_invitations/client.py +0 -546
- mirascope/api/_generated/organization_invitations/raw_client.py +0 -1519
- mirascope/api/_generated/organization_invitations/types/__init__.py +0 -53
- mirascope/api/_generated/organization_invitations/types/organization_invitations_accept_response.py +0 -34
- mirascope/api/_generated/organization_invitations/types/organization_invitations_accept_response_role.py +0 -7
- mirascope/api/_generated/organization_invitations/types/organization_invitations_create_request_role.py +0 -7
- mirascope/api/_generated/organization_invitations/types/organization_invitations_create_response.py +0 -48
- mirascope/api/_generated/organization_invitations/types/organization_invitations_create_response_role.py +0 -7
- mirascope/api/_generated/organization_invitations/types/organization_invitations_create_response_status.py +0 -7
- mirascope/api/_generated/organization_invitations/types/organization_invitations_get_response.py +0 -48
- mirascope/api/_generated/organization_invitations/types/organization_invitations_get_response_role.py +0 -7
- mirascope/api/_generated/organization_invitations/types/organization_invitations_get_response_status.py +0 -7
- mirascope/api/_generated/organization_invitations/types/organization_invitations_list_response_item.py +0 -48
- mirascope/api/_generated/organization_invitations/types/organization_invitations_list_response_item_role.py +0 -7
- mirascope/api/_generated/organization_invitations/types/organization_invitations_list_response_item_status.py +0 -7
- mirascope/api/_generated/organization_memberships/__init__.py +0 -19
- mirascope/api/_generated/organization_memberships/client.py +0 -302
- mirascope/api/_generated/organization_memberships/raw_client.py +0 -736
- mirascope/api/_generated/organization_memberships/types/__init__.py +0 -27
- mirascope/api/_generated/organization_memberships/types/organization_memberships_list_response_item.py +0 -33
- mirascope/api/_generated/organization_memberships/types/organization_memberships_list_response_item_role.py +0 -7
- mirascope/api/_generated/organization_memberships/types/organization_memberships_update_request_role.py +0 -7
- mirascope/api/_generated/organization_memberships/types/organization_memberships_update_response.py +0 -31
- mirascope/api/_generated/organization_memberships/types/organization_memberships_update_response_role.py +0 -7
- mirascope/api/_generated/organizations/__init__.py +0 -51
- mirascope/api/_generated/organizations/client.py +0 -869
- mirascope/api/_generated/organizations/raw_client.py +0 -2593
- mirascope/api/_generated/organizations/types/__init__.py +0 -71
- mirascope/api/_generated/organizations/types/organizations_create_payment_intent_response.py +0 -24
- mirascope/api/_generated/organizations/types/organizations_create_response.py +0 -26
- mirascope/api/_generated/organizations/types/organizations_create_response_role.py +0 -5
- mirascope/api/_generated/organizations/types/organizations_get_response.py +0 -26
- mirascope/api/_generated/organizations/types/organizations_get_response_role.py +0 -5
- mirascope/api/_generated/organizations/types/organizations_list_response_item.py +0 -26
- mirascope/api/_generated/organizations/types/organizations_list_response_item_role.py +0 -5
- mirascope/api/_generated/organizations/types/organizations_preview_subscription_change_request_target_plan.py +0 -7
- mirascope/api/_generated/organizations/types/organizations_preview_subscription_change_response.py +0 -47
- mirascope/api/_generated/organizations/types/organizations_preview_subscription_change_response_validation_errors_item.py +0 -33
- mirascope/api/_generated/organizations/types/organizations_preview_subscription_change_response_validation_errors_item_resource.py +0 -7
- mirascope/api/_generated/organizations/types/organizations_router_balance_response.py +0 -24
- mirascope/api/_generated/organizations/types/organizations_subscription_response.py +0 -53
- mirascope/api/_generated/organizations/types/organizations_subscription_response_current_plan.py +0 -7
- mirascope/api/_generated/organizations/types/organizations_subscription_response_payment_method.py +0 -26
- mirascope/api/_generated/organizations/types/organizations_subscription_response_scheduled_change.py +0 -34
- mirascope/api/_generated/organizations/types/organizations_subscription_response_scheduled_change_target_plan.py +0 -7
- mirascope/api/_generated/organizations/types/organizations_update_response.py +0 -26
- mirascope/api/_generated/organizations/types/organizations_update_response_role.py +0 -5
- mirascope/api/_generated/organizations/types/organizations_update_subscription_request_target_plan.py +0 -7
- mirascope/api/_generated/organizations/types/organizations_update_subscription_response.py +0 -35
- mirascope/api/_generated/project_memberships/__init__.py +0 -25
- mirascope/api/_generated/project_memberships/client.py +0 -437
- mirascope/api/_generated/project_memberships/raw_client.py +0 -1039
- mirascope/api/_generated/project_memberships/types/__init__.py +0 -29
- mirascope/api/_generated/project_memberships/types/project_memberships_create_request_role.py +0 -7
- mirascope/api/_generated/project_memberships/types/project_memberships_create_response.py +0 -35
- mirascope/api/_generated/project_memberships/types/project_memberships_create_response_role.py +0 -7
- mirascope/api/_generated/project_memberships/types/project_memberships_list_response_item.py +0 -33
- mirascope/api/_generated/project_memberships/types/project_memberships_list_response_item_role.py +0 -7
- mirascope/api/_generated/project_memberships/types/project_memberships_update_request_role.py +0 -7
- mirascope/api/_generated/project_memberships/types/project_memberships_update_response.py +0 -35
- mirascope/api/_generated/project_memberships/types/project_memberships_update_response_role.py +0 -7
- mirascope/api/_generated/projects/__init__.py +0 -7
- mirascope/api/_generated/projects/client.py +0 -428
- mirascope/api/_generated/projects/raw_client.py +0 -1302
- mirascope/api/_generated/projects/types/__init__.py +0 -10
- mirascope/api/_generated/projects/types/projects_create_response.py +0 -25
- mirascope/api/_generated/projects/types/projects_get_response.py +0 -25
- mirascope/api/_generated/projects/types/projects_list_response_item.py +0 -25
- mirascope/api/_generated/projects/types/projects_update_response.py +0 -25
- mirascope/api/_generated/reference.md +0 -4915
- mirascope/api/_generated/tags/__init__.py +0 -19
- mirascope/api/_generated/tags/client.py +0 -504
- mirascope/api/_generated/tags/raw_client.py +0 -1288
- mirascope/api/_generated/tags/types/__init__.py +0 -17
- mirascope/api/_generated/tags/types/tags_create_response.py +0 -41
- mirascope/api/_generated/tags/types/tags_get_response.py +0 -41
- mirascope/api/_generated/tags/types/tags_list_response.py +0 -23
- mirascope/api/_generated/tags/types/tags_list_response_tags_item.py +0 -41
- mirascope/api/_generated/tags/types/tags_update_response.py +0 -41
- mirascope/api/_generated/token_cost/__init__.py +0 -7
- mirascope/api/_generated/token_cost/client.py +0 -160
- mirascope/api/_generated/token_cost/raw_client.py +0 -264
- mirascope/api/_generated/token_cost/types/__init__.py +0 -8
- mirascope/api/_generated/token_cost/types/token_cost_calculate_request_usage.py +0 -54
- mirascope/api/_generated/token_cost/types/token_cost_calculate_response.py +0 -52
- mirascope/api/_generated/traces/__init__.py +0 -97
- mirascope/api/_generated/traces/client.py +0 -1103
- mirascope/api/_generated/traces/raw_client.py +0 -2322
- mirascope/api/_generated/traces/types/__init__.py +0 -155
- mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item.py +0 -29
- mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_resource.py +0 -27
- mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_resource_attributes_item.py +0 -23
- mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_resource_attributes_item_value.py +0 -38
- mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_resource_attributes_item_value_array_value.py +0 -19
- mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_resource_attributes_item_value_kvlist_value.py +0 -22
- mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_resource_attributes_item_value_kvlist_value_values_item.py +0 -20
- mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_scope_spans_item.py +0 -29
- mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_scope_spans_item_scope.py +0 -31
- mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_scope_spans_item_scope_attributes_item.py +0 -23
- mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_scope_spans_item_scope_attributes_item_value.py +0 -38
- mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_scope_spans_item_scope_attributes_item_value_array_value.py +0 -19
- mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_scope_spans_item_scope_attributes_item_value_kvlist_value.py +0 -22
- mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_scope_spans_item_scope_attributes_item_value_kvlist_value_values_item.py +0 -22
- mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_scope_spans_item_spans_item.py +0 -48
- mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_scope_spans_item_spans_item_attributes_item.py +0 -23
- mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_scope_spans_item_spans_item_attributes_item_value.py +0 -38
- mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_scope_spans_item_spans_item_attributes_item_value_array_value.py +0 -19
- mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_scope_spans_item_spans_item_attributes_item_value_kvlist_value.py +0 -24
- mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_scope_spans_item_spans_item_attributes_item_value_kvlist_value_values_item.py +0 -22
- mirascope/api/_generated/traces/types/traces_create_request_resource_spans_item_scope_spans_item_spans_item_status.py +0 -20
- mirascope/api/_generated/traces/types/traces_create_response.py +0 -24
- mirascope/api/_generated/traces/types/traces_create_response_partial_success.py +0 -22
- mirascope/api/_generated/traces/types/traces_get_analytics_summary_response.py +0 -60
- mirascope/api/_generated/traces/types/traces_get_analytics_summary_response_top_functions_item.py +0 -24
- mirascope/api/_generated/traces/types/traces_get_analytics_summary_response_top_models_item.py +0 -22
- mirascope/api/_generated/traces/types/traces_get_trace_detail_by_env_response.py +0 -33
- mirascope/api/_generated/traces/types/traces_get_trace_detail_by_env_response_spans_item.py +0 -88
- mirascope/api/_generated/traces/types/traces_get_trace_detail_response.py +0 -33
- mirascope/api/_generated/traces/types/traces_get_trace_detail_response_spans_item.py +0 -88
- mirascope/api/_generated/traces/types/traces_list_by_function_hash_response.py +0 -25
- mirascope/api/_generated/traces/types/traces_list_by_function_hash_response_traces_item.py +0 -44
- mirascope/api/_generated/traces/types/traces_search_by_env_request_attribute_filters_item.py +0 -26
- mirascope/api/_generated/traces/types/traces_search_by_env_request_attribute_filters_item_operator.py +0 -7
- mirascope/api/_generated/traces/types/traces_search_by_env_request_sort_by.py +0 -7
- mirascope/api/_generated/traces/types/traces_search_by_env_request_sort_order.py +0 -7
- mirascope/api/_generated/traces/types/traces_search_by_env_response.py +0 -26
- mirascope/api/_generated/traces/types/traces_search_by_env_response_spans_item.py +0 -50
- mirascope/api/_generated/traces/types/traces_search_request_attribute_filters_item.py +0 -26
- mirascope/api/_generated/traces/types/traces_search_request_attribute_filters_item_operator.py +0 -7
- mirascope/api/_generated/traces/types/traces_search_request_sort_by.py +0 -7
- mirascope/api/_generated/traces/types/traces_search_request_sort_order.py +0 -5
- mirascope/api/_generated/traces/types/traces_search_response.py +0 -26
- mirascope/api/_generated/traces/types/traces_search_response_spans_item.py +0 -50
- mirascope/api/_generated/types/__init__.py +0 -85
- mirascope/api/_generated/types/already_exists_error.py +0 -22
- mirascope/api/_generated/types/already_exists_error_tag.py +0 -5
- mirascope/api/_generated/types/bad_request_error_body.py +0 -50
- mirascope/api/_generated/types/click_house_error.py +0 -22
- mirascope/api/_generated/types/database_error.py +0 -22
- mirascope/api/_generated/types/database_error_tag.py +0 -5
- mirascope/api/_generated/types/date.py +0 -3
- mirascope/api/_generated/types/http_api_decode_error.py +0 -27
- mirascope/api/_generated/types/http_api_decode_error_tag.py +0 -5
- mirascope/api/_generated/types/immutable_resource_error.py +0 -22
- mirascope/api/_generated/types/internal_server_error_body.py +0 -49
- mirascope/api/_generated/types/issue.py +0 -38
- mirascope/api/_generated/types/issue_tag.py +0 -10
- mirascope/api/_generated/types/not_found_error_body.py +0 -22
- mirascope/api/_generated/types/not_found_error_tag.py +0 -5
- mirascope/api/_generated/types/number_from_string.py +0 -3
- mirascope/api/_generated/types/permission_denied_error.py +0 -22
- mirascope/api/_generated/types/permission_denied_error_tag.py +0 -5
- mirascope/api/_generated/types/plan_limit_exceeded_error.py +0 -32
- mirascope/api/_generated/types/plan_limit_exceeded_error_tag.py +0 -7
- mirascope/api/_generated/types/pricing_unavailable_error.py +0 -23
- mirascope/api/_generated/types/property_key.py +0 -7
- mirascope/api/_generated/types/property_key_key.py +0 -25
- mirascope/api/_generated/types/property_key_key_tag.py +0 -5
- mirascope/api/_generated/types/rate_limit_error.py +0 -31
- mirascope/api/_generated/types/rate_limit_error_tag.py +0 -5
- mirascope/api/_generated/types/service_unavailable_error_body.py +0 -24
- mirascope/api/_generated/types/service_unavailable_error_tag.py +0 -7
- mirascope/api/_generated/types/stripe_error.py +0 -20
- mirascope/api/_generated/types/subscription_past_due_error.py +0 -31
- mirascope/api/_generated/types/subscription_past_due_error_tag.py +0 -7
- mirascope/api/_generated/types/unauthorized_error_body.py +0 -21
- mirascope/api/_generated/types/unauthorized_error_tag.py +0 -5
- mirascope/api/client.py +0 -255
- mirascope/api/settings.py +0 -99
- mirascope/llm/formatting/output_parser.py +0 -178
- mirascope/llm/formatting/primitives.py +0 -192
- mirascope/llm/mcp/mcp_client.py +0 -130
- mirascope/llm/messages/_utils.py +0 -34
- mirascope/llm/models/thinking_config.py +0 -61
- mirascope/llm/prompts/prompts.py +0 -487
- mirascope/llm/providers/__init__.py +0 -62
- mirascope/llm/providers/anthropic/__init__.py +0 -11
- mirascope/llm/providers/anthropic/_utils/__init__.py +0 -27
- mirascope/llm/providers/anthropic/_utils/beta_decode.py +0 -282
- mirascope/llm/providers/anthropic/_utils/beta_encode.py +0 -266
- mirascope/llm/providers/anthropic/_utils/encode.py +0 -418
- mirascope/llm/providers/anthropic/_utils/errors.py +0 -46
- mirascope/llm/providers/anthropic/beta_provider.py +0 -374
- mirascope/llm/providers/anthropic/model_id.py +0 -23
- mirascope/llm/providers/anthropic/model_info.py +0 -87
- mirascope/llm/providers/anthropic/provider.py +0 -479
- mirascope/llm/providers/google/__init__.py +0 -6
- mirascope/llm/providers/google/_utils/errors.py +0 -50
- mirascope/llm/providers/google/model_id.py +0 -22
- mirascope/llm/providers/google/model_info.py +0 -63
- mirascope/llm/providers/google/provider.py +0 -492
- mirascope/llm/providers/mirascope/__init__.py +0 -5
- mirascope/llm/providers/mirascope/_utils.py +0 -73
- mirascope/llm/providers/mirascope/provider.py +0 -349
- mirascope/llm/providers/mlx/__init__.py +0 -9
- mirascope/llm/providers/mlx/_utils.py +0 -141
- mirascope/llm/providers/mlx/encoding/__init__.py +0 -8
- mirascope/llm/providers/mlx/encoding/base.py +0 -72
- mirascope/llm/providers/mlx/encoding/transformers.py +0 -150
- mirascope/llm/providers/mlx/mlx.py +0 -254
- mirascope/llm/providers/mlx/model_id.py +0 -17
- mirascope/llm/providers/mlx/provider.py +0 -452
- mirascope/llm/providers/model_id.py +0 -16
- mirascope/llm/providers/ollama/__init__.py +0 -7
- mirascope/llm/providers/ollama/provider.py +0 -71
- mirascope/llm/providers/openai/__init__.py +0 -15
- mirascope/llm/providers/openai/_utils/__init__.py +0 -5
- mirascope/llm/providers/openai/_utils/errors.py +0 -46
- mirascope/llm/providers/openai/completions/__init__.py +0 -7
- mirascope/llm/providers/openai/completions/base_provider.py +0 -542
- mirascope/llm/providers/openai/completions/provider.py +0 -22
- mirascope/llm/providers/openai/model_id.py +0 -31
- mirascope/llm/providers/openai/model_info.py +0 -303
- mirascope/llm/providers/openai/provider.py +0 -441
- mirascope/llm/providers/openai/responses/__init__.py +0 -5
- mirascope/llm/providers/openai/responses/provider.py +0 -513
- mirascope/llm/providers/provider_id.py +0 -24
- mirascope/llm/providers/provider_registry.py +0 -299
- mirascope/llm/providers/together/__init__.py +0 -7
- mirascope/llm/providers/together/provider.py +0 -40
- mirascope/llm/responses/usage.py +0 -95
- mirascope/ops/__init__.py +0 -111
- mirascope/ops/_internal/__init__.py +0 -5
- mirascope/ops/_internal/closure.py +0 -1169
- mirascope/ops/_internal/configuration.py +0 -177
- mirascope/ops/_internal/context.py +0 -76
- mirascope/ops/_internal/exporters/__init__.py +0 -26
- mirascope/ops/_internal/exporters/exporters.py +0 -395
- mirascope/ops/_internal/exporters/processors.py +0 -104
- mirascope/ops/_internal/exporters/types.py +0 -165
- mirascope/ops/_internal/exporters/utils.py +0 -29
- mirascope/ops/_internal/instrumentation/__init__.py +0 -8
- mirascope/ops/_internal/instrumentation/llm/__init__.py +0 -8
- mirascope/ops/_internal/instrumentation/llm/common.py +0 -530
- mirascope/ops/_internal/instrumentation/llm/cost.py +0 -190
- mirascope/ops/_internal/instrumentation/llm/encode.py +0 -238
- mirascope/ops/_internal/instrumentation/llm/gen_ai_types/__init__.py +0 -38
- mirascope/ops/_internal/instrumentation/llm/gen_ai_types/gen_ai_input_messages.py +0 -31
- mirascope/ops/_internal/instrumentation/llm/gen_ai_types/gen_ai_output_messages.py +0 -38
- mirascope/ops/_internal/instrumentation/llm/gen_ai_types/gen_ai_system_instructions.py +0 -18
- mirascope/ops/_internal/instrumentation/llm/gen_ai_types/shared.py +0 -100
- mirascope/ops/_internal/instrumentation/llm/llm.py +0 -161
- mirascope/ops/_internal/instrumentation/llm/model.py +0 -1798
- mirascope/ops/_internal/instrumentation/llm/response.py +0 -521
- mirascope/ops/_internal/instrumentation/llm/serialize.py +0 -300
- mirascope/ops/_internal/propagation.py +0 -198
- mirascope/ops/_internal/protocols.py +0 -133
- mirascope/ops/_internal/session.py +0 -139
- mirascope/ops/_internal/spans.py +0 -232
- mirascope/ops/_internal/traced_calls.py +0 -375
- mirascope/ops/_internal/traced_functions.py +0 -523
- mirascope/ops/_internal/tracing.py +0 -353
- mirascope/ops/_internal/types.py +0 -13
- mirascope/ops/_internal/utils.py +0 -123
- mirascope/ops/_internal/versioned_calls.py +0 -512
- mirascope/ops/_internal/versioned_functions.py +0 -357
- mirascope/ops/_internal/versioning.py +0 -303
- mirascope/ops/exceptions.py +0 -21
- mirascope-2.0.0.dist-info/RECORD +0 -423
- /mirascope/llm/{providers → clients}/base/kwargs.py +0 -0
- /mirascope/llm/{providers → clients}/google/message.py +0 -0
|
@@ -1,173 +1,32 @@
|
|
|
1
1
|
"""The `llm.format` decorator for defining response formats as classes."""
|
|
2
2
|
|
|
3
|
-
from __future__ import annotations
|
|
4
|
-
|
|
5
3
|
import inspect
|
|
6
|
-
import json
|
|
7
|
-
from dataclasses import dataclass
|
|
8
|
-
from typing import Any, Generic, cast
|
|
9
4
|
|
|
10
|
-
from ..tools import FORMAT_TOOL_NAME, ToolFn, ToolParameterSchema, ToolSchema
|
|
11
5
|
from ..types import NoneType
|
|
12
|
-
from .
|
|
13
|
-
from .
|
|
14
|
-
from .types import FormattableT, FormattingMode, HasFormattingInstructions
|
|
15
|
-
|
|
16
|
-
TOOL_MODE_INSTRUCTIONS = f"""Always respond to the user's query using the {FORMAT_TOOL_NAME} tool for structured output."""
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
JSON_MODE_INSTRUCTIONS = (
|
|
20
|
-
"Respond only with valid JSON that matches this exact schema:\n{json_schema}"
|
|
21
|
-
)
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
@dataclass(kw_only=True)
|
|
25
|
-
class Format(Generic[FormattableT]):
|
|
26
|
-
"""Class representing a structured output format for LLM responses.
|
|
27
|
-
|
|
28
|
-
A `Format` contains metadata needed to describe a structured output type
|
|
29
|
-
to the LLM, including the expected schema. This class is not instantiated directly,
|
|
30
|
-
but is created by calling `llm.format`, or is automatically generated by LLM
|
|
31
|
-
providers when a `Formattable` is passed to a call method.
|
|
32
|
-
|
|
33
|
-
Example:
|
|
34
|
-
|
|
35
|
-
```python
|
|
36
|
-
from mirascope import llm
|
|
37
|
-
|
|
38
|
-
class Book:
|
|
39
|
-
title: str
|
|
40
|
-
author: str
|
|
41
|
-
|
|
42
|
-
print(llm.format(Book, mode="tool"))
|
|
43
|
-
```
|
|
44
|
-
"""
|
|
45
|
-
|
|
46
|
-
name: str
|
|
47
|
-
"""The name of the response format."""
|
|
48
|
-
|
|
49
|
-
description: str | None
|
|
50
|
-
"""A description of the response format, if available."""
|
|
51
|
-
|
|
52
|
-
schema: dict[str, object]
|
|
53
|
-
"""JSON schema representation of the structured output format."""
|
|
54
|
-
|
|
55
|
-
mode: FormattingMode
|
|
56
|
-
"""The decorator-provided mode of the response format.
|
|
57
|
-
|
|
58
|
-
Determines how the LLM call may be modified in order to extract the expected format.
|
|
59
|
-
"""
|
|
60
|
-
|
|
61
|
-
formattable: type[FormattableT] | OutputParser[FormattableT]
|
|
62
|
-
"""The formattable type or custom output parser.
|
|
63
|
-
|
|
64
|
-
Can be one of:
|
|
65
|
-
- type[BaseModel]: A Pydantic model class for structured output
|
|
66
|
-
- PrimitiveType: A primitive type (str, int, list, etc.) for simple output
|
|
67
|
-
- OutputParser[FormattableT]: A custom parser created with @llm.output_parser
|
|
68
|
-
|
|
69
|
-
The type determines how the response will be parsed in response.parse().
|
|
70
|
-
OutputParser uses Any for the response type since it works with any response.
|
|
71
|
-
"""
|
|
72
|
-
|
|
73
|
-
@property
|
|
74
|
-
def formatting_instructions(self) -> str | None:
|
|
75
|
-
"""The formatting instructions that will be added to the LLM system prompt.
|
|
76
|
-
|
|
77
|
-
If the format has a custom `OutputParser`, its formatting instructions will be used.
|
|
78
|
-
Otherwise, if the format type has a `formatting_instructions` class method,
|
|
79
|
-
the output of that call will be used. Otherwise, instructions may be
|
|
80
|
-
auto-generated based on the formatting mode.
|
|
81
|
-
"""
|
|
82
|
-
if is_output_parser(self.formattable) or isinstance(
|
|
83
|
-
self.formattable, HasFormattingInstructions
|
|
84
|
-
):
|
|
85
|
-
return self.formattable.formatting_instructions()
|
|
86
|
-
|
|
87
|
-
if self.mode == "tool":
|
|
88
|
-
return TOOL_MODE_INSTRUCTIONS
|
|
89
|
-
elif self.mode == "json":
|
|
90
|
-
json_schema = json.dumps(self.schema, indent=2)
|
|
91
|
-
instructions = JSON_MODE_INSTRUCTIONS.format(json_schema=json_schema)
|
|
92
|
-
return inspect.cleandoc(instructions)
|
|
93
|
-
elif self.mode == "parser":
|
|
94
|
-
return None # pragma: no cover
|
|
95
|
-
|
|
96
|
-
def create_tool_schema(
|
|
97
|
-
self,
|
|
98
|
-
) -> ToolSchema[ToolFn[..., None]]:
|
|
99
|
-
"""Generate a `ToolSchema` for parsing this format.
|
|
100
|
-
|
|
101
|
-
Returns:
|
|
102
|
-
`ToolSchema` for the format tool
|
|
103
|
-
"""
|
|
104
|
-
|
|
105
|
-
schema_dict: dict[str, Any] = self.schema.copy()
|
|
106
|
-
schema_dict["type"] = "object"
|
|
107
|
-
|
|
108
|
-
properties = schema_dict.get("properties")
|
|
109
|
-
if not properties or not isinstance(properties, dict):
|
|
110
|
-
properties = {} # pragma: no cover
|
|
111
|
-
properties = cast(dict[str, Any], properties)
|
|
112
|
-
required: list[str] = list(properties.keys())
|
|
113
|
-
|
|
114
|
-
description = (
|
|
115
|
-
f"Use this tool to extract data in {self.name} format for a final response."
|
|
116
|
-
)
|
|
117
|
-
if self.description:
|
|
118
|
-
description += "\n" + self.description
|
|
119
|
-
|
|
120
|
-
parameters = ToolParameterSchema(
|
|
121
|
-
properties=properties,
|
|
122
|
-
required=required,
|
|
123
|
-
additionalProperties=False,
|
|
124
|
-
)
|
|
125
|
-
if "$defs" in schema_dict and isinstance(schema_dict["$defs"], dict):
|
|
126
|
-
parameters.defs = schema_dict["$defs"]
|
|
127
|
-
|
|
128
|
-
def _unused_format_fn() -> None:
|
|
129
|
-
raise TypeError(
|
|
130
|
-
"Format tool function should not be called."
|
|
131
|
-
) # pragma: no cover
|
|
132
|
-
|
|
133
|
-
return ToolSchema(
|
|
134
|
-
fn=cast(ToolFn[..., None], _unused_format_fn),
|
|
135
|
-
name=FORMAT_TOOL_NAME,
|
|
136
|
-
description=description,
|
|
137
|
-
parameters=parameters,
|
|
138
|
-
strict=None, # Provider determines whether to use strict mode.
|
|
139
|
-
)
|
|
6
|
+
from ._utils import default_formatting_instructions
|
|
7
|
+
from .types import Format, FormattableT, FormattingMode, HasFormattingInstructions
|
|
140
8
|
|
|
141
9
|
|
|
142
10
|
def format(
|
|
143
|
-
formattable: type[FormattableT] |
|
|
11
|
+
formattable: type[FormattableT] | None,
|
|
144
12
|
*,
|
|
145
13
|
mode: FormattingMode,
|
|
146
14
|
) -> Format[FormattableT] | None:
|
|
147
|
-
"""Returns a `Format` that describes structured output
|
|
148
|
-
|
|
149
|
-
This function converts a Formattable type (e.g. Pydantic `BaseModel` or primitive type)
|
|
150
|
-
or an `OutputParser` into a `Format` object that describes how the output should be
|
|
151
|
-
formatted and parsed. Calling `llm.format` is optional, as all the APIs that expect
|
|
152
|
-
a `Format` can also take the Formattable type or `OutputParser` directly. However,
|
|
153
|
-
calling `llm.format` is necessary in order to specify the formatting mode for
|
|
154
|
-
`BaseModel`/primitive types.
|
|
15
|
+
"""Returns a `Format` that describes structured output for a Formattable type.
|
|
155
16
|
|
|
156
|
-
|
|
157
|
-
|
|
17
|
+
This function converts a Formattable type (e.g. Pydantic BaseModel) into a `Format`
|
|
18
|
+
object that describes how the object should be formatted. Calling `llm.format`
|
|
19
|
+
is optional, as all the APIs that expect a `Format` can also take the Formattable
|
|
20
|
+
type directly. However, calling `llm.format` is necessary in order to specify the
|
|
21
|
+
formatting mode that will be used.
|
|
158
22
|
|
|
159
23
|
Args:
|
|
160
|
-
|
|
161
|
-
- BaseModel type: Uses structured output with JSON schema
|
|
162
|
-
- Primitive type: Wrapped in schema for structured output
|
|
163
|
-
- OutputParser: Uses custom parsing with instructions
|
|
164
|
-
mode: The format mode to use (required):
|
|
24
|
+
mode: The format mode to use, one of the following:
|
|
165
25
|
- "strict": Use model strict structured outputs, or fail if unavailable.
|
|
166
26
|
- "tool": Use forced tool calling with a special tool that represents a
|
|
167
27
|
formatted response.
|
|
168
28
|
- "json": Use provider json mode if available, or modify prompt to request
|
|
169
29
|
json if not.
|
|
170
|
-
- "parser": Must be used for OutputParser types.
|
|
171
30
|
|
|
172
31
|
The Formattable type may provide custom formatting instructions via a
|
|
173
32
|
`formatting_instructions(cls)` classmethod. If that method is present, it will be called,
|
|
@@ -178,44 +37,34 @@ def format(
|
|
|
178
37
|
you can add the `formatting_instructions` classmethod and have it return `None`.
|
|
179
38
|
|
|
180
39
|
Returns:
|
|
181
|
-
A `Format` object describing the
|
|
40
|
+
A `Format` object describing the Formattable type.
|
|
182
41
|
|
|
183
42
|
Example:
|
|
184
|
-
Using with
|
|
43
|
+
Using with an LLM call:
|
|
185
44
|
|
|
186
45
|
```python
|
|
187
46
|
from pydantic import BaseModel
|
|
47
|
+
|
|
188
48
|
from mirascope import llm
|
|
189
49
|
|
|
50
|
+
|
|
190
51
|
class Book(BaseModel):
|
|
191
52
|
title: str
|
|
192
53
|
author: str
|
|
193
54
|
|
|
194
55
|
format = llm.format(Book, mode="strict")
|
|
195
56
|
|
|
196
|
-
@llm.call(
|
|
57
|
+
@llm.call(
|
|
58
|
+
provider="openai:completions",
|
|
59
|
+
model_id="gpt-4o-mini",
|
|
60
|
+
format=format,
|
|
61
|
+
)
|
|
197
62
|
def recommend_book(genre: str):
|
|
198
63
|
return f"Recommend a {genre} book."
|
|
199
64
|
|
|
200
65
|
response = recommend_book("fantasy")
|
|
201
66
|
book: Book = response.parse()
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
Example:
|
|
205
|
-
|
|
206
|
-
Using with an `OutputParser`:
|
|
207
|
-
|
|
208
|
-
```python
|
|
209
|
-
@llm.output_parser(
|
|
210
|
-
formatting_instructions="Return XML: <book><title>...</title></book>"
|
|
211
|
-
)
|
|
212
|
-
def parse_book_xml(response: llm.AnyResponse) -> Book:
|
|
213
|
-
# ... parsing logic ...
|
|
214
|
-
return Book(...)
|
|
215
|
-
|
|
216
|
-
@llm.call("openai/gpt-5-mini", format=parse_book_xml)
|
|
217
|
-
def recommend_book(genre: str):
|
|
218
|
-
return f"Recommend a {genre} book."
|
|
67
|
+
print(f"{book.title} by {book.author}")
|
|
219
68
|
```
|
|
220
69
|
"""
|
|
221
70
|
# TODO: Add caching or memoization to this function (e.g. functools.lru_cache)
|
|
@@ -223,71 +72,33 @@ def format(
|
|
|
223
72
|
if formattable is None or formattable is NoneType:
|
|
224
73
|
return None
|
|
225
74
|
|
|
226
|
-
if is_output_parser(formattable):
|
|
227
|
-
if mode != "parser":
|
|
228
|
-
raise ValueError(f"mode must be 'parser' for OutputParser, got '{mode}'")
|
|
229
|
-
return Format[Any](
|
|
230
|
-
name=formattable.__name__,
|
|
231
|
-
description=formattable.__doc__,
|
|
232
|
-
schema={},
|
|
233
|
-
mode="parser",
|
|
234
|
-
formattable=formattable,
|
|
235
|
-
)
|
|
236
|
-
|
|
237
|
-
if is_primitive_type(formattable):
|
|
238
|
-
wrapper_model = create_wrapper_model(formattable)
|
|
239
|
-
schema = wrapper_model.model_json_schema()
|
|
240
|
-
name = (
|
|
241
|
-
formattable.__name__
|
|
242
|
-
if hasattr(formattable, "__name__")
|
|
243
|
-
else str(formattable)
|
|
244
|
-
)
|
|
245
|
-
|
|
246
|
-
return Format[FormattableT](
|
|
247
|
-
name=name,
|
|
248
|
-
description=None,
|
|
249
|
-
schema=schema,
|
|
250
|
-
mode=mode,
|
|
251
|
-
formattable=formattable,
|
|
252
|
-
)
|
|
253
|
-
|
|
254
75
|
description = None
|
|
255
76
|
if formattable.__doc__:
|
|
256
77
|
description = inspect.cleandoc(formattable.__doc__)
|
|
257
78
|
|
|
258
79
|
schema = formattable.model_json_schema()
|
|
80
|
+
formatting_instructions = None
|
|
81
|
+
if isinstance(formattable, HasFormattingInstructions):
|
|
82
|
+
formatting_instructions = formattable.formatting_instructions()
|
|
83
|
+
else:
|
|
84
|
+
formatting_instructions = default_formatting_instructions(schema, mode)
|
|
259
85
|
|
|
260
86
|
return Format[FormattableT](
|
|
261
87
|
name=formattable.__name__,
|
|
262
88
|
description=description,
|
|
263
89
|
schema=schema,
|
|
264
90
|
mode=mode,
|
|
91
|
+
formatting_instructions=formatting_instructions,
|
|
265
92
|
formattable=formattable,
|
|
266
93
|
)
|
|
267
94
|
|
|
268
95
|
|
|
269
96
|
def resolve_format(
|
|
270
|
-
formattable:
|
|
271
|
-
type[FormattableT] | Format[FormattableT] | OutputParser[FormattableT] | None
|
|
272
|
-
),
|
|
97
|
+
formattable: type[FormattableT] | Format[FormattableT] | None,
|
|
273
98
|
default_mode: FormattingMode,
|
|
274
99
|
) -> Format[FormattableT] | None:
|
|
275
|
-
"""Resolve a `Format` (or None) from a possible `Format
|
|
276
|
-
|
|
277
|
-
Args:
|
|
278
|
-
formattable: The format specification:
|
|
279
|
-
- Format: Returned as-is
|
|
280
|
-
- BaseModel/primitive type: Converted to Format with default_mode
|
|
281
|
-
- OutputParser: Converted to Format with mode='parser'
|
|
282
|
-
default_mode: The mode to use for BaseModel/primitive types.
|
|
283
|
-
|
|
284
|
-
Returns:
|
|
285
|
-
A Format object or None.
|
|
286
|
-
"""
|
|
100
|
+
"""Resolve a `Format` (or None) from a possible `Format` or Formattable."""
|
|
287
101
|
if isinstance(formattable, Format):
|
|
288
102
|
return formattable
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
return format(formattable, mode="parser")
|
|
292
|
-
|
|
293
|
-
return format(formattable, mode=default_mode)
|
|
103
|
+
else:
|
|
104
|
+
return format(formattable, mode=default_mode)
|
|
@@ -8,16 +8,10 @@ serves as an acknowledgment of the original author's contribution to this projec
|
|
|
8
8
|
--------------------------------------------------------------------------------
|
|
9
9
|
"""
|
|
10
10
|
|
|
11
|
-
import
|
|
12
|
-
from typing import Any, Generic, NoReturn, Union, cast, get_args, get_origin
|
|
13
|
-
|
|
14
|
-
from pydantic import BaseModel, create_model
|
|
11
|
+
from typing import Generic, NoReturn
|
|
15
12
|
|
|
16
13
|
from .format import FormattableT
|
|
17
14
|
|
|
18
|
-
# Cache for generated partial models to avoid recreation
|
|
19
|
-
_partial_model_cache: dict[type[Any], type[Any]] = {}
|
|
20
|
-
|
|
21
15
|
|
|
22
16
|
class Partial(Generic[FormattableT]):
|
|
23
17
|
"""Generate a new class with all attributes optionals.
|
|
@@ -40,9 +34,7 @@ class Partial(Generic[FormattableT]):
|
|
|
40
34
|
Raises:
|
|
41
35
|
TypeError: Direct instantiation not allowed.
|
|
42
36
|
"""
|
|
43
|
-
raise TypeError(
|
|
44
|
-
"Cannot instantiate abstract Partial class."
|
|
45
|
-
) # pragma: no cover
|
|
37
|
+
raise TypeError("Cannot instantiate abstract Partial class.")
|
|
46
38
|
|
|
47
39
|
def __init_subclass__(
|
|
48
40
|
cls,
|
|
@@ -54,78 +46,13 @@ class Partial(Generic[FormattableT]):
|
|
|
54
46
|
Raises:
|
|
55
47
|
TypeError: Subclassing not allowed.
|
|
56
48
|
"""
|
|
57
|
-
raise TypeError(f"Cannot subclass {cls.__module__}.Partial")
|
|
49
|
+
raise TypeError(f"Cannot subclass {cls.__module__}.Partial")
|
|
58
50
|
|
|
59
51
|
def __class_getitem__(
|
|
60
52
|
cls,
|
|
61
53
|
wrapped_class: type[FormattableT],
|
|
62
54
|
) -> type[FormattableT]:
|
|
63
|
-
"""Convert model to a partial model with all fields being optionals.
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
Args:
|
|
69
|
-
wrapped_class: The BaseModel class to make partial
|
|
70
|
-
|
|
71
|
-
Returns:
|
|
72
|
-
A new BaseModel class with all fields optional (or original if not BaseModel)
|
|
73
|
-
|
|
74
|
-
Example:
|
|
75
|
-
>>> class Author(BaseModel):
|
|
76
|
-
... first_name: str
|
|
77
|
-
... last_name: str
|
|
78
|
-
>>> class Book(BaseModel):
|
|
79
|
-
... title: str
|
|
80
|
-
... author: Author
|
|
81
|
-
>>> PartialBook = Partial[Book]
|
|
82
|
-
>>> partial = PartialBook(title="The Name")
|
|
83
|
-
>>> partial.author # None
|
|
84
|
-
"""
|
|
85
|
-
# Return non-BaseModel types unchanged
|
|
86
|
-
if not (
|
|
87
|
-
inspect.isclass(wrapped_class) and issubclass(wrapped_class, BaseModel)
|
|
88
|
-
):
|
|
89
|
-
return wrapped_class
|
|
90
|
-
|
|
91
|
-
# Check cache to avoid regenerating
|
|
92
|
-
if wrapped_class in _partial_model_cache:
|
|
93
|
-
return cast(type[FormattableT], _partial_model_cache[wrapped_class])
|
|
94
|
-
|
|
95
|
-
# Recursively make all fields optional
|
|
96
|
-
partial_fields: dict[str, Any] = {}
|
|
97
|
-
for field_name, field_info in wrapped_class.model_fields.items():
|
|
98
|
-
field_type = field_info.annotation
|
|
99
|
-
|
|
100
|
-
# Recursively handle nested BaseModel fields
|
|
101
|
-
if inspect.isclass(field_type) and issubclass(field_type, BaseModel):
|
|
102
|
-
field_type = Partial[field_type]
|
|
103
|
-
|
|
104
|
-
# Handle generic types with BaseModel args (e.g., list[Book], dict[str, Book])
|
|
105
|
-
origin = get_origin(field_type)
|
|
106
|
-
if origin is not None:
|
|
107
|
-
args = get_args(field_type)
|
|
108
|
-
# Recursively convert BaseModel args to partial
|
|
109
|
-
new_args = tuple(
|
|
110
|
-
Partial[arg]
|
|
111
|
-
if inspect.isclass(arg) and issubclass(arg, BaseModel)
|
|
112
|
-
else arg
|
|
113
|
-
for arg in args
|
|
114
|
-
)
|
|
115
|
-
# Reconstruct generic type with new args
|
|
116
|
-
if new_args != args:
|
|
117
|
-
field_type = origin[new_args]
|
|
118
|
-
|
|
119
|
-
# Make field optional with None default
|
|
120
|
-
optional_type = Union[field_type, None] # noqa: UP007
|
|
121
|
-
partial_fields[field_name] = (optional_type, None)
|
|
122
|
-
|
|
123
|
-
# Create new model with "Partial" prefix
|
|
124
|
-
partial_model = create_model(
|
|
125
|
-
f"Partial{wrapped_class.__name__}", __base__=BaseModel, **partial_fields
|
|
126
|
-
)
|
|
127
|
-
|
|
128
|
-
# Cache the generated model
|
|
129
|
-
_partial_model_cache[wrapped_class] = partial_model
|
|
130
|
-
|
|
131
|
-
return cast(type[FormattableT], partial_model)
|
|
55
|
+
"""Convert model to a partial model with all fields being optionals."""
|
|
56
|
+
# TODO: Implement proper partial model generation
|
|
57
|
+
# For now, return the original class to avoid import errors
|
|
58
|
+
return wrapped_class
|
|
@@ -1,26 +1,18 @@
|
|
|
1
1
|
"""Type for the formatting module."""
|
|
2
2
|
|
|
3
|
-
from
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from typing import Generic, Literal, Protocol, runtime_checkable
|
|
4
5
|
from typing_extensions import TypeVar
|
|
5
6
|
|
|
6
7
|
from pydantic import BaseModel
|
|
7
8
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
FormattableT = TypeVar(
|
|
11
|
-
"FormattableT", bound=BaseModel | PrimitiveType | None, default=None
|
|
12
|
-
)
|
|
9
|
+
# TODO: Support primitive types (e.g. `format=list[Book]`)
|
|
10
|
+
FormattableT = TypeVar("FormattableT", bound=BaseModel | None, default=None)
|
|
13
11
|
"""Type variable for structured response format types.
|
|
14
12
|
|
|
15
13
|
This TypeVar represents the type of structured output format that LLM responses
|
|
16
|
-
can be parsed into, or None if no format is specified.
|
|
17
|
-
|
|
18
|
-
Supported format types:
|
|
19
|
-
- Pydantic BaseModel subclasses
|
|
20
|
-
- Primitive types: str, int, float, bool, bytes, list, set, tuple, dict
|
|
21
|
-
- Generic collections: list[Book], dict[str, int], etc.
|
|
22
|
-
- Union, Literal, and Annotated types
|
|
23
|
-
- Enum types
|
|
14
|
+
can be parsed into, or None if no format is specified.
|
|
15
|
+
If format is specified, it must extend Pydantic BaseModel.
|
|
24
16
|
"""
|
|
25
17
|
|
|
26
18
|
|
|
@@ -28,14 +20,15 @@ FormattingMode = Literal[
|
|
|
28
20
|
"strict",
|
|
29
21
|
"json",
|
|
30
22
|
"tool",
|
|
31
|
-
"parser",
|
|
32
23
|
]
|
|
33
24
|
"""Available modes for response format generation.
|
|
34
25
|
|
|
35
26
|
- "strict": Use strict mode for structured outputs, asking the LLM to strictly adhere
|
|
36
27
|
to a given JSON schema. Not all providers or models support it, and may not be
|
|
37
|
-
compatible with tool calling. When making a call using this mode, an
|
|
38
|
-
`llm.
|
|
28
|
+
compatible with tool calling. When making a call using this mode, an
|
|
29
|
+
`llm.FormattingModeNotSupportedError` error may be raised (if "strict" mode is wholly
|
|
30
|
+
unsupported), or an `llm.FeatureNotSupportedError` may be raised (if trying to use
|
|
31
|
+
strict along with tools and that is unsupported).
|
|
39
32
|
|
|
40
33
|
- "json": Use JSON mode for structured outputs. In contrast to strict mode, we ask the
|
|
41
34
|
LLM to output JSON as text, though without guarantees that the model will output
|
|
@@ -50,14 +43,64 @@ FormattingMode = Literal[
|
|
|
50
43
|
content (abstracting over the tool call). If other tools are present, they will
|
|
51
44
|
be handled as regular tool calls.
|
|
52
45
|
|
|
53
|
-
- "parser": Use custom parsing with formatting instructions. No schema generation or
|
|
54
|
-
structured output features. The LLM receives only formatting instructions and the
|
|
55
|
-
response is parsed using a custom parser function created with `@llm.output_parser`.
|
|
56
|
-
|
|
57
46
|
Note: When `llm.format` is not used, the provider will automatically choose a mode at call time.
|
|
58
47
|
"""
|
|
59
48
|
|
|
60
49
|
|
|
50
|
+
@dataclass(kw_only=True)
|
|
51
|
+
class Format(Generic[FormattableT]):
|
|
52
|
+
"""Class representing a structured output format for LLM responses.
|
|
53
|
+
|
|
54
|
+
A `Format` contains metadata needed to describe a structured output type
|
|
55
|
+
to the LLM, including the expected schema. This class is not instantiated directly,
|
|
56
|
+
but is created by calling `llm.format`, or is automatically generated by LLM
|
|
57
|
+
providers when a `Formattable` is passed to a call method.
|
|
58
|
+
|
|
59
|
+
Example:
|
|
60
|
+
|
|
61
|
+
```python
|
|
62
|
+
from mirascope import llm
|
|
63
|
+
|
|
64
|
+
class Book:
|
|
65
|
+
title: str
|
|
66
|
+
author: str
|
|
67
|
+
|
|
68
|
+
print(llm.format(Book, mode="tool"))
|
|
69
|
+
```
|
|
70
|
+
"""
|
|
71
|
+
|
|
72
|
+
name: str
|
|
73
|
+
"""The name of the response format."""
|
|
74
|
+
|
|
75
|
+
description: str | None
|
|
76
|
+
"""A description of the response format, if available."""
|
|
77
|
+
|
|
78
|
+
schema: dict[str, object]
|
|
79
|
+
"""JSON schema representation of the structured output format."""
|
|
80
|
+
|
|
81
|
+
mode: FormattingMode
|
|
82
|
+
"""The decorator-provided mode of the response format.
|
|
83
|
+
|
|
84
|
+
Determines how the LLM call may be modified in order to extract the expected format.
|
|
85
|
+
"""
|
|
86
|
+
|
|
87
|
+
formatting_instructions: str | None
|
|
88
|
+
"""The formatting instructions that will be added to the LLM system prompt.
|
|
89
|
+
|
|
90
|
+
If the format type has a `formatting_instructions` class method, the output of that
|
|
91
|
+
call will be used for instructions. Otherwise, instructions may be auto-generated
|
|
92
|
+
based on the formatting mode.
|
|
93
|
+
"""
|
|
94
|
+
|
|
95
|
+
formattable: type[FormattableT]
|
|
96
|
+
"""The `Formattable` type that this `Format` describes.
|
|
97
|
+
|
|
98
|
+
While the `FormattbleT` typevar allows for `None`, a `Format` will never be
|
|
99
|
+
constructed when the `FormattableT` is `None`, so you may treat this as
|
|
100
|
+
a `RequiredFormattableT` in practice.
|
|
101
|
+
"""
|
|
102
|
+
|
|
103
|
+
|
|
61
104
|
@runtime_checkable
|
|
62
105
|
class HasFormattingInstructions(Protocol):
|
|
63
106
|
"""Protocol for classes that have been decorated with `@format()`."""
|
mirascope/llm/mcp/__init__.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"""MCP compatibility module."""
|
|
2
2
|
|
|
3
|
-
from .
|
|
3
|
+
from .client import MCPClient, sse_client, stdio_client, streamablehttp_client
|
|
4
4
|
|
|
5
|
-
__all__ = ["MCPClient", "sse_client", "stdio_client", "
|
|
5
|
+
__all__ = ["MCPClient", "sse_client", "stdio_client", "streamablehttp_client"]
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import contextlib
|
|
2
|
+
from collections.abc import AsyncIterator, Callable
|
|
3
|
+
from datetime import timedelta
|
|
4
|
+
|
|
5
|
+
import httpx
|
|
6
|
+
from mcp import ClientSession
|
|
7
|
+
from mcp.client.session import ListRootsFnT, SamplingFnT
|
|
8
|
+
from mcp.client.sse import sse_client as mcp_sse_client
|
|
9
|
+
from mcp.client.stdio import StdioServerParameters
|
|
10
|
+
from mcp.client.streamable_http import (
|
|
11
|
+
streamablehttp_client as mcp_streamablehttp_client,
|
|
12
|
+
)
|
|
13
|
+
from mcp.shared._httpx_utils import McpHttpClientFactory
|
|
14
|
+
|
|
15
|
+
from ..tools import AsyncTool
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class MCPClient:
|
|
19
|
+
"""Mirascope wrapper around a MCP ClientSession.
|
|
20
|
+
|
|
21
|
+
It provides a way to get MCP results that are pre-converted into Mirascope-friendly
|
|
22
|
+
types.
|
|
23
|
+
|
|
24
|
+
The underlying MCP ClientSession may be accessed by .session if needed.
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
def __init__(self, session: ClientSession) -> None:
|
|
28
|
+
self._session = session
|
|
29
|
+
|
|
30
|
+
@property
|
|
31
|
+
def session(self) -> ClientSession:
|
|
32
|
+
"""Access the underlying MCP ClientSession if needed."""
|
|
33
|
+
return self._session
|
|
34
|
+
|
|
35
|
+
async def list_tools(self) -> list[AsyncTool]:
|
|
36
|
+
"""List all tools available on the MCP server.
|
|
37
|
+
|
|
38
|
+
Returns:
|
|
39
|
+
A list of dynamically created `llm.Tool`s.
|
|
40
|
+
"""
|
|
41
|
+
raise NotImplementedError()
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
@contextlib.asynccontextmanager
|
|
45
|
+
async def streamablehttp_client(
|
|
46
|
+
url: str,
|
|
47
|
+
headers: dict[str, str] | None = None,
|
|
48
|
+
timeout: float | timedelta | None = None,
|
|
49
|
+
sse_read_timeout: float | timedelta | None = None,
|
|
50
|
+
terminate_on_close: bool = True,
|
|
51
|
+
httpx_client_factory: McpHttpClientFactory | None = None,
|
|
52
|
+
auth: httpx.Auth | None = None,
|
|
53
|
+
) -> AsyncIterator[MCPClient]:
|
|
54
|
+
"""Create a Mirascope MCPClient using StreamableHTTP."""
|
|
55
|
+
kwargs = {}
|
|
56
|
+
if headers is not None:
|
|
57
|
+
kwargs["headers"] = headers
|
|
58
|
+
if timeout is not None:
|
|
59
|
+
kwargs["timeout"] = timeout
|
|
60
|
+
if sse_read_timeout is not None:
|
|
61
|
+
kwargs["sse_read_timeout"] = sse_read_timeout
|
|
62
|
+
if httpx_client_factory is not None:
|
|
63
|
+
kwargs["httpx_client_factory"] = httpx_client_factory
|
|
64
|
+
if auth is not None:
|
|
65
|
+
kwargs["auth"] = auth
|
|
66
|
+
|
|
67
|
+
async with (
|
|
68
|
+
mcp_streamablehttp_client(
|
|
69
|
+
url,
|
|
70
|
+
terminate_on_close=terminate_on_close,
|
|
71
|
+
**kwargs,
|
|
72
|
+
) as (read, write, get_session_id),
|
|
73
|
+
ClientSession(
|
|
74
|
+
read,
|
|
75
|
+
write,
|
|
76
|
+
) as session,
|
|
77
|
+
):
|
|
78
|
+
await session.initialize()
|
|
79
|
+
yield MCPClient(session)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
@contextlib.asynccontextmanager
|
|
83
|
+
async def stdio_client(
|
|
84
|
+
server_parameters: StdioServerParameters,
|
|
85
|
+
read_stream_exception_handler: Callable[[Exception], None] | None = None,
|
|
86
|
+
) -> AsyncIterator[MCPClient]:
|
|
87
|
+
"""Create a Mirascope MCPClient using stdio."""
|
|
88
|
+
|
|
89
|
+
async with (
|
|
90
|
+
ClientSession(None, None) as session, # pyright: ignore [reportArgumentType]
|
|
91
|
+
):
|
|
92
|
+
raise NotImplementedError()
|
|
93
|
+
await session.initialize()
|
|
94
|
+
yield MCPClient(session)
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
@contextlib.asynccontextmanager
|
|
98
|
+
async def sse_client(
|
|
99
|
+
url: str,
|
|
100
|
+
list_roots_callback: ListRootsFnT | None = None,
|
|
101
|
+
read_timeout_seconds: timedelta | None = None,
|
|
102
|
+
sampling_callback: SamplingFnT | None = None,
|
|
103
|
+
session: ClientSession | None = None,
|
|
104
|
+
) -> AsyncIterator[MCPClient]:
|
|
105
|
+
"""Create a Mirascope MCPClient using sse."""
|
|
106
|
+
|
|
107
|
+
async with (
|
|
108
|
+
mcp_sse_client(url) as (read, write),
|
|
109
|
+
ClientSession(
|
|
110
|
+
read,
|
|
111
|
+
write,
|
|
112
|
+
read_timeout_seconds=read_timeout_seconds,
|
|
113
|
+
sampling_callback=sampling_callback,
|
|
114
|
+
list_roots_callback=list_roots_callback,
|
|
115
|
+
) as session,
|
|
116
|
+
):
|
|
117
|
+
await session.initialize()
|
|
118
|
+
yield MCPClient(session)
|