language-model-common 2.0.91__tar.gz → 2.0.92__tar.gz
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.
- {language_model_common-2.0.91/language_model_common.egg-info → language_model_common-2.0.92}/PKG-INFO +1 -1
- language_model_common-2.0.92/VERSION +1 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92/language_model_common.egg-info}/PKG-INFO +1 -1
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/converters/streaming_manager.py +9 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/converters/tool_event_handlers.py +31 -8
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/mcp/mcp_client/langchain_adapter.py +2 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/mcp/mcp_client/tool_invocation.py +80 -11
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/mcp/mcp_tool_provider.py +22 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/models/model_factory.py +11 -2
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/structures/openai/request/chat_completion_api_request_wrapper.py +23 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/structures/openai/request/chat_request_wrapper.py +23 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/structures/openai/request/responses_api_request_wrapper.py +36 -2
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/tools/mcp/call_tool_tool.py +11 -4
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/utilities/environment/language_model_common_environment_variables.py +29 -0
- language_model_common-2.0.91/VERSION +0 -1
- {language_model_common-2.0.91 → language_model_common-2.0.92}/LICENSE +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/MANIFEST.in +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/README.md +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/language_model_common.egg-info/SOURCES.txt +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/language_model_common.egg-info/dependency_links.txt +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/language_model_common.egg-info/requires.txt +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/language_model_common.egg-info/top_level.txt +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/__init__.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/auth/__init__.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/auth/authenticator/__init__.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/auth/exceptions/__init__.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/auth/exceptions/authorization_mcp_tool_token_invalid_exception.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/auth/exceptions/authorization_token_cache_item_expired_exception.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/auth/exceptions/authorization_token_cache_item_not_found_exception.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/auth/models/__init__.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/auth/models/token_cache_item.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/auth/oauth_provider_registrar.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/auth/pass_through_token_manager.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/auth/token_exchange/__init__.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/auth/token_exchange/token_exchange_manager.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/auth/token_storage_auth_manager.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/auth/tools/__init__.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/auth/tools/tool_auth_manager.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/aws/__init__.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/aws/aws_client_factory.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/configs/__init__.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/configs/config_reader/__init__.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/configs/config_reader/config_reader.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/configs/config_reader/file_config_reader.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/configs/config_reader/github_directory_downloader.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/configs/config_reader/github_directory_helper.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/configs/config_reader/mcp_json_fetcher.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/configs/config_reader/mcp_json_reader.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/configs/config_reader/s3_config_reader.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/configs/prompt_library/__init__.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/configs/prompt_library/prompt_library_environment_variables.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/configs/prompt_library/prompt_library_manager.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/configs/prompt_library/prompt_store.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/configs/schemas/__init__.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/configs/schemas/config_schema.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/configs/schemas/mcp_json_schema.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/container/__init__.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/container/container_factory.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/context/__init__.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/context/request_context.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/converters/__init__.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/converters/langgraph_to_openai_converter.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/converters/stream_buffer.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/converters/stream_context_mixin.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/converters/stream_debug_output_manager.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/converters/streaming_formatters.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/converters/streaming_tool_node.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/exceptions/__init__.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/exceptions/bailey_exception.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/exceptions/rate_limit_exception.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/file_managers/__init__.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/file_managers/aws_s3_file_manager.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/file_managers/file_manager.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/file_managers/file_manager_factory.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/file_managers/file_writer.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/file_managers/local_file_manager.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/github/__init__.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/github/token_provider.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/graph/__init__.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/graph/graph_builder.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/history/__init__.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/history/context_compactor.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/history/conversation_history_manager.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/history/smart_history_manager.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/http/__init__.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/http/http_client_factory.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/image_generation/__init__.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/image_generation/aws_image_generator.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/image_generation/image_generator.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/image_generation/image_generator_factory.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/image_generation/managers/__init__.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/image_generation/managers/image_generation_manager.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/image_generation/openai_image_generator.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/image_generation/providers/__init__.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/image_generation/providers/base_image_generation_provider.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/image_generation/providers/image_generation_provider.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/markdown/__init__.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/markdown/html_to_markdown_converter.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/mcp/__init__.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/mcp/auth/__init__.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/mcp/auth/auth_server_metadata_discovery.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/mcp/auth/mcp_authorization_helper.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/mcp/callbacks.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/mcp/exceptions/__init__.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/mcp/exceptions/mcp_tool_exception.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/mcp/exceptions/mcp_tool_not_found_exception.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/mcp/exceptions/mcp_tool_unauthorized_exception.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/mcp/exceptions/mcp_tool_unknown_exception.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/mcp/interceptors/__init__.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/mcp/interceptors/auth.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/mcp/interceptors/tracing.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/mcp/interceptors/truncation.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/mcp/interceptors/types.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/mcp/mcp_client/__init__.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/mcp/mcp_client/content_conversion.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/mcp/mcp_client/mcp_app_proxy.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/mcp/mcp_client/mcp_tool_list_store.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/mcp/mcp_client/server_card_discovery.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/mcp/mcp_client/session.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/mcp/mcp_client/session_pool.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/mcp/mcp_client/tool_list_cache.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/mcp/mcp_client/ui_resource.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/mcp/tool_catalog.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/mcp/tool_discovery_middleware.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/mocks/__init__.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/mocks/mock_aws_client_factory.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/mocks/mock_http_client_factory.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/mocks/mock_image_generator.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/mocks/mock_image_generator_factory.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/models/__init__.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/models/bedrock_models.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/models/sanitizing_bedrock_converse.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/ocr/__init__.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/ocr/aws_ocr_extractor.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/ocr/ocr_extractor.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/ocr/ocr_extractor_factory.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/persistence/__init__.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/persistence/persistence_factory.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/pipeline/__init__.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/pipeline/context.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/pipeline/output/__init__.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/pipeline/pipeline.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/pipeline/step.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/pipeline/steps/__init__.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/py.typed +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/schema/__init__.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/schema/openai/__init__.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/schema/openai/completions.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/schema/openai/image_generation.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/schema/openai/responses.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/state/__init__.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/state/messages_state.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/structures/__init__.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/structures/openai/__init__.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/structures/openai/message/__init__.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/structures/openai/message/chat_completion_api_message_wrapper.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/structures/openai/message/chat_message_wrapper.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/structures/openai/message/responses_api_message_wrapper.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/structures/openai/request/__init__.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/tools/__init__.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/tools/mcp/__init__.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/tools/mcp/search_tools_tool.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/utilities/__init__.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/utilities/cache/__init__.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/utilities/cache/advisory_lock.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/utilities/cache/model_config_cache_store.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/utilities/chat_message_helpers.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/utilities/config_substitution.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/utilities/environment/__init__.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/utilities/header_reader/__init__.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/utilities/header_reader/header_reader.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/utilities/logger/__init__.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/utilities/logger/debug_configuration.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/utilities/logger/exception_formatter.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/utilities/logger/exception_logger.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/utilities/logger/log_levels.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/utilities/logger/logging_response.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/utilities/logger/logging_transport.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/utilities/message_preprocessing/__init__.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/utilities/message_preprocessing/composite_message_preprocessor.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/utilities/message_preprocessing/message_preprocessor.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/utilities/mongo_url_utils.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/utilities/openai/__init__.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/utilities/openai/responses_api_converter.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/utilities/request_information.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/utilities/s3_url.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/utilities/security/__init__.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/utilities/security/normalize.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/utilities/security/off_topic_detector.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/utilities/security/prompt_extraction_detector.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/utilities/security/prompt_sanitizer.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/utilities/slash_command/__init__.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/utilities/slash_command/handlers/__init__.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/utilities/slash_command/handlers/debug_command_handler.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/utilities/slash_command/handlers/skill_command_handler.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/utilities/slash_command/slash_command_handler.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/utilities/slash_command/slash_command_parser.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/utilities/slash_command/slash_command_processor.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/utilities/text_humanizer.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/utilities/token_reducer/__init__.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/utilities/token_reducer/token_reducer.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/utilities/tool_display_name_mapper.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/utilities/url_parser.py +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/pyproject.toml +0 -0
- {language_model_common-2.0.91 → language_model_common-2.0.92}/setup.cfg +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
2.0.92
|
|
@@ -379,6 +379,15 @@ class LangGraphStreamingManager(StreamContextMixin):
|
|
|
379
379
|
)
|
|
380
380
|
if chunk:
|
|
381
381
|
yield chunk
|
|
382
|
+
elif name == "mcp_tool_heartbeat":
|
|
383
|
+
data = dict(event.get("data", {}))
|
|
384
|
+
chunk = chat_request_wrapper.create_tool_heartbeat_sse_event(
|
|
385
|
+
request_id=request_information.request_id,
|
|
386
|
+
tool_name=data.get("tool_name", "unknown"),
|
|
387
|
+
elapsed_seconds=float(data.get("elapsed_seconds", 0.0)),
|
|
388
|
+
)
|
|
389
|
+
if chunk:
|
|
390
|
+
yield chunk
|
|
382
391
|
else:
|
|
383
392
|
logger.debug("Skipped custom event: %s", name)
|
|
384
393
|
|
|
@@ -36,6 +36,20 @@ from languagemodelcommon.utilities.tool_display_name_mapper import ToolDisplayNa
|
|
|
36
36
|
logger = logging.getLogger(__name__)
|
|
37
37
|
logger.setLevel(SRC_LOG_LEVELS.LLM)
|
|
38
38
|
|
|
39
|
+
# Cap on the tool result/error text forwarded to the response.output_item.done
|
|
40
|
+
# SSE event, so a tool that echoes back large content (e.g. a full skill body)
|
|
41
|
+
# doesn't bloat every trace event; full output remains available via the
|
|
42
|
+
# existing debug-file-writer path for cases that need it.
|
|
43
|
+
TOOL_END_OUTPUT_TRACE_MAX_CHARS = 2000
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def _truncate_for_trace(
|
|
47
|
+
text: str, *, max_chars: int = TOOL_END_OUTPUT_TRACE_MAX_CHARS
|
|
48
|
+
) -> str:
|
|
49
|
+
if len(text) <= max_chars:
|
|
50
|
+
return text
|
|
51
|
+
return text[:max_chars] + f"...[truncated {len(text) - max_chars} chars]"
|
|
52
|
+
|
|
39
53
|
|
|
40
54
|
class ToolEventHandler(StreamContextMixin):
|
|
41
55
|
def __init__(
|
|
@@ -161,23 +175,32 @@ class ToolEventHandler(StreamContextMixin):
|
|
|
161
175
|
tool_name,
|
|
162
176
|
)
|
|
163
177
|
|
|
178
|
+
tool_message_content: str = (
|
|
179
|
+
convert_message_content_into_string(tool_message=tool_message)
|
|
180
|
+
if tool_message
|
|
181
|
+
else ""
|
|
182
|
+
)
|
|
183
|
+
artifact: Optional[Any] = tool_message.artifact
|
|
184
|
+
# tool_message.status only flips to "error" for an uncaught exception;
|
|
185
|
+
# meta-tools like call_tool catch their own failures and report them via
|
|
186
|
+
# the artifact instead (see CallToolTool), so check both signals.
|
|
187
|
+
is_error: bool = tool_message.status == "error" or (
|
|
188
|
+
isinstance(artifact, dict) and artifact.get("is_error") is True
|
|
189
|
+
)
|
|
190
|
+
|
|
164
191
|
tool_end_event = chat_request_wrapper.create_tool_end_sse_event(
|
|
165
192
|
request_id=request_information.request_id,
|
|
166
193
|
tool_name=tool_name,
|
|
167
194
|
tool_input=tool_input,
|
|
168
195
|
runtime_seconds=runtime_seconds,
|
|
196
|
+
output=_truncate_for_trace(tool_message_content)
|
|
197
|
+
if tool_message_content
|
|
198
|
+
else None,
|
|
199
|
+
is_error=is_error,
|
|
169
200
|
)
|
|
170
201
|
if tool_end_event:
|
|
171
202
|
yield tool_end_event
|
|
172
203
|
|
|
173
|
-
tool_message_content: str = (
|
|
174
|
-
convert_message_content_into_string(tool_message=tool_message)
|
|
175
|
-
if tool_message
|
|
176
|
-
else ""
|
|
177
|
-
)
|
|
178
|
-
|
|
179
|
-
artifact: Optional[Any] = tool_message.artifact
|
|
180
|
-
|
|
181
204
|
logger.debug(
|
|
182
205
|
"Tool %s has artifact of type %s: %s",
|
|
183
206
|
tool_name,
|
|
@@ -62,6 +62,7 @@ def mcp_tool_to_langchain_tool(
|
|
|
62
62
|
server_name: str | None = None,
|
|
63
63
|
session_pool: McpSessionPool | None = None,
|
|
64
64
|
tool_list_cache: ToolListCache | None = None,
|
|
65
|
+
heartbeat_interval_seconds: float = 15.0,
|
|
65
66
|
) -> BaseTool:
|
|
66
67
|
"""Convert an MCP Tool to a LangChain BaseTool.
|
|
67
68
|
|
|
@@ -84,6 +85,7 @@ def mcp_tool_to_langchain_tool(
|
|
|
84
85
|
mcp_callbacks=mcp_callbacks,
|
|
85
86
|
session_pool=session_pool,
|
|
86
87
|
tool_list_cache=tool_list_cache,
|
|
88
|
+
heartbeat_interval_seconds=heartbeat_interval_seconds,
|
|
87
89
|
)
|
|
88
90
|
handler = build_interceptor_chain(
|
|
89
91
|
base_handler=execute_tool, tool_interceptors=tool_interceptors
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
"""Tool invocation — interceptor chain and raw MCP tool calls."""
|
|
2
2
|
|
|
3
|
+
import asyncio
|
|
4
|
+
import contextlib
|
|
3
5
|
import logging
|
|
4
6
|
from collections.abc import Awaitable, Callable
|
|
5
7
|
from typing import Any
|
|
6
8
|
|
|
9
|
+
from langchain_core.callbacks.manager import adispatch_custom_event
|
|
7
10
|
from mcp.types import CallToolResult
|
|
8
11
|
from pydantic import ValidationError
|
|
9
12
|
|
|
@@ -174,8 +177,6 @@ async def _execute_tool_as_task(
|
|
|
174
177
|
Raises TaskProtocolError if the server does not respond with a valid
|
|
175
178
|
CreateTaskResult (e.g., returns a plain CallToolResult instead).
|
|
176
179
|
"""
|
|
177
|
-
from langchain_core.callbacks.manager import adispatch_custom_event
|
|
178
|
-
|
|
179
180
|
try:
|
|
180
181
|
create_result = await session.experimental.call_tool_as_task(
|
|
181
182
|
name, arguments, ttl=60000
|
|
@@ -222,12 +223,69 @@ async def _execute_tool_as_task(
|
|
|
222
223
|
return result
|
|
223
224
|
|
|
224
225
|
|
|
226
|
+
async def _execute_tool_call_with_heartbeat(
|
|
227
|
+
*,
|
|
228
|
+
session: Any,
|
|
229
|
+
name: str,
|
|
230
|
+
arguments: dict[str, Any],
|
|
231
|
+
progress_callback: Any,
|
|
232
|
+
server_name: str,
|
|
233
|
+
heartbeat_interval_seconds: float,
|
|
234
|
+
) -> CallToolResult:
|
|
235
|
+
"""Call ``session.call_tool``, emitting a synthetic ``mcp_tool_heartbeat``
|
|
236
|
+
custom event every ``heartbeat_interval_seconds`` while the call is in
|
|
237
|
+
flight, regardless of whether the tool reports real progress.
|
|
238
|
+
|
|
239
|
+
Uses ``asyncio.shield`` so a heartbeat tick's wait_for timeout never
|
|
240
|
+
cancels the underlying tool call -- only the local wait is abandoned and
|
|
241
|
+
retried on the same call_task. If this coroutine itself is cancelled
|
|
242
|
+
(e.g. the overall chat turn is aborted), the inner call_task is
|
|
243
|
+
cancelled too rather than left running in the background.
|
|
244
|
+
"""
|
|
245
|
+
call_task: "asyncio.Task[CallToolResult]" = asyncio.ensure_future(
|
|
246
|
+
session.call_tool(name, arguments, progress_callback=progress_callback)
|
|
247
|
+
)
|
|
248
|
+
elapsed_seconds = 0.0
|
|
249
|
+
try:
|
|
250
|
+
while True:
|
|
251
|
+
try:
|
|
252
|
+
return await asyncio.wait_for(
|
|
253
|
+
asyncio.shield(call_task), timeout=heartbeat_interval_seconds
|
|
254
|
+
)
|
|
255
|
+
except asyncio.TimeoutError:
|
|
256
|
+
elapsed_seconds += heartbeat_interval_seconds
|
|
257
|
+
try:
|
|
258
|
+
await adispatch_custom_event(
|
|
259
|
+
"mcp_tool_heartbeat",
|
|
260
|
+
{
|
|
261
|
+
"server_name": server_name,
|
|
262
|
+
"tool_name": name,
|
|
263
|
+
"elapsed_seconds": elapsed_seconds,
|
|
264
|
+
},
|
|
265
|
+
)
|
|
266
|
+
except RuntimeError as e:
|
|
267
|
+
logger.debug(
|
|
268
|
+
"Skipping mcp_tool_heartbeat event dispatch: %s (tool=%s)",
|
|
269
|
+
e,
|
|
270
|
+
name,
|
|
271
|
+
)
|
|
272
|
+
finally:
|
|
273
|
+
# Ensure call_task is never left orphaned, whether this coroutine
|
|
274
|
+
# exits via CancelledError, a return, or any other exception raised
|
|
275
|
+
# from the loop body (e.g. adispatch_custom_event).
|
|
276
|
+
if not call_task.done():
|
|
277
|
+
call_task.cancel()
|
|
278
|
+
with contextlib.suppress(asyncio.CancelledError):
|
|
279
|
+
await call_task
|
|
280
|
+
|
|
281
|
+
|
|
225
282
|
def _make_execute_tool(
|
|
226
283
|
*,
|
|
227
284
|
config: MCPConnectionConfig,
|
|
228
285
|
mcp_callbacks: _MCPCallbacks,
|
|
229
286
|
session_pool: McpSessionPool | None = None,
|
|
230
287
|
tool_list_cache: ToolListCache | None = None,
|
|
288
|
+
heartbeat_interval_seconds: float = 15.0,
|
|
231
289
|
) -> Callable[[MCPToolCallRequest], Awaitable[MCPToolCallResult]]:
|
|
232
290
|
"""Create an execute_tool handler that opens a session and calls the tool.
|
|
233
291
|
|
|
@@ -285,10 +343,13 @@ def _make_execute_tool(
|
|
|
285
343
|
)
|
|
286
344
|
if tool_list_cache is not None:
|
|
287
345
|
await tool_list_cache.invalidate_async(key=cache_key)
|
|
288
|
-
return await
|
|
289
|
-
|
|
290
|
-
request.
|
|
346
|
+
return await _execute_tool_call_with_heartbeat(
|
|
347
|
+
session=session,
|
|
348
|
+
name=request.name,
|
|
349
|
+
arguments=request.args,
|
|
291
350
|
progress_callback=mcp_callbacks.progress_callback,
|
|
351
|
+
server_name=request.server_name,
|
|
352
|
+
heartbeat_interval_seconds=heartbeat_interval_seconds,
|
|
292
353
|
)
|
|
293
354
|
except Exception:
|
|
294
355
|
await session_pool.evict(effective_config)
|
|
@@ -324,16 +385,22 @@ def _make_execute_tool(
|
|
|
324
385
|
)
|
|
325
386
|
if tool_list_cache is not None:
|
|
326
387
|
await tool_list_cache.invalidate_async(key=cache_key)
|
|
327
|
-
result = await
|
|
328
|
-
|
|
329
|
-
request.
|
|
388
|
+
result = await _execute_tool_call_with_heartbeat(
|
|
389
|
+
session=session,
|
|
390
|
+
name=request.name,
|
|
391
|
+
arguments=request.args,
|
|
330
392
|
progress_callback=mcp_callbacks.progress_callback,
|
|
393
|
+
server_name=request.server_name,
|
|
394
|
+
heartbeat_interval_seconds=heartbeat_interval_seconds,
|
|
331
395
|
)
|
|
332
396
|
else:
|
|
333
|
-
result = await
|
|
334
|
-
|
|
335
|
-
request.
|
|
397
|
+
result = await _execute_tool_call_with_heartbeat(
|
|
398
|
+
session=session,
|
|
399
|
+
name=request.name,
|
|
400
|
+
arguments=request.args,
|
|
336
401
|
progress_callback=mcp_callbacks.progress_callback,
|
|
402
|
+
server_name=request.server_name,
|
|
403
|
+
heartbeat_interval_seconds=heartbeat_interval_seconds,
|
|
337
404
|
)
|
|
338
405
|
except Exception as e:
|
|
339
406
|
captured_exception = e
|
|
@@ -355,6 +422,7 @@ async def call_mcp_tool_raw(
|
|
|
355
422
|
tool_interceptors: list[ToolCallInterceptor] | None = None,
|
|
356
423
|
session_pool: McpSessionPool | None = None,
|
|
357
424
|
tool_list_cache: ToolListCache | None = None,
|
|
425
|
+
heartbeat_interval_seconds: float = 15.0,
|
|
358
426
|
) -> CallToolResult:
|
|
359
427
|
"""Call an MCP tool and return the raw CallToolResult.
|
|
360
428
|
|
|
@@ -374,6 +442,7 @@ async def call_mcp_tool_raw(
|
|
|
374
442
|
mcp_callbacks=mcp_callbacks,
|
|
375
443
|
session_pool=session_pool,
|
|
376
444
|
tool_list_cache=tool_list_cache,
|
|
445
|
+
heartbeat_interval_seconds=heartbeat_interval_seconds,
|
|
377
446
|
)
|
|
378
447
|
handler = build_interceptor_chain(
|
|
379
448
|
base_handler=execute_tool, tool_interceptors=tool_interceptors
|
|
@@ -6,6 +6,7 @@ from typing import Any, Dict, List
|
|
|
6
6
|
|
|
7
7
|
import httpx
|
|
8
8
|
from httpx import HTTPStatusError
|
|
9
|
+
from langchain_core.callbacks.manager import adispatch_custom_event
|
|
9
10
|
from langchain_core.tools import BaseTool
|
|
10
11
|
from mcp import ClientSession
|
|
11
12
|
from mcp.types import (
|
|
@@ -210,6 +211,24 @@ class MCPToolProvider:
|
|
|
210
211
|
logger.info(
|
|
211
212
|
f"MCP Tool Progress - Server: {context.server_name}, Progress: {progress}, Total: {total}, Message: {message}"
|
|
212
213
|
)
|
|
214
|
+
try:
|
|
215
|
+
await adispatch_custom_event(
|
|
216
|
+
"mcp_task_progress",
|
|
217
|
+
{
|
|
218
|
+
"task_id": "",
|
|
219
|
+
"status": f"{progress:g}/{total:g}" if total else f"{progress:g}",
|
|
220
|
+
"message": message,
|
|
221
|
+
"server_name": context.server_name,
|
|
222
|
+
"tool_name": context.tool_name,
|
|
223
|
+
},
|
|
224
|
+
)
|
|
225
|
+
except RuntimeError as e:
|
|
226
|
+
logger.debug(
|
|
227
|
+
"Skipping mcp_task_progress event dispatch: %s (server=%s, tool=%s)",
|
|
228
|
+
e,
|
|
229
|
+
context.server_name,
|
|
230
|
+
context.tool_name,
|
|
231
|
+
)
|
|
213
232
|
|
|
214
233
|
def _build_connection_config(self, tool_config: AgentConfig) -> MCPConnectionConfig:
|
|
215
234
|
"""Build an MCPConnectionConfig from an AgentConfig."""
|
|
@@ -285,6 +304,7 @@ class MCPToolProvider:
|
|
|
285
304
|
],
|
|
286
305
|
server_name=tool_config.name,
|
|
287
306
|
tool_list_cache=self.tool_list_cache,
|
|
307
|
+
heartbeat_interval_seconds=self.environment_variables.mcp_tool_heartbeat_interval_seconds,
|
|
288
308
|
)
|
|
289
309
|
tools.append(langchain_tool)
|
|
290
310
|
|
|
@@ -451,6 +471,7 @@ class MCPToolProvider:
|
|
|
451
471
|
server_name=tool_config.name,
|
|
452
472
|
session_pool=session_pool,
|
|
453
473
|
tool_list_cache=self.tool_list_cache,
|
|
474
|
+
heartbeat_interval_seconds=self.environment_variables.mcp_tool_heartbeat_interval_seconds,
|
|
454
475
|
)
|
|
455
476
|
for mcp_tool in mcp_tools
|
|
456
477
|
]
|
|
@@ -1010,6 +1031,7 @@ class MCPToolProvider:
|
|
|
1010
1031
|
],
|
|
1011
1032
|
session_pool=session_pool,
|
|
1012
1033
|
tool_list_cache=self.tool_list_cache,
|
|
1034
|
+
heartbeat_interval_seconds=self.environment_variables.mcp_tool_heartbeat_interval_seconds,
|
|
1013
1035
|
)
|
|
1014
1036
|
|
|
1015
1037
|
async def fetch_mcp_app_embed(
|
|
@@ -152,7 +152,7 @@ class ModelFactory:
|
|
|
152
152
|
return model_name.startswith(("anthropic.", "us.anthropic."))
|
|
153
153
|
|
|
154
154
|
@staticmethod
|
|
155
|
-
def _resolve_anthropic_bedrock_max_tokens(model_name: str) -> int | None:
|
|
155
|
+
def _resolve_anthropic_bedrock_max_tokens(*, model_name: str) -> int | None:
|
|
156
156
|
"""Look up the real max_output_tokens for a Bedrock Anthropic model ID.
|
|
157
157
|
|
|
158
158
|
``ChatAnthropicBedrock`` inherits ``ChatAnthropic.set_default_max_tokens``,
|
|
@@ -174,6 +174,11 @@ class ModelFactory:
|
|
|
174
174
|
_get_default_model_profile,
|
|
175
175
|
)
|
|
176
176
|
except ImportError:
|
|
177
|
+
logger.warning(
|
|
178
|
+
"langchain_aws._get_default_model_profile unavailable; "
|
|
179
|
+
"falling back to unset max_tokens for %s",
|
|
180
|
+
model_name,
|
|
181
|
+
)
|
|
177
182
|
return None
|
|
178
183
|
profile = _get_default_model_profile(model_name)
|
|
179
184
|
max_output_tokens = profile.get("max_output_tokens")
|
|
@@ -192,8 +197,12 @@ class ModelFactory:
|
|
|
192
197
|
from pydantic import SecretStr
|
|
193
198
|
import boto3
|
|
194
199
|
|
|
200
|
+
model_parameters_dict = dict(model_parameters_dict)
|
|
201
|
+
|
|
195
202
|
if "max_tokens" not in model_parameters_dict:
|
|
196
|
-
resolved_max_tokens = self._resolve_anthropic_bedrock_max_tokens(
|
|
203
|
+
resolved_max_tokens = self._resolve_anthropic_bedrock_max_tokens(
|
|
204
|
+
model_name=model_name
|
|
205
|
+
)
|
|
197
206
|
if resolved_max_tokens is not None:
|
|
198
207
|
model_parameters_dict["max_tokens"] = resolved_max_tokens
|
|
199
208
|
logger.info(
|
|
@@ -67,6 +67,7 @@ class ChatCompletionApiRequestWrapper(ChatRequestWrapper):
|
|
|
67
67
|
enable_debug_logging: bool,
|
|
68
68
|
environment_variables: "LanguageModelCommonEnvironmentVariables",
|
|
69
69
|
emit_task_progress: bool | None = None,
|
|
70
|
+
emit_tool_heartbeat: bool | None = None,
|
|
70
71
|
) -> None:
|
|
71
72
|
"""
|
|
72
73
|
Wraps an OpenAI /chat/completions request to provide a consistent interface for different request types.
|
|
@@ -84,6 +85,11 @@ class ChatCompletionApiRequestWrapper(ChatRequestWrapper):
|
|
|
84
85
|
if emit_task_progress is not None
|
|
85
86
|
else environment_variables.emit_task_progress_in_chat_completions
|
|
86
87
|
)
|
|
88
|
+
self._emit_tool_heartbeat: bool = (
|
|
89
|
+
emit_tool_heartbeat
|
|
90
|
+
if emit_tool_heartbeat is not None
|
|
91
|
+
else environment_variables.emit_tool_heartbeat_in_chat_completions
|
|
92
|
+
)
|
|
87
93
|
self._debug_prefixes = environment_variables.debug_prefixes
|
|
88
94
|
self._apply_debug_prefix_toggle()
|
|
89
95
|
|
|
@@ -278,6 +284,23 @@ class ChatCompletionApiRequestWrapper(ChatRequestWrapper):
|
|
|
278
284
|
source="task_progress",
|
|
279
285
|
)
|
|
280
286
|
|
|
287
|
+
@override
|
|
288
|
+
def create_tool_heartbeat_sse_event(
|
|
289
|
+
self,
|
|
290
|
+
*,
|
|
291
|
+
request_id: str,
|
|
292
|
+
tool_name: str,
|
|
293
|
+
elapsed_seconds: float,
|
|
294
|
+
) -> str | None:
|
|
295
|
+
if not self._emit_tool_heartbeat:
|
|
296
|
+
return None
|
|
297
|
+
return self.create_sse_message(
|
|
298
|
+
request_id=request_id,
|
|
299
|
+
content=f"\n[Still running {tool_name}... ({elapsed_seconds:.0f}s)]\n",
|
|
300
|
+
usage_metadata=None,
|
|
301
|
+
source="tool_heartbeat",
|
|
302
|
+
)
|
|
303
|
+
|
|
281
304
|
@override
|
|
282
305
|
def create_non_streaming_response(
|
|
283
306
|
self,
|
|
@@ -272,6 +272,21 @@ class ChatRequestWrapper(abc.ABC):
|
|
|
272
272
|
"""
|
|
273
273
|
return None
|
|
274
274
|
|
|
275
|
+
def create_tool_heartbeat_sse_event(
|
|
276
|
+
self,
|
|
277
|
+
*,
|
|
278
|
+
request_id: str,
|
|
279
|
+
tool_name: str,
|
|
280
|
+
elapsed_seconds: float,
|
|
281
|
+
) -> str | None:
|
|
282
|
+
"""Emit a synthetic heartbeat while a tool call is in flight without
|
|
283
|
+
reporting real progress.
|
|
284
|
+
|
|
285
|
+
The default implementation returns None (no-op). Subclasses override
|
|
286
|
+
to emit the event in their respective SSE formats.
|
|
287
|
+
"""
|
|
288
|
+
return None
|
|
289
|
+
|
|
275
290
|
def create_tool_start_sse_event(
|
|
276
291
|
self,
|
|
277
292
|
*,
|
|
@@ -294,9 +309,17 @@ class ChatRequestWrapper(abc.ABC):
|
|
|
294
309
|
tool_name: str,
|
|
295
310
|
tool_input: dict[str, Any] | None,
|
|
296
311
|
runtime_seconds: float | None,
|
|
312
|
+
output: str | None = None,
|
|
313
|
+
is_error: bool = False,
|
|
297
314
|
) -> str | None:
|
|
298
315
|
"""Emit an SSE event when a tool finishes execution.
|
|
299
316
|
|
|
317
|
+
``output`` is the tool's own result/error text (already converted to a
|
|
318
|
+
plain string and truncated by the caller) and ``is_error`` reflects the
|
|
319
|
+
underlying ToolMessage's status, so consumers can distinguish a tool
|
|
320
|
+
that ran to completion from one that failed without having to parse
|
|
321
|
+
free-text content.
|
|
322
|
+
|
|
300
323
|
The default implementation returns None (no-op). Subclasses that
|
|
301
324
|
support structured tool events (e.g. Responses API) override this
|
|
302
325
|
to emit a typed SSE frame the client can use for status updates.
|
|
@@ -338,8 +338,16 @@ class ResponsesApiRequestWrapper(ChatRequestWrapper):
|
|
|
338
338
|
tool_name: str,
|
|
339
339
|
tool_input: Dict[str, Any] | None,
|
|
340
340
|
runtime_seconds: float | None,
|
|
341
|
+
output: str | None = None,
|
|
342
|
+
is_error: bool = False,
|
|
341
343
|
) -> str | None:
|
|
342
|
-
"""Emit a ``response.output_item.done`` event with a ``function_call`` item.
|
|
344
|
+
"""Emit a ``response.output_item.done`` event with a ``function_call`` item.
|
|
345
|
+
|
|
346
|
+
Includes ``runtime_seconds`` and the tool's own ``output``/``is_error``
|
|
347
|
+
so consuming UIs (e.g. baileyai-skills-service's Stream events panel)
|
|
348
|
+
can show how long a call took and surface failures, rather than only
|
|
349
|
+
ever seeing a "completed" status with no result.
|
|
350
|
+
"""
|
|
343
351
|
event: Dict[str, Any] = {
|
|
344
352
|
"type": "response.output_item.done",
|
|
345
353
|
"output_index": 0,
|
|
@@ -350,7 +358,10 @@ class ResponsesApiRequestWrapper(ChatRequestWrapper):
|
|
|
350
358
|
"call_id": f"call_{request_id}_{tool_name}",
|
|
351
359
|
"name": tool_name,
|
|
352
360
|
"arguments": json.dumps(tool_input) if tool_input else "",
|
|
353
|
-
"status": "completed",
|
|
361
|
+
"status": "failed" if is_error else "completed",
|
|
362
|
+
"runtime_seconds": runtime_seconds,
|
|
363
|
+
"output": output or "",
|
|
364
|
+
"is_error": is_error,
|
|
354
365
|
},
|
|
355
366
|
}
|
|
356
367
|
return f"data: {json.dumps(event)}\n\n"
|
|
@@ -373,6 +384,29 @@ class ResponsesApiRequestWrapper(ChatRequestWrapper):
|
|
|
373
384
|
}
|
|
374
385
|
return f"data: {json.dumps(event)}\n\n"
|
|
375
386
|
|
|
387
|
+
@override
|
|
388
|
+
def create_tool_heartbeat_sse_event(
|
|
389
|
+
self,
|
|
390
|
+
*,
|
|
391
|
+
request_id: str,
|
|
392
|
+
tool_name: str,
|
|
393
|
+
elapsed_seconds: float,
|
|
394
|
+
) -> str | None:
|
|
395
|
+
"""Emit a synthetic heartbeat as a ``task.progress`` SSE event.
|
|
396
|
+
|
|
397
|
+
Reuses the exact same wire format as create_task_progress_sse_event
|
|
398
|
+
so existing consumers (e.g. baileyai-skills-service's frontend, which
|
|
399
|
+
already treats any `type: "task.progress"` payload as a non-content
|
|
400
|
+
trace event) require no changes.
|
|
401
|
+
"""
|
|
402
|
+
event: Dict[str, Any] = {
|
|
403
|
+
"type": "task.progress",
|
|
404
|
+
"task_id": "",
|
|
405
|
+
"status": "in_progress",
|
|
406
|
+
"message": f"Still running {tool_name}... ({elapsed_seconds:.0f}s)",
|
|
407
|
+
}
|
|
408
|
+
return f"data: {json.dumps(event)}\n\n"
|
|
409
|
+
|
|
376
410
|
@staticmethod
|
|
377
411
|
def _convert_usage_to_response_usage(
|
|
378
412
|
usages: list[UsageMetadata],
|
|
@@ -98,7 +98,7 @@ class CallToolTool(BaseTool):
|
|
|
98
98
|
if entry is None:
|
|
99
99
|
return (
|
|
100
100
|
f"Tool '{name}' not found. Use search_tools to find available tools.",
|
|
101
|
-
|
|
101
|
+
{"is_error": True},
|
|
102
102
|
)
|
|
103
103
|
|
|
104
104
|
try:
|
|
@@ -122,9 +122,16 @@ class CallToolTool(BaseTool):
|
|
|
122
122
|
session_token=self.session_token,
|
|
123
123
|
)
|
|
124
124
|
|
|
125
|
+
# Surface the *inner* tool's own error status (result.isError), not just
|
|
126
|
+
# transport-level failures — CallToolTool itself never raises for a
|
|
127
|
+
# rejected/invalid call, so this is the only place that signal exists.
|
|
125
128
|
artifact: dict[str, Any] | None = None
|
|
126
|
-
if app_embed is not None:
|
|
127
|
-
artifact = {
|
|
129
|
+
if app_embed is not None or result.isError:
|
|
130
|
+
artifact = {}
|
|
131
|
+
if app_embed is not None:
|
|
132
|
+
artifact["mcp_app_embed"] = app_embed
|
|
133
|
+
if result.isError:
|
|
134
|
+
artifact["is_error"] = True
|
|
128
135
|
|
|
129
136
|
return text, artifact
|
|
130
137
|
except AuthorizationNeededException:
|
|
@@ -138,4 +145,4 @@ class CallToolTool(BaseTool):
|
|
|
138
145
|
entry.server_name,
|
|
139
146
|
error_detail,
|
|
140
147
|
)
|
|
141
|
-
return f"Error calling tool '{name}': {error_detail}",
|
|
148
|
+
return f"Error calling tool '{name}': {error_detail}", {"is_error": True}
|
|
@@ -17,6 +17,7 @@ logger = logging.getLogger(__name__)
|
|
|
17
17
|
DEFAULT_STREAMING_BUFFER_FLUSH_INTERVAL_SECONDS = 0.05
|
|
18
18
|
DEFAULT_LANGGRAPH_MAX_CONCURRENCY = 4
|
|
19
19
|
DEFAULT_LANGGRAPH_RECURSION_LIMIT = 100
|
|
20
|
+
DEFAULT_MCP_TOOL_HEARTBEAT_INTERVAL_SECONDS = 15.0
|
|
20
21
|
|
|
21
22
|
# Default generic error message when not exposing technical details
|
|
22
23
|
DEFAULT_GENERIC_ERROR_MESSAGE = (
|
|
@@ -289,6 +290,34 @@ class LanguageModelCommonEnvironmentVariables(
|
|
|
289
290
|
"""Timeout in seconds for tool calls."""
|
|
290
291
|
return int(os.environ.get("TOOL_CALL_TIMEOUT_SECONDS", "600"))
|
|
291
292
|
|
|
293
|
+
@property
|
|
294
|
+
def mcp_tool_heartbeat_interval_seconds(self) -> float:
|
|
295
|
+
"""Interval in seconds between synthetic heartbeat events emitted
|
|
296
|
+
while an MCP tool call is in flight without reporting progress."""
|
|
297
|
+
value = os.environ.get("MCP_TOOL_HEARTBEAT_INTERVAL_SECONDS")
|
|
298
|
+
if value is None:
|
|
299
|
+
return DEFAULT_MCP_TOOL_HEARTBEAT_INTERVAL_SECONDS
|
|
300
|
+
try:
|
|
301
|
+
parsed = float(value)
|
|
302
|
+
return max(1.0, parsed)
|
|
303
|
+
except ValueError:
|
|
304
|
+
logger.warning(
|
|
305
|
+
"Invalid MCP_TOOL_HEARTBEAT_INTERVAL_SECONDS value '%s'; using default=%s",
|
|
306
|
+
value,
|
|
307
|
+
DEFAULT_MCP_TOOL_HEARTBEAT_INTERVAL_SECONDS,
|
|
308
|
+
)
|
|
309
|
+
return DEFAULT_MCP_TOOL_HEARTBEAT_INTERVAL_SECONDS
|
|
310
|
+
|
|
311
|
+
@property
|
|
312
|
+
def emit_tool_heartbeat_in_chat_completions(self) -> bool:
|
|
313
|
+
"""When True, synthetic MCP tool heartbeats are emitted as content
|
|
314
|
+
deltas in the Chat Completions streaming format. Separate from
|
|
315
|
+
emit_task_progress_in_chat_completions so enabling one does not
|
|
316
|
+
change the volume/behavior of the other."""
|
|
317
|
+
return self.str2bool(
|
|
318
|
+
os.environ.get("EMIT_TOOL_HEARTBEAT_IN_CHAT_COMPLETIONS", "false")
|
|
319
|
+
)
|
|
320
|
+
|
|
292
321
|
@property
|
|
293
322
|
def app_login_uri(self) -> str:
|
|
294
323
|
value = os.environ.get("APP_LOGIN_URI")
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
2.0.91
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/__init__.py
RENAMED
|
File without changes
|
{language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/auth/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{language_model_common-2.0.91 → language_model_common-2.0.92}/languagemodelcommon/aws/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|