language-model-common 4.0.0__tar.gz → 4.2.0__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-4.0.0/language_model_common.egg-info → language_model_common-4.2.0}/PKG-INFO +3 -1
- language_model_common-4.2.0/VERSION +1 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0/language_model_common.egg-info}/PKG-INFO +3 -1
- {language_model_common-4.0.0 → language_model_common-4.2.0}/language_model_common.egg-info/SOURCES.txt +4 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/language_model_common.egg-info/requires.txt +2 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/configs/config_reader/config_reader.py +382 -7
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/configs/config_reader/github_directory_downloader.py +44 -20
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/configs/config_reader/github_directory_helper.py +106 -12
- language_model_common-4.2.0/languagemodelcommon/configs/config_reader/override_config_cache.py +191 -0
- language_model_common-4.2.0/languagemodelcommon/configs/config_reader/override_read_result.py +29 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/container/container_factory.py +40 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/converters/tool_event_handlers.py +62 -12
- language_model_common-4.2.0/languagemodelcommon/mcp/mcp_client/cached_output_validation.py +267 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/mcp/mcp_client/langchain_adapter.py +6 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/mcp/mcp_client/tool_invocation.py +41 -0
- language_model_common-4.2.0/languagemodelcommon/utilities/cache/model_config_cache_store.py +165 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/utilities/environment/language_model_common_environment_variables.py +73 -0
- language_model_common-4.2.0/languagemodelcommon/utilities/learned_title_store.py +161 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/utilities/tool_display_name_mapper.py +110 -4
- {language_model_common-4.0.0 → language_model_common-4.2.0}/pyproject.toml +2 -0
- language_model_common-4.0.0/VERSION +0 -1
- language_model_common-4.0.0/languagemodelcommon/utilities/cache/model_config_cache_store.py +0 -75
- {language_model_common-4.0.0 → language_model_common-4.2.0}/LICENSE +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/MANIFEST.in +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/README.md +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/language_model_common.egg-info/dependency_links.txt +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/language_model_common.egg-info/top_level.txt +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/__init__.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/auth/__init__.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/auth/authenticator/__init__.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/auth/exceptions/__init__.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/auth/models/__init__.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/aws/__init__.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/aws/aws_client_factory.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/configs/__init__.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/configs/config_reader/__init__.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/configs/config_reader/file_config_reader.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/configs/config_reader/mcp_json_fetcher.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/configs/config_reader/mcp_json_reader.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/configs/config_reader/s3_config_reader.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/configs/prompt_library/__init__.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/configs/prompt_library/prompt_library_environment_variables.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/configs/prompt_library/prompt_library_manager.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/configs/prompt_library/prompt_store.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/configs/schemas/__init__.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/configs/schemas/config_schema.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/configs/schemas/mcp_json_schema.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/container/__init__.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/context/__init__.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/context/request_context.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/converters/__init__.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/converters/history_cache_middleware.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/converters/langgraph_to_openai_converter.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/converters/stream_buffer.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/converters/stream_context_mixin.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/converters/stream_debug_output_manager.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/converters/streaming_formatters.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/converters/streaming_manager.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/converters/streaming_tool_node.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/exceptions/__init__.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/exceptions/bailey_exception.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/exceptions/rate_limit_exception.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/file_managers/__init__.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/file_managers/aws_s3_file_manager.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/file_managers/file_manager.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/file_managers/file_manager_factory.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/file_managers/file_writer.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/file_managers/local_file_manager.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/github/__init__.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/github/token_provider.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/graph/__init__.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/graph/graph_builder.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/history/__init__.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/history/context_compactor.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/history/conversation_history_manager.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/history/smart_history_manager.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/http/__init__.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/http/http_client_factory.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/image_generation/__init__.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/image_generation/aws_image_generator.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/image_generation/image_generator.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/image_generation/image_generator_factory.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/image_generation/managers/__init__.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/image_generation/managers/image_generation_manager.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/image_generation/openai_image_generator.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/image_generation/providers/__init__.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/image_generation/providers/base_image_generation_provider.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/image_generation/providers/image_generation_provider.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/markdown/__init__.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/markdown/html_to_markdown_converter.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/mcp/__init__.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/mcp/auth/__init__.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/mcp/callbacks.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/mcp/exceptions/__init__.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/mcp/interceptors/__init__.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/mcp/interceptors/redaction.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/mcp/interceptors/tracing.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/mcp/interceptors/truncation.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/mcp/interceptors/types.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/mcp/mcp_client/__init__.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/mcp/mcp_client/content_conversion.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/mcp/mcp_client/mcp_tool_list_store.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/mcp/mcp_client/negotiation_mode.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/mcp/mcp_client/server_card_discovery.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/mcp/mcp_client/session.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/mcp/mcp_client/session_pool.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/mcp/mcp_client/tool_list_cache.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/mocks/__init__.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/mocks/mock_aws_client_factory.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/mocks/mock_http_client_factory.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/mocks/mock_image_generator.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/mocks/mock_image_generator_factory.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/models/__init__.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/models/bedrock_models.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/models/model_factory.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/models/sanitizing_bedrock_converse.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/ocr/__init__.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/ocr/aws_ocr_extractor.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/ocr/ocr_extractor.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/ocr/ocr_extractor_factory.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/persistence/__init__.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/persistence/persistence_factory.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/pipeline/__init__.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/pipeline/context.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/pipeline/output/__init__.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/pipeline/pipeline.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/pipeline/step.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/pipeline/steps/__init__.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/py.typed +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/schema/__init__.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/schema/openai/__init__.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/schema/openai/completions.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/schema/openai/image_generation.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/schema/openai/responses.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/state/__init__.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/state/messages_state.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/structures/__init__.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/structures/openai/__init__.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/structures/openai/message/__init__.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/structures/openai/message/chat_completion_api_message_wrapper.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/structures/openai/message/chat_message_wrapper.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/structures/openai/message/responses_api_message_wrapper.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/structures/openai/request/__init__.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/structures/openai/request/chat_completion_api_request_wrapper.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/structures/openai/request/chat_request_wrapper.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/structures/openai/request/responses_api_request_wrapper.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/tools/__init__.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/tools/mcp/__init__.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/utilities/__init__.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/utilities/cache/__init__.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/utilities/chat_message_helpers.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/utilities/config_substitution.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/utilities/environment/__init__.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/utilities/header_reader/__init__.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/utilities/header_reader/header_reader.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/utilities/logger/__init__.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/utilities/logger/debug_configuration.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/utilities/logger/exception_formatter.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/utilities/logger/exception_logger.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/utilities/logger/log_levels.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/utilities/logger/logging_response.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/utilities/logger/logging_transport.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/utilities/logger/summarization.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/utilities/message_preprocessing/__init__.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/utilities/message_preprocessing/composite_message_preprocessor.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/utilities/message_preprocessing/message_preprocessor.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/utilities/mongo_url_utils.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/utilities/openai/__init__.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/utilities/openai/responses_api_converter.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/utilities/ref_hash.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/utilities/request_information.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/utilities/s3_url.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/utilities/security/__init__.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/utilities/security/normalize.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/utilities/security/off_topic_detector.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/utilities/security/prompt_extraction_detector.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/utilities/security/prompt_sanitizer.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/utilities/security/url_validation.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/utilities/slash_command/__init__.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/utilities/slash_command/handlers/__init__.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/utilities/slash_command/handlers/debug_command_handler.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/utilities/slash_command/handlers/skill_command_handler.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/utilities/slash_command/slash_command_handler.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/utilities/slash_command/slash_command_parser.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/utilities/slash_command/slash_command_processor.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/utilities/text_humanizer.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/utilities/token_reducer/__init__.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/utilities/token_reducer/token_reducer.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/languagemodelcommon/utilities/url_parser.py +0 -0
- {language_model_common-4.0.0 → language_model_common-4.2.0}/setup.cfg +0 -0
{language_model_common-4.0.0/language_model_common.egg-info → language_model_common-4.2.0}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: language-model-common
|
|
3
|
-
Version: 4.
|
|
3
|
+
Version: 4.2.0
|
|
4
4
|
Summary: Provides the underlying framework to enhance langchain and add loading configurations
|
|
5
5
|
Author-email: Imran Qureshi <imran.qureshi@bwell.com>
|
|
6
6
|
License: Apache-2.0
|
|
@@ -22,6 +22,8 @@ Requires-Dist: langchain-openai>=1.1.6
|
|
|
22
22
|
Requires-Dist: langgraph>=1.0.0
|
|
23
23
|
Requires-Dist: pydantic<3.0.0,>=2.0
|
|
24
24
|
Requires-Dist: mcp>=2.3.0
|
|
25
|
+
Requires-Dist: jsonschema>=4.26.0
|
|
26
|
+
Requires-Dist: referencing>=0.37.0
|
|
25
27
|
Requires-Dist: langgraph-checkpoint>=3.0.0
|
|
26
28
|
Requires-Dist: langgraph-checkpoint-mongodb>=0.3.1
|
|
27
29
|
Requires-Dist: langgraph-store-mongodb>=0.1.0
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
4.2.0
|
{language_model_common-4.0.0 → language_model_common-4.2.0/language_model_common.egg-info}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: language-model-common
|
|
3
|
-
Version: 4.
|
|
3
|
+
Version: 4.2.0
|
|
4
4
|
Summary: Provides the underlying framework to enhance langchain and add loading configurations
|
|
5
5
|
Author-email: Imran Qureshi <imran.qureshi@bwell.com>
|
|
6
6
|
License: Apache-2.0
|
|
@@ -22,6 +22,8 @@ Requires-Dist: langchain-openai>=1.1.6
|
|
|
22
22
|
Requires-Dist: langgraph>=1.0.0
|
|
23
23
|
Requires-Dist: pydantic<3.0.0,>=2.0
|
|
24
24
|
Requires-Dist: mcp>=2.3.0
|
|
25
|
+
Requires-Dist: jsonschema>=4.26.0
|
|
26
|
+
Requires-Dist: referencing>=0.37.0
|
|
25
27
|
Requires-Dist: langgraph-checkpoint>=3.0.0
|
|
26
28
|
Requires-Dist: langgraph-checkpoint-mongodb>=0.3.1
|
|
27
29
|
Requires-Dist: langgraph-store-mongodb>=0.1.0
|
|
@@ -24,6 +24,8 @@ languagemodelcommon/configs/config_reader/github_directory_downloader.py
|
|
|
24
24
|
languagemodelcommon/configs/config_reader/github_directory_helper.py
|
|
25
25
|
languagemodelcommon/configs/config_reader/mcp_json_fetcher.py
|
|
26
26
|
languagemodelcommon/configs/config_reader/mcp_json_reader.py
|
|
27
|
+
languagemodelcommon/configs/config_reader/override_config_cache.py
|
|
28
|
+
languagemodelcommon/configs/config_reader/override_read_result.py
|
|
27
29
|
languagemodelcommon/configs/config_reader/s3_config_reader.py
|
|
28
30
|
languagemodelcommon/configs/prompt_library/__init__.py
|
|
29
31
|
languagemodelcommon/configs/prompt_library/prompt_library_environment_variables.py
|
|
@@ -87,6 +89,7 @@ languagemodelcommon/mcp/interceptors/tracing.py
|
|
|
87
89
|
languagemodelcommon/mcp/interceptors/truncation.py
|
|
88
90
|
languagemodelcommon/mcp/interceptors/types.py
|
|
89
91
|
languagemodelcommon/mcp/mcp_client/__init__.py
|
|
92
|
+
languagemodelcommon/mcp/mcp_client/cached_output_validation.py
|
|
90
93
|
languagemodelcommon/mcp/mcp_client/content_conversion.py
|
|
91
94
|
languagemodelcommon/mcp/mcp_client/langchain_adapter.py
|
|
92
95
|
languagemodelcommon/mcp/mcp_client/mcp_tool_list_store.py
|
|
@@ -139,6 +142,7 @@ languagemodelcommon/tools/mcp/__init__.py
|
|
|
139
142
|
languagemodelcommon/utilities/__init__.py
|
|
140
143
|
languagemodelcommon/utilities/chat_message_helpers.py
|
|
141
144
|
languagemodelcommon/utilities/config_substitution.py
|
|
145
|
+
languagemodelcommon/utilities/learned_title_store.py
|
|
142
146
|
languagemodelcommon/utilities/mongo_url_utils.py
|
|
143
147
|
languagemodelcommon/utilities/ref_hash.py
|
|
144
148
|
languagemodelcommon/utilities/request_information.py
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import asyncio
|
|
2
2
|
import logging
|
|
3
|
+
import re
|
|
4
|
+
import time
|
|
3
5
|
|
|
6
|
+
from hashlib import sha256
|
|
4
7
|
from pathlib import Path
|
|
5
8
|
from typing import Any, List, Optional, cast
|
|
6
9
|
from uuid import UUID, uuid4
|
|
@@ -11,7 +14,17 @@ from languagemodelcommon.configs.config_reader.file_config_reader import (
|
|
|
11
14
|
from languagemodelcommon.configs.config_reader.github_directory_helper import (
|
|
12
15
|
GitHubDirectoryHelper,
|
|
13
16
|
)
|
|
17
|
+
from languagemodelcommon.configs.config_reader.override_config_cache import (
|
|
18
|
+
CachedOverride,
|
|
19
|
+
ClientOverrideConfigCache,
|
|
20
|
+
)
|
|
21
|
+
from languagemodelcommon.configs.config_reader.override_read_result import (
|
|
22
|
+
DownloadLockHeldError,
|
|
23
|
+
OverrideOutcome,
|
|
24
|
+
OverrideReadResult,
|
|
25
|
+
)
|
|
14
26
|
from languagemodelcommon.configs.config_reader.s3_config_reader import S3ConfigReader
|
|
27
|
+
from oidcauthlib.cache_gate.advisory_lock import AdvisoryLock
|
|
15
28
|
from languagemodelcommon.configs.schemas.config_schema import (
|
|
16
29
|
ChatModelConfig,
|
|
17
30
|
PromptConfig,
|
|
@@ -34,6 +47,13 @@ from languagemodelcommon.configs.config_reader.mcp_json_reader import (
|
|
|
34
47
|
logger = logging.getLogger(__name__)
|
|
35
48
|
logger.setLevel(SRC_LOG_LEVELS.CONFIG)
|
|
36
49
|
|
|
50
|
+
_MAX_CLIENT_ID_LENGTH = 64
|
|
51
|
+
_COLD_MISS_TIMEOUT_SECONDS = 180.0
|
|
52
|
+
_OVERRIDE_PEER_POLL_INTERVAL_SECONDS = 0.05
|
|
53
|
+
_OVERRIDE_PEER_POLL_TIMEOUT_SECONDS = 3.0
|
|
54
|
+
_OVERRIDE_REFRESH_LOCK_TTL_SECONDS = 60
|
|
55
|
+
_OVERRIDE_REFRESH_RETRY_AFTER_SECONDS = 15.0
|
|
56
|
+
|
|
37
57
|
|
|
38
58
|
class ConfigReader:
|
|
39
59
|
SCHEMA_VERSION = 1
|
|
@@ -48,6 +68,7 @@ class ConfigReader:
|
|
|
48
68
|
model_config_cache_store: BaseStore | None = None,
|
|
49
69
|
file_config_reader: FileConfigReader | None = None,
|
|
50
70
|
s3_config_reader: S3ConfigReader | None = None,
|
|
71
|
+
override_config_cache: ClientOverrideConfigCache | None = None,
|
|
51
72
|
) -> None:
|
|
52
73
|
self._identifier: UUID = uuid4()
|
|
53
74
|
self._lock: asyncio.Lock = asyncio.Lock()
|
|
@@ -65,6 +86,13 @@ class ConfigReader:
|
|
|
65
86
|
self._model_config_cache_collection = (
|
|
66
87
|
self._environment_variables.model_config_cache_collection_name
|
|
67
88
|
)
|
|
89
|
+
self._override_config_cache = override_config_cache
|
|
90
|
+
# In-flight override refreshes by cache key, so concurrent requests in
|
|
91
|
+
# this process share one refresh. Holds tasks only, removed on completion;
|
|
92
|
+
# it is not a cache of config data.
|
|
93
|
+
self._inflight_override_refreshes: dict[
|
|
94
|
+
str, asyncio.Task[OverrideReadResult]
|
|
95
|
+
] = {}
|
|
68
96
|
|
|
69
97
|
async def read_model_configs_async(
|
|
70
98
|
self, *, client_id: str | None = None
|
|
@@ -316,12 +344,76 @@ class ConfigReader:
|
|
|
316
344
|
)
|
|
317
345
|
if override_path is None:
|
|
318
346
|
return []
|
|
347
|
+
cache = self._override_config_cache
|
|
348
|
+
if (
|
|
349
|
+
cache is None
|
|
350
|
+
or not self._environment_variables.model_config_override_cache_enabled
|
|
351
|
+
or not self._is_remote_config_path(config_path=config_path)
|
|
352
|
+
or not self._is_cacheable_client_id(client_id=client_id)
|
|
353
|
+
):
|
|
354
|
+
# Flag off, no store, local path, or an id that must not become a
|
|
355
|
+
# cache key: read live exactly as before the cache existed.
|
|
356
|
+
result = await self._fetch_override_result(
|
|
357
|
+
override_path=override_path, confirm_absent=False
|
|
358
|
+
)
|
|
359
|
+
return list(result.models)
|
|
360
|
+
return await self._read_override_models_cached(
|
|
361
|
+
cache=cache,
|
|
362
|
+
config_path=config_path,
|
|
363
|
+
override_path=override_path,
|
|
364
|
+
client_id=client_id,
|
|
365
|
+
)
|
|
366
|
+
|
|
367
|
+
@staticmethod
|
|
368
|
+
def _is_cacheable_client_id(*, client_id: str) -> bool:
|
|
369
|
+
"""Stricter than ``_is_valid_client_id``: applies only where a cache key
|
|
370
|
+
is built, so flag-off behaviour for existing ids is unchanged."""
|
|
371
|
+
return len(client_id) <= _MAX_CLIENT_ID_LENGTH and bool(
|
|
372
|
+
re.fullmatch(r"[a-zA-Z0-9_-]+", client_id)
|
|
373
|
+
)
|
|
374
|
+
|
|
375
|
+
@staticmethod
|
|
376
|
+
def _is_remote_config_path(*, config_path: str) -> bool:
|
|
377
|
+
return GitHubDirectoryHelper.is_github_path(
|
|
378
|
+
config_path
|
|
379
|
+
) or config_path.startswith("s3")
|
|
380
|
+
|
|
381
|
+
async def _fetch_override_result(
|
|
382
|
+
self, *, override_path: str, confirm_absent: bool
|
|
383
|
+
) -> OverrideReadResult:
|
|
384
|
+
"""Read one override from its source and classify the outcome.
|
|
385
|
+
|
|
386
|
+
A bare ``FileNotFoundError`` is ambiguous (fsspec raises it for any
|
|
387
|
+
non-2xx reply, e.g. a bad token or rate limit). With ``confirm_absent``
|
|
388
|
+
it is classified ``ABSENT`` only after GitHub positively shows the
|
|
389
|
+
directory is missing; otherwise ``FAILED``.
|
|
390
|
+
"""
|
|
319
391
|
try:
|
|
320
|
-
|
|
321
|
-
config_path=override_path,
|
|
392
|
+
models = await self.read_models_from_path_async(
|
|
393
|
+
config_path=override_path,
|
|
394
|
+
log_diagnostics_on_not_found=False,
|
|
395
|
+
# Strict only on the cached path; flag-off keeps the pre-cache
|
|
396
|
+
# fallback to the pod-local copy when the lock is held.
|
|
397
|
+
raise_on_lock_held=confirm_absent,
|
|
322
398
|
)
|
|
323
|
-
except
|
|
324
|
-
|
|
399
|
+
except DownloadLockHeldError:
|
|
400
|
+
logger.info(
|
|
401
|
+
"ConfigReader with id: %s override download lock held for %s",
|
|
402
|
+
self._identifier,
|
|
403
|
+
override_path,
|
|
404
|
+
)
|
|
405
|
+
return OverrideReadResult(outcome=OverrideOutcome.LOCK_HELD)
|
|
406
|
+
except FileNotFoundError:
|
|
407
|
+
logger.debug(
|
|
408
|
+
"No client overrides found at %s (path does not exist)", override_path
|
|
409
|
+
)
|
|
410
|
+
if confirm_absent and not await self._confirm_override_absent(
|
|
411
|
+
override_path=override_path
|
|
412
|
+
):
|
|
413
|
+
return OverrideReadResult(outcome=OverrideOutcome.FAILED)
|
|
414
|
+
return OverrideReadResult(outcome=OverrideOutcome.ABSENT)
|
|
415
|
+
except ValueError as e:
|
|
416
|
+
if "FileNotFoundError" in str(e):
|
|
325
417
|
logger.debug(
|
|
326
418
|
"No client overrides found at %s (path does not exist)",
|
|
327
419
|
override_path,
|
|
@@ -330,12 +422,285 @@ class ConfigReader:
|
|
|
330
422
|
logger.warning(
|
|
331
423
|
"Failed to load client overrides from %s: %s", override_path, e
|
|
332
424
|
)
|
|
333
|
-
return
|
|
425
|
+
return OverrideReadResult(outcome=OverrideOutcome.FAILED)
|
|
334
426
|
except Exception as e:
|
|
335
427
|
logger.warning(
|
|
336
428
|
"Failed to load client overrides from %s: %s", override_path, e
|
|
337
429
|
)
|
|
430
|
+
return OverrideReadResult(outcome=OverrideOutcome.FAILED)
|
|
431
|
+
return OverrideReadResult(
|
|
432
|
+
outcome=OverrideOutcome.FOUND,
|
|
433
|
+
models=tuple(models),
|
|
434
|
+
has_unresolved_mcp=self._has_unresolved_mcp_servers(models),
|
|
435
|
+
)
|
|
436
|
+
|
|
437
|
+
async def _confirm_override_absent(self, *, override_path: str) -> bool:
|
|
438
|
+
if not GitHubDirectoryHelper.is_github_path(override_path):
|
|
439
|
+
return False
|
|
440
|
+
return await self._github_directory_helper.confirm_directory_absent(
|
|
441
|
+
github_uri=GitHubDirectoryHelper.to_github_uri(override_path)
|
|
442
|
+
)
|
|
443
|
+
|
|
444
|
+
async def _read_override_models_cached(
|
|
445
|
+
self,
|
|
446
|
+
*,
|
|
447
|
+
cache: ClientOverrideConfigCache,
|
|
448
|
+
config_path: str,
|
|
449
|
+
override_path: str,
|
|
450
|
+
client_id: str,
|
|
451
|
+
) -> List[ChatModelConfig]:
|
|
452
|
+
"""Read-through, stale-while-revalidate read of one client's override."""
|
|
453
|
+
started = time.monotonic()
|
|
454
|
+
cached = await self._safe_override_cache_get(
|
|
455
|
+
cache=cache, config_path=config_path, client_id=client_id
|
|
456
|
+
)
|
|
457
|
+
if cached is not None:
|
|
458
|
+
if cached.is_fresh:
|
|
459
|
+
outcome = (
|
|
460
|
+
"hit"
|
|
461
|
+
if cached.result.outcome == OverrideOutcome.FOUND
|
|
462
|
+
else "absent_hit"
|
|
463
|
+
)
|
|
464
|
+
else:
|
|
465
|
+
outcome = "stale"
|
|
466
|
+
self._start_override_refresh(
|
|
467
|
+
cache=cache,
|
|
468
|
+
config_path=config_path,
|
|
469
|
+
override_path=override_path,
|
|
470
|
+
client_id=client_id,
|
|
471
|
+
stale=cached,
|
|
472
|
+
)
|
|
473
|
+
self._log_override_read(
|
|
474
|
+
outcome=outcome,
|
|
475
|
+
client_id=client_id,
|
|
476
|
+
age=cached.age_seconds,
|
|
477
|
+
started=started,
|
|
478
|
+
)
|
|
479
|
+
return list(cached.result.models)
|
|
480
|
+
|
|
481
|
+
task = self._start_override_refresh(
|
|
482
|
+
cache=cache,
|
|
483
|
+
config_path=config_path,
|
|
484
|
+
override_path=override_path,
|
|
485
|
+
client_id=client_id,
|
|
486
|
+
stale=None,
|
|
487
|
+
)
|
|
488
|
+
# shield: a cancelled request must not cancel a refresh others share.
|
|
489
|
+
try:
|
|
490
|
+
result = await asyncio.wait_for(
|
|
491
|
+
asyncio.shield(task), timeout=_COLD_MISS_TIMEOUT_SECONDS
|
|
492
|
+
)
|
|
493
|
+
except asyncio.TimeoutError:
|
|
494
|
+
logger.warning(
|
|
495
|
+
"ConfigReader with id: %s override refresh for client %s exceeded %.0fs; "
|
|
496
|
+
"serving base config for this request",
|
|
497
|
+
self._identifier,
|
|
498
|
+
client_id,
|
|
499
|
+
_COLD_MISS_TIMEOUT_SECONDS,
|
|
500
|
+
)
|
|
338
501
|
return []
|
|
502
|
+
self._log_override_read(
|
|
503
|
+
outcome="miss", client_id=client_id, age=0.0, started=started
|
|
504
|
+
)
|
|
505
|
+
return list(result.models)
|
|
506
|
+
|
|
507
|
+
def _start_override_refresh(
|
|
508
|
+
self,
|
|
509
|
+
*,
|
|
510
|
+
cache: ClientOverrideConfigCache,
|
|
511
|
+
config_path: str,
|
|
512
|
+
override_path: str,
|
|
513
|
+
client_id: str,
|
|
514
|
+
stale: CachedOverride | None,
|
|
515
|
+
) -> "asyncio.Task[OverrideReadResult]":
|
|
516
|
+
key = cache.build_key(config_path=config_path, client_id=client_id)
|
|
517
|
+
existing = self._inflight_override_refreshes.get(key)
|
|
518
|
+
if existing is not None:
|
|
519
|
+
return existing
|
|
520
|
+
task = asyncio.ensure_future(
|
|
521
|
+
self._refresh_override(
|
|
522
|
+
cache=cache,
|
|
523
|
+
key=key,
|
|
524
|
+
config_path=config_path,
|
|
525
|
+
override_path=override_path,
|
|
526
|
+
client_id=client_id,
|
|
527
|
+
stale=stale,
|
|
528
|
+
)
|
|
529
|
+
)
|
|
530
|
+
self._inflight_override_refreshes[key] = task
|
|
531
|
+
task.add_done_callback(
|
|
532
|
+
lambda _t: self._inflight_override_refreshes.pop(key, None)
|
|
533
|
+
)
|
|
534
|
+
return task
|
|
535
|
+
|
|
536
|
+
async def _refresh_override(
|
|
537
|
+
self,
|
|
538
|
+
*,
|
|
539
|
+
cache: ClientOverrideConfigCache,
|
|
540
|
+
key: str,
|
|
541
|
+
config_path: str,
|
|
542
|
+
override_path: str,
|
|
543
|
+
client_id: str,
|
|
544
|
+
stale: CachedOverride | None,
|
|
545
|
+
) -> OverrideReadResult:
|
|
546
|
+
"""Refresh one override under a cross-worker advisory lock.
|
|
547
|
+
|
|
548
|
+
Never raises: a failure keeps stale data (if any) and returns the
|
|
549
|
+
classified result. LOCK_HELD and FAILED are never written to the cache.
|
|
550
|
+
"""
|
|
551
|
+
try:
|
|
552
|
+
lock_name = (
|
|
553
|
+
f"override_refresh:{sha256(key.encode('utf-8')).hexdigest()[:16]}"
|
|
554
|
+
)
|
|
555
|
+
async with AdvisoryLock(
|
|
556
|
+
cache.store,
|
|
557
|
+
lock_name,
|
|
558
|
+
ttl_seconds=_OVERRIDE_REFRESH_LOCK_TTL_SECONDS,
|
|
559
|
+
) as acquired:
|
|
560
|
+
if not acquired:
|
|
561
|
+
# Another worker is refreshing. Serve stale if we have it;
|
|
562
|
+
# else wait (bounded) for its row. There is no "fetch
|
|
563
|
+
# directly" bypass: the downloader has its own lock, so it
|
|
564
|
+
# would only return lock-held anyway. On timeout, return
|
|
565
|
+
# LOCK_HELD (no override for this request, nothing cached),
|
|
566
|
+
# the same outcome the uncached path gives.
|
|
567
|
+
if stale is not None:
|
|
568
|
+
return stale.result
|
|
569
|
+
peer = await self._await_peer_override(
|
|
570
|
+
cache=cache, config_path=config_path, client_id=client_id
|
|
571
|
+
)
|
|
572
|
+
if peer is not None:
|
|
573
|
+
return peer.result
|
|
574
|
+
return OverrideReadResult(outcome=OverrideOutcome.LOCK_HELD)
|
|
575
|
+
return await self._fetch_and_store_override(
|
|
576
|
+
cache=cache,
|
|
577
|
+
config_path=config_path,
|
|
578
|
+
override_path=override_path,
|
|
579
|
+
client_id=client_id,
|
|
580
|
+
stale=stale,
|
|
581
|
+
)
|
|
582
|
+
except Exception as e:
|
|
583
|
+
logger.warning(
|
|
584
|
+
"override_refresh unexpected error for client %s: [%s] %s",
|
|
585
|
+
client_id,
|
|
586
|
+
type(e).__name__,
|
|
587
|
+
e,
|
|
588
|
+
)
|
|
589
|
+
if stale is not None:
|
|
590
|
+
return stale.result
|
|
591
|
+
return OverrideReadResult(outcome=OverrideOutcome.FAILED)
|
|
592
|
+
|
|
593
|
+
async def _await_peer_override(
|
|
594
|
+
self,
|
|
595
|
+
*,
|
|
596
|
+
cache: ClientOverrideConfigCache,
|
|
597
|
+
config_path: str,
|
|
598
|
+
client_id: str,
|
|
599
|
+
) -> CachedOverride | None:
|
|
600
|
+
"""Poll briefly for the lock holder's row; None if it never appears."""
|
|
601
|
+
deadline = time.monotonic() + _OVERRIDE_PEER_POLL_TIMEOUT_SECONDS
|
|
602
|
+
while time.monotonic() < deadline:
|
|
603
|
+
await asyncio.sleep(_OVERRIDE_PEER_POLL_INTERVAL_SECONDS)
|
|
604
|
+
peer = await self._safe_override_cache_get(
|
|
605
|
+
cache=cache, config_path=config_path, client_id=client_id
|
|
606
|
+
)
|
|
607
|
+
if peer is not None:
|
|
608
|
+
return peer
|
|
609
|
+
return None
|
|
610
|
+
|
|
611
|
+
async def _fetch_and_store_override(
|
|
612
|
+
self,
|
|
613
|
+
*,
|
|
614
|
+
cache: ClientOverrideConfigCache,
|
|
615
|
+
config_path: str,
|
|
616
|
+
override_path: str,
|
|
617
|
+
client_id: str,
|
|
618
|
+
stale: CachedOverride | None,
|
|
619
|
+
) -> OverrideReadResult:
|
|
620
|
+
started = time.monotonic()
|
|
621
|
+
result = await self._fetch_override_result(
|
|
622
|
+
override_path=override_path, confirm_absent=True
|
|
623
|
+
)
|
|
624
|
+
duration_ms = int((time.monotonic() - started) * 1000)
|
|
625
|
+
served_stale = False
|
|
626
|
+
try:
|
|
627
|
+
if result.outcome in (OverrideOutcome.FOUND, OverrideOutcome.ABSENT):
|
|
628
|
+
await cache.put(
|
|
629
|
+
config_path=config_path, client_id=client_id, result=result
|
|
630
|
+
)
|
|
631
|
+
elif stale is not None:
|
|
632
|
+
served_stale = True
|
|
633
|
+
await cache.defer_refresh(
|
|
634
|
+
config_path=config_path,
|
|
635
|
+
client_id=client_id,
|
|
636
|
+
cached=stale,
|
|
637
|
+
retry_after_seconds=_OVERRIDE_REFRESH_RETRY_AFTER_SECONDS,
|
|
638
|
+
)
|
|
639
|
+
except Exception as e:
|
|
640
|
+
logger.warning(
|
|
641
|
+
"override_cache write failed for client %s: [%s] %s",
|
|
642
|
+
client_id,
|
|
643
|
+
type(e).__name__,
|
|
644
|
+
e,
|
|
645
|
+
)
|
|
646
|
+
logger.info(
|
|
647
|
+
"override_refresh result=%s download_ms=%d served_stale=%s client_id=%s",
|
|
648
|
+
result.outcome.value,
|
|
649
|
+
duration_ms,
|
|
650
|
+
served_stale,
|
|
651
|
+
client_id,
|
|
652
|
+
)
|
|
653
|
+
if stale is not None and result.outcome in (
|
|
654
|
+
OverrideOutcome.FAILED,
|
|
655
|
+
OverrideOutcome.LOCK_HELD,
|
|
656
|
+
):
|
|
657
|
+
return stale.result
|
|
658
|
+
return result
|
|
659
|
+
|
|
660
|
+
async def _safe_override_cache_get(
|
|
661
|
+
self,
|
|
662
|
+
*,
|
|
663
|
+
cache: ClientOverrideConfigCache,
|
|
664
|
+
config_path: str,
|
|
665
|
+
client_id: str,
|
|
666
|
+
) -> CachedOverride | None:
|
|
667
|
+
"""Cache read that degrades to a live read if the store errors.
|
|
668
|
+
|
|
669
|
+
The override cache is an optimisation layer, so a store or row
|
|
670
|
+
deserialisation error is logged and treated as a miss (unlike the base
|
|
671
|
+
model-config cache, which fails fast).
|
|
672
|
+
"""
|
|
673
|
+
try:
|
|
674
|
+
return await cache.get(config_path=config_path, client_id=client_id)
|
|
675
|
+
except Exception as e:
|
|
676
|
+
logger.warning(
|
|
677
|
+
"override_cache read failed for client %s, using live read: [%s] %s",
|
|
678
|
+
client_id,
|
|
679
|
+
type(e).__name__,
|
|
680
|
+
e,
|
|
681
|
+
)
|
|
682
|
+
return None
|
|
683
|
+
|
|
684
|
+
def _log_override_read(
|
|
685
|
+
self, *, outcome: str, client_id: str, age: float, started: float
|
|
686
|
+
) -> None:
|
|
687
|
+
logger.info(
|
|
688
|
+
"override_cache outcome=%s age_s=%.1f duration_ms=%d client_id=%s",
|
|
689
|
+
outcome,
|
|
690
|
+
age,
|
|
691
|
+
int((time.monotonic() - started) * 1000),
|
|
692
|
+
client_id,
|
|
693
|
+
)
|
|
694
|
+
|
|
695
|
+
async def clear_client_override(self, *, client_id: str) -> None:
|
|
696
|
+
"""Drop one client's cached override so the next request re-reads it."""
|
|
697
|
+
cache = self._override_config_cache
|
|
698
|
+
if cache is None or not self._is_valid_client_id(client_id):
|
|
699
|
+
return
|
|
700
|
+
await cache.delete(
|
|
701
|
+
config_path=self._environment_variables.models_official_path,
|
|
702
|
+
client_id=client_id,
|
|
703
|
+
)
|
|
339
704
|
|
|
340
705
|
async def read_models_from_path_async(
|
|
341
706
|
self,
|
|
@@ -343,8 +708,13 @@ class ConfigReader:
|
|
|
343
708
|
config_path: str,
|
|
344
709
|
exclude_dirs: set[str] | None = None,
|
|
345
710
|
log_diagnostics_on_not_found: bool = True,
|
|
711
|
+
raise_on_lock_held: bool = False,
|
|
346
712
|
) -> List[ChatModelConfig]:
|
|
347
713
|
"""
|
|
714
|
+
raise_on_lock_held (BAI-1380): when the GitHub download lock is held by
|
|
715
|
+
another worker, raise ``DownloadLockHeldError`` instead of returning
|
|
716
|
+
``[]``, so callers can tell "nothing read" from "no models".
|
|
717
|
+
|
|
348
718
|
log_diagnostics_on_not_found (BAI-941): pass ``False`` when a missing
|
|
349
719
|
``config_path`` is an expected, benign outcome for this caller (e.g.
|
|
350
720
|
an optional per-client override) rather than a genuine failure worth
|
|
@@ -363,8 +733,11 @@ class ConfigReader:
|
|
|
363
733
|
resolved = await self._github_directory_helper.resolve_github_path(
|
|
364
734
|
config_path,
|
|
365
735
|
log_diagnostics_on_not_found=log_diagnostics_on_not_found,
|
|
736
|
+
raise_on_lock_held=raise_on_lock_held,
|
|
366
737
|
)
|
|
367
738
|
if resolved is None:
|
|
739
|
+
if raise_on_lock_held:
|
|
740
|
+
raise DownloadLockHeldError(config_path)
|
|
368
741
|
logger.info(
|
|
369
742
|
"ConfigReader with id: %s download lock held for %s — using cache",
|
|
370
743
|
self._identifier,
|
|
@@ -372,7 +745,8 @@ class ConfigReader:
|
|
|
372
745
|
)
|
|
373
746
|
return []
|
|
374
747
|
local_config_path = str(resolved)
|
|
375
|
-
models =
|
|
748
|
+
models = await asyncio.to_thread(
|
|
749
|
+
self._file_config_reader.read_model_configs,
|
|
376
750
|
config_path=local_config_path,
|
|
377
751
|
exclude_dirs=exclude_dirs,
|
|
378
752
|
)
|
|
@@ -382,7 +756,8 @@ class ConfigReader:
|
|
|
382
756
|
len(models),
|
|
383
757
|
)
|
|
384
758
|
else:
|
|
385
|
-
models =
|
|
759
|
+
models = await asyncio.to_thread(
|
|
760
|
+
self._file_config_reader.read_model_configs,
|
|
386
761
|
config_path=config_path,
|
|
387
762
|
exclude_dirs=exclude_dirs,
|
|
388
763
|
)
|